WPF Binding to UserControl´s DependencyProperty not working as expected [duplicate] - c#

This question already has answers here:
How to implement two-way binding on a property?
(2 answers)
Closed 6 years ago.
I have some strange problem with DependecyProperty-binding.
To keep the question simpler i´ve created some dummy-control, that has the same unwanted behaviour
I have a UserControl, that has a DependencyProperty defined in code behind:
public static readonly DependencyProperty TestValueProperty = DependencyProperty.Register("TestValue", typeof(string), typeof(Test), new PropertyMetadata(default(string)));
public string TestValue
{
get { return (string)GetValue(TestValueProperty); }
set { SetValue(TestValueProperty, value); }
}
This property is used in XAML:
<Label Content="{Binding TestValue}" />
This control should be used in another control like this:
<views:Test TestValue="{Binding Settings.Setting123}" />
Settings is defined in viewmodel as property.
But the content of Settings.Setting123 is not visible in my usercontrol´s label.
When writing some fixes value instead of the binding it works fine:
<views:Test TestValue="Test" />
But of course i do not want a fixed value, but the content of the bound object.
Any hint what is going wrong here?
Thanks in advance!

You didn't share enough code for anybody to recreate the issue, but reading between the lines, I'm guessing that Label is in your UserControl XAML. If TestValue is a property of your UserControl, this will probably work:
<Label Content="{Binding TestValue, RelativeSource={RelativeSource AncestorType=UserControl}}" />
However, one reason you might have done that (and had it semi-work, with literal strings) is if you made your UserControl its own DataContext. In that case, then the problem is that you made your UserControl its own DataContext. If you did that, that Binding on the bound one is being evaluated in the context of the UserControl, which does not have a Settings.Setting123 property.
What a control's DataContext means, is that when you have a Binding on one of the controls properties or inside its XAML, that's where the Binding goes to look for the property you bind to. You're explicitly telling it to look in the wrong place.
If you make your UserControl its own DataContext, you can't bind anything to it. That's why you shouldn't do that. It's like one of those machines that does nothing but unplug itself from the wall. Instead, use {RelativeSource AncestorType=UserControl} bindings as above inside the UserControl XAML.
I shouldn't have to guess. You claim you created a minimal verifiable example, but didn't bother sharing it. If you share it, we can solve your problem with confidence.

Related

Set NotifyOnTargetUpdated for existing binding

I have a binding in xaml <TextBlock Style="{StaticResource textStyle}" Text="{Binding DisplayText}" />.
I am attempting to write an attached behavior that reacts to the bound DisplayText value changing. If I specify NotifyOnTargetUpdated=True in the xaml, I can react to the change within the behavior and everything is fine, but I'd rather not depend on binding the Text property in a specific way just to make the behavior work.
My thought was to change the NotifyOnTargetUpdated value on the existing TextBlock.TextProperty binding when the behavior is opted in. I am using the below code to do so, where tb is the TextBlock being opted in.
var textBinding = BindingOperations.GetBinding(tb, TextBlock.TextProperty);
textBinding.NotifyOnTargetUpdated = true;
tb.SetBinding(TextBlock.TextProperty, textBinding);
The behavior is opted in like so, in the style:
<Setter Property="behaviors:Text.AutoSizeText" Value="True"/>
Initially this didn't work because textBinding was null. I can get around this by binding the Text property in xaml before the behavior property, but this still leaves an external dependency that I don't like (xaml ordering). If I do go this route, I get the below exception, which seems to indicate that I can't accomplish this in this way, at all.
InvalidOperationException: Binding cannot be changed after it has been used.
So then, how can I go about automatically handling setting NotifyOnTargetUpdated for the Text binding when the behavior is opted in?
I was able to solve my problem thanks to direction provided by #canton7. I was originally (as is often the case) looking for the way to implement my imagined solution, rather than a solution that fit my need. After adjusting my outlook, my working solution is thus:
Add the AttachedProperty InternalText to the behavior class, with a property changed handler.
private static readonly DependencyProperty InternalTextProperty = DependencyProperty.RegisterAttached(
"InternalText", typeof(string), typeof(Text), new PropertyMetadata(default(string), HandleInternalTextChanged));
In the changed handler (HandleInternalTextChanged above) do the work that I would have done in a TargetUpdated handler if my original idea to set NotifyOnTargetUpdated had worked out.
On opt-in to my behavior, create a binding from the opted-in TextBlock.Text to the InternalText attached property.
var internalBinding = new Binding { Source = tb, Path = new PropertyPath(TextBlock.TextProperty) };
tb.SetBinding(InternalTextProperty, internalBinding);
The HandleInternalTextChanged callback on InternalTextProperty allows me to work around being unable to change the NotifyOnTargetUpdated value by providing an alternate means of notifying on each change.
I prefer to bind internally to DisplayText because I'd prefer to bind to the source, if possible, rather than daisy-chain through the TextBlock.Text property.
If you need a binding to a source property, then it is created in a slightly different way.
var binding = BindingOperations.GetBindingBase(tb, TextBox.TextProperty);
if (binding == null)
{
tb.ClearValue(InternalTextProperty);
}
else
{
tb.SetBinding(InternalTextProperty, binding);
}

