Reloading an entity does not trigger NotifyPropertyChanged - wpf

I'm having two properties, one is a collection named Items and the other is an Item. The collection is bind to a Datagrid and when I double click on the datagrid, the selection is loaded into Item that is binded to a textbox via Item.Name. When I modify the text into the textbox, the changes are reflected into the Datagrid thanks to UpdateSourceTrigger=PropertyChanged. My problem is that when I cancel this changes, I reload the entity from db with _context.Entry(Item).Reload(); but the OnPropertyChanged it's never triggered. I also tried to call OnPropertyChanged(nameof(Item)) after the reload but with no succes. The only thing that seems to work is the fallowing:
_itemRepository.Reload(Item);
Item.Name = Item.Name;
//OnPropertyChanged(nameof(Post));
Is this a bug or a feature? How can I update my UI without using this hack.
I'm using WPF on .NET 5 with EF Core 5.
Item Model:
public class Item : BaseEntity
{
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
}
Update:
Until I have some time to test in a minimal environment, like BionicCode sugested, I got a quick fix:
Datagrid.Items.Refresh();

Entity Framework is disconnecting the entity from the PropertyChanged infrastructure to improve performance during internal entity manipulations. You would have to enable property change propagation explicitly by configuring the DbContext and its associated model(s) to use the appropriate tracking strategy.This is done by using the ModelBuilder.
For example, if your entity is implementing INotifyPropertyChanged alone, setting the change tracking strategy to ChangeTrackingStrategy.ChangedNotifications would be sufficient. If it also implements INotifyPropertyChanging use ChangeTrackingStrategy.ChangingAndChangedNotifications or any other appropriate enumeration value that includes enabling changed notifications. See ChangeTrackingStrategy enum to find more available configuration values or to get an explanation.
To use the ModelBuilder you must override the virtual DbContext.OnModelCreating method in your DbContext:
public class ItemsContext : DbContext
{
public DbSet<Item> Items { get; set; }
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Item>()
.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications);
}
}

Related

Is DataBinding to a composite object's fields possible?

