CSharp:WPF Soluzioni varie
From Aino Wiki
Interfaccia
ViewModel
URL di esempi:
- Da stackoverflow How do I set a ViewModel on a window in XAML using DataContext property?
- A Practical Quick-start Tutorial on MVVM in WPF
- Un esempio di WPF: dotnetcampania
Aggiunta manuale per il Binding
Supponendo di voler aggiungere manualmente ad una vista un ViewModel, ci occorrerà definire una classe di ViewModel e associarlo al DataContext della vista via programma.
Il modello possiamo supporre provenga dall'App.Config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <appSettings> <add key="StartDirectory_SourcePath" value="D:\Documenti\FOX_New Zeland\Development" /> <add key="EndDirectory_DestinationPath" value="D:\Backup\xxx" /> <add key="DefaultFilePatternToSearch" value="APPOINTMENT.cs" /> <add key="FoxClientLogPath_Debug" value="D:\Documenti\FOX_New Zeland\Development\FOX.Dashboard\bin\Debug\log\FoxDashboard.log" /> </appSettings> </configuration>
L'interfaccia alla nostra vista sarà il view model con la seguente Classe
namespace SimplyFileBackup.ViewModels { using System.Configuration; using System.Windows.Input; public class MainViewModel : ViewModelBase { #region Properties public string AppCfg_StartDirectory_SourcePath { get { return ConfigurationManager.AppSettings["StartDirectory_SourcePath"]; } } public string AppCfg_EndDirectory_DestinationPath { get { return ConfigurationManager.AppSettings["EndDirectory_DestinationPath"]; } } public string AppCfg_DefaultFilePatternToSearch { get { return ConfigurationManager.AppSettings["DefaultFilePatternToSearch"]; } } public static string AppCfg_FoxClientLogPath_Debug { get { return ConfigurationManager.AppSettings["FoxClientLogPath_Debug"]; } } #endregion } }
La classe Vista da cui eredita sarà:
using System.ComponentModel; namespace SimplyFileBackup.ViewModels { /// <summary> /// Provides common functionality for ViewModel classes /// </summary> public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } } }
Nel code behind della vista nel suo costruttore basterà assegnargli il DataContext voluto
// Per il DataBinding this.DataContext = new ViewModels.MainViewModel();
La vista sarà così composta nei punti salienti dello XAML:
<TextBox x:Name="TxtSourcePath" Grid.Column="1" Grid.Row="0" Text="{Binding Path=AppCfg_StartDirectory_SourcePath, Mode=OneWay}" Width="Auto" Margin="10,15,0,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" IsEnabled="True"/>
Aggiunta statica per ogni vista
ToDo
Binding
Tra ComboBox dipendenti
Ci sono due comboBox in funzione della scelta fatta su quella di sinistra quella di destra si caricherà di conseguenza.
Contenuto della comboBox di sinistra:
Caso 1, selezionando "Form", la combo di destra è così popolata:
Caso 2, selezionando "Questionnaire", la combo di destra è così popolata:
Esempio 1
La comboBox di sinistra ha il binding (ItemsSource nello XAML) mediante una Property che è una Lista di oggetti, la selezione (SelectedItem nello XAML) effettuata verrà riportata in un'altra property dedicata, allo stesso modo quella di destra. Nonappena viene fatta una selezione a sinistra la property con la selezione si riempie e verrà popolata la property del binding della comboBox di destra.
ATTENZIONE la property del binding della combo di destra è popolata assegnandovi un NUOVO oggetto e non è popolata direttamente! (questo è indipensabile per il binding dinamico pena il funzionamento al solo primo caricamento!)
XAML
La vista inizierà con le seguenti direttive:
<UserControl x:Class="Fox.Module.Customer.SubModule.FormsAndQuestionnaires" ...etc xmlns:FOX_Module_Questionnaires_ViewModel="clr-namespace:Fox.Module.Customer.ViewModel" ...etc x:Name="UserControl">
E conterrà anche le seguenti due definizioni di ComboBox ComboBox di sinsitra:
<telerik:RadComboBox x:Name="cboQuestionnairType" Grid.Column="1" Margin="10,0,0,0" Width="200" HorizontalAlignment="Left" Style="{StaticResource cboStandard}" Cursor="Hand" ClearSelectionButtonVisibility="Visible" ItemsSource="{Binding QuestionnaireGroupTypes}" SelectedItem="{Binding SelectedQuestGroupType, Mode=TwoWay, UpdateSourceTrigger='PropertyChanged'}" DisplayMemberPath="Description" > </telerik:RadComboBox>
ComboBox di destra:
<telerik:RadComboBox x:Name="cboDocumentType" Grid.Column="3" Margin="15,0,0,0" Width="200" HorizontalAlignment="Left" Style="{StaticResource cboStandard}" Cursor="Hand" ClearSelectionButtonVisibility="Visible" IsEnabled="{Binding Path=IsMandatoryDocumentType, Converter={StaticResource BooleanToVisibility}, ConverterParameter='1', UpdateSourceTrigger='PropertyChanged'}" ItemsSource="{Binding QuestionnaireTypes}" SelectedItem="{Binding SelectedQuestType, Mode=TwoWay, UpdateSourceTrigger='PropertyChanged'}" DisplayMemberPath="Description" > </telerik:RadComboBox>
Code beihnd
Nella Classe del ViewModel "QuestionnaireViewModel", a cui è associata la vista (XAML riportato nel paragrafo precedente), abbiamo il seguente codice ceh gestisce il Binding:
namespace Fox.Module.Customer.ViewModel { public class QuestionnaireViewModel : ViewModelBase { // ------------------------- ComboBox di Sinistra ---------------------- private List<QuestionnaireTemplateModel> _QuestionnaireGroupTypes; public List<QuestionnaireTemplateModel> QuestionnaireGroupTypes { get { return _QuestionnaireGroupTypes; } set { if (_QuestionnaireGroupTypes != value) { _QuestionnaireGroupTypes = value; OnPropertyChanged(() => this.QuestionnaireGroupTypes); } } } private QuestionnaireTemplateModel _SelectedQuestGroupType = null; public QuestionnaireTemplateModel SelectedQuestGroupType { get { return _SelectedQuestGroupType; } set { if (_SelectedQuestGroupType != value) { IsSave = true; SelectedQuestType = null; QuestionnaireTypes = null; switch (value.Code) { case "GPR": case "ARL": IsMandatoryDocumentType = false; // Used to show the * and to enable the control "cboDocumentType" break; case "FRM": case "QST": IsMandatoryDocumentType = true; // Used to show the * and to enable the control "cboDocumentType" QuestionnaireTemplateModel qtm = new QuestionnaireTemplateModel(); List<QuestionnaireTemplateModel> __QuestionnaireTypes = new List<QuestionnaireTemplateModel>(); foreach (CU_S_QUESTIONNAIRE_TEMPLATEDto q in m_Cache_LstQuestionnaireTemplate) { if (q.TEMPLATE_GROUP_CODE.Equals(value.Code)) { qtm = new QuestionnaireTemplateModel() { Code = q.TEMPLATE_CODE, Description = q.TEMPLATE_DESCR }; __QuestionnaireTypes.Add(qtm); } } QuestionnaireTypes = __QuestionnaireTypes; break; default: break; } _SelectedQuestGroupType = value; OnPropertyChanged(() => this.SelectedQuestGroupType); } } } // ------------------------- ComboBox di Destra ---------------------- private List<QuestionnaireTemplateModel> _QuestionnaireTypes; public List<QuestionnaireTemplateModel> QuestionnaireTypes { get { return _QuestionnaireTypes; } set { if (_QuestionnaireTypes != value) { _QuestionnaireTypes = value; OnPropertyChanged(() => this.QuestionnaireTypes); } } } private QuestionnaireTemplateModel _SelectedQuestType = null; public QuestionnaireTemplateModel SelectedQuestType { get { return _SelectedQuestType; } set { if (_SelectedQuestType != value) { _SelectedQuestType = value; VisibilityOfGrid(); IsWorking = true; LoadAnswersQuestionnaires(); IsWorking = false; } } } // etc } }
Esempio 2
La comboBox di sinistra ha il binding (ItemsSource nello XAML) mediante una Property che è una Lista di oggetti, la selezione (SelectedItem nello XAML) effettuata verrà riportata in un'altra property dedicata, allo stesso modo quella di destra. Nonappena viene fatta una selezione a sinistra la property con la selezione si riempie e verrà popolata la property del binding della comboBox di destra.
XAML
La vista inizierà con le seguenti direttive:
<UserControl x:Class="FOX.Module.NewCustomer.views.RegProfilo" .. etc xmlns:local="clr-namespace:FOX.Module.NewCustomer.BindingObjects" .. etc >
E conterrà anche le seguenti due definizioni di ComboBox ComboBox di sinsitra:
<telerik:RadComboBox Grid.Row="1" Grid.Column="2" Style="{StaticResource cboStandard}" Cursor="Hand" Width="178" HorizontalAlignment="Left" ItemsSource="{Binding sourcelist}" IsEnabled="{Binding isInsertMode,Converter={StaticResource BooleanConverter}}" Margin="6,10,0,0" SelectedValuePath="ENTITY_CODE" TextSearch.TextPath="ENTITY_DESCR" TextSearchMode="Contains" IsTextSearchEnabled="True" CanAutocompleteSelectItems="True" IsReadOnly="False" IsEditable="True" IsFilteringEnabled="True"> <telerik:RadComboBox.SelectedValue> <Binding Path="SOURCE_CODE" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"> <Binding.ValidationRules> <CustomerValidators:RequiredCustomerValidator Controlname="{l:LocalizeByModule DefaultValue='Source', Key=txbsourcelist}"> <CustomerValidators:RequiredCustomerValidator.CustomerType> <CustomerValidators:CustomerType DataContext="{Binding Source={StaticResource DataContextBridge}, Path=DataContext}" CustomerTypeCode="{Binding isSourceRequired}" /> </CustomerValidators:RequiredCustomerValidator.CustomerType> <CustomerValidators:RequiredCustomerValidator.ValidationParam> <CustomerValidators:Param DataContext="{Binding Source={StaticResource DataContextBridge}, Path=DataContext}" Key="SOURCE_CODE" Value="{Binding SOURCE_CODE}" /> </CustomerValidators:RequiredCustomerValidator.ValidationParam> </CustomerValidators:RequiredCustomerValidator> </Binding.ValidationRules> </Binding> </telerik:RadComboBox.SelectedValue> <telerik:RadComboBox.ItemTemplate> <DataTemplate> <TextBlock> <TextBlock.Text> <Binding Path="ENTITY_DESCR" /> </TextBlock.Text> </TextBlock> </DataTemplate> </telerik:RadComboBox.ItemTemplate> </telerik:RadComboBox>
ComboBox di destra:
<telerik:RadComboBox Grid.Row="1" Grid.Column="6" IsEnabled="{Binding isInsertMode,Converter={StaticResource BooleanConverter}}" Style="{StaticResource cboStandard}" Cursor="Hand" Margin="5,10,0,0" ItemsSource="{Binding subsourcelist}" SelectedValuePath="ENTITY_CODE" Width="178" HorizontalAlignment="Left" TextSearch.TextPath="ENTITY_DESCR" TextSearchMode="Contains" IsTextSearchEnabled="True" CanAutocompleteSelectItems="True" IsReadOnly="False" IsEditable="True" IsFilteringEnabled="True"> <telerik:RadComboBox.SelectedValue> <Binding Path="SUB_SOURCE_CODE" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"> <Binding.ValidationRules> <CustomerValidators:RequiredCustomerValidator Controlname="{l:LocalizeByModule DefaultValue='SubSource', Key=txbsubsourcelist}"> <CustomerValidators:RequiredCustomerValidator.CustomerType> <CustomerValidators:CustomerType DataContext="{Binding Source={StaticResource DataContextBridge}, Path=DataContext}" CustomerTypeCode="{Binding isSubSourceRequired}" /> </CustomerValidators:RequiredCustomerValidator.CustomerType> <CustomerValidators:RequiredCustomerValidator.ValidationParam> <CustomerValidators:Param DataContext="{Binding Source={StaticResource DataContextBridge}, Path=DataContext}" Key="SUB_SOURCE_CODE" Value="{Binding SUB_SOURCE_CODE}" /> </CustomerValidators:RequiredCustomerValidator.ValidationParam> </CustomerValidators:RequiredCustomerValidator> </Binding.ValidationRules> </Binding> </telerik:RadComboBox.SelectedValue> <telerik:RadComboBox.ItemTemplate> <DataTemplate> <TextBlock> <TextBlock.Text> <Binding Path="ENTITY_DESCR" /> </TextBlock.Text> </TextBlock> </DataTemplate> </telerik:RadComboBox.ItemTemplate> </telerik:RadComboBox>
Code beihnd
Nella Classe del ViewModel "QuestionnaireViewModel", a cui è associata la vista (XAML riportato nel paragrafo precedente), abbiamo il seguente codice ceh gestisce il Binding:
namespace FOX.Module.NewCustomer.BindingObjects { public class WizardBinding : INotifyPropertyChanged, ICloneable { // ------------------ Prima ComboBox ------------------ private List<CM_S_REFERRAL_HIERARCHY_EXT_NZLDto> _sourcelist; public List<CM_S_REFERRAL_HIERARCHY_EXT_NZLDto> sourcelist { get { _sourcelist = ReferralHierachyCache.GetList(UserLogin.User.CurrentUser, false). Where(x => x.COMPANY_CODE == UserLogin.User.CurrentUser.COMPANY_CODE && x.DIVISION_CODE == UserLogin.User.CurrentUser.DIVISION_CODE && x.ENTITY_TYPE == "SOU").ToList(); return _sourcelist; } } private string _SOURCE_CODE; public string SOURCE_CODE { get { return _SOURCE_CODE; } set { if (_SOURCE_CODE != value) { _SOURCE_CODE = value; OnPropertyChanged("SOURCE_CODE"); OnPropertyChanged("subsourcelist"); SUB_SOURCE_CODE = null; OnPropertyChanged("referrallist"); REFERRAL_CODE = null; MEDIATYPE_CODE = null; MEDIATYPE_DESCRIZ = null; OnPropertyChanged("MEDIATYPE_DESCRIZ"); } } } // ------------------ Seconda ComboBox ------------------ private string _SUB_SOURCE_CODE; public string SUB_SOURCE_CODE { get { return _SUB_SOURCE_CODE; } set { if (_SUB_SOURCE_CODE != value) { _SUB_SOURCE_CODE = value; OnPropertyChanged("SUB_SOURCE_CODE"); // referrallist.Clear(); OnPropertyChanged("referrallist"); if (isInsertMode) { if (_SUB_SOURCE_CODE!=null) MEDIATYPE_CODE = subsourcelist.Where(X => X.ENTITY_CODE == _SUB_SOURCE_CODE).FirstOrDefault().MEDIATYPE_CODE; OnPropertyChanged("MEDIATYPE_DESCRIZ"); } } } } private List<CM_S_REFERRAL_HIERARCHY_EXT_NZLDto> _subsourcelist; public List<CM_S_REFERRAL_HIERARCHY_EXT_NZLDto> subsourcelist { get { _subsourcelist = ReferralHierachyCache.GetList(UserLogin.User.CurrentUser, false). Where(x => x.COMPANY_CODE == UserLogin.User.CurrentUser.COMPANY_CODE && x.DIVISION_CODE == UserLogin.User.CurrentUser.DIVISION_CODE && x.ENTITY_TYPE == "SUB" && x.FATHER_CODE == SOURCE_CODE && x.FATHER_TYPE == "SOU").ToList(); return _subsourcelist; } } } }
UserControl
Validators
Campo obbligatorio
Esempio 1
Caso del semplice validatore di campo mandatorio\obbligatorio.
Porzione dello XAML associato ad un controllo da validare:
<UserControl x:Class="NomeModulo_XAML" ... xmlns:CustomValidators="clr-namespace:Fox.Module.Customer.Validations" ...> <TextBlock Name="lblMandatoryQuestionnaireType" Grid.Column="0" Text="*" VerticalAlignment="Bottom" Margin="70,0,3,2" ToolTip="{l:LocalizeByModule DefaultValue='Mandatory field', Key='lblMandatoryQuestionnaireType'}" /> <telerik:RadComboBox x:Name="cboQuestionnairType" Grid.Column="1" Margin="10,0,0,0" Width="200" HorizontalAlignment="Left" Style="{StaticResource cboStandard}" Cursor="Hand" ClearSelectionButtonVisibility="Visible" ItemsSource="{Binding QuestionnaireGroupTypes}" DisplayMemberPath="Description" > <telerik:RadComboBox.SelectedItem> <Binding Path="SelectedQuestGroupType" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"> <Binding.ValidationRules> <CustomValidators:RequiredValidator ControlName="{l:LocalizeByModule DefaultValue='Source', Key=cboQuestionnairType}"> </CustomValidators:RequiredValidator> </Binding.ValidationRules> </Binding> </telerik:RadComboBox.SelectedItem> </telerik:RadComboBox>Il codice code behind rispecchia la seguente struttura:
La classe del validatore dovrà ereditare dalla classe "ValidationRule"
using System; using System.Collections.Generic; using System.Globalization; using System.Windows.Controls; namespace Fox.Module.Customer.Validations { public class RequiredValidator : ValidationRule { #region Properties /// <summary> /// Control name to validate /// </summary> public string ControlName { get; set; } private List<Param> _inputParamsListToCheck = new List<Param>(); public List<Param> InputParamsListToCheck { get { return _inputParamsListToCheck; } set { _inputParamsListToCheck = value; } } private Param _inputVvalidationRule; public Param InputValidationRule { get { return _inputVvalidationRule; } set { _inputVvalidationRule = value; } } #endregion public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo cultureInfo) { bool isValid = true; if (InputValidationRule != null) { isValid = CheckRule(InputValidationRule.Key, value); } else { if (value == null || string.IsNullOrWhiteSpace(value.ToString()) || value.ToString() == "False") { isValid = false; } else { isValid = true; } } if (isValid == false) { string message1 = FOX.Localization.LocalizationManager.GetProvider(GetType().FullName).GetString("RequiredValue", "is a mandatory field"); //string message2 = FOX.Localization.LocalizationManager.GetProvider(GetType().FullName).GetString("RequiredValue2", "is mandatory"); return new System.Windows.Controls.ValidationResult(false, string.Format("'{0}' '{1}'.", message1, ControlName)); } return new System.Windows.Controls.ValidationResult(true, null); } #region Custom private methods private bool CheckRule(string rule, object value) { switch (rule) { case "ONE_OPTION_IS_CHECKED": return FindParamValue(InputParamsListToCheck, true); case "ONE_CHECKBOX_IS_CHECKED": return FindParamValue(InputParamsListToCheck); case "VALIDATE_IF_NOT_CHECKED": return ChekParamValue(InputParamsListToCheck, "FLAG_TO_CHECK", "False,N", value); case "VALIDATE_IF_CHECKED": return ChekParamValue(InputParamsListToCheck, "FLAG_TO_CHECK", "True,Y", value); } return true; } private bool FindParam(List<Param> myList, string valueToFind) { Param result = myList.Find( delegate (Param myObj) { return (myObj.Key == valueToFind); } ); if (result != null) { return true; } else { return false; } } private bool FindParamValue(List<Param> myList) { Param result = myList.Find(delegate (Param myObj) { return (!String.IsNullOrEmpty(myObj.Value)); } ); if (result != null) { return true; } else { return false; } } private bool FindParamValue(List<Param> myList, object valueToFind) { Param result = myList.Find( delegate (Param myObj) { return (myObj.Value == valueToFind.ToString() || myObj.Value != null); } ); if (result != null) { return true; } else { return false; } } private bool ChekParamValue(List<Param> myList, string Key, object valueToFind, object value) { Param result = myList.Find( delegate (Param myObj) { if (myObj.Value == null) return false; return (myObj.Key == Key && valueToFind.ToString().Contains(myObj.Value)); } ); if (result != null) { if (value == null || string.IsNullOrEmpty((string)value)) { return false; } else { return true; } } else { return true; } } #endregion } }
Pre finire l'esempio segue la classe usata per passare i parametri:
using System.Windows; namespace Fox.Module.Customer.Validations { public class Param : FrameworkElement { public static readonly DependencyProperty ParamPropFieldKey = DependencyProperty.Register("Key", typeof(string), typeof(Param), new UIPropertyMetadata(string.Empty)); public string Key { get { return (string)GetValue(ParamPropFieldKey); } set { SetValue(ParamPropFieldKey, value); } } public static readonly DependencyProperty ParamPropValue = DependencyProperty.Register("Value", typeof(string), typeof(Param), new UIPropertyMetadata(string.Empty)); public string Value { get { return (string)GetValue(ParamPropValue); } set { SetValue(ParamPropValue, value); } } } }
Regular Expression
Il seguente validatore verifica che si sia inserito un numero decimale con tre cifre nella parte intera e al più due cifre decimali.
XAML della vista:
<TextBox Name="txbLeftdBDb" FontWeight="Bold" Margin="5,0" Width="50" IsEnabled="{Binding IsClinicalEditable}" Style="{StaticResource txtStandard}" MaxLength="6" Height="23" VerticalAlignment="Top"> <TextBox.Text> <Binding Path="ClaimInfo.PTA1_L" StringFormat="N2" NotifyOnSourceUpdated="True" Mode="TwoWay"> <Binding.ValidationRules> <CustomValidators:RegExValidator ControlName="txbLeftdBDb" RegExPattern="^\d{0,3}(\.\d{0,2})?$" ErrorMessage="{l:LocalizeByModule DefaultValue='Value not allowed', Key=ErrValueNotallowd}"> </CustomValidators:RegExValidator> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
Validatore dal code Behind:
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Controls; namespace FOX.Module.ClaimsInformation.Apac.Validations { public class RegExValidator : ValidationRule { #region Properties public string ControlName { get; set; } public string RegExPattern { get; set; } public string ErrorMessage { get; set; } #endregion public override System.Windows.Controls.ValidationResult Validate(object value, CultureInfo cultureInfo) { Regex regEx = new Regex(RegExPattern); if (String.IsNullOrWhiteSpace(ErrorMessage)) { ErrorMessage = string.Format("Value not allowed"); } if (regEx.IsMatch((string)value)) { return ValidationResult.ValidResult; } else { return new ValidationResult(false, ErrorMessage); } } } }
Mappa e Link
Parole chiave: