具有许多控件的 WPF UserControl - 如何创建映射到许多控件的依赖属性?

发布于 2024-10-14 19:13:48 字数 1196 浏览 3 评论 0原文

我已经成功创建了一个带有 Depedency 属性的 UserControl,允许我绑定到 UserControl 中的单个 TextBox。但是,当我的 UserControl 中有许多控件并且只想绑定到单个属性(根据许多控件中的值构建)时,我不确定如何执行此操作?

UserControl 有 3 个文本框,分别表示年、月和日期,我想将其绑定到单个日期属性,到目前为止我已经得到了:

<UserControl x:Class="MyApp.DateControl"...>
<StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBox Name="textbox_year" />
        <TextBox Name="textbox_month" />
        <TextBox Name="textbox_day" />
    </StackPanel>
</StackPanel>
</UserControl>

我需要在后面的代码中添加什么才能使从三个文本框获取日期属性在另一个容器中使用我的控件可以绑定到日期。我意识到因为我的 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;
        }
    }

I have successfully created a UserControl with a Depedency property allowing me to bind to a single TextBox within my UserControl. However Im unsure how to go about doing this when I have many Controls within my UserControl and only want to bind to single Property (built from the values in the many controls)?

The UserControl has 3 textboxes for year, month and date I want to bind this to a single Date Property, so far I have got this:

<UserControl x:Class="MyApp.DateControl"...>
<StackPanel>
    <StackPanel Orientation="Horizontal">
        <TextBox Name="textbox_year" />
        <TextBox Name="textbox_month" />
        <TextBox Name="textbox_day" />
    </StackPanel>
</StackPanel>
</UserControl>

What do I need to add to the code behind to make the Date Property got from the three textboxes so in another container using my control can just bind to Date. I realise since my UserControl is the target I have to make a Dependency Property but it seems so complicated..

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;
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

征﹌骨岁月お 2024-10-21 19:13:48

我建议使用转换器来实现你想要的。

您的用户控件的 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();
    }
}

I suggest using a converter to achieve what you want.

Your user control's XAML will look like this:

<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>

In code-behind you will have only you dependency property:

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));

And the converter will look something like this:

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();
    }
}
策马西风 2024-10-21 19:13:48

这就是我个人处理类似事情的方式。通常,我会将成员移到一个单独的逻辑类中,并在设置器中包含一些其他验证。

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>

This is how I personally would approach something like this. Normally, I would move the members out into a separate logic class and include some other validation in the setters.

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
}

And the 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>
寻找一个思念的角度 2024-10-21 19:13:48

如果您提到的情况是针对 DateTime,那么您可以选择 Masked TextBox。

WPF 屏蔽文本框,其值不包含掩码

If the case you mentioned is for DateTime, then you can go for Masked TextBox.

WPF Masked Textbox with a value that does not contain mask

骑趴 2024-10-21 19:13:48

您可以使用 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>

当然,这非常简单。剩下的留给读者作为练习:)

You can use the TextChanged events on the TextBoxes to set the date:

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>

And the container:

<StackPanel>
    <DateControlApp:DateControl x:Name="dateControl" />
    <TextBlock Text="{Binding ElementName=dateControl, Path=Date}" />
</StackPanel>

It's very simplistic of course. The rest is left as an exercise for the reader :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文