0

My issue is my OnMatrixPropertyChanged method never gets called. The label, which is bound to the same property, does update so I know binding is happening on the Matrix property.

I have a UserControl that I want to add a DependencyProperty to in order that it can be bound to. My MainWindow looks like this:

<Window.DataContext>
    <local:MainWindowViewModel />
</Window.DataContext>

<StackPanel>
    <Button
        Command="{Binding LoadMatrixCommand}"
        Content="Load"
        Width="150">
    </Button>

    <Label
        Content="{Binding Matrix.Title}">
    </Label>

    <controls:MatrixView
        Matrix="{Binding Path=Matrix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    </controls:MatrixView>
</StackPanel>

In my MatrixView UserControl code-behind I have the DependencyProperty set as such:

public partial class MatrixView : UserControl
{
    public static readonly DependencyProperty MatrixProperty =
        DependencyProperty.Register(nameof(Matrix), typeof(Matrix), typeof(MatrixView), new PropertyMetadata(default(Matrix), OnMatrixPropertyChanged));

    private static void OnMatrixPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Do Something
    }

    public Matrix Matrix
    {
        get => (Matrix)GetValue(MatrixProperty);
        set => SetValue(MatrixProperty, value);
    }

    public MatrixView()
    {
        InitializeComponent();
    }
}

I must be missing something very obvious...

EDIT #1: View Models

public class MatrixViewModel : ViewModelBase
{
    public MatrixViewModel()
    {
    }
}

public class MainWindowViewModel : ViewModelBase
{
    private IMatrixService _matrixService;
    private Matrix _matrix;

    public Matrix Matrix
    {
        get => _matrix;
        set
        {
            _matrix = value;
            base.RaisePropertyChanged();
        }
    }

    public ICommand LoadMatrixCommand { get; private set; }

    public MainWindowViewModel()
    {
        LoadMatrixCommand = new RelayCommand(LoadMatrix);
        _matrixService = new MatrixService();
    }

    private void LoadMatrix()
    {
        var matrixResult = _matrixService.Get(1);

        if (matrixResult.Ok)
        {
            Matrix = matrixResult.Value;
        }
    }
}
Pseudonymous
  • 839
  • 5
  • 13
  • 1
    @Christopher I'm not binding a list. _Matrix_ is a single object (with child objects). I've got to be either missing something silly or I've completely misunderstood DPs! – Pseudonymous Oct 17 '18 at 12:32
  • Do you see any binding errors in the Debug Output view? – dymanoid Oct 17 '18 at 12:33
  • How does your view model look like? When you bind a Label's Content to `Matrix.Title`, does that work? How does the Matrix class look like? Sure you don't confuse it with the framework's Matrix struct? – Clemens Oct 17 '18 at 12:33
  • @Clemens The label's content does update as expected. I will updated the code samples to provide a bit more context! – Pseudonymous Oct 17 '18 at 12:37
  • @dymanoid A clue! `System.Windows.Data Error: 40 : BindingExpression path error: 'Matrix' property not found on 'object' ''MatrixViewModel' (HashCode=8274172)'. BindingExpression:Path=Matrix; DataItem='MatrixViewModel' (HashCode=8274172); target element is 'MatrixView' (Name=''); target property is 'Matrix' (type 'Matrix')` – Pseudonymous Oct 17 '18 at 12:39
  • This data binding error explains your issue. Please post your view-model `MatrixViewModel`. – dymanoid Oct 17 '18 at 12:42
  • I looks like your UserControl has a "private" MatrixViewModel instance in its DataContext. Besides that this view model doesn't seem to have a Matrix property, it should not be there at all. Do not explicitly assign a UserControl's DataContext when you want to bind its properties to a view model that is passed by value inheritance from its parent element (i.e. the MainWindowViewModel). See e.g. here: https://stackoverflow.com/a/40184402/1136211 – Clemens Oct 17 '18 at 12:45
  • @Clemens I'm not sure I understand. Both the MainWindow and the MatrixView have their own DataContexts set. There is a Matrix property on the MainWindowViewModel... – Pseudonymous Oct 17 '18 at 12:55
  • But you must not set the DataContext of the UserControl. See my answer. – Clemens Oct 17 '18 at 12:56

1 Answers1

1

There certainly is something like

<UserControl.DataContext>
    <local:MatrixViewModel/>
</UserControl.DataContext>

in the XAML of your UserControl. Remove that, because it prevents that a Binding like

<controls:MatrixView Matrix="{Binding Matrix}" />

looks up the Matrix property in the correct view model instance, i.e. the one inherited from the MainWindow.

UserControls with bindable (i.e. dependency) properties should never set their own DataContext, because doing so breaks any DataContext based bindings of these properties.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • This is the answer... but I'm not sure I understand why. I clearly don't understand DPs! I thought the dependency property in the user control's code-behind was to be bound to by it's parent controls. – Pseudonymous Oct 17 '18 at 13:00
  • If I specify a DataContext for a UserControl, do any DPs on that UserControl automatically try to bind to matching properties on the specifed DataContext? – Pseudonymous Oct 17 '18 at 13:08
  • It's not the dependency property, but the Binding. When you do not explicitly specify a Binding's Source, RelativeSource or ElementName, it looks up the source property in the current DataContext of the target object of the Binding. The value of the DataContext is passed by [property value inheritance](https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/property-value-inheritance) from parent to child elements, e.g. from your MainWindow to your MatrixView. Unless you overwrite that by an explicitly set DataContext in your UserControl. – Clemens Oct 17 '18 at 13:31