Content.Binding.ElementName vs DataContext.Binding.ElementName, can anyone explain the subtle differences?

I have encountered the following strange behaviour in WPF:
Let's say I have a Window that is implemented in SomeWindow:
<Window x:Name="MainWindow" x:Class="SomeWindow">
</Window>
In this Class I expose two public properties: one of type SomeModel and one of type ObservableCollection<SomeModel>.
SomeModel implements INotifyPropertyChanged and exposes an notifying property SomeProperty.
public partial class SomeWindow : Window
{
public ObservableCollection<SomeModel> ListOfSomeModel { get; set; }
public SomeModel InstanceOfSomeModel { get; set; }
}
Both are initialized elsewhere, but timely.
Now I can bind these properties to WPF Controls in a couple of different ways:
Method 1
I set the DataContext of the Window to itself. The children use this value when none is set on the children itself.
public SomeWindow()
{
InitializeComponent();
DataContext = this;
}
Next, I do the binding in XAML as follows:
<Label x:Name="Label1" Content="{Binding Path=ListOfSomeModel[0].SomeProperty}" />
<Label x:Name="Label2" Content="{Binding Path=InstanceOfSomeModel.SomeProperty}" />
This works. (As in: both Labels are bound to the Object that is referenced and are updated on mutation of the data).
Method 2
There is no default DataContext in SomeWindow, but DataContext is set on the element itself in XAML.
<Label x:Name="Label1" DataContex="{Binding ElementName=SomeWindow}" Content="{Binding Path=ListOfSomeModel[0].SomeProperty}" />
<Label x:Name="Label2" DataContex="{Binding ElementName=SomeWindow}" Content="{Binding Path=InstanceOfSomeModel.SomeProperty}" />
This also works. So far, so good!
Method 3
Now finally, I don't set the DataContext at all, but rather set the Binding.ElementName directly on the Content.
<Label x:Name="Label1" Content="{Binding ElementName=SomeWindow, Path=ListOfSomeModel[0].SomeProperty}" />
This doesn't work. OK, so maybe I'm not correctly understanding the subtle differences of setting the DataContext vs telling the Binding directly where to look. Maybe this way you can only access the build in properties of the base class. Or maybe....
... BUT WAIT
The other one still works:
<Label x:Name="Label2" Content="{Binding ElementName=SomeWindow, Path=InstanceOfSomeModel.SomeProperty}" />
So can anyone explain the technical working of both methods and why in Method 3 the elements of the ObservableCollection are no longer accessible (or at least bindable)? Or am I overlooking something trivial?
Disclaimers
I know it's not safe to access an unordered collection by index.
I know it's ridiculous to put a reference to a member of a collection in the XAML, as you can never be sure it exists and it's crazy anyway and I should use a ViewModel, etc, etc
It's just like this to demonstrate the observed behaviour.
This is solved with help from the comments above.
The main culprit was (from my original question):
Both are initialized elsewhere, but timely.
This was false. They were not initialized timely, but after InitializeComponent(). I didn't notice that, because Method 1 and 2 did work.
So the 'subtle difference' I was looking for seems to be:
In the case of methods 1 and 2 a permanent DataContext is set, so even if initial binding of the value fails because the collection is not yet initialized, subsequent updates triggered by the raised PropertyChanged event DO succeed.
In case of method 3, a permanent DataContext is not set and after the initial failed binding, the control is left completely unbound and will not respond to further PropertyChanged events.
Thanks for helping out!

