WPF DataBinding: Using SetCurrentValue in CLR property? - c#

I have a custom WPF DependencyObject which owns a property DependentValue. DepedentValue will only ever output its value, the object never reads it. The idea is that the DependentValue property is bound in XAML either as a TwoWay or OneWayToSource binding and updated by the object. To bugs when DependentValue is assigned before the binding has completed, I would like to use SetCurrentValue to update the property.
The conventional CLR property would look like this:
public object DependentValue
{
get { return GetValue(DependentValueProperty); }
set { SetValue(DependentValueProperty, value); }
}
Because of the issue with SetValue, this setter should never be used from inside the object. Therefore, I would like to implement the following CLR property:
public object DependentValue
{
set { SetCurrentValue(DependentValueProperty, value); }
}
This works as expected, but the convention seems to imply that only SetValue should ever be used inside a CLR property. What are the issues with this approach?
For context: I have built a WPF component which can be used to conditionally link two databindings: A 'driver' value is forwarded to a 'dependent' value only if a boolean property is true. This means that the 'dependent' value is 'write-only' from the perspective of this component.

Related

What's wrong with the simplest Data-binding with WPF without using `INotifyPropertyChanged` or `DependencyProperty`

I'm just been playing with Data Bindings in WPF (I'm new to all this) and the following code is the simplest implementation that I can get to work. Code below:
My question is:
Why would I need to use INotifyPropertyChanged and all the boilerplate code that comes with it or DependencyProperty etc when the simple below works just fine out of the box?
I'm trying to understand why the examples and answers on this site are far more complicated that the example below.
My XAML
<TextBox Text="{Binding ConduitWidth, Mode = TwoWay}" />
<TextBox Text="{Binding ConduitWidth, Mode = TwoWay}" />
My Code-behind
public partial class ConduitCapacityCalculator : UserControl
{
ConduitCapacity conduitCapacity = new ConduitCapacity();
public ConduitCapacityCalculator()
{
InitializeComponent();
this.DataContext = conduitCapacity;
conduitCapacity.ConduitWidth = 10; //Just to check the textboxes update properly
}
}
And my Class
public class ConduitCapacity
{
private double _conduitWidth;
public double ConduitWidth
{
get { return _conduitWidth; }
set { _conduitWidth = value; } //add any conditions or Methods etc here
}
}
Because Mode = TwoWay is not true in your example.
Without any signalling (INotifyPropertyChanged) from the Source you are only getting OneWayToSource + OneTime modes.
To test this, add a button and make it do: conduitCapacity.ConduitWidth = 100;
See if you get that 100 in your Control.
Such a binding does work indeed, but will create a memory leak. The framework will create a static reference to the source object ConduitCapacity to observe it. Since static references are never eligible for the garbage collector, the static object reference will keep the object ConduitCapacity alive, preventing it from being collected. This also applies when binding to collections that do not implement INotifyCollectionChanged.
If you are concerned to avoid memory leaks, then the source of a data binding must implement INotifyPropertyChanged, INotifyCollectionChanged or the property must be a DependencyProperty.
DependencyProperty provides the best performance. This means when the source object is a DependencyObject, you should prefer to implement properties that are intended to be used as binding source as DependencyProperty.
Update:
How data binding works when not following the INotifyPropertyChanged or DependencyProperty binding pattern and how to enable TwoWay binding using this method
After some discussions in the comment section, I felt the need to update the question to explain the background a little bit more.
Combining the information provided by Microsoft Docs: How Data Binding References are Resolved and the Microsoft Knowledge Base document
KB 938416,
we understand that WPF uses three methods to establish a data binding from a DependencyProperty (binding target) to any CLR object (binding source):
TypeDescrtiptor (component inspection)
INotifyPropertyChanged
DependencyProperty
The original question relates to method 1): creating a data binding to a source, that does not implement INotifyPropertyChanged or a DependencyProperty. Therefore, the framework has to make use of the heavy TypeDescriptor to setup the binding and tracking of property changes.
From the KB 938416 document we learn that the binding engine will store a static reference to the obtained PropertyDescriptor (by using the TypeDescriptor). Since obtaining the PropertyDescriptor this way is very slow, the PropertyDescriptor reference is stored in a static HashTable (to avoid successive component inspection).
The framework uses this PropertyDescriptor to listen to property changes. Now, because the descriptor intance is stored in a static HashTable, it will never be eligible for garbage collection.
Static references or objects are generally never managed by the farbage collector.
Hence the memory leak, as the static reference will keep the source object alive for the lifetime of the application.
To unlock TwoWay binding, we have to explicitly enable the support in order to make the PropertyDescriptor be aware of property changes on the Binding.Source.
We can test this awareness by querying the PropertyDescriptor.SupportsChangeEvents property. It is true when:
We use DependencyObject.SetValue to modify the property (which means the property is also a DependencyProperty) or
The Binding.Source provides an event, that must comply with the following naming pattern: "[property_name]Changed"
This means, without an extra event, the binding to the plain CLR object can only be OneTime or OneWayToSource. Initialization from source to target will always work.
Example
CLR object
The binding source, that does not implement INotifyPropertyChanged, but still supports TwoWay binding.
class ClrObject
{
public string TextProperty { get; set; }
// Event to enable TwoWay data binding
public event EventHandler TextPropertyChanged;
protected virtual void OnTextPropertyChanged()
=> this.TextPropertyChanged?.Invoke(this, EventArgs.Empty);
}
This is what the WPF framework is doing:
// Simplified. Would use reflection and binding engine lookup table to retrieve the binding.
// Example references a TextBox control named "BindingTarget" for simplicity
Binding binding = BindingOperations.GetBinding(this.BindingTarget, TextBox.TextProperty);
// Only observe Binding.Source when binding is TwoWay or OneWay
if (binding.Mode != BindingMode.OneWay
&& binding.Mode != BindingMode.TwoWay)
{
return;
}
object bindingSource = binding.Source ?? this.BindingTarget.DataContext;
// Use heavy TypeDescriptor inspection to obtain the object's PropertyDescriptors
PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(bindingSource);
foreach (PropertyDescriptor descriptor in descriptors)
{
if (descriptor.Name.Equals(binding.Path.Path, StringComparison.OrdinalIgnoreCase)
&& descriptor.SupportsChangeEvents)
{
// Add descriptor to static HashTable for faster lookup
// (e.g. in case for additional data bindings to this source object).
// TypeDescriptor is too slow
// Attach a change delegate
descriptor.AddValueChanged(bindingSource, UpdateTarget_OnSourcePropertyChanged);
break;
}
}
We can see why this way of data binding performs do bad. TheTypeDescriptor is very slow. Additionally, the engine has to use more reflection to find theTextPropertyChanged event to initialize the TwoWay binding.
We can conclude, that even if it wasn't for the memory leak, we would avoid this solution and rather implement INotifyCollectionChanged on CLR objects or better implement properties as DependencyProperty (in case the source is a DependencyObject) to improve the application's performance significantly (an application usually defines hundreds of bindings).
This is not wrong. The reason for the INotifyPropertyChanged is so your UI updates when you change your class.
For Example, if you decide to update conduitCapacity later your UI would still display the old Value. INotifyPropertChanged Documentation
The reason for the DepedencyProperties is that you can extend your UserControl with it let's say you want to use your Usercontrol in your MainWindow and give it some Property like MaximumConduitWidth. Than you would do something like this:
public double MaximumConduitWidth
{
get { return (double)GetValue(MaximumConduitWidthProperty); }
set { SetValue(MaximumConduitWidthProperty, value); }
}
public static readonly DependencyProperty MaximumConduitWidthProperty =
DependencyProperty.Register("MaximumConduitWidth", typeof(double), typeof(ConduitCapacityCalculator), new PropertyMetadata(0));
And then you could write this in your MainWindow.xaml
<ConduitCapacityCalculator MaximumConduitWidth=10/>
Dependency Property

Can a property change without its setter getting called?

I have a simple property within a Model class with a getter and setter
private bool _isThresholdBreached;
public bool IsThresholdBreached
{
get
{
return _isThresholdBreached;
}
set
{
if(_isThresholdBreached == value)
return;
_isThresholdBreached = value;
OnThresholdBreached?.Invoke(this, new EventArgs());
}
}
Strangely enough the property's value is getting changed without the setter getter hit. I have put a breakpoint both within the getter and setter and I see that when the getter is called on successive occasions the value has changed but the setter breakpoint never hit. How is this even possible ? Any pointers please.
EDIT : I have verified and can confirm that the backing field is not modified anywhere else in the class except in the setter
Your property has a backing field.
Any code calling the field directly would update that value without calling the setter (this could be both direct or via reflection).
I would start with Find usages of the _isThresholdBreached followed by full text search in the solution.

How do I get a `BindingExpression` from a `Binding` object?

In short(?), I have a ListView (target) one-way bound to an XmlDataProvider (source) two-way bound to a TextBox (target) using standard XAML for the control bindings and custom XAML extensions for the bindings to the XmlDataProvider. This is a convenience for the application as the XmlDataProvider is dynamically loaded from user inputs after the application is running,...
Anyway, at run-time, after modifying the TextBox.Text property, the IMultiValueConverter.ConvertBack(...) method is called to propagate the update from this target back to the source. But, because the XmlDataProvider object is not a DependencyProperty, the update is not further propagated from the changed XmlDataProvider source to the other binding to the ListView target.
Without rearchitecting, which you could legitimately advise, I need to notify WPF that any target with this XmlDataProvider as a source needs to be updated. I am hoping to maintain a generic, reusable binding class and have, so far, enjoyed the low coding burden of my mostly XAML solution.
Currently, the only code behind access I have is from within the IMultiValueConverter.ConvertBack(...) method. From within this method I do have access to the Binding object for the XmlDataProvider <--> TextBox link. If I could get the BindingExpression for the Binding.Source object I could then make the call to BindingExpression.UpdateTarget() to complete the update propagation,...
But, I do not know how to get a BindingExpressionfrom a Binding.Source object, that is not associated with a DependencyProperty.
Thanks in advance for your advice and assistance.
You can create a custom MarkupExtension which accepts a Binding as a constructor argument. In XAML usage, yours will be an outer binding that wraps the WPF one:
<StackPanel>
<TextBox x:Name="tb" />
<TextBlock Text="{local:MyBinding {Binding ElementName=tb,Path=Text,Mode=OneWay}}" />
</StackPanel>
In the MyBinding constructor you will receive a WPF Binding object. Store a copy for later when your ProvideValue is called. At that time, you can call ProvideValue on the binding you saved--and pass it the IServiceProvider instance you now have. You'll get back a BindingExpression that you can then return from your own ProvideValue.
Here's a minimal example. For a simple demonstration, it just adds (or overwrites) a Binding.StringFormat property to the inner (wrapped) binding.
[MarkupExtensionReturnType(typeof(BindingExpression))]
public sealed class MyBindingExtension : MarkupExtension
{
public MyBindingExtension(Binding b) { this.m_b = b; }
Binding m_b;
public override Object ProvideValue(IServiceProvider sp)
{
m_b.StringFormat = "---{0}---"; // modify wrapped Binding first...
return m_b.ProvideValue(sp); // ...then obtain its BindingExpression
}
}
If you try it with the XAML above, you'll see that a live binding is indeed set on the target, and you didn't have to unpack the IProvideValueTarget at all.
This covers the basic insight, so if you know exactly what to do now, you probably won't need to read the rest of this answer...
More details
In most cases, digging into the IProvideValueTarget is actually the point of the whole exercise, because you can then modify the wrapped binding dynamically according to runtime conditions. The expanded MarkupExtension below shows the extraction of the relevant objects and properties, and there are obviously numerous possibilities for what you can do from there.
[MarkupExtensionReturnType(typeof(BindingExpression))]
[ContentProperty(nameof(SourceBinding))]
public sealed class MyBindingExtension : MarkupExtension
{
public MyBindingExtension() { }
public MyBindingExtension(Binding b) => this.b = b;
Binding b;
public Binding SourceBinding
{
get => b;
set => b = value;
}
public override Object ProvideValue(IServiceProvider sp)
{
if (b == null)
throw new ArgumentNullException(nameof(SourceBinding));
if (!(sp is IProvideValueTarget pvt))
return null; // prevents XAML Designer crashes
if (!(pvt.TargetObject is DependencyObject))
return pvt.TargetObject; // required for template re-binding
var dp = (DependencyProperty)pvt.TargetProperty;
/*** INSERT YOUR CODE HERE ***/
// finalize binding as a BindingExpression attached to target
return b.ProvideValue(sp);
}
};
For completeness, this version can also be used with XAML object tag syntax, where the wrapped Binding is set as a property, instead of in the constructor.
Insert your customization code for manipulating the binding where indicated. You can do pretty much anything you want here, such as:
Check or modify the runtime situation and/or state:
var x = dobj.GetValue(dp);
dobj.SetValue(dp, 12345);
dobj.CoerceValue(dp); // etc.
Reconfigure and/or customize the binding prior to sealing it into the BindingExpression:
b.Converter = new FooConverter(/* customized values here */);
b.ConverterParameter = Math.PI;
b.StringFormat = "---{0}---"; // ...as shown above
Perhaps decide binding is not needed in certain cases; do not proceed with binding:
if (binding_not_needed)
return null;
Lots more, limited by your imagination. When ready, call the binding's ProvideValue method and it will create its BindingExpression. Because you pass it your own IProvideValueTarget info (i.e. your IServiceProvider), the new binding will substitute itself for your markup extension. It gets attached to the target object/property where your MarkupExtension was authored in XAML, which is exactly what you want.
Bonus: You can also manipulate the returned BindingExpression
If pre-configuring the binding isn't enough, note that you also have access to the instantiated BindingExpression. Instead of tail-calling the ProvideValue result as shown, just store the result into a local. Prior to returning it, you can set up monitoring or interception of the binding traffic via the various notification options that are available on BindingExpression.
Final note: as discussed here, there are special considerations when WPF markup extensions are used inside templates. In particular, you will notice that your markup extension is initially probed with IProvideValueTarget.TargetObject set to an instance of System.Windows.SharedDp. Because loading templates is a naturally a deferred process, I believe the purpose here is early probing of your markup extension to determine its characteristics, i.e. long prior to the existence of any real data which could populating the template properly. As shown in the above code, you [must return 'this'] c̲a̲n̲ r̲e̲t̲u̲r̲n̲ t̲h̲e̲ p̲r̲o̲b̲e̲ o̲b̲j̲e̲c̲t̲ i̲t̲s̲e̲l̲f̲ for these cases; if you don't, your ProvideValue won't be called back again when the real TargetObject is available [see edit].
edit: WPF tries really hard to make XAML resources shareable, and this especially includes the BindingBase and derived classes. If using the technique I describe here in a reusable context (such as a Template), you need to make sure that the wrapped binding does not meet the criteria for shareability, otherwise the wrapped binding will become BindingBase.isSealed=true after the first time it generates a BindingExpression; subsequent attempts to modify the Binding will fail with:
InvalidOperationException: Binding cannot be changed after it has been used.
There are several workarounds to do this, which you can ascertain by studying the source code of the (non-public) WPF function TemplateContent.TrySharingValue. One method I found was to return the System.Windows.SharedDp object from your markup extension anytime it shows up. You can detect System.Windows.SharedDp either by looking for any non-DependencyObject value, or more specifically as follows:
if (pvt.TargetObject.GetType().Name == "SharedDp")
return pvt.TargetObject;
(Technically, checking for .GUID value {00b36157-dfb7-3372-8b08-ab9d74adc2fd} instead would the most correct). I've updated the code in my original post to reflect this, but I welcome further insight on how to preserve maximal resource sharing for both of the use cases, template vs. non-template.
edit: I'm thinking that, for the purposes of the sharability determination in template usage, the main difference between returning this (as I had originally suggested) and my revised suggestion to return pvt.TargetObject is that the former derives from MarkupExtension--versus the base class of System.Windows.SharedDp being Object--and it's clear that the probing code recurses into nested markup extensions.

