Press "Enter" to skip to content

3 User Control WPF Riutilizzabili per Spiegare come si fanno le Dependency property

In questo post spiegheremo come creare 3 user control WPF che riutilizzeremo più volte nelle nostre applicazioni, si tratta dei controlli File Picker, Directory picker e SQL Connections File Modifier.

Introduzione

Fra le nuove cose introdotte da WPF nel mondo .Net ci sono le Dependency Property, questi nuovi oggetti sono costruiti per permettere a noi programmatori di creare in modo semplice delle property che sono autonomamente bindabili e forniscono tutto il supporto agli eventi di update del sistema. Per saperne di più:  Dependency Properties Overview – su MSDN  è un ottimo punto di partenza.

In teoria, tutte le property usate da XAML nel proprio Model dovrebbero essere Dependency property, in pratica, questo dipende sempre dall’uso e dal contesto che noi applicheremo. Creare tutte le Entity come oggetti dotati di Dependency Property invece che semplici property potrebbe essere laborioso ed appesantire il codice. Ecco perché nelle classi che abbiamo visto fino ad ora abbiamo utilizzato le normali property e l’interfaccia OnPropertyChanged. Ma visto che siamo qui per imparare a pescare, creiamo un nuovo amo da utilizzare per prendere gli squali.

Che cosa vogliamo fare?

Nel post precedente a questo abbiamo molto sinteticamente creato una collezione di parametri serializzabili, siccome siamo dei programmatori attenti al lavoro dei nostri utenti, siano essi utilizzatori finali delle nostre applicazioni oppure gli amministratori di sistema, non vogliamo che questi ultimi (che sono noti per essere pigri e per stressare l’anima ai programmatori) debbano imparare a scrivere in XML, men che meno vogliamo che debbano perdere tempo a cercare le cartelle ove salvare le opzioni di configurazione dei nostri Software. Inoltre vogliamo creare delle applicazioni che siano per quanto possibile User Friendly, e quindi abbiano in dotazione delle preferenze per fare in modo che gli utenti possano configurare il proprio sistema in modo confortevole.

Tradotto in codice C# tutto questo significa che i parametri applicativi o i parametri utente devono essere dotati di un interfaccia utente che permetta di modificarli senza necessità di conoscere XML.

I tre User control

Gli user control che genereremo in questo post sono molto simili fra loro, ecco perché li trattiamo in un unico post, ci forniranno tre servizi riutilizzabili:

  • Un Directory Picker, che fornirà una textbox dotata del pulsante per selezionare una cartella del sistema
  • Un File Picker, che fornirà una textbox dotata del pulsante per selezionare un File sul sistema
  • Un Sql Connection file Picker e modifier, per poter selezionare un file di connessioni a SQL Server oppure generarne uno e modificare il suo contenuto.

Avvertenze e controindicazioni

Prima di discutere i tre controlli, avviso chi non ha ancora avuto modo di lavorare con WPF che sfortunatamente nelle librerie standard non esistono tre Dialog fondamentali. OpenFileDialog, SaveFileDialog e FolderBrowserDialog non sono state replicate in WPF,  esiste un Toolkit Gratuito di Microsoft  il Windows API Codepack che fornisce una serie di DLL con varie applicazioni e servizi fra cui sono anche le dialog su citate o dei surrogati che possiamo modificare ed usare nel nostro codice. Maggiori informazioni QUI. Se non vogliamo scaricare, imparare come sono fatte e poi usare queste librerie accessorie, dobbiamo fare qualche piccola “porcheria” ovvero utilizzare delle DLL che non sono parte di WPF. La prima è Microsoft.Win32 che abbiamo già utilizzato nei progetti di test e ci fornisce un Wrapper alla OpenFileDialog e alla SaveFileDialog. Invece, la FolderBrowserDialog sfortunatamente è presente solo in System.Windows.Forms, pertanto dovremo attaccare alla nostra applicazione WPF anche la Dll delle Windows Forms.

E’ qualcosa che da il prurito (ai puristi che vorrebbero usare una tecnologia soltanto), concordiamo con chi legge, ma visto che ai fini della spiegazione primaria di questi post non è indispensabile introdurre una DLL esaminare come funziona e fornire i suoi oggetti per i compiti semplici e banali quali selezionare una cartella o un file, abbiamo deciso di non utilizzarla. Siamo sicuri che in futuro avremo l’occasione di utilizzare le librerie dell’API Codepack e apprezzarne il contenuto e il funzionamento.

Le soluzioni e i progetti

