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.
Related
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
I've spent a fair bit of time trying a number of different ways to solve an issue I'm having, to no avail, so I'm hoping someone here can help.
I have a Text Box element with Two-Way binding, which utilises a Type Converter to convert the value from a string to a custom Data type, say, MyCustomType. This is working fine, however due to a change in my project's requirements, I now need to perform extra processing prior to the conversion taking place.
In order to perform this extra processing, however, I need to be able to access the "source" text box, or the binding context. Neither of which I have been able to access.
Is there any way to access the source text box, from a Type Converter's ConvertFrom() method?
I have tried to use the ITypeDescriptorContext parameter passed (by WPF) to the ConvertFrom() method, however most of the properties therein are null.
i.e.
public class MyCustomTypeConverter : TypeConverter
{
...
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
// Context is
return new MyCustomType(value);
}
...
}
I have also tried using a MultiValueConverter, and avoiding the Type converter entirely, however this led to a LOT of extra code, and didn't really help. I would prefer to avoid going down this route, as a Type Converter is much more elegant.
Any advice/assistance would be greatly appreciated! :)
EDIT: I ended up changing the way that validation is performed (using INotifyDataError instead of validating on exceptions), and ended up re-writing the ConvertFrom() method in my Type Converter, such that I wouldn't need to access the TypeDescriptor's context anymore.
I wouldn't recommend using the context from the ConvertFrom() method, as it (being a private property) isn't guaranteed that the property will exist in the future (though I haven't read anything to support this, it is best to assume that private properties can be removed/renamed without notification from the MS development team), and it isn't set when setting a property's value programmatically, like so:
TypeConverter converter = TypeDescriptor.GetConverter(typeof(MyCustomType));
converter.ConvertFrom(mySourceValue);
If you're reading this and really need to access the context parameter, you can do so using my method below, at your own risk.
I was able to solve this by interrogating the ValueConverterContext class, and accessing the private _targetElement field, like this:
var sourceTextBox = context.GetType().GetField("_targetElement", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(context)
Thanks for your help nonetheless. :)
edit: To access the Bindings for this TextBox, you can simply cast sourceTextBox as TextBox and then:
var BindingExpression = sourceTextBox.GetBindingExpression(TextBox.TextProperty);
I am using the new WPF Viewer for Crystal Reports in C#. As I am using MVVM, I would really like to bind the source of the data to be displayed instead of doing this in the loaded event. Therefore, I wanted to implement an attached property for the source - but the binding just doesn't work, the Getter method is not even called. The other posts about binding attached properties also didn't help and I am not sure what I am doing different. Can anybody help? Here is my simplified code for the attached property:
public static class CrystalReportsAttached {
public static readonly DependencyProperty SourceProperty =
DependencyProperty.RegisterAttached(
"Source",
typeof(IEnumerable),
typeof(CrystalReportsAttached),
new UIPropertyMetadata(new ObservableList<Participant>() as IEnumerable, SourceChanged));
public static void SetSource(DependencyObject target, IEnumerable value) {
target.SetValue(SourceProperty, value);
}
public static IEnumerable GetSource(DependencyObject target) {
return (IEnumerable)target.GetValue(SourceProperty);
}
private static void SourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
CrystalReportsViewer reportViewer = d as CrystalReportsViewer;
if (reportViewer != null) {
MyCrystalReport report = new MyCrystalReport();
report.SetDataSource(d.GetValue(SourceProperty) as IEnumerable);
reportViewer.ViewerCore.ReportSource = report;
}
}
}
where MyCrystalReport is the wrapper around my rpt report file.
If I bind to the source like this now, it's not working:
<my:CrystalReportsViewer prop:CrystalReportsAttached.Source="{Binding MyList, Mode=OneWay}"/>
I tried to bind a DataGrids ItemsSource in the same way and that works, so there seems to be no mistake with the path name or smth similar.
Any help is greatly appreciated. Thanks a lot!
With dependency properties, all you can ever be certain of is that your property changed callback will be called when the property changes and that the underlying property will actually be changed if your getter is called. This might seem strange but your getter and setter just access that underlying property, so if the XAML parser calls target.GetValue(SourceProperty) it gets the correct thing without calling your getter.
The real question is does your property changed callback get called?
To receive changes to the collection, the source collection must implement INotifyCollectionChanged.
You can use ObservableCollection, find a custom notifying collection online, or wrap an existing collection with a class that you write that implements the interface of the inner collection and INotifyCollectionChanged.
If the initial binding fails, check that you have set the DataContext (to your View-Model), that the property name on the VM is correct and that the property has a public getter.
Edit:
This part is wrong:
new UIPropertyMetadata(new ObservableList<Participant>() as IEnumerable, SourceChanged));
You are setting the same list instance as the default value for all controls.
Set the default value in the constructor instead (put null in the DP reg. line).
I finally figured out where the problem was:
It seems like the CrystalReportViewer's DataContext is overridden for some reason. Therefore, the binding worked in every other context (e.g. in a DataGrid), but not here. I found out the problem by using the snoop tool mentioned by default.kramer above. I could fix it by changing the binding to
<my:CrystalReportsViewer prop:CrystalReportsAttached.Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.Participants, Mode=OneWay}"/>
so it does access the DataContext of the UserControl (which should usually be the same as the one for the specific control, but is not for CrystalReportsViewer) and it is working now.
Thanks for everybody's help!
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
I'm building a custom web control with a public property which I only want to be available in design time (i.e. make it unavailable in code behind).
The DesignOnly attribute promises to do just that, but when I set [DesignOnly(true)] it has no noticeable effect whatsoever:
[Bindable(true)]
[Category("Appearance")]
[DefaultValue(null)]
[Localizable(false)]
[DesignOnly(true)]
public string MyProp
{
get
{
return ViewState["MyProp"] as string;
}
set
{
ViewState["MyProp"] = value;
}
}
The property still appears in code behind IntelliSense. Setting a value to it in code behind works. In these respects, the behavior is just as if the attribute had never been set. And I've cleaned and rebuilt the complete solution. Twice.
Am I doing it wrong? Can you please tell me what is the right way to go about this, then?
Many thanks in advance.
The DesignOnly attribute promises to do just that
Actually, no; it tries to make it clear when accessing it in code isn't available; if you lie (i.e. claim that something is design-only when it is available) then you should expect it to misbehave. The compiler knows what is available, and this design-only attribute is not defined in the C# spec, so it makes no difference to the compiler if you add this attribute.
Try adding:
[EditorBrowsable(EditorBrowsableState.Never)]
which the code editor (IDE) looks at (but only when using a separate assembly) - note that this doesn't stop you using it - it just hides it.
I believe the MSDN text is trying to describe the difference between properties that actually exist on code, vs properties that only pretend to exist; you can actually do all sorts of things to make fake properties appear in the designer, and it is these pretend properties that might be marked as design-only.