How can I persuade System.Xaml.XamlXmlWriter.WriteValue to fire if the Value is NULL

I'm using the XamlServices.Transform to take an object model and serialize it to Xaml.
I've implemented a class which inherits from XamlXmlWriter which overrides WriteValue. I'm using this to reinstantiate a custom MarkupExtension back into the rendered Xaml. My code works fine except when the value of the property is null, in which case the WriteValue doesn't fire and I don't get chance to "swap out" the value in the overriden class.
A related issue is where a property has the same value as that specified by the System.ComponentModel.DefaultValue() attribute. For example say i've got a property in my object model decorated like this:
[DefaultValue(true)]
public Boolean IsVisible {get; set;}
Then the WriteValue method only fires if the IsVisible property is false (which kind of makes sense).
The Remarks section in the documentation (http://msdn.microsoft.com/en-us/library/system.xaml.xamlxmlwriter.writevalue.aspx) mentions something about null values, but I don't understand it:
The input value may be null, which
supports explicitly writing out null
as a serialized value. This behavior
uses the XamlLanguage.Null definition
as WriteStartObject input and then
immediately calls WriteEndObject.
How to I a) make the "WriteValue" fire when the property is null, and b) make the "WriteValue" fire when the property is the same as the DefaultValue Attribute?
I'm not sure if they are related, a solution for either of them would be very welcome.
Thanks,
Daniel
To answer ‘b’ first: The XamlObjectReader’s intended behavior is to skip properties whose value is the declared “default value” and we have no configuration feature to override that. Note that “default value” here is the one declared with the [DefaultValue()] attribute, not the C# language default(T), so things may not be as bad as you fear. I mean not every “0” in an “Int” property is skipped because it is the “default value”.
‘a’: The XamlXmlWriter’s output Node stream for a null value is not “WriteValue(null)”, but instead is “WriteStartObject(nullExtensions); WriteEndObject()”. This is the behavior the documentation you quoted was describing. So you should be fine. Look for StartObject “nullExtension” instead of value “null”.

OneWay binding on WinForms?

I have a control with a property public MyClass MyProperty{...} which value is shown on the screen as a graph. I want this property to be bindable to any other MyClass in the program by using the Binding class (MyProperty would be the propertyName parameter in this Binding constructor, and the other MyClass would be the dataMember parameter) .
MyClass implements INotifyPropertyChanged so on that side everything is all right. But it happens that if I don't implement a get accessor in MyProperty and try to bind something to it, I get a "Cannot bind to the property 'MyProperty' on the target control.
Parameter name: PropertyName" error.
Does this mean I have to implement a get accessor even if I know I will never need to read it's value and I want a OneWay (source to target) binding, and even if I just return null in the get accessor?
I'm guessing the Binding class uses this to compare the new value to the old one or to do some other internal stuff. I'm not sure, then, if it's a good idea to just return null, or it would be better to keep always a copy of whatever last object was assigned with the set accessor and return it in the get accessor. Maybe I really don't even need to write a get accessor and I'm doing something else wrong. It just happens that I get the error only when I comment out the get accessor and stop getting it when I put it back.
Edit: In case there is any confusion: When I say MyProperty's value is shown on the screen as a graph I don't mean it has a value that some other code reads and show in the screen. No one reads any value from MyProperty. MyProperty's set accessor is the one that draws stuff on the screen and that's the end of the cycle.
I'm not 100% sure I understand what you mean, but I think the exception you're encountering stems from the Binding class's CheckBinding function (reflectored):
if (descriptor.IsReadOnly && (this.controlUpdateMode != ControlUpdateMode.Never))
{
throw new ArgumentException(SR.GetString("ListBindingBindPropertyReadOnly", new object[] { this.propertyName }), "PropertyName");
}
Therefore, changing the Binding's ControlUpdateMode to ControlUpdateMode.Never may be what you're looking for

Categories