I've defined the following view:
<CollectionViewSource x:Key="PatientsView" Source="{Binding Source={x:Static Application.Current}, Path=Patients}"/>
Where Patient is the following property:
public IEnumerable<Patient> Patients
{
get
{
return from patient in Database.Patients
orderby patient.Lastname
select patient;
}
}
Somewhere in my code, I change the Patients database, and I want to have the controls that display this data (using the "PatientsView") to be automatically notified. What's a proper way to do this?
Can the CollectionViewSource be invalidated or something?
How to invalidate a CollectionViewSource in code behind:
CollectionViewSource patientsView = FindResource("PatientsView") as CollectionViewSource;
patientsView.View.Refresh();
I think this is a bit more complex than it seems. Notifying your client application about changes in database is a non-trivial task. But your life is easier if the database is changed only from your application - this makes you able to put "refreshing logic" whenever you change the database.
Your "Patients" property seems to be present in one class (maybe a little more than one? :) ). And you probably bind some ListBox to the CollectionViewSource. So instead of calling Refresh on the CollectionViewSource you can make WPF re-call the getter. For this the class that has Patients property has to implement INotifyPropertyChanged interface.
The code would look like this:
public class TheClass : INotifyPropertyChanged
{
public IEnumerable<Patient> Patients
{
get
{
return from patient in Database.Patients
orderby patient.Lastname
select patient;
}
}
#region INotifyPropertyChanged members
// Generated code here
#endregion
public void PatientsUpdated()
{
if (PropertyChanged != null)
PropertyChanged(this, "Patients");
}
}
Now, call PatientsUpdated() on an instance of TheClass to trigger update of the binding.
P.S. Having said all that it just feels like a bad design somehow.
Table<T> does not support IListChanged events, you will have to do this yourself (I had to do the same earlier today).
Related
I have a Silverlight/WPF application I'm enhancing. I have a UserControl and it needs to disable or enable some controls when a property on the model changes. It has to do some other logic, so I can't just bind them to the property.
In the control's code behind, I have a reference to the model. I know there is a way to bind to certain properties, and I know how to do it in XAML, but not in the code-behind.
I've seen a lot of instances say to use the INotifyPropertyChanged interface, but it doesn't seem to apply in this case.
An example of what I'm trying to do:
public partial class MyControl : UserControl
{
private readonly MyModel _model;
public MyControl(MyModel model)
{
_model = model;
// bind to model's ImportantThing property here
}
...
// Some method gets called when property changes
...
}
public class MyModel
{
...
public bool ImportantThing
{
get { return _importantThing; }
set
{
_importantThing = value;
// This is existing code and notifies some controls, but not the ones
// I'm interested in. It should notify MyControl as well. I know in
// most applications, this is OnPropertyChanged();
RaisePropertyChanged("ImportantThing");
}
}
}
Any pointers?
Some Pointers....
Your issue\solution sounds like a task for a ValueConverter. But first, I can see code in the UserControl code-behind file, you really should adopt and apply the MVVM pattern... OK there is a [steep] learning curve and sometimes you wonder if it's worth the effort (know I did when I started with XAML)... But take my word for it.... MVVM, there simply in no other way to develop using WPF. If you try to apply the WinForms UI Logic to WPF it will become an unmaintainable, unmanageable monolithic pile of spaghetti code....
you might find this link to Rachel Lim's Blog useful....
https://rachel53461.wordpress.com/category/mvvm/
and for ValueConverter take a look at this.....
http://www.wpftutorial.net/ValueConverters.html
I apologize, my original question wasn't all that clear, but I've found a solution. It turns out the UserControl (MyControl in my original example) was already watching the Model for changes:
_myModel.PropertyChanged += Model_PropertyChanged;
In the existing callback (Model_PropertyChanged()), I just looked for the property I was interested in and added everything else I needed:
void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "StatusEnabled")
{
// do stuff
}
else if (e.PropertyName == "ImportantThing")
{
// my stuff
}
}
Thanks for everyone's input!
I've been struggling with this problem for hours now:
I'm building an UWP application in the MVVM pattern.
Problem: I can't get my ListView's ItemSource to update/rebind to a new instance of ObservableCollection, when I change the ItemSource's property's value, even though I have implemented IPropertyChanged on the property.
Details:
The page I am working on has a ListView which ItemSource is bound to an observable collection:
Page:
<ListView ItemsSource="{Binding Orders}" x:Name="OrderListView"><!--Inner bindings works fine--></ListView>
ViewModel:
public ObservableCollection<Order> Orders {
get { return _Orders; }
set { Set(ref _Orders, value); }
}
private ObservableCollection<Order> _Orders;
(...)
//Property changed implementation from video (06:48): https://mva.microsoft.com/en-US/training-courses/windows-10-data-binding-14579?l=O5mda3EsB_1405632527
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//If property is updated with, raise property changed, else don't
public bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
return false;
storage = value;
RaisePropertyChanged(propertyName);
return true;
}
When pressing a button in the UI:
<Button Content="Waiter" Click="Waiter_Click" Background="{Binding Waiter, Converter={StaticResource BoolToColorConverter}, FallbackValue='Yellow', TargetNullValue='Red'}"/>
It will tricker the "Waiter_Click" event handler in the code behind:
private void Waiter_Click(object sender, RoutedEventArgs e)
{
var viewModel = ((Button)sender).DataContext as OrderPageViewModel;
if (viewModel.Waiter) viewModel.Waiter = false;
else viewModel.Waiter = true;
}
The Waiter property is implemented, so it raises PropertyChangedEvent in the ViewModel:
public bool Waiter
{
get { return _waiter; }
set { Set(ref _waiter, value); FilterView(); }
}
private bool _waiter;
The button is updated in the UI - This works fine.
After setting the private field of _waiter, I also want to filter out some data that I don't want to show.
I do this in the FilterView(); method.
This is done by creating a new ObservableCollection, and setting the Orders property:
public void FilterView()
{
var filteredOrderList = new ObservableCollection<Order>();
//Sorting is done
Orders = filteredOrderList;
}
Now. I thought that the code: Orders = filteredOrderList; would trigger the "setter" of Orders property, and raise the PropertyChanged event as it does with the button and all other UI elements that works fine - and tell the ListView to rebind to it. However it seems like the ListView is running on an old instance of the ObservableCollection, and does not care about my PropertyChanged event being fired...
I've debugged the program, and I see that filteredOrderList contains the data I want, and that Orders is changed. But it's like ViewList does not care about the PropertyChanged event is fired...
I don't want to add and remove items from the ObservableCollection because the filtering becomes very complicated... I just want to replace the Orders ObservableCollection with a new one, and let the ViewList reflect my changes...
Am I missing something? Can't I call PropertyChanged event multiple times? (I'm calling it once for the button update, and once for the Orders ObservableCollection to be changed).
Why is ListView not rebinding to the the new list? -Does ListView not care about PropertyChanged events being fired?
I will deeply appreciate any answer or workaround that will fix this or send me in the right direction.
Thank you, and sorry for long post.
Have a look at these two:
Sort ObservableCollection C#
Common mistakes while using ObservableCollection
After 8 and half an hour I found my mistake:
I had accidentally defined the DataContext two times:
<Page.DataContext>
<vm:OrderPageViewModel/>
</Page.DataContext>
and further down the XAML file just before the ListView:
<Grid.DataContext>
<vm:OrderPageViewModel/>
</Grid.DataContext>
This caused two instances of my OrderPageViewModel.
My ViewList was then bound to the old instance of the ViewModel, and any changes to the new ViewModel instance of ObservableCollection did not reflect any changes.
If someone else get's the same problem: That ViewList does not update when changing ObservableCollection - It's properbly because you created multiple instances of the ObservableCollection.
Also I found out that ObservableCollection only updates when adding og removing objects from it and clearing it using .Add(), .Remove() and .Clear() methods. It is not posible to call "CollectionChanged" or "PropertyChanged" without implementing it in the property itself, as I did on the Waiter property.
I remade the filtering function to clear the collection and add new elements to it. It fixed my issue. :-)
3 lines in XAML can really mess up your day, if you are not carefull.
Have a great day, and thank you people who posted previous answers :-)
I have a view with a viewmodel as it's DataContext. In the viewmodel I have an ObservableCollection of objects:
AvailableCategories = new ObservableCollection<Category>();
I can bind an ListView to this ObservableCollection without any trouble like this:
ItemsSource="{Binding Path=AvailableCategories}"
I now have the requirement to wrap the ObservableCollection in a class (to aid in xml serialization as in here: How to rename XML attribute that generated after serializing List of objects)
The wrapper class looks like this:
public class CategoryList : ObservableObject
{
private ObservableCollection<Category> _categories;
public ObservableCollection<Category> Categories
{
get
{
return _categories;
}
set
{
if (_categories == value)
{
return;
}
_categories = value;
RaisePropertyChanged(()=>Categories);
}
}
}
and it gets created in the VM like this:
CategoryList cl = new CategoryList();
cl.Categories = new ObservableCollection<Category>();
How do I now bind to a Collection within a wrapper class in my VM? This doesn't seem to work:
ItemsSource="{Binding cl.Categories}"
;
EDIT: My VM now exposes the CategoryList like this:
private CategoryList _cl;
public CategoryList cl
{
get
{
return _cl;
}
set
{
if (value==_cl)
{
return;
}
_cl = value;
RaisePropertyChanged(()=>cl);
}
}
But still no joy.
Try subscribing to the cl.PropertyChanged event in your VM, and in the handler call RaisePropertyChanged(()=>cl) again (you can check the e.PropertyName first if you want, to avoid too many calls). If that works, it means your view is not being notified when the collection property changes because you're binding to a subproperty.
If that doesn't work either, you might have to subscribe to the Categories.CollectionChanged, and Raise the cl property change from that handler too... If that works, that means it is the collection items changing (being added or removed) what's not being notified to the view.
But all this can lead to over-complicated code, if you want to make sure all these handlers are correctly added and removed every time the properties are set...
In general, it is ill-advised to create Bindings to subproperties, as it tends to leads to these notification problems.
I know this is very similar to other questions that have been asked, and I have looked at them but in one way or another the solutions don't work for this specific scenario.
I have a collection of objects in a BindingList:
private BindingList<PathologyModel> _patientPathologies;
public BindingList<PathologyModel> PatientPathologies { get { return _patientPathologies; } }
This collection needs to be bound to different XamDataGrid controls (Infragistics' version of a DataGrid). I need to use BindingList because I need to know when a user edits a particular item in the collection, which other types of collections don't seem to support.
I need the collection to be sorted, so for now I just have to do this every time an item is added/removed from the list:
private void Pathologies_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType == ListChangedType.ItemAdded || e.ListChangedType == ListChangedType.ItemDeleted)
{
_pathologies = new BindingList<PathologyModel>(_pathologies.OrderBy(x => x.Tooth.ToToothNumber()).ThenBy(x => x.DiagnosisDate).ToList());
}
}
It would be nice if this could be done automatically without the extra copying. But that's not my biggest problem right now.
I need the different XamDataGrids to have different filtered views of this same collection, which currently I am achieving with this:
public ICollectionView AllPathologies { get { return CollectionViewSource.GetDefaultView(PatientPathologies); } }
public ICollectionView TodaysPathologies { get { return CollectionViewSource.GetDefaultView(PatientPathologies.Where(x => x.DiagnosisDate.Date == DateTime.Today.Date)); } }
This almost works... I say almost because the views are showing the correct data, but the final requirement is that I also need to track the CurrentItemChanged event, so that I can enable/disable certain operations depending on which record the user is on. This works fine with the AllPathologies view, but does not ever get raised with TodaysPathologies, I'm guessing because it gets a different copy of the collection source every time you access the property? Strangely enough, the ListItem_Changed event still works properly against the original collection source.
I have tried making private CollectionViewSource objects to back the ICollectionView properties as I've seen in other articles, such as this:
private CollectionViewSource _todaysPathologies;
public ICollectionView TodaysPathologies { get { return _todaysPathologies.View; } }
...
_todaysPathologies = new CollectionViewSource{Source= _patientPathologies}.View;
But since the source is a BindingList I can't apply a filter predicate:
TodaysPathologies.CanFilter <--- false
So now I'm stuck. I place my fate in your hands, dear StackOverflowers.
"I need to know when a user edits a particular item in the collection, which other types of collections don't seem to support."
This isn't entirely true. A Collection used within a Class that inherits from INotifyPropertyChanged would solve this.
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
I would recommend making an inner class in your window page. With a Property for each value and the editable properties would call the NotifyPropertyChanged event. You would then have a collection of these inner class objects. The inner class would represent a row in the grid.
Another way I've solved this before is to specify text column where users enter information:
In XAML:
<DataGridTextColumn //... Binding="{Binding Path=Value, Mode=TwoWay}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox"/>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
In code:
private string value;
public string Value
{
get
{ return value; }
set
{
this.value = value;
NotifyPropertyChanged("Value");
}
}
The inner class contains this Value Property as well as a reference to an object of the class I used to have a collection of.
Now I have a collection of objects of this inner class, which stores both. I use this to display info of the class and have an extra column to add a value. I then make a new object out of the old class information + this value.
If you've found this helpful and would like me to go into more detail, let me know. I would be more than happy to. That or someone will post :P
I am trying to come up with a good way of implementing the MVVM pattern using Entity-Framework where my entities are my models. My DataContext is my viewmodel. This is a small reproduction of the problem.
View
<TextBox Text="{Binding MyText}" />
ViewModel:
I have the requirement of needing to navigate record by record from my DB. When a button is clicked in the View a command is sent to the Viewmodel that executes nextRecord(). EF does its magic and _myObject is the next row/record from the database
public class myViewModel: INotifyPropertyChanged
{
private MyEntityObject _myObject;
public string MyText
{
get { return _myObject.MyText; }
set
{
if (_myObject.MyText != value)
{
_myObject.MyText = value;
OnPropertyChanged("MyText");
}
}
}
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord() //pseudocode
}
}
Autogenerated Entity Model
public partial class MyEntityObject
{
public string MyText { get; set; }
}
Since the View has no knowledge of _myObject changing, it doesn't update when _myObject changes. A few approaches I have thought of.
I haven't tested wrapping my entities in a INotifyPropertyChanged wrapper class but am wary to do this as I have a lot of entity objects.
I could call OnPropertyChanged("...") for all properties, but some of my entities have a lot of properties to them, which would be ugly. Possible to use reflection to make it cleaner, but I may have properties that aren't databound.
I might be able to defer this to the UI, somehow refreshing the bindings when I click "Next Record", but this breaks MVVM and looks dirty
How can I get the UI to recognize changes from _myObject?
As I've mentioned in the comments, calling OnPropertyChanged("") or OnPropertyChanged(null) invalidates all properties and is equivalent to calling OnPropertyChanged for each and every property. This behavior is also documented here:
The PropertyChanged event can indicate all properties on the object
have changed by using either null or String.Empty as the property name
in the PropertyChangedEventArgs.
This means that you can simply add a call to OnPropertyChanged("") when you update your object to force WPF to reevaluate all bindings to your view model:
private void _nextRecord()
{
_myObject = myEntitiesContext.NextRecord();
OnPropertyChanged("");
}
That being said, I'd still go with #Anand's solution (+1). There's an ongoing debate on whether it's OK or not for the viewmodel to expose the model as a property, and I tend to go with exposing it until you need to introduce some view model specific logic. Most of the time you won't have to and it's not worth the trouble of wrapping model properties.
The problem with your code is that when _myObject changes the MyText property changed event is not fired. A work around would be to create a new property to hold you entity
and make this property as your Grids DataContext in your view as shown below. Now when this line is executed MyObject = myEntitiesObject.NextRecord() your view will be notified about the change.
public class myViewModel : INotifyPropertyChanged
{
private MyEntityObject _myObject;
public MyEntityObject MyObject
{
get { return _myObject; }
set {
if (_myObject != value)
{
_myObject = value;
OnPropertyChanged("MyObject");
}
}
}
private void _nextRecord()
{
MyObject = myEntitiesObject.NextRecord() //pseudocode
}
}
View:
<Grid DataContext="{Binding MyObject}">
<TextBlock Text="{Binding MyText}"/>
</Grid>
An extremely simple but not very elegant solution that I believe would meet needs: upon switching records, set the DataContext to null, then back to the ViewModel.
However, there are arguably more elegant alternatives that require more work to meet all requirements. See Anand's answer for an improvement upon this.
The tag in View should have the mode and UpdateSourceTrigger attribute set with values.