bindowanie w WPF

0

Problem w stylu "działa ale nie wiem dlaczego działa":)
Potrzebuje narysować pewne obrazki na podstawie dość skomplikowanych danych więc w tym celu zrobiłem sobie własną kontrolkę MyCanvas dziedziczącą ze standardowego WPFowego Canvas z kilkoma właściwościami, do których chcę bindować dane i na tej podstawie je wyświetlać. W celu zobrazowanie problemu trochę uprościłem zadanie do zwyczajnego rysowania prostej linii.

Kod nowej kontrolki MyCanvas

public class MyCanvas:Canvas
    {
        static MyCanvas()
        {
            var metaData = new FrameworkPropertyMetadata(new PropertyChangedCallback(OnDataChange));
            metaData.AffectsRender = true;
            DataDependencyProperty = DependencyProperty.Register("Coordinates", typeof(LineCoordinates), typeof(MyCanvas),metaData);
        }
        public static void OnDataChange(DependencyObject d,DependencyPropertyChangedEventArgs e)
        {
            var canvas = d as MyCanvas;
            canvas.Coordinates = e.NewValue as LineCoordinates;
            canvas.Update();
        }
        public static readonly DependencyProperty DataDependencyProperty;
        public LineCoordinates Coordinates
        {
            get { return GetValue(DataDependencyProperty) as LineCoordinates; }
            set { SetValue(DataDependencyProperty, value); }
        }
        public void Update()
        {
            Children.Clear();
            Children.Add(new Line() { X1 = Coordinates.X1, Y1 = Coordinates.Y1, X2 = Coordinates.X2, Y2 = Coordinates.Y2,Stroke=Brushes.Red,StrokeThickness=3 });
        }
    }
public class LineCoordinates 
    { 
        public double X1 { get; set; }
        public double Y1 { get; set; }
        public double X2 { get; set; }
        public double Y2 { get; set; }
        
    }
 

Mój view:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="4*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Content="klik" Command="{Binding DrawLine}"/>
        <local:MyCanvas Grid.Row="0" Coordinates="{Binding Line,Mode=TwoWay}"/>
    </Grid>

Mój ViewModel

 
public class MainViewModel : ViewModelBase
    {
        public RelayCommand DrawLine { get; set; }
        public MainViewModel()
        {
            DrawLine = new RelayCommand(updateLine);
            Line = new LineCoordinates();

        }
        private void updateLine()
        {
            Line.X1 = 0;
            Line.Y1 = 0;
            Line.Y2 = 50;
            Line.X2 = 50;
            RaisePropertyChanged(() => Line);
        }
        public LineCoordinates Line { get; set; }

    }

Problem jest z metodą updateLine. Metoda w postaci przedstawionej powyżej nie działa. Tzn po kliknięciu w przycisk linia nie zostaje narysowana (tak jakby RaisePropertChanged nie działało.
Problem ten rozwiązałem poprzez zmianę metody updateLine na (i działa;)):

private void updateLine()
        {
            var line = new LineCoordinates() { X1 = 0, Y1 = 0, X2 = 50, Y2 = 50 };
            Line = line;
            RaisePropertyChanged(() => Line);
        }

tzn tworzę nową instancję klasy LineCoordinates i przypisuję ją do odpowiedniej właściwości ViewModelu.
Zastanawia mnie dlaczego pierwszy sposób nie działa a drugi działa (i za każdym razem muszę tworzyć nową instancję klasy do bindowania). Jakieś pomysły?

Pozdrawiam

1

W przypadku "niedziałającym" RaisePropertyChanged jest w porządku, bo zaraz po nim jest wywoływany getter dla Line. Powodem różnego działania jest różne traktowanie zmiany składowych obiektu Line i podmiany samego obiektu:

A property-changed callback is relevant if your dependency property has interactions with other dependency properties, or if it is used to set an internal property or state of your object. You do not need to compare OldValue and NewValue to determine whether there was a change; if the callback is invoked, then the property system has already determined that there is an effective property value change.

(https://msdn.microsoft.com/en-us/library/cc903933%28v=vs.95%29.aspx#Anchor_6)

Czyli: Twój callback OnDataChange jest wywoływany wtedy, kiedy nastąpiła zmiana wartości Coordinates, ale zmiana samego Coordinates, a nie tego, co w środku.

Jeśli chcesz, żeby działało bez tworzenia nowej instancji za każdym razem, klasa LineCoordinates powinna też mieć zdarzenie PropertyChanged, a MyCanvas je obsługiwać.

0

ogólnie do koordynatów lepiej używać struktur - najlepiej gotowych takich jak Rect

1 użytkowników online, w tym zalogowanych: 0, gości: 1