Event Handling - sender as main class - c#

Apologies if the title is misleading, but I'm not sure precisely what to call what I'm looking for here. I have the following "main" class:
public class Entity : INotifyPropertyChanged
{
public string Name { get; set; }
public EntityRole Role { get; set; }
public EntityStats Stats = new EntityStats();
//Other stuff....
}
And a ... sub class? (proper name would be appreciated for this) ... called EntityStats:
public class EntityStats : INotifyPropertyChanged
{
public int CurrentHealth
{
get { return _currentHealth; }
set
{
if (value != _currentHealth)
{
_currentHealth = value;
OnPropertyChanged("CurrentHealth");
}
}
}
//other properties...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
Where an Entity object contains a property of type EntityStats.
On the other side of my code, I'm registering an Entity object to listen for PropertyChanged events:
public void RegisterEntity(Entity entity)
{
entity.Stats.PropertyChanged += entity_PropertyChanged;
}
void entity_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "CurrentHealth")
{
Message.Write("CurrentHealth property changed!");
DeathCheck((Entity)sender);
}
}
The issue I'm having is with DeathCheck((Entity)sender); -- Because the CurrentHealth OnPropertyChanged event is part of the EntityStats class, the object is of type EntityStats, which only contains part of the data I need.
How can I determine the Entity object that sender belongs to, or how can I refactor this code so that when a property inside EntityStats changes, an event is raised in the Entity class?

You can listen the EntityStats event's in Entity constructor and dispatch a Entity event's. Like this:
public class Entity : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Entity() {
Stats.PropertyChanged += Stats_PropertyChanged
}
//Other stuff....
void Stats_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
e.PropertyName = "Stats." + e.PropertyName;
PropertyChanged(this, e);
}
}
}
Now, in the RegisterEntity, the listener method can be associated direct to the Entity instance:
public void RegisterEntity(Entity entity)
{
entity.PropertyChanged += entity_PropertyChanged;
}
and the sender of the listener method is Entity instance, but is possible identify if the property was changed in Stats property:
void entity_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Stats.CurrentHealth")
{
Message.Write("Stats.CurrentHealth property changed!");
DeathCheck((Entity)sender);
}
}

You could give the EntityStats class a property named Entity (or somesuch) and set that property in the EntityStats constructor:
public class Entity : INotifyPropertyChanged
{
public string Name { get; set; }
public EntityRole Role { get; set; }
public EntityStats Stats = new EntityStats(this);
//Other stuff....
}
public class EntityStats : INotifyPropertyChanged
{
private Entity _entity;
public Entity Entity
{
get { return _entity; }
}
public EntityStats(Entity entity)
{
_entity = entity;
}
// ...
}

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?

Listening to PropertyChanged from another class

How can I listen in class B to the PropertyChanged events from class A? I would like to listen to changes of a property from class A.
class A : INotifyPropertyChanged
{
private int _x;
public int X
{
get => _x;
set
{
if (_x == value) return;
_x = value;
OnPropertyChanged(nameof(X));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class B
{
public B(int x)
{
// In this class I want to listen to changes of the property X from class A
}
}
Just listen to the event:
class B
{
public A _myA;
public B(int x)
{
_myA = new A();
_myA.PropertyChanged += A_PropertyChanged;
}
private void A_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(_myA.X)) return;
}
}

Inheritance with INotifyPropertyChanged

I was wondering if anyone knows how to solve the following problem... I have a base class that needs to update it's modifiedDateTime property when a derived class property is changed.
BaseObject.cs
public class BaseObject
{
private DateTime? _modifiedDateTime;
public DateTime? modifiedDateTime
{
get { return _modifiedDateTime ; }
set { _modifiedDateTime = value; }
}
public BaseObject
{
_modifiedDateTime = DateTime.Now.ToUniversalTime();
}
public void Update(object sender, PropertyChangedEventArgs e)
{
_modifiedDateTime = DateTime.Now.ToUniversalTime();
}
}
ExampleClass1.cs
public class ExampleClass1: BaseObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _data;
public int data
{
get { return _data; }
set
{
_data = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("data"));
}
}
public ExampleClass1()
{
PropertyChanged += base.Update;
}
}
The previous example is working as expected.
However if the derived class contains an object of another class. For example:
ExampleClass2.cs
public class ExampleClass2: BaseObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _data;
private ExampleClass1 _objClass1;
public int data
{
get { return _data; }
set
{
_data = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("data"));
}
}
public ExampleClass1 objClass1
{
get { return _objClass1; }
set
{
_objClass1 = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("objClass1"));
}
}
public ExampleClass2()
{
PropertyChanged += base.Update;
}
}
When I change the data property of the objClass1, the modifiedDateTime property of the ExampleClass2 inherited by the base class BaseObject is not updated.
How can I solve this problem?
When you set the value of objClass1 subscribe to its property changed event as well
public ExampleClass1 objClass1 {
get { return _objClass1; }
set {
//in case one already existed. unsubscribe from event
if(_objClass1 != null) _objClass1.PropertyChanged -= base.Update
_objClass1 = value;
//subscribe to event
if(_objClass1 != null) _objClass1.PropertyChanged += base.Update
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("objClass1"));
}
}

Where i need to define INotifyPropertyChanged in case of Base and sub classes

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}"/>

Simple status message implementation

I'm trying to create basic class which will allow different controls to bind-in and display some values.
I want to have static list of objects, where each object has some properties like caption, ticks counter, whatever.
Then I want to bind label to last added item to this list and datagridview to allow to see all of them.
Would be great if such solution could be for both winforms and wpf environments.
If you could point me what I'm doing wrong. Thanks.
Draft of the idea (one of many already tested and failed) below.
status class:
public class Status: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
//implementation of observable collection
private static ObservableCollection<Status> _list;
public static ObservableCollection<Status> List
{
get { return _list ?? (_list = new ObservableCollection<Status>{new Status()}); }
}
//object properties
public string Message { get; set; }
public bool Finished { get; set; }
//object views
public string View
{
get { return Message + "(" + Finished + ")" ; }
}
//object methods
public static Status Add(string message)
{
var result = new Status
{
Message = message,
Finished = false
};
List.Add(result);
return result;
}
public void Finish()
{
Finished = true;
}
}
form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
label1.DataBindings.Add("Text", Status.List, "Message");
listBox1.DisplayMember = "View";
listBox1.DataSource = Status.List;
}
private void Button1_Click(object sender, EventArgs e)
{
label5.Text = Status.Add(textBox1.Text).Message;
textBox1.Text = "";
}
private void Button2_Click(object sender, EventArgs e)
{
((Status)listBox1.SelectedItem).Finish();
}
}
This did the trick:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using WindowsFormsApplication2.Annotations;
namespace WindowsFormsApplication2
{
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
#region Notyfier implementation
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region collection implemetation
public BindingList<Item> Items = new BindingList<Item>();
public string Count
{
get { return (Items.Count == 1)
? "1 item."
: Items.Count + " items."; }
}
public Item Current
{
get { return Items.Count == 0
? new Item {Colour = Color.Chartreuse} //default initial item
: Items.Last(); }
}
#endregion
#region object implemetation
protected object ID { get; set; }
public Color Colour { get; set; }
public void NewItem(Color color)
{
Items.Add(new Item
{
ID = Guid.NewGuid(),
Colour = color
});
OnPropertyChanged("Count");
OnPropertyChanged("Current");
}
#endregion
}
}

Categories

Resources