Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a 'bound' value to a dependency property

My application has a MainWindow. Within this, there is 1 control, a ListBox, binding to a property of the MainWindowViewModel. This property is a UserControl, of type CriteriaVm

CriteriaVm has a single string property called MyString

Within the Criteria View I have the following code

<UserControl x:Class="CompoundInterests.View.CriteriaView"
             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:vm="clr-namespace:CompoundInterests.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="600">

    <UserControl.Resources>
        <ResourceDictionary Source="../Dictionary.xaml" />
    </UserControl.Resources>
    <Grid>
        <Border>
            <Expander Header="{Binding MyString}" >
                <vm:PropertiesVm ThresholdName="{Binding MyString}" />
            </Expander>
        </Border>
    </Grid>
</UserControl>

As you can see, I'm binding MyString in 2 places. The binding in the Expander works fine, the one in the vm:PropertiesVm does not (which is using Dependency Properites). The following error shows in the Output window

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=MyString; DataItem=null; target element is 'PropertiesVm' (HashCode=5991653); target property is 'ThresholdName' (type 'String')

OK the error message tells me it's looking for MyString in ProperitesVm ... I should be looking for MyString in CriteriaVm. This means I need to use RelativeSource I think, which is up 1 level and of type UserControl. So I updated to:

<vm:PropertiesVm ThresholdName="{Binding Path=MyString, RelativeSource={RelativeSource AncestorLevel=1,AncestorType=UserControl,Mode=FindAncestor}}" />

I get a slightly different issue but it appears as if the same underlying fault exists

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=MyString; DataItem=null; target element is 'PropertiesVm' (HashCode=24649639); target property is 'ThresholdName' (type 'String')

At the moment, PropertiesVm only has the dependency properties, the PropertiesView is just an empty grid. This is so I can work on this error first and worry about the next stage of binding later.

I don't see why I'm getting the error message or what I'm doing wrong. I can happily provide more code if needed. The project at this stage is very early, as such, minimal code.

like image 420
MyDaftQuestions Avatar asked Oct 19 '22 23:10

MyDaftQuestions


1 Answers

I'd expect the view to look like this:

<Style TargetType="{x:Type ns:YourViewModel}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ns:YourViewModel}">
                <Grid>
                    <TextBlock Text="{Binding ThresholdName, 
                               RelativeSource={RelativeSource TemplatedParent}}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The view model code would be pretty much unchanged from what you've shown:

public class YourViewModel: Control
{
    public static readonly DependencyProperty ThreshNameProperty = DependencyProperty.Register("ThresholdName", typeof(string), typeof(PropertiesVm), new PropertyMetadata(string.Empty));

    public string ThresholdName
    {
        get { return GetValue(ThreshNameProperty).ToString(); }
        set { SetValue(ThreshNameProperty, value); }
    }
}

You weren't totally clear about what "MyStringValue" is so I'd expect it to be a normal property of the MainWindow. MainWindow would create an instance of your control, setting "ThresholdName" to this "MyStringValue" property:

<Grid>
    <ns:YourViewModel ThresholdName="{Binding MyStringValue}"/>
</Grid>

Finally your MainWindow code would be:

public string MyStringValue { get; set; }

public MainWindow()
{
    InitializeComponent();
    DataContext = this;
    MyStringValue = "dog";
}
like image 94
James Harcourt Avatar answered Oct 21 '22 16:10

James Harcourt