Press "Enter" to skip to content

Un Editor di tipo ComboBox Generato dinamicamente a Runtime

Vediamo come abbiamo modificato lo User Control che abbiamo implementato nel post Generazione di componenti da codice WPF per permettere di generare un Editor di tipo ComboBox implementando la possibilità di modificare dei parametri di configurazione di tipo enumerato.

Nel post precedente abbiamo già introdotto una modifica sostanziale a questo User Control determinata dalla verifica di possibili futuri problemi per implementare la logica applicativa. Abbiamo tolto a questo controllo le funzionalità di caricamento e salvataggio dei dati che saranno invece delegate al manager che lo utilizzerà nella user interface.

Abbiamo invece aggiunto a questo controllo la possibilità di creare ed utilizzare un editor di tipo DropDownList, vediamo come:

La classe DnwDropDownItem

Abbiamo creato questa classe e la sua collection per fornire un oggetto consistente per la gestione delle dropdown list.

public class DnwDropDownItem:INotifyPropertyChanged
{

	public string Description
	{
		get
		{
			return mDescription;
		}
		set
		{
			mDescription = value;
			OnPropertyChanged(FLD_Description);
		}
	}

	public string Value
	{
		get
		{
			return mValue;
		}
		set
		{
			mValue = value;
			OnPropertyChanged(FLD_Value);
		}
	}


}

Abbiamo omesso ciò che non è interessante nel codice, ed implementato una semplice entity con le funzionalità relative al notify property changed.

La collection DnwDropDownItemsCollection

public class DnwDropDownItemsCollection: List<DnwDropDownItem>
{

	public override string ToString()
	{
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < this.Count; i++)
		{
			sb.AppendLine(this[i].ToString());
		}
		return (sb.ToString());
	}

}

La collezione tipizzata che abbiamo creato è molto semplice, infatti crea solo una tipizzazione di una generic List.

L’implementazione della DropDown nello User Control.

Come si implementa una Combobox in codice XAML, abbiamo preso l’esempio dallo User Control che ci permette di modificare una Stringa di connessione a SQL Server.

<ComboBox Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" 	
	Name="cboConnectionType" ItemsSource="{Binding ConnectionTypes}"  SelectedValuePath="Item1" SelectedValue="{Binding  CurrentConnection.ConnectionType, Mode=TwoWay}"   IsEnabled="{Binding IsInEditMode}" >
	<ComboBox.ItemTemplate>
		<DataTemplate>
			<StackPanel FlowDirection="LeftToRight"
			 Orientation="Vertical" Margin="4,4,4,4">
				<TextBlock Text="{Binding Item2}"/>
			</StackPanel>
		</DataTemplate>
	</ComboBox.ItemTemplate>
</ComboBox>

A parte per gli attributi di formattazione, ed il binding funzionale per la attivazione della modifica, le cose importanti in una combobox sono:

  • Il binding ItemsSource, che fornisce gli elementi selezionabili alla lista.
  • Il binding SelectedValue, che collega l’oggetto che la modifica alla combobox deve modificare.
  • Il DataTemplate, che stabilisce l’aspetto della lista e l’elemento che dovrà essere mostrato nella dropdown della lista.

Quanto da noi realizzato per la creazione da codice dei controlli TextBox, CheckBox e gli user control parametrici ci ha già dato buona parte di quanto necessario. La sola cosa che non abbiamo mai creato è un DataTemplate. Per crearlo abbiamo due possibilità e le abbiamo provate entrambe.

Prima possibilità: Se il DataTemplate è sempre uguale, indipendentemente dal numero di combobox che creeremo e da come le inseriremo nei controlli dello User Control come nel nostro caso, infatti tutte le DropDown useranno una DnwDropDownItemsCollection e mostreranno nella lista la property Description. Il modo più veloce di creare il DataTemplate per la dropdown è questo:

<UserControl.Resources>
	<ResourceDictionary>
		<ResourceDictionary.MergedDictionaries>
			<ResourceDictionary Source="pack://application:,,,/Dnw.Base.Wpf.v4.0;component/Styles.xaml"/>
		</ResourceDictionary.MergedDictionaries>
		<DataTemplate x:Key="cboDT">
			<StackPanel FlowDirection="LeftToRight" Orientation="Vertical" Margin="4,4,4,4">
				<TextBlock Text="{Binding Description}"/>
			</StackPanel>
		</DataTemplate>
	</ResourceDictionary>