Anche questi nuovi controlli essendo creati per poter essere riutilizzati saranno inseriti nelle librerie di Base di Dotnetwork.it, ecco le modifiche che abbiamo fatto ai progetti e alla soluzione DnwLibraries.

pickers_01[6]

Le due classi User control dei Picker standard, che abbiamo inserito nella libreria Base Wpf perché potranno essere utili ovunque.

pickers_02[6]

Le classi specifiche per SQL Server, lo User Control e una Window che utilizzerà lo User control che abbiamo spiegato nei post precedenti per fornire la funzionalità di modifica dei dati delle connessioni presenti in un file (oppure genererà un nuovo file se non esistesse).

La classe DnwDirectoryPicker

DirPick_01[6]

Lo User control in se è molto semplice, Una textbox e un button, vediamo cosa abbiamo inserito nello XAML

<UserControl x:Class="Dnw.Base.Wpf.Controls.DnwDirectoryPicker"
	     x:Name="DnwDP"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"               mc:Ignorable="d"               d:DesignHeight="36" d:DesignWidth="600">
	<Grid Margin="0,0,0,0" >
		<Grid.RowDefinitions>
			<RowDefinition Height="36"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*"/>
			<ColumnDefinition Width="64"/>
		</Grid.ColumnDefinitions>
		<TextBox Name="txtFileName" Grid.Row="0" Grid.Column="0"
			Text="{Binding Path=DirectoryName,  			ElementName=DnwDP, Mode=TwoWay}" VerticalAlignment="Center"  HorizontalAlignment="Stretch" Margin="5,2,5,2"/>
		<Button Name="btnSearch" Grid.Row="0" Grid.Column="1" 
			Click="btnSearch_Click" Margin="5,6,5,6">
			<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal">
				<Image Source="/Dnw.Base.Wpf.v4.0;component/Images/btn_032_159.png"   Height="16" Width="16" Margin=" 5,0,5,0"/>
			</StackPanel>
		</Button>
	</Grid>
</UserControl>

Lo XAML dello User control è piuttosto semplice, Una grid, con una riga e due colonne, una TextBox nella prima ed un Button con all’interno un immagine nella seconda colonna. Salvo le informazioni di formattazione, la sola cosa interessante è ciò che abbiamo evidenziato in giallo, Abbiamo dato un x:Name al controllo (DnwDP), perché ci serve per riferirci a lui come Binding Source. Ed abbiamo effettuato il Binding della property test ad una property (Path=DirectoryName) che si trova nell’ elemento (ElementName=DnwDP) il nostro user control. Abbiamo indicato inoltre che il binding è bidirezionale.

E’ un po’ diverso dal binding fatto nei post precedenti, esaminiamo il Code Behind per verificare cosa fa il nostro controllo e come.

public partial class DnwDirectoryPicker : UserControl
{

La dichiarazione dello User Control.

private void btnSearch_Click(object sender, RoutedEventArgs e)
{
	System.Windows.Forms.FolderBrowserDialog fbd = new System.Windows.Forms.FolderBrowserDialog();
	fbd.Description = Description;
	fbd.RootFolder = RootFolder;
	fbd.ShowNewFolderButton = ShowNewFolderButton;

	System.Windows.Forms.DialogResult ret = fbd.ShowDialog();
	if (ret == System.Windows.Forms.DialogResult.OK)
	{

		this.DirectoryName = fbd.SelectedPath;

	}

}

L’event handler del click del Button, che mostra la finestra Folder Browser e acquisisce la cartella selezionata.

public string Description
{
	get
	{
		return mDescription;
	}
	set
	{
		mDescription = value;
	}
}

public Environment.SpecialFolder RootFolder
{
	get
	{
		return mRootFolder;
	}
	set
	{
		mRootFolder = value;
	}
}

public bool ShowNewFolderButton
{
	get
	{
		return mShowNewFolderButton;
	}
	set
	{
		mShowNewFolderButton = value;
	}
}

Tre property che permetteranno di  configurare la Dialog se necessario nei luoghi ove sarà utilizzato il controllo.

public string DirectoryName
{
	get
	{
		return (string)this.GetValue(DirectoryNameProperty);
	}
	set
	{
		this.SetValue(DirectoryNameProperty, value);
	}
}

public static readonly DependencyProperty DirectoryNameProperty = DependencyProperty.Register(
	FLD_DirectoryName, typeof(string), typeof(DnwDirectoryPicker), new PropertyMetadata(""));

Ed infine, la porzione di codice più importante, la definizione della Property “Bindable” e corrispondente dependency property. Una Dependency property, viene definita registrandola sul sistema una sola volta quando viene instanziata la classe, una volta registrata  il suo valore può essere manipolato utilizzando i due metodi GetValue e SetValue, attenzione se noi abbiamo 2 istanze della classe DnwDirectoryPicker, ciascuna delle istanze avrà una diversa istanza di DirectoryName, la funzione di registrazione statica serve solo ad agganciare questa property al sistema permettendogli di gestirne correttamente le funzionalità.

Cosa ricordare quando si genera la registrazione di una Dependency property:

  • Name convention, per convenzione le dependency property si chiamano con il nome della Property a cui viene aggiunto il suffisso Property.
  • Parametri di registrazione, sono Importantissimi per far funzionare la dependency property:
    • name = Nome della property come dovrà essere inserito nella clausola Binding dello XAML
    • type = Il tipo della property (string, int, double, bool, ecc.)
    • ownerType = Il tipo della classe che possiede la property (DnwDirectoryPicker, DnwFilePicker, ecc.)
    • typeMetaData = Principalmente usato per indicare il valore di default della property se non usate il tipo giusto avrete degli errori di compilazione quindi ad esempio:
      • Per una stringa new PropertyMetadata(“”) oppure new PropertyMetadata(null)
      • Per un booleano new PropertyMetadata(false)
      • Per un intero new PropertyMetadata(-1)

La classe DnwFilePicker

E’ identica alla classe precedente se non per il binding che riportiamo di seguito

<TextBox Name="txtFileName" Grid.Row="0" Grid.Column="0"  Text="{Binding Path=FileName, ElementName=DnwFP, Mode=TwoWay}"  VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="5,2,5,2"/>

Il codice differisce solo perché il controllo Open File Dialog che usiamo per acquisire il nome file ha una serie di parametri di configurazione in più

private void btnSearch_Click(object sender, RoutedEventArgs e)
{
	OpenFileDialog ofd = new OpenFileDialog();
	ofd.Title = Title;
	ofd.Multiselect = MultiSelect;
	ofd.DefaultExt = DefaultExt;
	ofd.Filter = SearchFilters;
	ofd.FilterIndex = FilterIndex;
	ofd.CheckFileExists = CheckFileExists;
	ofd.CheckPathExists = CheckFolderExists;
	bool? ret = ofd.ShowDialog();
	if (ret.HasValue && ret.Value)
	{
		if (ofd.Multiselect)
		{
			string sep = "";
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < ofd.FileNames.Length; i++)
			{

				sb.Append(sep);
				sb.Append(ofd.FileNames[i]);
				sep = ", ";
			}
			this.FileName = sb.ToString();
		}
		else
		{
			this.FileName = ofd.FileName;
		}
	}

}

Il button click, simile al precedente, solo che nel caso sia richiesto di permettere la selezione multipla dei files componiamo la stringa che aggiorna la textbox.

public string Title
{
	get
	{
		return mTitle;
	}
	set
	{
		mTitle = value;
	}
}

public string SearchFilters
{
	get
	{
		return mSearchFilters;
	}
	set
	{
		mSearchFilters = value;
	}
}

public int FilterIndex
{
	get
	{
		return mFilterIndex;
	}
	set
	{
		mFilterIndex = value;
	}
}

public bool CheckFileExists
{
	get
	{
		return mCheckFileExists;
	}
	set
	{
		mCheckFileExists = value;
	}
}

public bool CheckFolderExists
{
	get
	{
		return mCheckFolderExists;
	}
	set
	{
		mCheckFolderExists = value;
	}
}

public bool MultiSelect
{
	get
	{
		return mMultiSelect;
	}
	set
	{
		mMultiSelect = value;
	}
}

public string DefaultExt
{
	get
	{
		return mDefaultExt;
	}
	set
	{
		mDefaultExt = value;
	}
}

Le property che permetteranno di configurare la dialog e il suo comportamento dove useremo lo user control.

public string FileName
{
	get
	{
		return (string)this.GetValue(FileNameProperty);
	}
	set
	{
		this.SetValue(FileNameProperty, value);
	}
}

public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register(
	FLD_FileName, typeof(string), typeof(DnwFilePicker), new PropertyMetadata(""));

La dependency property per il nome del file restituito, abbiamo rispettato le naming conventions e modificato i parametri necessari.

La classe SqlConnectionFileEditorControl

