-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
One-Way Binding immediately lost when the target Dependency Property is internally updated #9910
Comments
The |
Sorry, can you explain what you mean? |
Sorry, I went with 8 years of professional WPF experience but probably also accounted for telepathy: In https://source.dot.net/#PresentationFramework/System/Windows/Controls/TextBox.cs,666 If you set |
Thanks for explaining! 😅 telepathy didn't work as expected The That is my "internal technique" inside of Using Needless to say this is just a sample to reproduce the issue, not my productive code, so it does not make a lot of sense on first sight. |
I just wanted to show you with the trigger that you updating the property via code-behind has no effect on whether it breaks or not (so the "reproduction" steps could have been simplified). Your "internal technique" has a basic design flaw if I understand you correctly, and that's you wanting to have You're gonna need two dependency properties (one for each binding, TwoWay for TextBox, OneWay for your case) and handle the "OneWay" synchronization via |
What you understood is correct: In my sample, I want to have this: However, the internal binding ( My requirement in more simple words: I have a custom Control/UserControl with an exposed DependencyProperty The predefined TextBox in WPF perfectly fulfills this simple requirement with its DP Is the TextBox doing some special magic to prevent breaking the binding? Is this a desired behavior of WPF (PS: I'll give it a try later with 2 DPs, but I don't think that would make a difference, I already tried setting the DP value in code instead of the binding, no chance) |
There's no magic, the difference is that your VM property is not a Here's the working example for your desired effect, this way MyCustomInput.xaml.cs: public static readonly DependencyProperty VMBoundProperty = DependencyProperty.Register(
nameof(VMBound), typeof(string), typeof(MyCustomInput),
new FrameworkPropertyMetadata(default(string), (d, e) => { d.SetValue(TextBoundProperty, e.NewValue); Debug.WriteLine($"VM-Old: {e.OldValue}"); Debug.WriteLine($"VM-New: {e.NewValue}"); }));
public string VMBound
{
get => (string)GetValue(VMBoundProperty);
set => SetValue(VMBoundProperty, value);
}
public static readonly DependencyProperty TextBoundProperty = DependencyProperty.Register(
nameof(TextBound), typeof(string), typeof(MyCustomInput),
new FrameworkPropertyMetadata(default(string), (d, e) => { Debug.WriteLine($"Text-Old: {e.OldValue}"); Debug.WriteLine($"Text-New: {e.NewValue}"); }));
public string TextBound
{
get => (string)GetValue(TextBoundProperty);
set => SetValue(TextBoundProperty, value);
} MyCustomInput.xaml: <TextBox Text="{Binding ElementName=UserControl, Path=TextBound, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/> MyView.xaml: <lostBindingSample:MyCustomInput x:Name="MyCustomInput" VMBound="{Binding TheValue, Mode=OneWay}"/> |
Thanks a lot for the working example! I see now what you mean by using 2 DPs. You propagate the I consider this proposition an alternative workaround for the problem. The requirement is having one single exposed DP that can be bound either I am more concerned about understanding the theoretical root cause of why the |
A very interesting point about When you edit the text via This is what I called previously "some special magic". I'll try to dig into TextBox code to find out how that works. |
Don't dig into And possibly into understanding the differences between Same as what happens when you attempt to have a single And one more EDIT: It breaks because the expression tries to assign a value to |
Thanks for bringing up To summarize (correct me if I am wrong):
That explains that this Binding chain has a "design flow" (as you mentioned): So, either this: or this: If all of this is correct, this introduces new rules to write custom Control/UserControl that does not accidentally break eventual
Unfortunately, these rules, despite being rational, seems to be unnatural and would complicate writing custom controls and bloat the code. Alternatively, we do not introduce any new rules. Instead of that we make our WPF developers aware, that Actually, I barely use |
What a nice summary you've made, this would be pretty much a good answer to someone. Unluckily I don't have that gift, haha. Yeah, based on your models, there's also
I do not agree with the "new rules", you should be aware how binding expressions work and what may break it. Imho what should be said is; use The main takeaway is, don't expect one DP to work in two exclusive modes as it was expected in this case. Binding between DPs is not the same as binding between POCO property and a DP: |
I would also say, the awareness of the "breakability" of I like always to have clear best practices to "enforce" within my team to avoid such "silent" problems. But I think this is not the case. To close up our discussion: WPF bindings have the following behaviors that should be known by developers
Although these behaviors are somehow confusing and can theoretically be improved, I see nothing that may be touched here in the deepest WPF codes. We do not want to break half the world again 🤣 Nevertheless, I suggest adding a binding Error/Warning that can be seen in Output and in the "XAML Binding Failures" window in case a Such an Error/Warning would be very helpful for every developer struggling with such broken bindings. Since we have no clear best practices to 100% eliminate the risk. What do you think? |
The tracing could be theoretically improved as to reason why the detach has happened. Touching anything around that is a thin ice though, it still uses Documentation could be improved as to what is by design, I agree. |
Description
Hi,
I've been writing advanced WPF stuff for the last 8 years. This is the first time I notice this odd behavior of One-Way bindings in such a basic use case:
The One-Way binding is cleared by the WPF engine when the target Dependency Property (in a custom Control/UserControl) is internally updated (in code or via internal binding).
I prepared a very simple sample to reproduce that, to which I added a
BindingObserver
control that continuously outputs the state of theBindingExpression
on the UI.Here is the sample:
https://github.com/amine2050/wpf-lost-binding
Am I missing a basic knowledge of how One-Way bindings behave? Or is this a known issue? I could not find any literature about the topic.
Reproduction Steps
Expected behavior
One-way binding to ViewModel is expected to continue working even after changing the value of the DependencyProperty (by changing TextBox content).
The new content of the TextBox should not propagate to the binding Source (which is correct here), but changes in the Source are expected to be reflected on the TextBox (this does not happen anymore).
I want the same behavior of a simple TextBox bound to ViewModel using One-Way binding.
Actual behavior
One-way binding to ViewModel has been cleared by the WPF engine without any warnings or output. Changes in ViewModel are no loger propagating to UI.
Regression?
I tested this with .NET 8.0 and .NET Framework 7.2.1 and got the same behavior. So no regression is known.
Known Workarounds
The only workaround I found is to use Two-Way binding instead of One-Way. But this is not necessarily what I want actually.
Impact
This applies to any custom Control/UserControl that has properties you want to change internally while allowing binding them using One-Way binding.
Configuration
Other information
I added
PresentationTraceSources.TraceLevel=High
to the binding and got the following output when the binding gets broken:The text was updated successfully, but these errors were encountered: