我已经成功创建了一个带有Depedency属性的UserControl,允许我绑定到UserControl中的单个TextBox。但是,当我在UserControl中有许多控件并且只想绑定到单个属性(根据许多控件中的值构建)时,我不确定如何执行此操作?
UserControl有3个用于年,月和日期的文本框我想将它绑定到单个Date属性,到目前为止我已经得到了这个:
<UserControl x:Class="MyApp.DateControl"...>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Name="textbox_year" />
<TextBox Name="textbox_month" />
<TextBox Name="textbox_day" />
</StackPanel>
</StackPanel>
</UserControl>
我需要在后面的代码中添加什么才能使Date属性从三个文本框中获取,因此在使用我的控件的另一个容器中可以绑定到Date。我意识到,因为我的UserControl是目标,我必须创建一个依赖属性,但它看起来很复杂..
public partial class DateControl : UserControl
{
public DateControl()
{
InitializeComponent();
}
public DateTime Date
{
get
{
DateTime dt;
if (DateTime.TryParseExact(String.Format("{0}-{1}-{2}", this.textbox_year.Text, this.textbox_month.Text, this.textbox_day.Text), "yyyy-MM-dd", null, System.Globalization.DateTimeStyles.None, out dt))
return dt;
else
return DateTime.MinValue;
}
}
答案 0 :(得分:6)
我建议使用转换器来实现您想要的效果。
您的用户控件的XAML将如下所示:
<UserControl x:Class="MyDateControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:MyDateControl"
x:Name="root">
<UserControl.Resources>
<my:DatePartConverter x:Key="DatePartConverter"
Date="{Binding ElementName=root, Path=Date}"/>
</UserControl.Resources>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Name="textbox_year" Text="{Binding ElementName=root, Path=Date, Converter={StaticResource DatePartConverter}, ConverterParameter=year, Mode=TwoWay}"/>
<TextBox Name="textbox_month" Text="{Binding ElementName=root, Path=Date, Converter={StaticResource DatePartConverter}, ConverterParameter=month, Mode=TwoWay}" />
<TextBox Name="textbox_day" Text="{Binding ElementName=root, Path=Date, Converter={StaticResource DatePartConverter}, ConverterParameter=day, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</UserControl>
在代码隐藏中,您将只拥有依赖属性:
public DateTime Date {
get { return (DateTime)GetValue(DateProperty); }
set { SetValue(DateProperty, value); }
}
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register("Date", typeof(DateTime), typeof(MyDateControl),
new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
转换器看起来像这样:
public class DatePartConverter : Freezable, IValueConverter
{
public DateTime Date {
get { return (DateTime)GetValue(DateProperty); }
set { SetValue(DateProperty, value); }
}
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register("Date", typeof(DateTime), typeof(DatePartConverter), new UIPropertyMetadata(DateTime.Now));
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
DateTime date = (DateTime)value;
string datePartType = (string)parameter;
string result;
switch (datePartType) {
case "year":
result = date.Year.ToString().PadLeft(4, '0');
break;
case "month":
result = date.Month.ToString().PadLeft(2, '0');
break;
case "day":
result = date.Day.ToString().PadLeft(2, '0');
break;
default:
throw new InvalidOperationException("Unknown date part type (ConverterParameter)");
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
string datePartValue = (string)value;
string datePartType = (string)parameter;
DateTime result;
switch (datePartType) {
case "year":
result = new DateTime(int.Parse(datePartValue), Date.Month, Date.Day);
break;
case "month":
result = new DateTime(Date.Year, int.Parse(datePartValue), Date.Day);
break;
case "day":
result = new DateTime(Date.Year, Date.Month, int.Parse(datePartValue));
break;
default:
throw new InvalidOperationException("Unknown date part type (ConverterParameter)");
}
return result;
}
protected override Freezable CreateInstanceCore() {
return new DatePartConverter();
}
}
答案 1 :(得分:0)
这就是我个人会这样做的方式。通常,我会将成员移到一个单独的逻辑类中,并在setter中包含一些其他验证。
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public DateTime Date
{
get
{
DateTime dt;
if (DateTime.TryParseExact(String.Format("{0}-{1}-{2}", DateYear, DateMonth, DateDay), "yyyy-MM-dd", null, System.Globalization.DateTimeStyles.None, out dt))
return dt;
return DateTime.MinValue;
}
}
private string year = "2011";
public String DateYear
{
get { return year; }
set { if (year == value) return; year = value; NotifyPropertyChanged("DateYear"); NotifyPropertyChanged("Date"); }
}
private string month = "11";
public String DateMonth
{
get { return month; }
set { if (month == value) return; month = value; NotifyPropertyChanged("DateMonth"); NotifyPropertyChanged("Date"); }
}
private string day = "11";
public String DateDay
{
get { return day; }
set { if (day == value) return; day = value; NotifyPropertyChanged("DateDay"); NotifyPropertyChanged("Date"); }
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
}
和xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Date:</Label>
<Label Grid.Column="1" Content="{Binding Path=Date}" />
<Label Grid.Row="1">Year</Label>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=DateYear}" />
<Label Grid.Row="2">Month</Label>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=DateMonth}" />
<Label Grid.Row="3">Day</Label>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=DateDay}" />
</Grid>
</Window>
答案 2 :(得分:0)
如果您提到的案例是针对DateTime的,那么您可以使用Masked TextBox。
答案 3 :(得分:0)
您可以使用TextBoxes上的TextChanged事件来设置日期:
public partial class DateControl : UserControl
{
public DateControl()
{
InitializeComponent();
textbox_year.TextChanged += RecalculateDate;
textbox_month.TextChanged += RecalculateDate;
textbox_day.TextChanged += RecalculateDate;
}
private void RecalculateDate( object sender, TextChangedEventArgs e )
{
DateTime dt;
if ( DateTime.TryParseExact( String.Format( "{0}-{1}-{2}", textbox_year.Text, textbox_month.Text, textbox_day.Text ), "yyyy-MM-dd", null, DateTimeStyles.None, out dt ) )
SetValue( DateProperty, dt );
}
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register( "Date", typeof( DateTime ), typeof( DateControl ), new PropertyMetadata( DateTime.MinValue ) );
public DateTime Date
{
get { return (DateTime)GetValue( DateProperty ); }
}
}
XAML:
<UserControl x:Class="DateControlApp.DateControl"
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="300" d:DesignWidth="300">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox Name="textbox_year" />
<TextBox Name="textbox_month" />
<TextBox Name="textbox_day" />
</StackPanel>
</StackPanel>
</UserControl>
容器:
<StackPanel>
<DateControlApp:DateControl x:Name="dateControl" />
<TextBlock Text="{Binding ElementName=dateControl, Path=Date}" />
</StackPanel>
当然,这非常简单。其余部分留给读者练习:)