WPF UserControl со многими элементами управления - как создать свойство зависимости, которое сопоставляется со многими элементами управления? - PullRequest
3 голосов
/ 31 января 2011

Я успешно создал UserControl со свойством Depedency, позволяющим мне привязываться к одному TextBox внутри моего UserControl.Однако я не уверен, как это сделать, когда у меня есть много элементов управления в моем UserControl и я хочу привязать только одно свойство (построенное из значений во многих элементах управления)?месяц и дату Я хочу связать это с одним свойством 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 получало из трех текстовых полейпоэтому в другом контейнере с помощью моего элемента управления можно просто привязать к дате.Я понимаю, так как мой 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;
        }
    }

Ответы [ 4 ]

6 голосов
/ 02 февраля 2011

Я предлагаю использовать конвертер для достижения того, что вы хотите.

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();
    }
}
0 голосов
/ 08 февраля 2011

Вы можете использовать события TextChanged в TextBoxes для установки даты:

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>

Это очень упрощенно, конечно.Остальное оставлено в качестве упражнения для читателя:)

0 голосов
/ 31 января 2011

Если указанный вами случай относится к DateTime, то вы можете перейти к Masked TextBox.

WPF Masked Textbox со значением, которое не содержит маску

0 голосов
/ 31 января 2011

Вот как я лично подхожу к чему-то подобному. Обычно я бы переместил члены в отдельный логический класс и включил бы некоторые другие проверки в установщики.

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