Questa classe è uno user control un po’ più complesso dei due precedenti, infatti oltre a permettere di selezionare un file dal file system ha anche un pulsante per permettere di inserire il contenuto in un nuovo file o modificare il contenuto di un file esistente.

Per realizzare questa seconda funzionalità abbiamo aggiunto una nuova classe, una Window che utilizza lo User Control SqlConnectionInfoControl che abbiamo generato nella serie di articoli che trovate QUI.

Vediamo entrambe le classi e il codice che le contraddistingue.

<UserControl x:Class="Dnw.UI.SqlServer.Controls.SqlConnectionFileEditorControl"
			 x:Name="DnwSCP"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"               mc:Ignorable="d"               d:DesignHeight="36" d:DesignWidth="600">
	<Grid Margin="0,0,0,0" >
		<Grid.RowDefinitions>
			<RowDefinition Height="36"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*"/>
			<ColumnDefinition Width="64"/>
			<ColumnDefinition Width="64"/>
		</Grid.ColumnDefinitions>
		<TextBox Name="txtFileName" Text="{Binding Path=FileName, ElementName=DnwSCP, Mode=TwoWay}" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="5,2,5,2"></TextBox>
		<Button Name="btnEdit" Grid.Row="0" Grid.Column="1"  Click="btnEdit_Click" Margin="5,6,5,6">
			<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal">
				<Image Source="/Dnw.UI.SqlServer.v4.0;component/Images/btn_032_113.png"  Height="16" Width="16" Margin=" 5,0,5,0"/>
			</StackPanel>
		</Button>
 
		<Button Name="btnSearch" Grid.Row="0" Grid.Column="2"  Click="btnSearch_Click" Margin="5,6,5,6">
			<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal">
				<Image Source="/Dnw.UI.SqlServer.v4.0;component/Images/btn_032_159.png"  Height="16" Width="16" Margin=" 5,0,5,0"/>
			</StackPanel>
		</Button>
	</Grid>
</UserControl>

Lo XAML del controllo, differisce dai precedenti perché ha 2 bottoni, ma il binding è uno solo ed è identico a quello del File Picker, infatti la sua funzionalità è la stessa, quello che fornisce è il nome di un file limitato ai file che contengono informazioni di connessione. Il metodo che permette di modificarne il contenuto non ha necessità di esporre nulla all’esterno della classe.

private void btnEdit_Click(object sender, RoutedEventArgs e)
{
	bool ret = true;
	if (this.FileName.XDwIsNullOrTrimEmpty())
	{
		ret = GetFileName();
	}
	if (ret)
	{
		SqlGetConnectionsWindow win = new SqlGetConnectionsWindow(Icon, this.FileName);
		win.ShowDialog();
	}
	else
	{
		MessageBox.Show(Properties.Resources.txtSCFECNoFileName);
	}
}

private void btnSearch_Click(object sender, RoutedEventArgs e)
{
	try
	{
		GetFileName();
	}
	catch (Exception ex)
	{
		EventLogger.SendMsg(ex);
		MessageBox.Show(ex.Message,
			Properties.Resources.excError,
			MessageBoxButton.OK, MessageBoxImage.Error);
	}

}

private bool GetFileName()
{
	bool gotten = false;
	OpenFileDialog ofd = new OpenFileDialog();
	ofd.Title = Properties.Resources.txtSCFEOfdTitle;
	ofd.Multiselect = false;
	ofd.DefaultExt = EXT_Dnwx;
	ofd.Filter = TXT_Filters;
	ofd.FilterIndex = 2;
	ofd.CheckFileExists = false;
	ofd.CheckPathExists = true;
	bool? ret = ofd.ShowDialog();
	if (ret.HasValue && ret.Value)
	{
		this.FileName = ofd.FileName;
		if (!this.FileName.XDwIsNullOrTrimEmpty())
		{
			gotten = true;
		}
	}
	return (gotten);
}

I 3 metodi che gestiscono il click dei due button, possiamo notare che per questo controllo non abbiamo esposto le property della dialog di selezione file perché sono specifiche di questo controllo e della sua funzione.

public ImageSource Icon
{
	get
	{
		return mIcon;
	}
	set
	{
		mIcon = value;
	}
}

Esponiamo una sola property, che permette volendo di personalizzare l’icona della Window di gestione delle connessioni, (ad esempio con l’icona applicativa del programma dove la useremo oppure con un icona specifica).

public string FileName
{
	get
	{
		return (string)this.GetValue(FileNameProperty);
	}
	set
	{
		this.SetValue(FileNameProperty, value);
	}
}

