Martin Braun
Martin Braun

Reputation: 12639

ViewModel data will not bind to UserControl exclusively

I have a simple test ViewModel for my MainWindow:

namespace MyApp.ViewModels
{
    class MainWindowViewModel : ViewModelBase
    {
        public string Test { get; set; } = "TEST";
    }
}

Can't get any simpler than that. Now I give my MainWindow knowledge about it and bind the Test property to a TextBox and an own UserControl:

<Window x:Class="MyApp.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                xmlns:local="clr-namespace:MyApp"
                xmlns:uc="clr-namespace:MyApp.UserControls"
                xmlns:vm="clr-namespace:MyApp.ViewModels"
                mc:Ignorable="d"
                Width="800"
                Height="600">
    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBox HorizontalAlignment="Left"
                             Grid.Row="0"
                             TextWrapping="Wrap"
                             VerticalAlignment="Top"
                             Width="120"
                             Text="{Binding Test, PresentationTraceSources.TraceLevel=High}" />

        <uc:TestUserControl Grid.Row="1" Test="{Binding Test, PresentationTraceSources.TraceLevel=High}" />

    </Grid>
</Window>

Again, very simple.

My TestUserControl looks like that:

<UserControl x:Class="MyApp.UserControls.TestUserControl"
             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" 
             xmlns:local="clr-namespace:MyApp.UserControls"
                         mc:Ignorable="d"
                         DataContext="{Binding RelativeSource={RelativeSource Self}}"
                         d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBox HorizontalAlignment="Left"
                         TextWrapping="Wrap"
                         VerticalAlignment="Top"
                         Width="120"
                         Text="{Binding Test, PresentationTraceSources.TraceLevel=High}" />
    </Grid>
</UserControl>
namespace MyApp.UserControls
{
    public partial class TestUserControl : UserControl
    {
        public static readonly DependencyProperty TestProperty =
                DependencyProperty.Register("Test", typeof(string), typeof(TestUserControl),
                        new FrameworkPropertyMetadata((string)String.Empty));

        public string Test
        {
            get { return (string)GetValue(TestProperty); }
            set { SetValue(TestProperty, value); }
        }


        public TestUserControl()
        {
            InitializeComponent();
        }
    }
}

What I basically end up with are two TextBoxes, one wrapped in my UserControl, the other not.

Both TextBoxes should contain the text "TEST", but only the one that is not embedded in the UserControl does.

When I skip the binding of the ViewModel like so:

<uc:TestUserControl Grid.Row="1" Test="TEST" />

The text shows up in the TextBox, which indicates that the user control is fine.

This is the debug output:

System.Windows.Data Warning: 56 : Created BindingExpression (hash=52579650) for Binding (hash=50581426) BindingExpression:Path=Test; DataItem=null; 
System.Windows.Data Warning: 58 :  Path: 'Test'
System.Windows.Data Warning: 60 : BindingExpression (hash=52579650): Default mode resolved to TwoWay
System.Windows.Data Warning: 61 : BindingExpression (hash=52579650): Default update trigger resolved to LostFocus
System.Windows.Data Warning: 62 : BindingExpression (hash=52579650): Attach to System.Windows.Controls.TextBox.Text (hash=15537542)
System.Windows.Data Warning: 67 : BindingExpression (hash=52579650): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=52579650): Found data context element: TextBox (hash=15537542) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=52579650): Activate with root item MainWindowViewModel (hash=13189358)
System.Windows.Data Warning: 108 : BindingExpression (hash=52579650):   At level 0 - for MainWindowViewModel.Test found accessor RuntimePropertyInfo(Test)
System.Windows.Data Warning: 104 : BindingExpression (hash=52579650): Replace item at level 0 with MainWindowViewModel (hash=13189358), using accessor RuntimePropertyInfo(Test)
System.Windows.Data Warning: 101 : BindingExpression (hash=52579650): GetValue at level 0 from MainWindowViewModel (hash=13189358) using RuntimePropertyInfo(Test): 'TEST'
System.Windows.Data Warning: 80 : BindingExpression (hash=52579650): TransferValue - got raw value 'TEST'
System.Windows.Data Warning: 89 : BindingExpression (hash=52579650): TransferValue - using final value 'TEST'
System.Windows.Data Warning: 56 : Created BindingExpression (hash=26543418) for Binding (hash=62601592) BindingExpression:Path=Test; DataItem=null; 
System.Windows.Data Warning: 58 :  Path: 'Test'
System.Windows.Data Warning: 60 : BindingExpression (hash=26543418): Default mode resolved to TwoWay
System.Windows.Data Warning: 61 : BindingExpression (hash=26543418): Default update trigger resolved to LostFocus
System.Windows.Data Warning: 62 : BindingExpression (hash=26543418): Attach to System.Windows.Controls.TextBox.Text (hash=21868813)
System.Windows.Data Warning: 67 : BindingExpression (hash=26543418): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=26543418): Found data context element: TextBox (hash=21868813) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=26543418): Activate with root item TestUserControl (hash=21522166)
System.Windows.Data Warning: 108 : BindingExpression (hash=26543418):   At level 0 - for TestUserControl.Test found accessor DependencyProperty(Test)
System.Windows.Data Warning: 104 : BindingExpression (hash=26543418): Replace item at level 0 with TestUserControl (hash=21522166), using accessor DependencyProperty(Test)
System.Windows.Data Warning: 101 : BindingExpression (hash=26543418): GetValue at level 0 from TestUserControl (hash=21522166) using DependencyProperty(Test): ''
System.Windows.Data Warning: 80 : BindingExpression (hash=26543418): TransferValue - got raw value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=26543418): TransferValue - using final value ''
System.Windows.Data Warning: 56 : Created BindingExpression (hash=3865173) for Binding (hash=22799085) BindingExpression:Path=Test; DataItem=null; 
System.Windows.Data Warning: 58 :  Path: 'Test'
System.Windows.Data Warning: 60 : BindingExpression (hash=3865173): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=3865173): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=3865173): Attach to MyApp.UserControls.TestUserControl.Test (hash=21522166)
System.Windows.Data Warning: 67 : BindingExpression (hash=3865173): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=3865173): Found data context element: TestUserControl (hash=21522166) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=3865173): Activate with root item TestUserControl (hash=21522166)
System.Windows.Data Warning: 107 : BindingExpression (hash=3865173):   At level 0 using cached accessor for TestUserControl.Test: DependencyProperty(Test)
System.Windows.Data Warning: 104 : BindingExpression (hash=3865173): Replace item at level 0 with TestUserControl (hash=21522166), using accessor DependencyProperty(Test)
System.Windows.Data Warning: 101 : BindingExpression (hash=3865173): GetValue at level 0 from TestUserControl (hash=21522166) using DependencyProperty(Test): ''
System.Windows.Data Warning: 80 : BindingExpression (hash=3865173): TransferValue - got raw value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=3865173): TransferValue - using final value ''

What is the reason for this issue and how to fix it, please?

Upvotes: 0

Views: 34

Answers (1)

ASh
ASh

Reputation: 35720

don't change UserControl DataContext - you are breaking the connection with Window DataContext. Instead change Text bidning to refer to Test property via ElementName.

<UserControl x:Class="MyApp.UserControls.TestUserControl"
             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" 
             xmlns:local="clr-namespace:MyApp.UserControls"
             mc:Ignorable="d"
             Name="userControl"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBox HorizontalAlignment="Left"
                         TextWrapping="Wrap"
                         VerticalAlignment="Top"
                         Width="120"
                         Text="{Binding Test, ElementName=userControl, PresentationTraceSources.TraceLevel=High}" />
    </Grid>
</UserControl>

note that even without ElementName=userControl the binding with work, but it will connect directly to dataContext - to MainWindowViewModel.Test and not TestUserControl.Test property. In that case setting property without binding - <uc:TestUserControl Grid.Row="1" Test="TEST" /> - won't work.

also change TestProperty to two-way property:

public static readonly DependencyProperty TestProperty = DependencyProperty.Register
(
    "Test", 
    typeof(string), 
    typeof(TestUserControl),
    new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);

Upvotes: 1

Related Questions