0
votes

I have a custom usercontrol with a custom dependency property of type string. It should be bound twoway to a textbox, which is in a surrounding app, like this:

<Textbox Name="TextBoxInHostApp"/>
<ctrl:MyControl 
     ....
         MyString = "{Binding ElementName=TextBoxInHostApp, Path=Text, Mode=TwoWay}"
</ctrl:MyControl>

Problem:

The Control should have the functionality to reveal the value of the dependency property also!

In MyControl, I have a simple string property "Name", which should update the MyString dependeny property value.

So normally, if MyString was not bound to the outside world, you would code in the xaml where the control is used:

<Textbox Name="TextBoxInHostApp"/>
<ctrl:MyControl 
     ....
         MyString = "{Binding Name}"
</ctrl:MyControl>

But that's not available in my case.

Attempt 1: I used a trigger inside the usercontrol xaml:

<UserControl.Style>
    <Style>
        <Style.Setters>
            <Setter Property="local:MyControl.MyString" Value="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
        </Style.Setters>
    </Style>

Now this trigger doesn't work, because the "local value" of the dp is already set here:

MyString = "{Binding ...

if i comment out:

<!--<MyString = "{Binding ...

...the trigger works fine, because the "local value" isn't set.

next attempt: I try the hard way and code in MyControls viewmodel:

public string Name
{
    get
    {
        return _name;
    }
    set
    {
        _name = value;
         RaisePropertyChanged("Name");

         MyOwningUICtrl.SetValue(MyControl.MyStringProperty, value); // ??
    }
}

I have to pass a DependencyObject to call a SetValue, which is unpleasant for MVVM. There must be better solutions for this scenario ?

2

2 Answers

3
votes

This is by design

Per WPF's Dependency Property Precedence List, values specified locally within a <Tag> will always take precedence over values specified in a Style or a Trigger

A common problem is setting a value in a <Tag>, then trying to change it in a Style.Trigger. The solution is to set the default value as a <Setter> in the <Style> instead of in the <Tag>, which will then allow the triggered value to take precedence over the styled value

<Style>
    <!-- Default Value
    <Setter Property="local:MyControl.MyString" 
            Value="{Binding ElementName=TextBoxInHostApp, Path=Text, Mode=TwoWay}"/>

    <Style.Triggers>
        <Trigger ...>
            <!-- Triggered Value -->
            <Setter Property="local:MyControl.MyString" 
                    Value="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
        </Trigger>
    </Style.Setters>
</Style>

But I don't actually see a Trigger defined in your post anywhere, so I'm not really sure if this is your problem or not.

I'm a bit unclear about the exact structure of your code, but keep in mind that a Dependency Property is kind of like a pointer to another value. You can set it to point to TextBox.Text, or to point to DataContext.Name, but not both.

If you want to set both TextBox.Text and MyUserControl.MyString to point to DataContext.Name, then you should bind both properties to "{Binding Name}" like Natxo suggests.

Or if you are trying to bind MyString to UserControl.Name if MyString is not specified in the <ctrl:MyControl ...> tag, then you can try using a PriorityBinding (example here), or just expose the Name property to the outside world to let outside elements use the control like <ctrl:MyControl Name="{Binding ...}" />

1
votes

If you display in both the Textbox and Mycontrol the same value... why dont you bind them to that property? :

<Textbox Name="TextBoxInHostApp" Text="{Binding Name}"/>
<ctrl:MyControl 
     ....
         MyString="{Binding Name}"
</ctrl:MyControl>