public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register(
	FLD_FileName, typeof(string), typeof(SqlConnectionFileEditorControl), new PropertyMetadata(""));

Ovviamente la nostra Dependency Property per il nome del file, il solo oggetto che servirà all’esterno di questo user control.

La classe SqlGetConnectionsWindow

L’implementazione della window usata dallo User Control per inserire e modificare i dati nei file di connessioni, è una variazione ridotta della window che abbiamo usato per il test del controllo.

<Window x:Class="Dnw.UI.SqlServer.Windows.SqlGetConnectionsWindow"
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:sctl="clr-namespace:Dnw.UI.SqlServer.Controls"
		Title="SqlGetConnectionsWindow" Height="646" Width="900" Loaded="Window_Loaded">
	<Grid>
		<Grid.RowDefinitions>
			<RowDefinition Height="*"/>
			<RowDefinition Height="48"/>
		</Grid.RowDefinitions>
		<sctl:SqlConnectionInfoControl x:Name="ctlConnections"/>
		<StackPanel Grid.Row="2"  FlowDirection="RightToLeft"  Orientation="Horizontal" Margin="10,2,10,2">
			<Button Name="btnExit" Margin="0,2,5,2"  Click="btnClick" Width="120" Height="36" IsCancel="True">
				<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal" Margin="0,0,0,0">
					<Image Width="24" Height="24" Source="/Dnw.UI.SqlServer.v4.0;component/Images/btn_032_157.png" Margin="5,2,5,2"/>
					<Label Content="_Exit" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5,0,5,0" />
				</StackPanel>
			</Button>
			<Button Name="btnSave" Margin="0,2,5,2"  Click="btnClick" Width="120" Height="36" IsDefault="True">
				<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal" Margin="0,0,0,0">
					<Image Width="24" Height="24" Source="/Dnw.UI.SqlServer.v4.0;component/Images/btn_032_367.png" Margin="5,2,5,2"/>
					<Label Content="_Save" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5,0,5,0" />
				</StackPanel>
			</Button>
 
		</StackPanel>
	</Grid>
</Window>

Lo XAML è molto semplice, infatti utilizza una grid con due righe, una per lo User Control ed una per i due bottoni di Salva ed Esci.

public string FileName
{
	get
	{
		return this.ctlConnections.FileName;
	}
	set
	{
		this.ctlConnections.FileName = value;
		bool isencrypted = Path.GetExtension(this.ctlConnections.FileName).ToLower() == EXT_Dnwx;
		if (File.Exists(value))
		{
			this.ctlConnections.Load(isencrypted);
		}
		else
		{
			
			this.ctlConnections.Clear();
		}
	}
}

La property per indicare il nome del file dati, che è il parametro necessario a far funzionare questa finestra specializzata, infatti quando aggiorniamo il nome del file automaticamente viene ricaricata la finestra con i dati.

public SqlGetConnectionsWindow(ImageSource icon, string fileName)
{
	InitializeComponent();
	this.DataContext = this;
	this.Icon = icon;
	this.FileName = fileName;
}

Il costruttore, che richiede l’icona e il nome del file da utilizzare.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
	if (this.FileName.XDwIsNullOrTrimEmpty())
	{
		MessageBox.Show(Properties.Resources.warSGCWNoFileName);
		this.Close();
	}
}

La verifica ed eventuale chiusura della finestra se non è stato indicato un nome di file. Il resto del codice della finestra è identico a quello già discusso per la finestra di Test del controllo di gestione delle stringhe di connessione a SQL Server in questo post: Uno user control per gestire i dati di connessione a SQL Server (Parte 7)

Il progetto di test dei nostri 3 user control

test_pickers_01[6]

Il progetto di test contiene una sola finestra molto semplice, con alcuni button e un istanza di ciascuno dei tre controlli che abbiamo generato.

