How distinguish between objects type c# - c#

I am creating a basketball WPF app which displays a HomeTeam and an AwayTeam.
I have created a Player object and in the main window I have created ObservableCollection of player objects for both the home (HomePlayersList) and the away teams (AwayPlayersList). I have used INotifyPropertyChanged interface on the player object so when IsInGame bool is true the player gets added to one of the two ObservableCollection<Player> depending on the count. (If list one ObservableCollection<Player> HomeTeam or ObservableCollection<Player> AwayTeam count is 5, then the rest gets added to the substitution list ObservableCollection<Team> HomeSub or ObservableCollection<Team> AwaySub.)
But I am trying to distinguish whether the player is in the home or away team, and depending on which list the player is in, the player would be added to the new list of home or away.
public static ObservableCollection<Player> HomePlayersList;
public static ObservableCollection<Player> AwayPlayersList;
public static ObservableCollection<Player> HomeTeam = new ObservableCollection<Player>();
public static ObservableCollection<Player> AwayTeam = new ObservableCollection<Player>();
public static ObservableCollection<Player> HomeSub = new ObservableCollection<Player>();
public static ObservableCollection<Player> AwaySub = new ObservableCollection<Player>();
public static int HomeSubCount = 7;
public class Player: INotifyPropertyChanged
{
public static bool IsHome = true;
private static int TotalSelected = 1;
public string Id { get; set; } //player ID
public string FirstName { get; set; } //player first name
public string SecondName { get; set; } //player second name
public string KnownName { get; set; } //player known name
public string Position { get; set; } //player position
public string Number { get; set; } //player number
public bool isInGame;
public bool IsInGame
{
get { return isInGame; }
set
{
if (value != isInGame)
{
isInGame = value;
if (isInGame)
{
OnPropertyChanged("IsInGame", true);
}
else
{
OnPropertyChanged("IsInGame", false);
}
}
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName, bool state)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
if (state)
{
if (TotalSelected > 5 + MainWindow.HomeSubCount)
{
this.IsInGame = false;
return;
}
if (MainWindow.HomeTeam.Count < 5)
MainWindow.HomeTeam.Add(this);
else
{
if (MainWindow.HomeSub.Count < MainWindow.HomeSubCount)
{
MainWindow.HomeSub.Add(this);
}
}
TotalSelected++;
}
else
{
if (SearchForMe(MainWindow.HomeTeam) != null)
{
MainWindow.HomeTeam.Remove(SearchForMe(MainWindow.HomeTeam));
TotalSelected--;
return;
}
if (SearchForMe(MainWindow.HomeSub) != null)
{
MainWindow.HomeSub.Remove(SearchForMe(MainWindow.HomeSub));
TotalSelected--;
return;
}
}
}
private Team SearchForMe(ObservableCollection<Team> OCT)
{
return OCT.Where(i => i.Number == this.Number).SingleOrDefault();
}
public event PropertyChangedEventHandler PropertyChanged;`enter code here`
}

Paste in the Player class a property
ObservableCollection<Player> CurrnetIn { get; set; }
and set this property, if you Add the player in some collection. Or have I misunderstood the question.

You could use the Contains method of the ObservableCollection<T> class to determine whether a specific object is already in the collection:
if (MainWindow.HomeTeam.Contains(this))
{
MainWindow.HomeTeam.Remove(this);
TotalSelected--;
return;
}
Since your Player class doesn't implement the IEquatable<T> interface the references that you pass to the Contains method will be compared to the items in the collection which is totally fine in this case:
Does List<String>.Contains(mystring) do a reference comparison or a value comparison?

Related

How to keep custom object sequence property in model when there are multiple lists of that type used in application

I have a wpf application with MVVM pattern. There is a view which displays the warnings (text) on screen, which are of type WarningModel.cs
In my ViewModel I have 3 properties of type ObservableCollection<WarningModel>(), these are bound to 3 different grids (telerik for wpf grids). Three grids because there are 3 types of warnings.
My requirement is when user adds new warning (new object of type WarningModel) into the grid, that object should be assigned an order sequence starting with 1 followed by 2 and so on for next object.
What I tried is added a static field in Warning.cs which will be auto incremented each time the constructor is called. Now the problem is since there are 3 ObservableCollection each time the object is instantiated from any of the collection, sequence gets incremented but all the three collection should maintain their own sequence.
What else can be employed here to achieve the desired output.
Here are my classes
public class Warning
{
public string MESSAGE { get; set; }
private int _type;
public int TYPE
{
get { return _type; }
set
{
if (value != _type)
_type = value;
}
}
public int SORT_ORDER { get; set; }
public static int SORT_SEQUENCE = 0;
public Warning()
{
SORT_ORDER = Interlocked.Increment(ref SORT_SEQUENCE);
}
}
public class WarningsViewModel
{
public ObservableCollection<WarningModel> WarningBP { get; set; }
public ObservableCollection<WarningModel> WarningPP { get; set; }
public ObservableCollection<WarningModel> WarningPB { get; set; }
public VoyageInfoViewModel()
{
WarningBP = new ObservableCollection<WarningModel>();
WarningPP = new ObservableCollection<WarningModel>();
Warning PB = new ObservableCollection<WarningModel>();
}
}
You can attach handler for CollectionChanged of ObservableCollection and assign ID as index of inserted item.
public VoyageInfoViewModel()
{
WarningBP = new ObservableCollection<WarningModel>();
WarningBP.CollectionChanged += WarningCollectionChanged;
...
}
private void WarningCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
for (var i = 0; i < e.NewItems.Count; i++)
((Warning)e.NewItems[i]).SORT_ORDER = e.NewStartingIndex + i + 1;
}
If you allow user also to delete warnings and want SORT_ORDER be recalculated on deletion you can write handler as:
private void WarningCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
for (var i = 0; i < ((ICollection)sender).Count; i++)
((Warning)((IList)sender)[i]).SORT_ORDER = i + 1;
}
You could for example create a custom ObservableCollection<T> that sets the sequence number of added items. Something like this:
public class SequencedObservableCollection : ObservableCollection<WarningModel>
{
private int _sequenceCounter = 0;
protected override void InsertItem(int index, WarningModel item)
{
item.SORT_ORDER = ++_sequenceCounter;
base.InsertItem(index, item);
}
}
public abstract class WarningModel
{
public int SORT_ORDER { get; set; }
}
Don't forget to change the type of your source properties in the view model:
public SequencedObservableCollection WarningBP { get; set; }
public SequencedObservableCollection WarningPP { get; set; }
public SequencedObservableCollection WarningPB { get; set; }

Best practice for sharing an Observable-collection with nested properties

I have an observable collection that is shared between different viewmodels.
public class UserInput1ViewModel: INotifyPropertyChanged
{
public ObservableCollection<ParamClass> ParamColl { get; set; }
public UserInput1ViewModel(<ParamClass> paramColl)
{
this.ParamColl = paramColl;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private void UpdateCollection()
{
this.ParamList = PerformCalculations();
}
}
public class ParamClass
{
public double Property1 { get; set; }
public double Property2 { get; set; }
public double Property3 { get; set; }
... ...
... ...
public double Property19 { get; set; }
}
The function PerformCalculations() will execute, but it will not update the all the properties inside the observable collection. I have learned that you cannot do that with observable collection https://stackoverflow.com/a/9984424/4387406.
So, this is what I am currently doing.
private void UpdateCollection()
{
var output = PerformCalculations();
for(int i = 0; i < output.Count(); i++)
{
this.ParamColl[i].Property1 = output[i].Property1;
this.ParamColl[i].Property2 = output[i].Property2;
... ...
... ...
this.ParamColl[i].Property19 = output[i].Property19;
}
}
My question is: is there a better way of sharing observable collection?
Many thanks in advance.
If you want the GUI to update whenever a property of an instance in a list changes, you should implement the INotifyPropertyChanged in the instance class, just as you have done in your ViewModel.
You haven't shown what your ParamClass looks like so I'm using a Person class in place. You could do the same thing as you've done in your ViewModel.
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int age;
public int Age
{
get { return age; }
set { age = value; OnPropertyChanged("Age"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Now, if even a single property in any of the instances is changed it will be reflected on your GUI.
Since you're using WPF, there's quite a few good MVVM tool-kits out there that will do a lot of this for you. For instance, MVVM Light Toolkit is one such examples. There's many others out there.

Access Parent level object

public class Activity
{
public games _Games {get;set;}
public sports _Sports {get;set;}
}
public class games : PropertyChangedBase
{
public int player
{
get;
set; //have if- else statement
}
}
public class sports : PropertyChangedBase
{
public int sub{get;set;}
}
Aim: when the games player is more than 2, I would like to update sports sub variable to 10.
Question: How can I access the parent class and update the sports class variable?
You could use an event that would signal to the Activity class that it is time to update.
public class games
{
public event UpdatePlayerSubDelegate UpdatePlayerSub;
public delegate void UpdatePlayerSubDelegate();
private int _player;
public int player
{
get { return _player; }
set
{
_player = value;
if (_player > 2)
{
// Fire the Event that it is time to update
UpdatePlayerSub();
}
}
}
}
In the Activity class you can register the event in the constructor and write in to the event handler the necessary update. In your case sub to 10:
public class Activity
{
public games _Games { get; set; }
public sports _Sports { get; set; }
public Activity()
{
this._Games = new games();
this._Games.UpdatePlayerSub += _Games_UpdatePlayerSub;
this._Sports = new sports();
}
private void _Games_UpdatePlayerSub()
{
if (_Sports != null)
{
_Sports.sub = 10;
}
}
}
EDIT
I just saw the tag INotifyPropertyChanged. Of course you could also use this interface and the provided event. Implement the interface as the following:
public class games : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _player;
public int player
{
get { return _player; }
set
{
_player = value;
if (_player > 2)
{
// Fire the Event that it is time to update
PropertyChanged(this, new PropertyChangedEventArgs("player"));
}
}
}
}
And in the Activity class register again to the event in the constructor:
public Activity()
{
this._Games = new games();
this._Games.PropertyChanged += _Games_PropertyChanged;
this._Sports = new sports();
}
and declare the body of the event handler:
private void _Games_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_Sports != null)
{
_Sports.sub = 10;
}
}
And _Sports.sub will get updated automatically. Hope it helps. There are of course other ways to accomplish this update. It is just the first the came to my mind
You need to create an instance of Activity. You also need to initialize _Sports in it
Activity activity = new Activity();
activity._Sports = new sports();
activity._Sports.sub = 10;
Or using object tantalizer
Activity activity = new Activity
{
_Sports = new sports()
};
activity._Sports.sub = 10;
By the way, Activity is not parent class of sports. Activity holds sports object as a member. In your example PropertyChangedBase is parent class of games.

Implementing ObservableCollection with INotifyChanged

It's a closed network and I don't have a VS on the internet computer, so sorry in advance for how the code looks.
Here is part of my view model:
public class Elements : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String p)
{​
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
private int _CardNumber;
private int _CardCode;
private Card _CurrentlySelectedRow;
public int CardNumber
{
get { return _CardNumber; }
set
{
if (value != _CardNumber)
{
_CardNumber = value;
OnPropertyChanged("CardNumber");
}
}
}
public int CardCode
{
get { return _CardCode; }
set
{
if (value != _CardCode)
{
_CardCode = value;
OnPropertyChanged("CardCode");
}
}
}
public Card CurrentlySelectedRow
{
get { return _CurrentlySelectedRow; }
set
{
if (value != _CurrentlySelectedRow)
{
_CurrentlySelectedRow= value;
OnPropertyChanged("CurrentlySelectedRow");
}
}
}
public class Card
{
public int CardNumber { get; set; }
public int CardCode { get; set; }
public Card() {}
public Card(int CardNumber_, int CardCode_)
{
CardNumber = CardNumber_;
CardCode = CardCode_;
}
}
private ObservableCollection<Card> _Cards { get; set; }
public ObservableCollection<Card> Cards
{
get
{
if (_Cards == null)
_Cards = new ObservableCollection<Card>();
return _Cards;
}
set
{
_Cards = value;
OnPropertyChanged("Cards");
}
}
public bool UpdateCard()
{
//selected row in gridcontrol is binded to CurrentlySelectedRow
CurrentlySelectedRow.CardNumber = CardNumber;
CurrentlySelectedRow.CardCode = CardCode ;
}
public bool AddCard()
{
Cards.Add(new Card(CardNumber, CardCode );
}
}​
Since the grid is not editable, the row is updated in external form, in which controls are binded to cardNumber and cardCode, and when pressing OK - the UpdateCard() is called (if we in update), and the AddCard called if we in add.
The AddCard works - the grid updates.
The UpdateCard - updates the list, but the grid isn't updated...
Maybe you can see were is the problem?...
My intial thought on this is that the update to the CurrentlySelectedRow.CardNumber and CurrentlySelectedRow.CardCode wont call OnPropertyChanged().
With ObservableCollection the UI will be updated if any items are added or removed but will not if any changes are made to the properties in an item.
Because no call is made, the UI doesn't know that these properties have changed.
You would have to make your Card class implement INotifyPropertyChanged similarly to Elements.
What I would normally do is create a BaseObject class that simply implements INotifyPropertyChanged. This class is then inherited by all of my other classes:
public abstract BaseObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string p)
{​
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
Then just change the properties in your Card class to call OnPropertyChanged.
Edit following on from comments:
Now that you've got INotifyPropertyChanged implemented you could try rebinding whatever is bound to Elements.CardNumber and Elements.CardCode to CurrentlySelectedCard.CardNumber and CurrentlySelectedCard.CardCode respectively. This would potentially allow you to delete the properties from Elements.
This will, however depend on the rest of your code.

C# - Event Design considerations

I want an event notification system that should notify the doctor when the heartbeat of the patient is greater than 120.I do not know, How to design such system. Just I have implemented the wrong one. Help me in implementing the correct one.
static void Main()
{
Patient[] patList = { new Patient
{ PatientID = "1", HeartBeat = 100 },
new Patient { PatientID = "2", HeartBeat = 130 } };
List<Patient> plist = patList.ToList();
Console.ReadKey(true);
}
public class Doctor
{
public event PulseNotifier AbnormalPulseRaised;
public string Name
{
get;
set;
}
}
public class Patient
{
public event PulseNotifier AbnormalPulseRaised;
static Random rnd = new Random();
public Patient()
{
PulseNotifier += new PulseNotifier(OnAbnormalPulseRaised);
}
public string PatientID
{
get;
set;
}
public int HeartBeat
{
get;
set;
}
public void HeartBeatSimulation(List<Patient> patList)
{
foreach(Patient p in patList)
{
if (p.HeartBeat > 120)
{
if (AbnormalPulseRaised != null)
{
AbnormalPulseRaised(p);
}
}
}
}
public void OnAbnormalPulseRaised(Patient p)
{
Console.WriteLine("Patient Id :{0},Heart beat {1}",
p.PatientID, p.HeartBeat);
}
}
Apart from that, I want to have a common clarification.
What is the best way to remember the publisher and observer pattern?. Because I am quite confusing about where to implement publisher and where to implement
Well, for starters, I usually think it's an bad Idea to listen to the events of a class in the same class if you have access to it.
It's also a good idea to derive from EventArgs, which is recommended by MS.
The responsibility of raising the event should indeed be in the patient class itself, but here you raise only the event of the class where you call the HardBeatSimulation function itself instead of on the patient that actually has an abnormal pusle :)
static void Main(string[] args) {
Patient pat1 = new Patient(1, 120);
Patient pat2 = new Patient(3, 150); // this one can have a 150 bpm hartbeat :)
Doctor fancyDoctor = new Doctor();
fancyDoctor.AddPatient(pat1);
fancyDoctor.AddPatient(pat2);
Console.ReadKey(true);
}
public class Doctor {
List<Patient> _patients;
public event EventHandler Working;
public Doctor() {
_patients = new List<Patient>();
}
public void AddPatient(Patient p) {
_patients.Add(p);
p.AbnormalPulses += new EventHandler<AbnormalPulseEventArgs>(p_AbnormalPulses);
}
void p_AbnormalPulses(object sender, AbnormalPulseEventArgs e) {
OnWorking();
Console.WriteLine("Doctor: Oops, a patient has some strange pulse, giving some valium...");
}
protected virtual void OnWorking() {
if (Working != null) {
Working(this, EventArgs.Empty);
}
}
public void RemovePatient(Patient p) {
_patients.Remove(p);
p.AbnormalPulses -= new EventHandler<AbnormalPulseEventArgs>(p_AbnormalPulses);
}
}
public class Patient {
public event EventHandler<AbnormalPulseEventArgs> AbnormalPulses;
static Random rnd = new Random();
System.Threading.Timer _puseTmr;
int _hartBeat;
public int HartBeat {
get { return _hartBeat; }
set {
_hartBeat = value;
if (_hartBeat > MaxHartBeat) {
OnAbnormalPulses(_hartBeat);
}
}
}
protected virtual void OnAbnormalPulses(int _hartBeat) {
Console.WriteLine(string.Format("Abnormal pulsecount ({0}) for patient {1}", _hartBeat, PatientID));
if (AbnormalPulses != null) {
AbnormalPulses(this, new AbnormalPulseEventArgs(_hartBeat));
}
}
public Patient(int patientId, int maxHartBeat) {
PatientID = patientId;
MaxHartBeat = maxHartBeat;
_puseTmr = new System.Threading.Timer(_puseTmr_Tick);
_puseTmr.Change(0, 1000);
}
void _puseTmr_Tick(object state) {
HartBeat = rnd.Next(30, 230);
}
public int PatientID {
get;
set;
}
public int MaxHartBeat {
get;
set;
}
}
public class AbnormalPulseEventArgs : EventArgs {
public int Pulses { get; private set; }
public AbnormalPulseEventArgs(int pulses) {
Pulses = pulses;
}
}
The method OnAbnormalPulseRaised(Patient p) should be placed in Doctor class because doctor is the one being notified about event. The event property should by placed in Patient class because patients are raising the events:
public class Doctor
{
public Doctor()
{
// doctor initialization - iterate through all patients
foreach(patient in patList)
{
// for each patient register local method as event handler
// of the AbnormalPulseRaised event.
patient.AbnormalPulseRaised +=
new PulseNotifier(this.OnAbnormalPulseRaised);
}
}
public void OnAbnormalPulseRaised(Patient p)
{
Console.WriteLine("Patient Id :{0},Heart beat {1}",
p.PatientID, p.HeartBeat);
}
public string Name
{
get;
set;
}
}
public class Patient
{
public event PulseNotifier AbnormalPulseRaised;
static Random rnd = new Random();
public Patient()
{
}
public string PatientID
{
get;
set;
}
public int HeartBeat
{
get;
set;
}
public void HeartBeatSimulation(List<Patient> patList)
{
foreach(Patient p in patList)
{
if (p.HeartBeat > 120)
{
if (AbnormalPulseRaised != null)
{
AbnormalPulseRaised(p);
}
}
}
}
}
Publisher is the object with event property. Subscriber is the object with handler method.

Categories