Update current class property when any item in observable collection changes - c#

I have a class
public class SomeModel : INotifyPropertyChanged {
public ObservableCollection<SomeSubModel> SubModels {get; set;}
public int Sum { get { return SubModels.Sum(x=> x.Count) }}
private string _url;
public string Url
{
get { return _url; }
set
{
_url = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SomeSubModel : INotifyPropertyChanged {
private string _count;
public string Count
{
get { return _count; }
set
{
_count = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm going to use binding to SomeSubModel.Sum in WPF UI.
The SomeSubModel.Count property is changed very frequently.
How to notify that SomeSubModel.Sum is changed when property SomeSubModel.Count from any item in SomeModel.SubModels observable collection is changed to reflect the actual SomeSubModel.Sum in WPF UI through the binding?
The main goal is to reflect in UI the actual Sum of all Count's of objects in observable collection.
Thank you!

You should fire a notify property changed for the Sum property as well in that case:
private string _count;
public string Count
{
get { return _count; }
set
{
_count = value;
OnPropertyChanged();
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Sum"));
}
}

Related

How to create a program with one model and multiple viewmodel?

I have multiple views (usercontrols) and a model in my program. I've created viewmodel classes for each view. The problem is, I don't know what is the best structure for this purpose. I want to create one model instance and use it in multiple viewmodels. Could you please suggest me a structure or a way to do this?
Model.cs
public class Model
{
....
public string NameModel1 = "Mike";
public string NameModel2 = "George";
}
ViewModel1.cs
public class ViewModel1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
....
private string nameViewModel1;
public string NameViewModel1
{
get
{
return value;
}
set
{
if(nameViewModel1!=value)
{
nameViewModel1=value;
OnPropertyChanged();
}
}
}
}
ViewModel2.cs
public class ViewModel2 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
....
private string nameViewModel2;
public string NameViewModel2
{
get
{
return value;
}
set
{
if(nameViewModel2!=value)
{
nameViewModel2=value;
OnPropertyChanged();
}
}
}
}
How can I equalize NameModel1 value to NameViewModel1 value and NameModel2 value to NameViewModel2 value by creating the model instance once?

observable collection not changing combobox

I have an observable collection which is the item source to a combo box. When I add a new item to the observable collection, it appears in the combobox. However, when I update an existing item, it does not update the combo box. What am I doing wrong?
xaml:
<ComboBox ItemsSource="{Binding ChildGroupOC}" DisplayMemberPath="childGroupName" />
observable collection property:
public ObservableCollection<ChildGroupBO> ChildGroupOC
{
get
{ return childGroupOC;}
set
{
childGroupOC = value;
}
}
public class ChildGroupBO: INotifyPropertyChanged
{
public int parentGroupId { get; set; }
public int childGroupId { get; set; }
public string childGroupName { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
Your implementation of ChildGroupComboBoxBO not only has to implement INotifyPropertyChanged but also call the event on changes:
OnPropertyChanged("parentGroupId");
Example from MSDN:
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public Person()
{
}
public Person(string value)
{
this.name = value;
}
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}

Wpf binded property not updating in 2-way-mode

I have simple gallery with notes and text box to add new one by title.
I use mvvm, other bindings work correcty.
When I set title of note and click add property NewNote.Title is updated and object saved in database. After that I clear NewNote.Title but UI is not updated.
here is short video https://youtu.be/l3vFwI-a4TQ
xaml
<TextBox Text="{Binding NewNote.Title}" />
page view model
class NotesPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<NoteViewModel> Notes { get; set; }
public NoteViewModel NewNote { get; set; }
public NotesPageViewModel()
{
NewNote = new NoteViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
internal void LoadNotes()
{
using (var uow = new UnitOfWork())
{
Notes.Clear();
uow.NotesRepository.OrderBy(n => n.Position).ToList()
.ForEach(note => Notes.Add((NoteViewModel)note));
}
}
internal void AddNote()
{
if (string.IsNullOrWhiteSpace(NewNote.Title))
return;
using (var uow = new UnitOfWork())
{
uow.NotesRepository.Add((Note)NewNote);
uow.Complete();
}
NewNote.Title = "";
LoadNotes();
}
}
object view model
class NoteViewModel : INotifyPropertyChanged
{
public int Id { get; set; }
public string Title { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You're not calling the OnPropertyChanged on the viewmodel when you're setting the property
class NoteViewModel : INotifyPropertyChanged
{
private int _id;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
OnPropertyChanged("Title");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also, the LoadNotes and AddNotes should be called via a command, and the new title should be sent as a parameter.
https://msdn.microsoft.com/en-us/library/ms752308(v=vs.110).aspx

Update data in MVVM periodically and automatically

In my Model and ViewModel for XAML View (List), I have a String property.
The data are retrieved in LoadData from a WebService.
I don't use MVVMlight.
Model:
public class LocationsModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _id;
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel:
public class MainViewSurroundingModel : INotifyPropertyChanged
{
public MainViewSurroundingModel()
{
this.Items = new ObservableCollection<LocationsModel>();
}
public ObservableCollection<LocationsModel> Items { get; set; }
private string _name = "";
private string _id = "";
public string Name
{
get
{
return _name;
}
set
{
if (value != _name)
{
_name = value;
NotifyPropertyChanged("Name");
}
}
}
public string ID
{
get
{
return _id;
}
set
{
if (value != _id)
{
_id = value;
NotifyPropertyChanged("ID");
}
}
}
public bool IsDataLoaded
{
get;
private set;
}
public async Task<bool> LoadData()
{
// WEB-API CALL IS HERE...
this.IsDataLoaded = true;
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I now want to update the Name property periodically by code (not re-retrieving data from WebService) every 5 minutes.
How and where can I implement this?
Sorry for vague answer, don't have much time so if anyone else cares to write more about what I said, they are welcome to it.
How to
One of the possibilities is using DispatcherTimer (more about it for example here) and using it's Tick event.(there is a tutorial in the link provided)
Now about where to create your timer.
It depends on what you want to change and when you want to start it.
If I'd like to change ALL of the data in the list some predefined way, i can do it like this:
public class MainViewSurroundingModel : INotifyPropertyChanged
{
//...omitted the parts you have done, only writing the things I'd change...
MainViewSurroundingModel()
{
..if there was something leave it here..
changeTimer = new DispatcherTimer...
changeTimer.Tick += Tick;
changeTimer.Interval = ....et cetera, setting the timer based on tutorial
}
public ObservableCollection<LocationsModel> items;
public ObservableCollection<LocationsModel> Items
{
get //edited so that I can write my own setter
{
return items;
}
set
{
if(value != items)
{
items = value;
//NotifyPropertyChanged("Items"); //can be used here, not necessary
changeTimer.Start();
}
}
}
private DispatcherTimer changeTimer;//based on the tutorial
//based on the tutorial provided, create dispatcher timer and
//tick definition somewhere around here
private Tick(...)
{
//code that iterates through the list and updates it goes here!
}
}

Send ObservableCollection bound to Datagrid to Database when property is changed

I have a datagrid bound to an ObservableCollection<T> as its ItemsSource. Then each column is bound to a property of Type T that is held in the model.
When the user changes a cell it changes the property in the Model gets the set called and then I want to write the entire ObservableCollection<T> to my database but I can't figure out how to make this change show up in the ViewModel where the ObservableCollection is located.
This is my class in the model. When the percentage property is changed I just want to update the Database with the updated ObservableCollection<MyObject> in the ViewModel. I am not using a DataTable because it is not a SQL database.
class MyObject : INotifyPropertyChanged
{
private string name;
private int percentage;
public int Percentage
{
get
{
return percentage;
}
set
{
percentage = value;
System.Diagnostics.Debug.WriteLine(percentage);
doSomething();
OnPropertyChanged("Percentage");
}
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public MyObject(string name, int percent)
{
Name = name;
Percentage = percent;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void NotifyPropertyChanged(String propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}

Categories

Resources