Dependency Property on a usercontrol that is bound to another VM

I've run into the following problem:
I'm currently creating an on screen keyboard that is a usercontrol that has its own viewmodel.
<UserControl.DataContext>
<Binding Source="{StaticResource Locator}" Path="AlphaNumericKeyboard" />
</UserControl.DataContext>
I'm attempting to add a dependency property called KeyboardAlphaMode that can be toggled by other view models that are using this usercontrol
public static readonly DependencyProperty KeyboardAlphaModeProperty = DependencyProperty.Register("KeyboardAlphaMode",
typeof(UIKeyboardAlphaMode), typeof(AlphaNumericKeyboardView),
new FrameworkPropertyMetadata(UIKeyboardAlphaMode.LowerCase, new PropertyChangedCallback(KeyboardAlphaModeCallBack)));
private static void KeyboardAlphaModeCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e){ ... }
But, when I attempt to bind to this property from another view, the callback was never fired ..
<k:AlphaNumericKeyboardView x:Name="alphaNumericKeyboard" KeyboardAlphaMode="{Binding KeyboardAlphaMode, UpdateSourceTrigger=PropertyChanged}">
</k:AlphaNumericKeyboardView>
What am I missing here? a setter? trigger?
Or this is just a thought, can a usercontrol that has dependency be bound to a viewmodel? or does it have to be bound to itself?
Edit - 10/10/2014 # 1:31pm
After rethinking the entire solution i came up with the following scenario for my problem.
I binded the Dependency Property to the view's viewmodel and let the viewmodels interact with each other instead having other viewmodel talking to this specific view ...
Here's the code for that ..
Binding alphaModeBinding = new Binding("KeyboardAlphaMode")
{
Mode = BindingMode.TwoWay,
TargetNullValue = UIKeyboardAlphaMode.LowerCase,
FallbackValue = UIKeyboardAlphaMode.LowerCase
};
this.SetBinding(KeyboardAlphaModeProperty, alphaModeBinding);
I also made the dependency property protected so no one else can access it.
Unless there is a better way to track property changes, i'm sticking with this for now.
Again, not sure this is the best solution but it gets the job done.
Try Mode=TwoWay on the binding.

How do dependency properties work? [duplicate]