</UserControl.Resources>

Creiamo il DataTemplate all’interno del Resource Dictionary dello User Control e gli diamo un nome.

private void GenerateDropdownRow(Grid gridContainer, DnwSetting item, int row)
{
	try
	{

		GenerateDescriptions(gridContainer, item, row);
		ComboBox cbo = new ComboBox();
		cbo.ItemsSource = item.DropdownValues;
		cbo.SelectedValuePath = DnwDropDownItem.FLD_Value;
		cbo.Margin = new Thickness(5);
		cbo.Padding = new Thickness(2);
		Binding vBinding = new Binding(DnwSetting.FLD_Value);
		vBinding.Source = item;
		vBinding.Mode = BindingMode.TwoWay;
		cbo.SetBinding(ComboBox.SelectedValueProperty, vBinding);
		gridContainer.Children.Add(cbo);
		Grid.SetRow(cbo, row);
		Grid.SetColumn(cbo, 2);

		var dt = this.FindResource("cboDT") as DataTemplate;
		cbo.ItemTemplate = dt;

	}
	catch (Exception ex)
	{
		EventLogger.SendMsg(ex);
		throw;
	}
}

Il metodo che genera il controllo da codice è quello qui sopra indicato, molto simile a quello che abbiamo creato per tutti gli altri controlli. Il metodo FindResource ci da modo di agganciare il DataTemplate alla combobox.

Seconda possibilità: se vogliamo creare tutto da codice in modo da avere tutto in un unico punto se in futuro dovremo fare delle modifiche non inseriamo nulla nello XAML e generiamo da codice anche il DataTemplate.

private void GenerateDropdownRow(Grid gridContainer, DnwSetting item, int row)
{
	try
	{

		GenerateDescriptions(gridContainer, item, row);
		ComboBox cbo = new ComboBox();
		cbo.ItemsSource = item.DropdownValues;
		cbo.SelectedValuePath = DnwDropDownItem.FLD_Value;
		cbo.Margin = new Thickness(5);
		cbo.Padding = new Thickness(2);
		Binding vBinding = new Binding(DnwSetting.FLD_Value);
		vBinding.Source = item;
		vBinding.Mode = BindingMode.TwoWay;
		cbo.SetBinding(ComboBox.SelectedValueProperty, vBinding);
		gridContainer.Children.Add(cbo);
		Grid.SetRow(cbo, row);
		Grid.SetColumn(cbo, 2);

		//create the data template
		DataTemplate cboDT = new DataTemplate();
		cboDT.DataType = typeof(ComboBox);

		//set up the stack panel
		FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(StackPanel));
		spFactory.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);
		spFactory.SetValue(StackPanel.FlowDirectionProperty, FlowDirection.LeftToRight);
		spFactory.SetValue(StackPanel.MarginProperty, new Thickness(4));


		//set up the card holder textblock
		FrameworkElementFactory txtData = new FrameworkElementFactory(typeof(TextBlock));
		txtData.SetBinding(TextBlock.TextProperty, new Binding(DnwDropDownItem.FLD_Description));
		spFactory.AppendChild(txtData);

		//set the visual tree of the data template
		cboDT.VisualTree = spFactory;

		//set the item template to be our shiny new data template
		cbo.ItemTemplate = cboDT;



	}
	catch (Exception ex)
	{
		EventLogger.SendMsg(ex);
		throw;
	}
}

Dobbiamo creare più codice e andare a scomodare alcuni oggetti con il suffisso Factory, nel namespace relativo a WPF che ci permettono di creare e modificare il VisualTree del DataTemplate.

I due metodi sono equivalenti nel risultato finale, possiamo applicare quello che ci piace di più. Oppure possiamo usarne un terzo e creare la stringa XAML e usare uno XamlWriter per aggiungerlo agli oggetti.

Il codice del progetto di esempio relativo alle librerie di uso comune è disponibile al link seguente:

Per qualsiasi domanda, curiosità approfondimento, potete usare il link al modulo di contatto in cima alla pagina.