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

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.

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

WPF<-> entity binding

I have problem with updating data from my datasource(database through entity fw) to wpf-windows. I generate files using entity framework, so i'm accesing data from datebase this way:
public partial class sampleWindow : Window
{
myEntity en = new myEntity();
public sampleWindow()
{
InitializeComponent();
Bind();
}
private void Bind()
{
var list = from o in en.table select o;
someDatagrid.ItemsSource = list.ToList();
}
This method, firstly, was adequate for my program, i was refreshing 'Bind' method after i was doing some operations on database, so the data in my datagrids or combos was fresh. The problem occurs when i was changing database in diffrent wpf-windows. I have read that I should implement observable interface and use load instead of itemsSource. I tried to do it but i'm begginer and my attempts faild miserably. Could someone tell me step by step, what i should do?
You need a Singleton to manage your data, combined with using an ObservableCollection to expose the data. When the collection is changed by any view, it will notify any subscribers to the observation and they will automatically update.
See: Example of bindable list in XAML app (first part)
Example of Singleton
You would want to use a singleton for the instance of your entity as The Sharp Ninja mentioned. His article in the link he posted does a good job of explaining. You will want to use an observable collection to bind your ItemSource to. When an item is added or removed from an Observable collection the UI is automatically notified. The problem you are going to have is that there is not a .ToObservableCollection()
extension method build in to .net so you will have to implement your own.
I use this extension method
public static ObservableCollection<T> ToObservableCollection<T>(
this IEnumerable<T> enumeration)
{
return new ObservableCollection<T>(enumeration);
}
So now your bind method can set your ItemSource to the observable collection
private void Bind()
{
var list = from o in en.table select o;
someDatagrid.ItemsSource = list.ToObservableCollection();
}
There are so many and better ways (MVVM pattern) to accomplish this than your approach. To keep it simple it can be accomplished this way:
//Gets the Load() extention method available for DbSet
using System.Data.Entity;
private void Bind()
{
myEntity.table.Load();
/*Local returns an obvervable collection convenient for data binding.
This is a synchronized local view of your data. It means any item added , deleted and updated will be reflected in your controls.*/
var obsColl = myEntity.table.Local;
someDatagrid.ItemsSource = obsColl;
}

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;
}
}
}

ObservableCollection and CollectionView

I'm writing an application that reads data from a local db and display them in a listbox(I'm working in WPF).
I've a DVD object, where its properties are the columns of the db. This DVD object also implements INotifyPropertyChanged. "MyDVDs" is the table that refers to db. Once created these object, I create a class that inherits from ObservableCollection and takes data from "MyDVDs" in the constructor. However I don't need only to add, remove and update data from the listbox, but I also need to sort and filter them. Here is the code for ObservableCollection:
class ObservableDVD : ObservableCollection<DVD>
{
private ICollectionView collection;
public ObservableDVD(MyDVDs e)
{
foreach (DVD d in e.DVDs)
{
this.Add(d);
}
Collection = CollectionViewSource.GetDefaultView(this);
}
public ICollectionView Collection
{
get { return collection; }
private set { collection = value; }
}
}
I wanted to know, this is a good way?? Or can I do better?
In the MainWindow of the project, when Load_Window event fires, I assign the Collection property to listbox.ItemSource(in MainWindow code-behind I declare a private field that obviously refers to an ObservableDVD Object). I have some buttons that allow me to do the operations I tell you before.In the event headler of the buttons, I directly update and modify the ObservableDVD Object, not its property Collection. However, the Collection property also reflects those changes.
Why this behavior occurs?
It's ok for me, but I can't understand why it's happens. Is because of the notifications?
The property Collection has a reference to the view of the ObservableDVD. Being a reference means pointing to the same data in memory.
ObservableCollection Class Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
The Collection you are specifying is just a "view" of the ObservableDVD collection. Which means that both are really pointing to the same data in the memory, they're not 2 separate things. A "view" can be a subset of items when you apply filters to a collection, for instance.
Otherwise said, your ObservableDVD contains your "Data Table" for the entire dataset while the ICollectionView lets you manipulate which records/objects are visible to the user through custom logic.

Binding attached property to IEnumerable

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!

Categories