1
votes

I am currently working on a MVVM project that uses a Window (with my ViewModel) and my own UserControl. The UserControl is nearly empty in the .xaml file because all of its functionality comes from code-behind, which draws different shapes. I wanted to bind a property from ViewModel to a DependencyProperty in the UserControl, but no matter what I do, i cannot get it to work. I have read tons of answers here and on different websites and noticed that it might be something with the UserControl's DataContext, but I eventually failed to fix the problem anyway. The way I raise the PropertyChanged event in my ViewModel is correct. I can successfully bind my property to other controls (like TextBoxes etc.), but not to my one. I would be grateful if you could explain to me why it is not working and how to fix that. Regards!

MainWindow.xaml binding:

<Grid Margin="10">
    <local:FretboardControl Grid.Row="0" Fretboard="{Binding CurrentFretboard, Mode=TwoWay}"/>
</Grid>

FretboardControl.xaml:

<UserControl ...>
    <Grid>
<TextBlock Text="{Binding Path=Fretboard, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:FretboardControl}}"/>
    //the TextBlock above is just a test
    <Canvas.../>
    </Grid>
</UserControl>

FretboardControl.xaml.cs (code-behind):

public static readonly DependencyProperty FretboardProperty = DependencyProperty.Register
    (nameof(Fretboard), typeof(Fretboard), typeof(FretboardControl), new PropertyMetadata(new Fretboard(), PropertyChangedCallback));

public Fretboard Fretboard {
    get {
        return GetValue(FretboardProperty) as Fretboard;
    }
    set {
        SetValue(FretboardProperty, value);
    }
}

protected static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e) {
    //breakpoint here. It is reached only once during runtime:
    //at start, when the default value is inserted
    if (o is FretboardControl) {
        (o as FretboardControl).RefreshFretboard();
    }
}
1
There isn't anything like DataContext = this; in the UserControl's constructor? That said, AncestorType=UserControl should be ok (instead of local:FretboardControl). - Clemens
As a note, if (o is FretboardControl) followed by (o as FretboardControl) is pointless. Just call ((FretboardControl)o).RefreshFretboard(). And if the PropertyChangedCallback is called that is usually in indication that the property was set to a value other than the default value. So your Binding may have worked... - Clemens
@Clemens The bound property (CurrentFretboard) changes at runtime (it can be selected with a ListBox). Although the property changes properly, PropertyChangedCallback does not ever get called, so the binding between the control and the window does not seem to work. Changing DataContext to self (either in the constructor, like you suggested or in the XAML) doesn't change much. PropertyChangedCallback doesn't get called at startup then, but the binding still does not work. - Doctor-Ned
I didn't suggest to set the DataContext, just asked about it. A UserControl should never set its own DataContext. Neither in XAML nor in code behind. - Clemens
Ah, sorry. So no, it is not currently set in any way. - Doctor-Ned

1 Answers

0
votes

Okay, so apparently i probably found the seed of my problem. My CurrentFretboard setter raised the PropertyChanged event, but i did not change the reference of the object itself. The object was modified, but it was still the same object. I thought that this would not matter and it would be sent anyway to the binding, but it looks like the PropertyChangedCallback is called only if the reference was changed. I guess i can replace the reference on each set or just listen to the PropertyChanged event already in the UserControl. Thanks for help!