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?
Related
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
i have this Base class:
public abstract class WiresharkFile
{
protected string _fileName;
protected int _packets;
protected int _packetsSent;
protected string _duration;
public int Packets
{
get { return _packets; }
set { _packets = value; }
}
public int PacketsSent
{
get { return _packetsSent; }
set { _packetsSent = value; }
}
}
And this sub class:
public class Libpcap : WiresharkFile, IDisposable, IEnumerable<WiresharkFilePacket>
{
....
}
Create my object:
WiresharkFile wiresahrkFile = new Libpcap(file);
My collection:
public ObservableCollection<WiresharkFile> wiresharkFiles { get; set; }
Send packets:
wiresahrkFile.Sendpackets();
At this point all my wiresahrkFile (Libpcap type) properties is changing so i wonder where i need to define this INotifyPropertyChanged.
If your xaml is binded to properties of WiresharkFile then a WiresharkFile have to implement the INotifyPropertyChanged, if not it will lead to the memory leaks (Top 3 Memory Leak Inducing Pitfalls of WPF Programming). If your binding is defined only on a Libpcap class then the Libpcap have to implement the INotifyPropertyChanged interface. In my projects I create a base implementation of the INotifyPropertyChanged interface ,and then each base models and base view models just inherits from that implementation. Here some base code:
1. Base implementation:
public class BaseObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
var propName = ((MemberExpression)raiser.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
2. Your model (in my opinion):
public abstract class WiresharkFile:BaseObservableObject
{
private string _fileName;
private int _packets;
private int _packetsSent;
private string _duration;
public int Packets
{
get { return _packets; }
set
{
_packets = value;
OnPropertyChanged();
}
}
public int PacketsSent
{
get { return _packetsSent; }
set
{
_packetsSent = value;
OnPropertyChanged();
}
}
}
regards,
Same answer with IIan but for C# 8 and .Net Framework 4.8.
1. Base Model
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
{
string propName = ((MemberExpression)raiser?.Body).Member.Name;
OnPropertyChanged(propName);
}
protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(name);
return true;
}
return false;
}
}
2. Your Model
public class Current : ObservableObject
{
private string _status;
public Current()
{
Status = "Not Connected";
}
public string Status
{
get { return _status; }
set
{
_status = value;
OnPropertyChanged(); // call this to update
}
}
}
3. How to use?
<Label Content="{Binding Status}"/>
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"));
}
}
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!
}
}
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
}