<Window x:Class="Dnw.TestDependencyProperty.Windows.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	        			xmlns:ctl="clr-namespace:Dnw.Base.Wpf.Controls;assembly=Dnw.Base.Wpf.v4.0"
		xmlns:sctl ="clr-namespace:Dnw.UI.SqlServer.Controls;assembly=Dnw.UI.SqlServer.v4.0"
		Title="Test the dependency property" Height="360" Width="1024">
	<Grid Margin="0, 0, 0, 0" >
		<Grid.RowDefinitions>
			<RowDefinition Height="48" />
			<RowDefinition Height="*" />
			<RowDefinition Height="48" />
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="*"/>
		</Grid.ColumnDefinitions>
		<StackPanel Grid.Row="0" Grid.Column="0" Margin="0,0,0,0" Background="Yellow">
			<TextBlock Text="Una finestra per testare User control e dependency property" FontFamily="Segoe UI" FontSize="16" FontWeight="Bold" Foreground="Navy" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,5,5,5"/>
		</StackPanel>
		<Grid Grid.Row="1" Grid.Column="0">
			<Grid.RowDefinitions>
				<RowDefinition Height="64" />
				<RowDefinition Height="64" />
				<RowDefinition Height="64" />
				<RowDefinition Height="64" />
			</Grid.RowDefinitions>
			<Grid.ColumnDefinitions>
				<ColumnDefinition Width="*"/>
 
			</Grid.ColumnDefinitions>
			<StackPanel Height="64" Grid.Row="0" Grid.Column="0" FlowDirection="LeftToRight" Orientation="Horizontal">
				<Button Name="btnTestSet" Click="btnTest_Click" Margin="10,10,10,10" Padding="10,5,10,5">Test Set File Picker</Button>
				<Button Name="btnTestGet" Click="btnTestGet_Click" Margin="10,10,10,10" Padding="10,5,10,5">Test Get File Picker</Button>
				<Button Name="btnTestGetCn" Click="btnTestGetCn_Click" Margin="10,10,10,10" Padding="10,5,10,5">Test Get Connection file</Button>
				<Button Name="btnTestSetCn" Click="btnTestSetCn_Click" Margin="10,10,10,10" Padding="10,5,10,5">Test Set Connection file</Button>
				<Button Name="btnTestGetDp" Click="btnTestGetDp_Click" Margin="10,10,10,10" Padding="10,5,10,5">Test Get Directory picker</Button>
				<Button Name="btnTestSetDp" Click="btnTestSetDp_Click" Margin="10,10,10,10" Padding="10,5,10,5">Test Set Directory picker</Button>
			</StackPanel>
						<ctl:DnwFilePicker Name="ctlFile" Grid.Row="1"/>
						<sctl:SqlConnectionFileEditorControl Name="sctlFile" Grid.Row="2"/>
						<ctl:DnwDirectoryPicker Name="ctlDir" Grid.Row="3"/>
 
		</Grid>
		<StackPanel Grid.Row="2" Grid.Column="0">
			<TextBlock Name="txtStatus" Text="status" FontFamily="Segoe UI" FontSize="16" Foreground="DarkBlue" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,5,5,5"/>
		</StackPanel>
	</Grid>
</Window>

Lo XAML della Window Di test, in cui abbiamo voluto evidenziare le due cose importanti, una di esse è come referenziare le librerie dei controlli affinché XAML le possa vedere, la seconda è come abbiamo usato gli user control, semplicemente mettendoli in 3 diverse righe di una grid.

private void btnTest_Click(object sender, RoutedEventArgs e)
{
	this.ctlFile.FileName = "c:\\pippo.txt";
}

private void btnTestGet_Click(object sender, RoutedEventArgs e)
{
	this.txtStatus.Text = ctlFile.FileName;
}

private void btnTestGetCn_Click(object sender, RoutedEventArgs e)
{
	this.txtStatus.Text = sctlFile.FileName;
}

private void btnTestSetCn_Click(object sender, RoutedEventArgs e)
{
	this.sctlFile.FileName = "c:\\Pluto.dnwx";

}

private void btnTestGetDp_Click(object sender, RoutedEventArgs e)
{
	this.txtStatus.Text = ctlDir.DirectoryName;
}

private void btnTestSetDp_Click(object sender, RoutedEventArgs e)
{
	ctlDir.DirectoryName = Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments);
}

Il codebehind, altrettanto semplice, abbiamo verificato che modificando la dependency property da codice la textbox sia riempita con il valore impostato e che viceversa, se impostiamo la textbox il valore della dependency property letto sia quello scritto sulla textbox. Infatti, Essendo la property in Binding con la property Text del controllo, questo è il comportamento che ci aspettiamo.

Conclusioni

Abbiamo visto come generare una serie di user control che forniscono funzionalità specifiche ma genericamente riutilizzabili all’interno dei nostri progetti ed abbiamo visto come implementare una Dependency Property.

Il progetto di esempio che testa l’uso dei controlli è disponibile al link seguente:

Le librerie di uso comune dove i controlli sono stati inseriti è disponibile al link seguente:

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

Le librerie di supporto ai controlli utilizzati dalle librerie di uso comune e nei progetti successivi sono disponibili ai link seguenti: