Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Binding to UserControl Custom DependencyProperty

I have a custom UserControl called SongDescription:

<UserControl x:Class="DPTestAp.SongDescription" ...>
    <Grid x:Name="LayoutRoot">
        <DockPanel Height="50">
            <TextBlock x:Name="title" Text="{Binding name}" Width="100" Height="30"/>
            <TextBox x:Name="lyrics"/>
        </DockPanel>
    </Grid>
</UserControl>

I added DependencyProperty to it:

public partial class SongDescription : UserControl
{
    public static readonly DependencyProperty SongProperty = DependencyProperty.Register("Song", typeof(Song), typeof(SongDescription));

    public Song Song
    {
        get
        {
            return (Song)GetValue(SongProperty);
        }
        set
        {
            SetValue(SongProperty, value);
            updateLyrics()
        }
    }

    private void updateLyrics()
    {
        lyrics.Text = Song.lyrics;
    }

    public SongDescription()
    {
        InitializeComponent();
    }
}

The question is: how to bind something to this SongProperty? I use SongDescription in my main window like this:

<local:SongDescription x:Name="songDescription" Song="{Binding DataContext}"/>

I cannot make my TextBox lyrics show lyrics. In main window I tried to set DataContext to songDescription, like this:

songDescription.DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };

or to window itself like this:

DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };

I even tried to make Song a resource and bind it to SongProperty like this:

<Window.Resources>
    <local:Song x:Key="res" name="Home" lyrics="Hold on, to me as we go"/>
</Window.Resources>
<Grid>
    <local:SongDescription x:Name="songDescription" Song="{StaticResource res}"/>
</Grid>

Nothing helped. TextBlock title binds song name fine. But I can't make updateLyrics() method be called. (In real life this method is more complicated, so I can't use Binding like with name).

Thank you!

like image 469
gs_vlad Avatar asked Feb 05 '13 15:02

gs_vlad


2 Answers

Yup, so that's a gotcha with dependency properties. You never ever put validation code inside of the accessor methods (get/set) because dependency properties are stored by WPF in a table that it itself manages. This is why you have to register dependency properties, it essentially creates entries on this table for storing the values associated with each property, and when you use 'GetValue' / 'SetValue' you are updating the entries on this table (which by the way relates to how WPF is able to manage data bindings in general).

The upshot of this though is that WPF can (and will) completely bypass your property accessors because it has direct access to the real data. Why should it use your accessors if it can just go to the data directly. Instead you need to implement a 'PropertyChanged' callback function or some WPF sanctioned method of doing validation, but never ever do it in your accessors.

See:

http://msdn.microsoft.com/en-us/library/ms752914.aspx

like image 186
sircodesalot Avatar answered Sep 28 '22 08:09

sircodesalot


In addition to sircodesalot's answer, you are not bound on your lyrics textbox. Also, since the song your bound to is a class, you will need to specify the paths fully for the properties you want to show in the boxes such as "Path=Song.Lyrics".

Another thing to consider is that with dependency properties; your mode will be oneway by default so making the text field editable would be moot really unless you change it.

Third, if you're using MVVM you only need your main window context to be set to the view model and have a matching Song property to bind against.

like image 39
Vynos Avatar answered Sep 28 '22 09:09

Vynos