This question already has answers here:
What's the framework mechanism behind dependency properties?
(4 answers)
Closed 2 years ago.
Trying to understand how this code works:
Create dependency property,
public int YearPublished
{
get { return (int)GetValue(YearPublishedProperty); }
set { SetValue(YearPublishedProperty, value); }
}
public static readonly DependencyProperty YearPublishedProperty =
DependencyProperty.Register(
"YearPublished",
typeof(int),
typeof(SimpleControl),
new PropertyMetadata(2000));
Then use it in a form,
<xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:SimpleControl x:Name="_simple" />
<TextBlock Text="{Binding YearPublished, ElementName=_simple}"
FontSize="30"
TextAlignment="Center" />
<Button Content="Change Value"
FontSize="20"
Click="Button_Click_1"/>
</StackPanel>
Then for Button_Click_1 do,
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_simple.YearPublished++;
}
It works. Each time when you press the button, number must be changed from PropertyMetadata - from 2000++, but also I saw it on form in textBox.
Question: Why?
If I don't put any code for updating TextBlock in main Form, is it automatically updating or is there some hidden mechanism for it? Or maybe I do not fully understand how it works. Or maybe if its property there are features, that update the number on the form.
When you created a DependencyProperty,
DependencyProperty.Register(
"YearPublished",
typeof(int),
typeof(SimpleControl),
new PropertyMetadata(2000));
based on the YearPublished property, you are basically registering it with the DependencyProperty framework in a way that every time the property is read from or written to, it notifies all subscribers of the event that has taken place. You register it by specifying the name of the property i.e. "YearPublished", the property type, the type of the control where the property resides and, in this case the initial value of 2000.
By binding it to the TextBlock,
<TextBlock Text="{Binding YearPublished, ElementName=_simple}" />
you are letting the text block know when the property changes so it can update itself. When the YearPublished property changes, it notifies the text block of this change, which in turn retrieves the updated value and updates its Text property with it.
This is a very high level view, though, but enough to use it properly. To further understand the internals take a look at this MSDN post.
If the binding has the correct settings and the data provides the proper notifications, then, when the data changes its value, the elements that are bound to the data reflect changes automatically.
check this overview

Custom UserControl "IsEnabled" data binding not working

I have a kinda awful problem with my WPF application right now...
I have a custom UserControl used to edit details of a component. It should start by being not enabled, and become enabled as soon as the user chose a component to edit.
The problem is: the IsEnabled property does not even change.
Here is my code:
<my:UcComponentEditor Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding EditorEnabled}"
DataContext="{Binding VmComponent}" />
EditorEnabled is a property in my ViewModel (VmComponent), and is by default false, becomes true when the user chose a component or created one
Just for the record, in my ViewModel:
private Boolean _editorEnabled = false;
public Boolean EditorEnabled
{
get { return _editorEnabled; }
set
{
_editorEnabled = value;
OnPropertyChanged("EditorEnabled");
}
}
When I try to launch my app, the UserControl is starting... enabled.
I added breakpoints everywhere, the EditorEnabled is false from the beginning.
I also did a horribly stupid thing to try to figure out what's happening: I created a converter (so useful -- converting a boolean to boolean -- eh), put a breakpoint on it, and... The code is never reached.
<my:UcComponentEditor Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsEnabled="{Binding EditorEnabled, Converter={StaticResource BoolConverter}}"
DataContext="{Binding VmComponent}" />
That probably means that the property isEnabled is never set, since the converter is never reached.
Do you see any kind of problem there? I started working in WPF about one week ago and therefore I may have missed something essential...
Thank you very much for your time :-)
You should add a DependencyProperty for the binding to work properly. See here for more information.
Code-behind:
public static readonly DependencyProperty EditorEnabledDependencyProperty = DependencyProperty.Register("EditorEnabled", typeof(bool), typeof(UcComponentEditor), new PropertyMetadata(false));
public bool EditorEnabled
{
get { return (bool)base.GetValue(UcComponentEditor.EditorEnabledDependencyProperty); }
set { base.SetValue(UcComponentEditor.EditorEnabledDependencyProperty, value); }
}
The issue I think is that there is a binding on the DataContext property of the user control. Which means the EditorEnabled property should be a property in the VmComponent object. At least that's what my problem was.
To get around it, I specified a proper source to the binding of IsEnabled. Once I did that the control started working as expected.
Hope that helps.
Encapsulating your control in a DockPanel (for example) will remove the need for a DependencyProperty.
You can then simply do your binding with the dockpanel instead of the custom control. Setting the variable bound to IsEnabled on the Dockpanel will automatically enable or disable the items contained in the Dockpanel.

Categories