3

System.Windows.Pointのような非常に単純な複合型のプロパティを持つDependencyProperties(またはINotifyPropertyChanged)を使用するViewModelがあります。 単純な複合型はDependencyPropertiesまたはINotifyPropertyChangedを使用せず、その方法をそのままにしています(これは私のコントロール外です)。WPF親プロパティを置き換えるプロパティのプロパティへの双方向バインディング

ここで私がやりたいことは、ポイントのXプロパティとYプロパティに双方向データバインディングを作成することですが、これらのいずれかが変更された場合、ちょうど更新するのではなく、メンバー。ただ、説明のための

コードサンプル:

<Window ...> 
    <StackPanel> 
     <TextBox Text="{Binding TestPoint.X, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/> 
     <TextBox Text="{Binding TestPoint.Y, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/> 
     <!-- make following label update based on textbox changes above --> 
     <Label Content="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"/> 
    </StackPanel> 
</Window> 

コードビハインド:

public partial class MainWindow : Window 
{ 
    public Point TestPoint 
    { 
     get { return (Point)GetValue(TestPointProperty); } 
     set { SetValue(TestPointProperty, value); } 
    } 
    public static readonly DependencyProperty TestPointProperty = DependencyProperty.Register("TestPoint", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0))); 
} 

私が考えていた何がテストポイントプロパティに直接両方のTextBoxを結合して、唯一の特定をフィルタリングするIValueConverterを使用していましたX値を変更するときにY値が存在しないため、ConvertBackメソッドに問題があります。

私はこれが本当に簡単な解決策でなければならないと感じています。

編集:

上記のコードは、単に簡略化した例であり、実際のアプリケーションは、より複雑です。複合型は約7つのメンバを持ち、一般的にアプリケーションを通して使用されるため、個々のメンバに分割することは正しく行われません。また、依存関係プロパティのOnChangedイベントに依存して他の更新を呼び出すので、クラス全体を置き換える必要があります。

+0

あなたはコンバータの中で他の値を保持することができます、あなたはそれを試してみましたか? – WPFUser

+0

コンバーターのメンバーであることを意味しますか?私はそれについて考えなかった。私は、フィールドごとに別々のコンバータインスタンスを用意する必要があると思います。それは安全ですか?ConvertBackの実行時にConvert中に保存されたY値が有効であることが保証されていますか? – markyxl

+0

'X'と' Y'プロパティにバインドするときに潜在的なメモリリークがあることに注意してください。 'Point'は' INotifyPropertyChanged'ではなく、 'DependencyProperties'もないので、Bindingは' PropertyDescriptor'を使用します。これはBindingが 'OneTime'でない場合にリークを引き起こす可能性があります:http://stackoverflow.com/questions/18542940/can-bindings-create-memory-leaks-in-wpf – wkl

答えて

7

なぜアクセサを使用しないのですか?

public partial class MainWindow : Window 
{ 
    public Point TestPoint 
    { 
     get { return (Point)GetValue(TestPointProperty); } 
     set { SetValue(TestPointProperty, value); } 
    } 

    public double TestPointX 
    { 
     get { return this.TestPoint.X; } 
     set 
     { 
      SetValue(TestPointProperty, new Point(value, this.TestPointY); 
     } 
    } 

    public double TestPointY 
    { 
     get { return this.TestPoint.Y; } 
     set 
     { 
      SetValue(TestPointProperty, new Point(this.TestPointX, value); 
     } 
    } 

    public static readonly DependencyProperty TestPointProperty = DependencyProperty.Register("TestPoint", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0))); 
} 

そして、あなたのXAMLで

:私はコメントで言ったとおり

<Window ...> 
<StackPanel> 
     <TextBox Text="{Binding TestPointX, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/> 
     <TextBox Text="{Binding TestPointY, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, StringFormat=\{0:F\}}"/> 
     <Label Content="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}"/> 
    </StackPanel> 
</Window> 
+0

シンプルな問題の解決には少し時間がかかりますが、これは最も安全で読みやすいソリューションになると思います。ありがとうございます! – markyxl

0

このケースでは、X値とY値の2つのDependencyPropertiesを導入して単純にバインドする方が簡単だと思います。 各DependencyPropertyのPropertyMetadataにメソッド、s.th.を登録します。それでも必要ならば、各値の変更でポイントオブジェクトを置き換えることができます。

適切な一方向のIMultiValueConverterを使用して、ラベルを両方のプロパティのMultiBindingにバインドすることもできます。

+0

申し訳ありませんが、値を完全に分けることはできません。元の質問の最後に説明を追加しました。 – markyxl

0

、あなたはこのように試すことができます。特定のコントロールのリソースにコンバーターを追加すると、同じインスタンスが子コントロールに使用されます。

<Grid> 
    <Grid.Resources /> 
    <StackPanel> 
     <StackPanel> 
      <StackPanel.Resources> 
       <local:PointConverter x:Key="PointConverter" /> 
      </StackPanel.Resources> 
      <TextBox Text="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=x}" /> 
      <TextBox Text="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=y}" /> 
      <!-- make following label update based on textbox changes above --> 
      <Label Content="{Binding TestPoint, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" /> 
     </StackPanel> 
     <StackPanel> 
      <StackPanel.Resources> 
       <local:PointConverter x:Key="PointConverter" /> 
      </StackPanel.Resources> 
      <TextBox Text="{Binding TestPoint2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=x}" /> 
      <TextBox Text="{Binding TestPoint2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, Converter={StaticResource PointConverter}, ConverterParameter=y}" /> 
      <!-- make following label update based on textbox changes above --> 
      <Label Content="{Binding TestPoint2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" /> 
     </StackPanel> 
    </StackPanel> 
</Grid> 

と後ろコード、

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 


    public Point TestPoint 
    { 
     get 
     { 
      return (Point)GetValue(TestPointProperty); 
     } 
     set 
     { 
      SetValue(TestPointProperty, value); 
     } 
    } 

    // Using a DependencyProperty as the backing store for TestPoint. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty TestPointProperty = 
     DependencyProperty.Register("TestPoint", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0))); 
    public Point TestPoint2 
    { 
     get 
     { 
      return (Point)GetValue(TestPoint2Property); 
     } 
     set 
     { 
      SetValue(TestPoint2Property, value); 
     } 
    } 

    // Using a DependencyProperty as the backing store for TestPoint. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty TestPoint2Property = 
     DependencyProperty.Register("TestPoint2", typeof(Point), typeof(MainWindow), new PropertyMetadata(new Point(1.0, 1.0))); 


} 

public class PointConverter : IValueConverter 
{ 
    double knownX = 0.0; 
    double knownY = 0.0; 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (parameter.ToString() == "x") 
     { 
      knownX = ((Point)value).X; 
      return ((Point)value).X; 
     } 
     else 
     { 
      knownY = ((Point)value).Y; 
      return ((Point)value).Y; 
     } 

    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 

     Point p = new Point(); 
     if (parameter.ToString() == "x") 
     { 
      p.Y = knownY; 
      p.X = double.Parse(value.ToString()); 
     } 
     else 
     { 
      p.X = knownX; 
      p.Y = double.Parse(value.ToString()); 
     } 
     return p; 
    } 
} 

注:私は、任意のヌルチェックを追加したhavent。

関連する問題