Binding attached property to IEnumerable - c#

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!

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

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.

Data Binding Combo Box in C# WPF

I'm having an issue with C# using WPF.
Just being brief here.
The following code below collects names via Entity Framework into a list.
This is in my MainWindow.xaml.cs file.
public ObservableCollection<string> FruitInfo
{
get
{
using (var context = new Fruit())
{
ObservableCollection<string> fruits= new ObservableCollection<string>();
foreach (var item in context.Fruits.OrderBy(s => s.FruitName))
{
fruits.Add(item.FruitName);
}
return fruits;
}
}
}
In my MainWindow.xaml file, I have the following:
<GroupBox Grid.Row="0" Grid.Column="0" Margin="5" Header="Fruit Info" >
<ComboBox Margin="5" SelectedItem="{Binding FruitInfo}"/>
</GroupBox>
When running my project, I see that the Combo Box does not populate the fruits.
Any ideas why I'm not seeing this?
All thoughts appreciated
You should bind the ItemsSource of the ComboBox to your collection, and the SelectedItem to another string that will represent the user's selection.
First:
<GroupBox Grid.Row="0" Grid.Column="0" Margin="5" Header="Fruit Info" >
<ComboBox Margin="5" ItemsSource="{Binding FruitInfo}" SelectedItem="{Binding SelectedFruit}"/>
</GroupBox>
Second: Make a SelectedFruit in your ViewModel
public string SelectedFruit { get; set; }
Ok, I understand what your trying to do, even though I'm still not sure why you're trying to do it.
The idea of using using is that it creates the variable for you, and the disposes of it when you finish the block of code you're running.
Now, you're creating a variable in that block, and return it ... and then, the system tries to dispose of it. So your return collection must be implicitly convertible to System.IDisposable, which I doubt yours is.
Putting that aside, you should follow emedbo advice. You will bind the source to the collection, and have another property for the selected index (since you're using mvvm).
I wouldn't get the data like that inside a using inside a getter, since it feels like that data you're getting might be deleted, and if it's not, then the whole use of your using is a bit wrong.
Not to mention it's not very readable, and you should aim for readability in most cases.
I don't use the Entity Framework, but I think the pattern for the FruitInfo property is missing of an important piece.
The problem is that the binding mechanism does not realize about the new ObservableCollection, because it expect some "notification" way to be alerted. That is, you have several ways to solve your problem:
use a DependencyPropety instead of an ordinary property: every time you set the property the bound controls are also notified.
I'd recommend this solution: reliable and versatile.
implement the INotifyPropertyChanged interface in the class exposing the FruitInfo property (e.g. MainWindow), then fire a PropertyChanged event on any actual FruitInfo's value changing.
This way is also valuable, but it looks useless adding a thing already exposed in any DependencyObject-derived class. The INotifyPropertyChanged fits perfectly for the POCO classes (Plain-Old CLR-Objects).
give a name to the combobox, then set the ItemsSource property explicitly.
It works fine, but you'd lose the benefits of the data-context inheritance, especially within templates.
the pattern you used creates the collection in a "lazy" fashion: consider avoiding the lazy-way, and set the FruitInfo value before the combobox is created/bound.
Doable, but typically may be applied in a few cases. Also requires that you know for sure the sequence of the objects creation. Keep as latest way.
== UPDATE
Try to modify your code as follows:
private ObservableCollection<string> _fruits = new ObservableCollection<string>();
public ObservableCollection<string> FruitInfo
{
get
{
using (var context = new Fruit())
{
this._fruits.Clear();
foreach (var item in context.Fruits.OrderBy(s => s.FruitName))
{
this._fruits.Add(item.FruitName);
}
return this._fruits;
}
}
}

Binding ItemsSource to an EntityFramework ObjectSet<T> does not generate content

Our application is in use of several TreeViewItem and DataGrid controls in XAML whose ItemsSource properties are bound to Entity Framework ObjectSet<Entity> collections. The problem is that the UI controls are behaving as if the ObjectSets are empty.
The EF ObjectContext is enclosed within a singleton class in a static class:
public static class BusinessData
{
public static readonly BizDataSource Source = BizDataSource.Instance;
}
public class BizDataSource : INotifyPropertyChanged
{
private BusinessEntitiesObjectContext _context = ...;
and the ObjectSets are being returned from readonly properties within the singleton:
public IEnumerable<Employee> RetiredEmployees
{
get {
return (from it in _context.Employees where it.Status == "Retired" select it);
}
}
and finally, the ItemsSource is data bound to the collection, with INotifyPropertyChanged serving to update the UI when its known that the data source is updated:
<TreeViewItem x:Name="PART_TVI" Header="Retired Employees"
ItemsSource="{Binding
Source={x:Static local:BusinessData.Source},
Path=RetiredEmployees}"
/>
Debug stepping has revealed that the binding is correctly providing an IEnumerable which resolves to the business objects, but it seems like the control isn't iterating upon it. For example, if I add this code to the window:
PART_TVI.ItemsSource = BusinessData.Source.RetiredEmployees;
The same behavior occcurs as with XAML Binding: nothing. However:
PART_TVI.ItemsSource = BusinessData.Source.RetiredEmployees.ToArray();
Ah hah! Now, we have generated content in our TreeViewItem. But, why was this necessary in the first place?
My guess is that: in the case of IEnumerable, you pass the expression to the ItemsSource, NOT a SOURCE. When you called: ToArray() which means that, it will execute the expression and return the actual data for the ItemsSource.
In summary, the first case: You tell the WPF how to get the ItemsSource. The second case: You pass the actual data source.

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