Search code examples
c#wpfxamldata-bindingwpf-controls

DependencyProperty fails to update window datacontext


I have a bad case of semi working bindings between a usercontrol with dependencyproperty and a window where I have a custom class as model in datacontext.

My window

<src:BaseWindow x:Class="Levscan.Wpf.Controls.DialogWindows.SystemSettingsDialog"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:src="clr-namespace:Levscan.Wpf.Controls.Abstract"
    xmlns:ctrl_io="clr-namespace:Levscan.Wpf.Controls.Controls.IO"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d">

    <ctrl_io:DirectoryBrowser Width="250" SelectedPath="{Binding Path=PathInbox, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
</src:BaseWindow>

My Usercontrol

<UserControl Name="DirectoryBrowserUC" x:Class="Levscan.Wpf.Controls.Controls.IO.DirectoryBrowser"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="53" d:DesignWidth="392">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="8*" />
        <ColumnDefinition Width="2*" />
    </Grid.ColumnDefinitions>

    <TextBox Text="{Binding ElementName=DirectoryBrowserUC, Path=SelectedPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Style="{StaticResource TextBoxInError}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0" Margin="5,0" />
    <Button Margin="5,0" Content="..." Grid.Column="1" Click="OnBrowse_Click" />
</Grid>

My Usercontrol - Code behind

public partial class DirectoryBrowser : UserControl
{
    public static readonly DependencyProperty SelectedPathProperty =
        DependencyProperty.Register("SelectedPath", typeof(string), typeof(DirectoryBrowser));
    public string SelectedPath
    {
        get { return this.GetValue(SelectedPathProperty) as string; }
        set { this.SetValue(SelectedPathProperty, value); }
    }
}

When I start my window I load a custom class which inherit INotifyPropertyChanged interface. My property does NotifyPropertyChanged() when new data is set.

When I start my textbox in the usercontrol gets updated by the data from my window and the custom class in the windows DataContext ( all good so far ).

However I have then noticed that when I edit the textbox either direct (enter new text) or with the button next to it and select a new path with the windows directory dialog, my property in the DataContext is not updated.

I've tried all kinds of binding variants but nothing has worked, either no data is loaded or I can't change the values.

I suspect that somewhere my bindings is just a little bit off.

UPDATE

Seems like all I needed in the codes above was to add the Mode=TwoWay on the binding on my window for the control. Since I swapped out stuff and added I never noticed that's the only thing missing so when I actually did have it there something else was off.


Solution

  • Add Mode=TwoWay in your binding of SelectedPath. Default binding mode for a custom dependency property is OneWay.

    It is not magical. First there is a default binding mode on a dependency property. A default "default binding mode" is OneWay when you write your own dependency property, you can override it to TwoWay as what Clemens suggests. If you do not specify binding mode in user control level, WPF will use default binding mode, which is OneWay. This explains why data context did not get updated.

    Second binding mode is defined on target, not source, so no matter what default binding mode is on SelectedPath, it will get updated from TextBox since default binding mode of Text property is two way binding(lost focus).

    In a word. Simply adding a two way binding on your user control level explicitly or overriding FrameworkPropertyMetadata and providing two way binding as default will solve your question.