I have a WPF window with controls I wish to bind to my model. The model implements INotifyPropertyChanged to notify the view when the Properties change value. The Properties are primitives backed by fields, e.g:
private bool m_isRunning;
public bool IsRunning
{
get { return m_isRunning; }
private set
{
m_isRunning= value;
OnPropertyChanged("IsRunning");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (String.IsNullOrEmpty(propertyName))
{
return;
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
This works fine for primtiive values. I also have a composite object with various primitive properties and a hash table. Or, I'd like to bind to an ObservableCollection's Count property. I would like to bind my View controls to properties within the composite object, but I do not think this is possible. Something like:
<Run Text="{Binding Path=CompositeObject.SomeInnerProperty, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
This doesn't seem possible, but is there some other way? I feel like my only option is to expose properties that map in to the inner property of the composite object, but this seems like a lot of repetiion, e.g.:
public bool SomeInnerProperty
{
get { return m_myComposite.SomeInnerProperty; }
private set
{
m_myComposite.SomeInnerProperty= value;
OnPropertyChanged("SomeInnerProperty");
}
}
There's nothing wrong with binding to something like CompositeObject.SomeInnerProperty, however if CompositeObject does not implement INotifyPropertyChanged, then your UI won't get notified of the change and know that it needs to update when SomeInnerProperty changes.
Also, note that you can only bind to properties (with get and set methods), and not fields. So you can bind to public string SomeValue { get; set; } but you can't bind to public string SomeValue;
In regards dealing with repetitive code, I personally use some Visual Studio macros to write my public properties for me, so perhaps you could look into doing something like that if you don't want to implement INotifyPropertyChanged on your CompositeObject class. It should be noted that Macros were removed from Visual Studio 2012 though, so if you have a newer version you might need to use some other alternative like creating an add-in to run your macros
I had this same problem some time ago. Look at how I solved it:
MVVM INotifyPropertyChanged conflict with base class PropertyChange
Basically I created a Base class that implemented INotifyPropertyChanged and I made all my classes inherit from that base class and data binding worked fine.
You have two options:
If your model classes do not implement INPC, then create wrapper properties in your ViewModel like you suggested, or
Implement INPC in your model and just expose your main object in the ViewModel, you can bind as deep as you want as long as inner properties notify changes.

Using WPF design data with the MVVM pattern

I'm using the MVVM pattern in our WPF application to allow for comprehensive unit testing. The MVVM pattern itself is working great, however I'm struggling to adapt the pattern in a way that means I can use the design-time data support of WPF.
As I'm using Prism the ViewModel instances are generally injected into the constructor of the view, like so
public MyView(MyViewModel viewModel)
{
DataContext = viewModel;
}
Dependencies for the ViewModel are then injected into the constructor, like so
public class MyViewModel
{
public MyViewModel(IFoo foo, IBar bar)
{
// ...
}
// Gets and sets the model represented in the view
public MyModel { get; set; }
// Read-only properties that the view data binds to
public ICollectionView Rows { get; }
public string Title { get; }
// Read-write properties are databound to the UI and are used to control logic
public string Filter { get; set; }
}
This is generally working really well except when it comes to design data - I wanted to avoid compiling design-data specific classes into my released assembly and so I opted to use the {d:DesignData} approach instead of the {d:DesignInstance} approach, however in order for this to work correctly my ViewModel now needs to have a parameterless constructor. In addition, I also often need to change additional properties either to have setters or to be modifiable collections in order to be able to set these properties in XAML.
public class MyViewModel
{
public MyViewModel()
{
}
public MyViewModel(IFoo foo, IBar bar)
{
// ...
}
// Gets and sets the model represented in the view
public MyModel { get; set; }
// My read-only properties are no longer read-only
public ObservableCollection<Something> Rows { get; }
public string Title { get; set; }
public string Filter { get; set; }
}
This is worrying me:
I have a parameterless constructor that is never intended to be called and isn't unit tested
There are setters for properties that only the ViewModel itself should be calling
My ViewModel is now a jumbled mixture of properties that should be modified by the view, and those that shouldn't - this makes it tricky to tell at a glance which piece of code is responsible for maintaining any given property
Setting certain properties at design time (e.g. to see styling on the Filter text) can actually end up invoking ViewModel logic! (so my ViewModel also needs to be tollerant of otherwise mandatory dependencies being missing at design time)
Is there a better way to get design-time data in a WPF MVVM application in a way that doesn't compromise my ViewModel in this way?
Alternatively should I be building my ViewModel differently so that it has more simple properties with the logic separated out somewhere else.
First, I would recommend you to have a look at this video where Brian Lagunas provides several best practices about MVVM. Brian is - at least - involved in the development of Prism, as his name appears in the nuget packages information. Didn't check further.
On my side I only use bits of Prism, and my Model and ViewModel always offer blank constructors (like what Brian shows), the data context is assigned in the view's XAML, and I set the properties values like :
<MyView.DataContext>
<MyViewModel />
</MyView.DataContext>
and
public void BringSomethingNew()
{
var myView = new View();
(myView.DataContext as ViewModel).Model = myModel;
UseMyView();
}
Another benefit with this approach is that the ViewModel is created once, with the same path at design and run time, so you create less objects and save GC efforts. I find this elegant.
With regards to the setters, the design data will still work if you make them private, like:
public string MyProp { get; private set; }
Ok, customize it to manage NotifyPropertyChange at your convenience, but you've got the idea.
Now, I don't have yet a solution to manage ObesrvableCollections (I face the same problem, although putting multiple values in XAML sometimes work... ???), and yes, I agree that you have to manage the case when the properties are not set, like setting default values in the constructor.
I hope this helps.
I too have worked with NUnit testing with WPF and MVVM implementation. However, my version is reversed from yours. You are creating the view first, then creating the model to control it.
In my version, I create the MVVM model FIRST and can unit test it till the cows come home and not worry about any visual design... if the model is broken, so too will the visual implementation.
in my MVVM model, I have a method to "GetTheViewWindow". So, when I derive from my MVVM baseline, each view model has its own view its responsible for. So via a virtual method, each instance will do its own new view window when being applied for production.
public class MyMVVMBase
{
private MyViewBaseline currentView;
public MyMVVMBase()
{ // no parameters required }
public virtual void GetTheViewWindow()
{ throw new exception( "You need to define the window to get"; ) }
}
public class MyXYZInstanceModel : MyMVVMBase
{
public override void GetTheViewWindow()
{
currentView = new YourActualViewWindow();
}
}
Hope this helps as an alternative to what you are running into.

MVVM multiple presentations for same object?

I need to present an object differently, twice.
as a node in a TreeView (navigation/rename)
as 2 TextBoxes (rename/edit content)
public class Item
{
public string Name{get;set;}
public string Content{get;set;}
}
My first solution was to keep things simple:
public class MainViewModel
{
// collection of items (treeview navigation)
public BindingList<ItemViewModel> Items{get;set;}
// selected item (from treeview navigation)
// used for textbox edit
public ItemViewModel SelectedItem{get;set;}
}
public class ItemViewModel
{
// Used for treeview navigation
public bool IsSelected{get;set;}
public bool IsExpanded{get;set;}
public bool IsInEditNameMode{get;set;}
public BindingList<ItemViewModel> Children{get;set;}
public void BuildChildren();
// Used for treeview display/rename
// Used for textbox display/rename
public string Name{get;set;}
// Used for textbox edit
public string Content{get;set;}
}
This works well for a while.
But as the application grows more complex, the view model gets "polluted" more and more.
For example, adding additional presentations for the same view model (Advanced properties, Graph representation, etc)
public class ItemViewModel
{
// Used for Advanced properties
public BindingList<PropertyEntry> Properties {get;set;}
public PropertyEntry SelectedProperty{get;set;}
// Used for graph relationship
public BindingList<ItemViewModel> GraphSiblings{get;set;}
public bool IsGraphInEditNameMode{get;set;}
public bool IsSelectedGraphNode {get;set;}
public void BuildGraphSiblings();
// Used for treeview navigation
public bool IsNavigationInEditNameMode{get;set;}
public bool IsSelectedNavigationNode{get;set;}
public bool IsExpandedNavigationNode{get;set;}
public BindingList<ItemViewModel> NavigationChildren{get;set;}
public void BuildNavigationChildren();
// Used for treeview display/rename
// Used for textbox display/rename
// Used for graph display
// Used for Advanced properties display
public string Name{get;set;}
// Used for textbox edit
public string Content{get;set;}
}
Currently, I'm still using a single view model for multiple presentations, because it keeps the selected item in-sync across all presentation.
Also, I do not have to keep duplicating properties (Name/Content).
And finally, PropertyChanged notification helps updates all presentation of the item (ie, changing Name in navigation updates TextBox/Graph/Advanced properties/etc).
However, it also feels like a violation of several principles (single responsibility, least privilege, etc).
But I'm not quite sure how to refactor it, without writing a lot of code to keep the sync/property notification working/duplicating the model's properties across each new view model/etc)
What I would like to know:
If it were up to you, how would you have solved this?
At the moment, everything is still working. I just feel like the code could be further improved, and that's what I need help with.
How about using inheritance? Have a basic ItemViewModel, then subclass it to create a TreeViewItemViewModel, where you add the properties that relate to the tree-view rendering of this item within the subclass.
Could we,
try separating-out various view-specific-behaviors from the ItemViewModel class.
place/encapsulate the view-specific-behaviors in separate class (Behavior classes).
This gives you flexibility at run-time to instantiate/inject/switch behaviors.
Yes, try to use Strategy pattern for making a cleaner, single responsible, easy to maintain code.

Silverlight: how to bind List<T> to data grid

MVVM pattern is implemented in my Silverlight4 application.
Originally, I worked with ObservableCollection of objects in my ViewModel:
public class SquadViewModel : ViewModelBase<ISquadModel>
{
public SquadViewModel(...) : base(...)
{
SquadPlayers = new ObservableCollection<SquadPlayerViewModel>();
...
_model.DataReceivedEvent += _model_DataReceivedEvent;
_model.RequestData(...);
}
private void _model_DataReceivedEvent(ObservableCollection<TeamPlayerData> allReadyPlayers, ...)
{
foreach (TeamPlayerData tpd in allReadyPlayers)
{
SquadPlayerViewModel sp = new SquadPlayerViewModel(...);
SquadPlayers.Add(sp);
}
}
...
}
Here is a peacie of XAML code for grid displaying:
xmlns:DataControls="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Data"
...
<DataControls:DataGrid ItemsSource="{Binding SquadPlayers}">
...</DataControls:DataGrid>
and my ViewModel is bound to DataContext property of the view.
This collection (SquadPlayers) is not changed after its creation so I would like to change its type to
List<SquadPlayerViewModel>
. When I did that, I also added
RaisePropertyChanged("SquadPlayers")
in the end of '_model_DataReceivedEvent' method (to notify the grid that list data are changed.
The problem is that on initial displaying grid doesn't show any record... Only when I click on any column header it will do 'sorting' and display all items from the list...
Question1: Why datagrid doesn't contain items initially?
Q2: How to make them displayed automatically?
Thanks.
P.S. Here is a declaration of the new List object in my view-model:
public List<SquadPlayerViewModel> SquadPlayers { get; set; }
You can't use List as a binding source, because List not implement INotifyCollectionChanged it is require for WPF/Silverlight to have knowledge for whether the content of collection is change or not. WPF/Sivlerlight than can take further action.
I don't know why you need List<> on your view model, but If for abstraction reason you can use IList<> instead. but make sure you put instance of ObservableCollection<> on it, not the List<>. No matter what Type you used in your ViewModel Binding Only care about runtime type.
so your code should like this:
//Your declaration
public IList<SquadPlayerViewModel> SquadPlayers { get; set; }
//in your implementation for WPF/Silverlight you should do
SquadPlayers = new ObservableCollection<SquadPlayerViewModel>();
//but for other reason (for non WPF binding) you can do
SquadPlayers = new List<SquadPlayerViewModel>();
I usually used this approach to abstract my "Proxied" Domain Model that returned by NHibernate.
You'll need to have your SquadPlayers List defined something like this:
private ObservableCollection<SquadPlayerViewModel> _SquadPlayers;
public ObservableCollection<SquadPlayerViewModel> SquadPlayers
{
get
{
return _SquadPlayers;
}
set
{
if (_SquadPlayers== value)
{
return;
}
_SquadPlayers= value;
// Update bindings, no broadcast
RaisePropertyChanged("SquadPlayers");
}
}
The problem is that whilst the PropertyChanged event informs the binding of a "change" the value hasn't actually changed, the collection object is still the same object. Some controls save themselves some percieved unnecessary work if they believe the value hasn't really changed.
Try creating a new instance of the ObservableCollection and assigning to the property. In that case the currently assigned object will differ from the new one you create when data is available.

WPF datagrid multiple windows question

I have a scenario where i load an ICollectionView in a datagrid.
In some cases I modify the data where the collectionview gets it's data from. If I then reload the grid with configGrid.ItemsSource = configData; for example, the data gets updated.
Now the thing is, I sometimes open a new window using:
var newWindow = new Edit(movie);
newWindow.Show();
The thing is, I also edit the data using this new window. Now I want the datagrid in the first window to be refreshed after I close this second window (actually, it doesn't matter when it gets refreshed, as long as it does).
How do I do this?
I might be missing something here (I have a crippling hangover unfortunately) but can't you handle the window closed event of newWindow and refresh confiGrids itemsource there?
Window newWindow = new Window();
newWindow.Closed += new EventHandler(newWindow_Closed);
newWindow.Show();
void newWindow_Closed(object sender, EventArgs e)
{
configGrid.ItemsSource = configData;
}
If the collection behind the ICollectionView supports INotifyCollectionChanged (like ObservableCollection) and the object itself supports INotifyPropertyChanged then the grid is supposed to update automatically
Otherwise you are on your own and the editing window should raise some sort of notification (maybe an event) that you should receive and update the list.
Ok, here's the long version:
WPF data-binding can update the UI automatically - but it needs to know that something changed in order to trigger the update, the easiest way to do this is to support INotifyPropertyChanged, let's create simple class:
public class Movie
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Now, let's add INotifyPropertyChanged support:
public class Movie : INotifyPropertyChanged
{
public event PropertyChanged;
protected virtual OnPropertyChanged(string property)
{
var ev = PropertyChanged;
if(ev!=null)
{
ev(this, new PropertyChangedEventArgs(property));
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
}
Now when you bind to the movie class and change the Name property the UI will be updated automatically.
The next step is to handle a list of Movie objects, we do that by using a collection class the implements INotifyCollectionChanged, luckily for us there's one already written in the framework called ObservableCollection, you user ObservableCollection<T> the same way you would use a List<T>.
So, just bind to ObservableCollection and WPF will automatically detect when objects change or when they are added or removed.
ICollectionView is very useful, it adds support for current item, sorting, filtering and grouping on top of the real collection, if that collection is an ObservableCollection everything will just work, so the code:
ObservableCollection<Movie> movies = new ObservableCollection<Movie>();
ICollectionView view = CollectionViewSource.GetDefaultView(movies);
will give you a collection view that supports automatic change notifications.

Resources