2011-01-31 5 views
3

私は自分のUserControl内の単一のTextBoxにバインドできるDepedencyプロパティを持つUserControlを正常に作成しました。しかし、私は私のUserControl内に多くのコントロールがあり、単一のプロパティ(多くのコントロールの値から構築された)にバインドしたいときにこれを行う方法についてはわかりません。私は背後にあるコードに追加する必要が何多くのコントロールを持つWPF UserControl - 多くのコントロールに対応する依存関係プロパティを作成する方法?

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

ザ・ユーザーコントロールは、これまでのところ、私はこれを持っている、年、月、私は、単一の日付プロパティにこれをバインドしたい日付の3つのテキストボックスを持っています3つのテキストボックスから取得したDateプロパティを作成して、コントロールを使用している別のコンテナでDateにバインドできます。私のユーザーコントロールは、私は依存関係プロパティをしなければならない対象であるが、それはとても複雑なようですので、私は

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

答えて

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(); 
    } 
} 
+0

私はそうは思わない、コンバータを使用すると、もっと多くの仕事のように見える - 私はそれが3つの余分なバインディングを作成する必要があるとは思っていない。バインディングを指定すると、時間の90%が日付を作ることに失敗します - 私の答えを参照してください。 – markmnl

+0

これらのバインディングは "余分な"ものではありません。あなたのコードでは、これらのバインディングをコード内で手作業で行っています。私のケースでは、すべての変換ロジックは、メインのユーザーコントロールロジックを単純にしてコンバータにカプセル化されています。そして、これらのバインディングは、コンバータに適切な変換ロジックを置く限り、失敗しません。 –

+0

主なユーザーコントロールロジックは、私が思うようなバインディングなしで簡単です。バインディングは、キーが押されるたびにコンバータを不必要に実行します。どんなことでも、探しているものではありません。 – markmnl

0

これは私が個人的にこのような何かに近づくだろうか..です実現しています。通常、メンバーを別のロジッククラスに移して、セッターに他のバリデーションを追加します。

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

残念ですが、私が他の場所で再利用して、ViewModelにコントロールのDateプロパティをバインドするUserControlではありません。 1つのように、TextBoxのText proppertyをViewModelにバインドします。 – markmnl

+0

ahhh、もっと詳しく読む必要があります。かなり単純な変換でなければならないが、残念ながら、私は後でそれをもう一度見ることはできない。 – Thomas

+0

心配しないで、その変換ではない – markmnl

0

あなたが言及した場合は、DateTimeのためである場合は、マスクされたテキストボックスのために行くことができます。

WPF Masked Textbox with a value that does not contain mask

+0

thats私が欲しいのは1つのTextBoxのような1つのコントロールしか持っていませんが、私の場合はIValueConverterを使用できない多くのforwhichがあります。 – markmnl

0

テキストボックスの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> 

それは当然の非常に単純です。残りの部分は、読者のための練習として残されています:)

関連する問題