Press "Enter" to skip to content

Come acquisire data e ora su due controlli diversi memorizzandola sullo stesso campo

Qualche giorno fa sul forum WPF di Microsoft Italia, è stata fatta una domanda da parte di uno sviluppatore che aveva la necessità di acquisire sia una data che un ora e voleva farlo su due controlli distinti.

I controlli standard WPF comprendono solo il controllo DateTime, che non permette di avere dei formati che diano modo di acquisire solo la data o solo l’ora.

E’ abbastanza primitivo da essere adatto solo a permettere l’acquisizizone utilizzando il calendario. Una acquisizione piuttosto lenta e priva del valore dell’ora.

Ricordandomi di aver usato, all’inizio dei miei test con WPF il WPF Toolkit Extended di Microsoft, che si trovava su Codeplex, e che in questo toolkit c’erano una serie di controlli più sofisticati di quelli di base, sono andata a cercarlo per vedere se fosse possibile risolvere il problema.

Il WPF toolkit Extended originale, che ho ancora fra le mie librerie non esiste più su codeplex, ma è stato ereditato, modificato ed è ancora portato avanti (l’ultima release è del 3/5/2016) da un azienda (o un gruppo) che si chiama XCeed che ne ha sviluppato una versione professional con molte cose in più che può essere acquistata ma che sviluppa e mantiene ancora oggi una versione Community che è gratuita e può essere scaricata qui: https://wpftoolkit.codeplex.com/

Scaricata l’ultima versione, ho verificato come referenziarla e ho creato un piccolo esempio:

In questo programma WPF, Ho referenziato la libreria Xceed.Wpf.Toolkit.dll ed ho modificato nel modo seguente MainWindow.xaml

<Window x:Class="DateAndTimePickers.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
		xmlns:xtkc="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
        xmlns:local="clr-namespace:DateAndTimePickers"
        mc:Ignorable="d"
        Title="My Date And Time Pickers" Height="140" Width="300">
 </Window>

Le modifiche alla Window riguardano l’inclusione del namespace necessario ad usare i controlli date time picker, il titolo e le dimensioni della Window.

   <Grid>
		<Grid.BindingGroup>
			<BindingGroup/>
		</Grid.BindingGroup>
		<Grid.RowDefinitions>
			<RowDefinition Height="50*"/>
			<RowDefinition Height="50*"/>
			<RowDefinition Height="Auto"/>
		</Grid.RowDefinitions>
		<Grid.ColumnDefinitions>
			<ColumnDefinition Width="50*"/>
			<ColumnDefinition Width="50*"/>
		</Grid.ColumnDefinitions>
		<StackPanel Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal" FlowDirection="RightToLeft">
			<Button Content="Show Date and time" Padding="10,4,10,4" Margin="4,2,4,2" Click="ShowDateTime_Click"/>
		</StackPanel>
		<Label 
			Grid.Row="0"
			Grid.Column="0"
			HorizontalAlignment="Right"
			VerticalAlignment="Center"
			Margin="4,10,4,2"
			Content="Insert Date" />
		<xtkc:DateTimePicker
			Grid.Row="0"
			Grid.Column="1"
			HorizontalAlignment="Left"
			VerticalAlignment="Center"
			Margin="4,10,4,2"
			Width="120"
			Value="{Binding MyDateTime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Format="ShortDate" />
		<Label 
			Grid.Row="1"
			Grid.Column="0"
			HorizontalAlignment="Right"
			VerticalAlignment="Center"
			Margin="4,2,4,2"
			Content="Insert Time" />
		<xtkc:DateTimeUpDown 
			Grid.Row="1"
			Grid.Column="1"
			HorizontalAlignment="Left"
			VerticalAlignment="Center"
			Margin="4,2,4,2"
			Width="120"
			Value="{Binding MyDateTime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Format="LongTime" />
 
	</Grid>

Nella grid fornita dalla Window Standard, ho inserito uno StackPanel orizzontale con un Button per testare il valore della variabile in binding, ed ho inserito 2 Label, un DateTimePicker ed un DateTimeUpDown.

Entrambi i controlli data, sono in Binding alla stessa variabile chiamata MyDateTime.

Vediamo ora il codice C# di MainWindow.cs

public MainWindow()
{
	InitializeComponent();
	this.DataContext = this;
}

Nel costruttore faccio divenire la finestra ViewModel di se stessa.

public const string FLD_MyDateTime = "MyDateTime";
public DateTime MyDateTime
{
	get
	{
		return (DateTime)this.GetValue(MyDateTimeProperty);
	}
	set
	{
		this.SetValue(MyDateTimeProperty, value);
	}
}
 
public static readonly DependencyProperty MyDateTimeProperty = DependencyProperty.Register(
	FLD_MyDateTime, typeof(DateTime), typeof(MainWindow), new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Definisco una dependency property, per il campo in binding, attenzione, non è strettamente indispensabile che sia una dependency property, può anche essere una normale property ma la classe deve implementare INotifyPropertyChanged e la property deve scatenare OnPropertyChanged quando viene modificata. (L’uso della costante per il nome della variabile è uno dei miei pattern personali, non è indispensabile, io lo uso per ricordarmi che se rinomino una property devo ricordarmi di modificare tutte le dichiarazioni.

private void ShowDateTime_Click(object sender, RoutedEventArgs e)
{
	MessageBox.Show(string.Format("Ho inserito: {0} {1}", MyDateTime.ToShortDateString(), MyDateTime.ToLongTimeString()));
}

Il codice del Button Click mostra il valore della property, in modo da testare se la modifica alla property funziona.

mainwindow_01

Provando a impostare data e ora e cliccando il bottone vediamo come entrambi i dati sono visualizzati

mainwindow_02

Modificando solo l’ora, vediamo che la data viene conservata.

 

Potete scaricare il progetto esempio dal link qui indicato:

Per qualsiasi domanda, approfondimento, commento, o per segnalare un errore potete usare il link al modulo di contatto in cima alla pagina.