3

System.Windows.Point와 같은 매우 단순한 복합 형식 속성이있는 DependencyProperties (또는 INotifyPropertyChanged)를 사용하는 ViewModel이 있습니다. 단순 복합 유형은 DependencyProperties 또는 INotifyPropertyChanged를 사용하지 않으며 그 방법으로 그대로 유지하려고합니다. (내 제어 권한을 벗어남).WPF 부모 속성을 바꿀 속성의 속성에 양방향 바인딩

지금 내가하고 싶은 것은 포인트의 X 및 Y 속성에 양방향 데이터 바인딩을 생성하는 것입니다.하지만이 중 하나가 변경되면, 그냥 Point 클래스를 업데이트하는 대신 전체 Point 클래스를 교체해야합니다. 회원. 단지 그림

코드 샘플 : 코드 숨김

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

내가 생각하는 것은 테스트 포인트 속성에 직접 두 텍스트 상자를 결합 만 특정 필터링 IValueConverter을 사용하는 것이 었습니다 멤버이지만 X 값을 수정할 때 Y 값이 더 이상 존재하지 않기 때문에 ConvertBack 메서드에 문제가 있습니다.

내가 얻지 못하는 정말 간단한 해결책이 있어야한다고 생각합니다.

편집 :

상기 코드는 단지 간단한 예이며, 실제 애플리케이션보다 더 복잡하다. 복합 형은 약 7 명의 멤버가 있으며 일반적으로 응용 프로그램을 통해 사용되므로 개별 멤버로 분할하는 것이 옳다고 생각하지 않습니다. 또한 다른 업데이트를 호출하는 종속성 속성의 OnChanged 이벤트에 의존하기를 원하므로 전체 클래스를 교체해야합니다.

+0

컨버터 내부에 다른 값을 넣을 수 있습니다. – WPFUser

+0

변환기의 회원이십니까? 나는 그것에 대해 생각하지 않았다. 각 필드마다 별도의 변환기 인스턴스가 필요할 것이라고 가정합니다. 또한 안전한가요?ConvertBack이 실행될 때 변환 중에 저장된 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

이 경우 두 개의 DependencyProperties를 도입하는 것이 더 쉬울 것이라고 생각합니다. 하나는 X-Value에 대한 것이고 다른 하나는 Y-Value에 대한 단순한 바인딩입니다. 각 DependencyProperty의 PropertyMetadata에서 s.th. 메서드를 등록하십시오. 각 값 변경에서 여전히 필요한 경우 Point 객체를 대체 할 수 있습니다.

적절한 단방향 -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; 
    } 
} 

참고 : 나는 어떤 널 (null) 검사를 추가 havent 한.

관련 문제