C# Class with many Properties and similar Setters, how to reuse logic - c#

I have a class with many Properties and public Setters. On each Setter I want to check if the value has changed and if so calling an EventHandler method. The code ended up looking too long, too repetitive and very programmer error prone because I can make small mistake on the multiple setter methods and mess up things.
I would like to ask if there is any way to make this code smaller and more reusable (in case I want to add new Properties for example):
using System;
public class CosmeticsModel
{
private string _skin;
public string Skin {
get => _skin;
set
{
if (value != _skin)
{
_skin = value;
OnCosmeticChanged("Skin", _skin);
}
}
}
private string _eyes;
public string Eyes {
get => _eyes;
set
{
if (value != _eyes)
{
_eyes = value;
OnCosmeticChanged("Eyes", _eyes);
}
}
}
private string _mouth;
public string Mouth {
get => _mouth;
set
{
if (value != _mouth)
{
_mouth = value;
OnCosmeticChanged("Mouth", _mouth);
}
}
}
private string _accessory;
public string Accessory {
get => _accessory;
set
{
if (value != _accessory)
{
_accessory = value;
OnCosmeticChanged("Accessory", _accessory);
}
}
}
private string _shoes;
public string Shoes {
get => _shoes;
set
{
if (value != _shoes)
{
_shoes = value;
OnCosmeticChanged("Shoes", _shoes);
}
}
}
private string _hat;
public string Hat {
get => _hat;
set
{
if (value != _hat)
{
_hat = value;
OnCosmeticChanged("Hat", _hat);
}
}
}
private string _oneHandedWeapon;
public string OneHandedWeapon {
get => _oneHandedWeapon;
set
{
if (value != _oneHandedWeapon)
{
_oneHandedWeapon = value;
OnCosmeticChanged("OneHandedWeapon", _oneHandedWeapon);
}
}
}
// [... rest of the Class]
}

You can extract a method called SetProperty. You can also make use of CallerMemberName to automatically set the property name.
private void SetProperty(ref string property, string value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") {
if (value != property)
{
property = value;
OnCosmeticChanged(propertyName, property);
}
}
Usage:
private string _hat;
public string Hat {
get => _hat;
set => SetProperty(ref _hat, value);
}

Related

JSON Deserialization not calling property methods

I have a JSON class file which contains three classes, all of which follow this structure:
public class ManifestJSON : INotifyPropertyChanged
{
[JsonProperty("dataType")]
private string dataType;
public string DataType
{
get
{
return dataType;
}
set
{
if(dataType != value)
{
dataType = value;
RaisePropertyChanged("DataType");
}
}
}
[JsonProperty("ttl")]
private int time_to_live;
public int Time_To_Live
{
get
{
return time_to_live;
}
set
{
if (time_to_live != value)
{
time_to_live = value;
RaisePropertyChanged("Time_To_Live");
}
}
}
[JsonProperty("serial")]
private long serial;
public long Serial
{
get
{
return serial;
}
set
{
if (serial != value)
{
serial = value;
RaisePropertyChanged("Serial");
}
}
}
[JsonProperty("modifiedIso8601")]
private string modifiedIso8601;
public string ModifiedIso8601
{
get
{
return modifiedIso8601;
}
set
{
if (modifiedIso8601 != value)
{
modifiedIso8601 = value;
RaisePropertyChanged("ModifiedIso8601");
}
}
}
[JsonProperty("modifiedTimestamp")]
private long modifiedTimestamp;
public long ModifiedTimestamp
{
get
{
return modifiedTimestamp;
}
set
{
if (modifiedTimestamp != value)
{
modifiedTimestamp = value;
RaisePropertyChanged("ModifiedTimestamp");
}
}
}
[JsonProperty("timezone")]
private string timezone;
public string Timezone
{
get
{
return timezone;
}
set
{
if (timezone != value)
{
timezone = value;
RaisePropertyChanged("Timezone");
}
}
}
[JsonProperty("exports")]
private ObservableCollection<ManifestItem> manifest_Items;
public ObservableCollection<ManifestItem> Manifest_Items
{
get
{
return manifest_Items;
}
set
{
if (manifest_Items != value)
{
manifest_Items = value;
RaisePropertyChanged("Manifest_Items");
}
}
}
//Event handling
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
Console.WriteLine("Updated");
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
In another class, I've created a global instance of type ManifestJSON
public ManifestJSON manifestData;
which is filled by deserializing a JSON string into this object using the DeserializeObject method from the Newtonsoft.json library like so:
manifestData = JsonConvert.DeserializeObject<ManifestJSON>(JSONString).
This fills the ManifestJSON class successfully, but none of my property methods or events are triggering. What am I doing wrong here?
If you want to update your existing data-bound ManifestJSON object, you should not replace this one with a new object but de-serialize the JSON string into new object and then set the properties of the existing manifestData object:
var newData = JsonConvert.DeserializeObject<ManifestJSON>(JSONString);
manifestData.DataType = newData.DataType;
manifestData.Time_To_Live = newData.Time_To_Live;
manifestData.Serial = newData.Serial;
//...

Update multiple properties on event raised

I am trying to understand how to trigger all the properties update when new data available.
For example I have two properties:
public string PropertyOne
{
get
{
return _propertyOne
}
set
{
_propertyOne= value;
this.OnPropertyChanged();
}
}
public string PropertyTwo
{
get
{
return _propertyTwo;
}
set
{
_propertyTwo = value;
this.OnPropertyChanged();
}
}
When I receive notification about new data I assign my properties:
Mediator<ViewModelMessages>.Instance.Register(ViewModelMessages.OnNewData, this.OnNewData);
private void OnNewData(object obj)
{
PropertyOne = (MyClass)obj.propertyOne;
PropertyTwo = (MyClass)obj.propertyTwo;
}
What I want to have is something like this:
private MyClass _myClass;
private void OnNewData(object obj)
{
_myClass = (MyClass)obj;
}
public string PropertyOne
{
get
{
return _myClass.PropertyOne;
}
set
{
_myClass.PropertyOne = value;
this.OnPropertyChanged();
}
}
public string PropertyTwo
{
get
{
return _myClass.propertyTwo;
}
set
{
_myClass.propertyTwo = value;
this.OnPropertyChanged();
}
}
So when new data arrived, my properties are automatically updated.
You can achieve that by passing an Empty string or null to your OnPropertyChanged rather than a property name, but note that property changed will get raised for all properties in this case.

c# casting string to class object

i want to cast a string from database to a class object without having to go over each possible outcome.
so not
if(type.StartsWith("ContactPersonType")){//} else if(type.StartsWith("ContactPersonTitle")){//}
this is what i have so far
private static T Create<T>(IDataRecord record)
{
var properties = typeof(T).GetProperties();
var returnVal = Activator.CreateInstance(typeof(T));
properties.ToList().ForEach(item =>
{
string type = item.GetMethod.ReturnParameter.ParameterType.Name;
if (type.StartsWith("ContactPerson"))
{
Type t = Type.GetType(item.GetMethod.ReturnParameter.ParameterType.ToString());
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), t));
}
else if (!type.StartsWith("ObservableCollection"))
{
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), item.PropertyType));
}
});
return (T)returnVal;
}
public class ContactPersonType
{
private int _id;
public int ID
{
get { return _id; }
set { _id = value; }
}
private String _name;
public String Name
{
get { return _name; }
set { _name = value; }
}
}
thanks
Use an anonymous collection when you want a certain action to apply to different input values.
foreach(var option in new[] {"ContactPerson", "ContactPersonTitle" }){
if (type.StartsWith(option))
{
Type t = Type.GetType(item.GetMethod.ReturnParameter.ParameterType.ToString());
item.SetValue(returnVal, Convert.ChangeType(record[item.Name].ToString(), t));
}
}

Convert a class into an array

I have a DisplayedData class ...
public class DisplayedData
{
private int _key;
private String _username;
private String _fullName;
private string _activated;
private string _suspended;
public int key { get { return _key; } set { _key = value; } }
public string username { get { return _username; } set { _username = value; } }
public string fullname { get { return _fullName; } set { _fullName = value; } }
public string activated { get { return _activated; } set { _activated = value; } }
public string suspended { get { return _suspended; } set { _suspended = value; } }
}
And I want to to put the objects from this class into an array where all objects inside of this class should be converted into an String[]
I have..
DisplayedData _user = new DisplayedData();
String[] _chosenUser = _user. /* Im stuck here :)
or can I create an array where all the items inside are consist of variables of different datatype so that the integer remains an integer and so the strings too?
You can create an array "with your own hands" (see Arrays Tutorial):
String[] _chosenUser = new string[]
{
_user.key.ToString(),
_user.fullname,
_user.username,
_user.activated,
_user.suspended
};
Or you could use Reflection (C# Programming Guide):
_chosenUser = _user.GetType()
.GetProperties()
.Select(p =>
{
object value = p.GetValue(_user, null);
return value == null ? null : value.ToString();
})
.ToArray();

C#/WPF: PropertyChanged for all Properties in ViewModel?

I've a class like this:
public class PersonViewModel : ViewModelBase //Here is the INotifyPropertyChanged Stuff
{
public PersonViewModel(Person person)
{
PersonEntity = person;
}
public Person PersonEntity {
get { return PersonEntity.Name; }
private set { PersonEntity.Name = value; RaisePropertyChanged("PersonEntity");
}
public string Name {
get { return PersonEntity.Name; }
set { PersonEntity.Name = value; RaisePropertyChanged("Name");
}
public int Age{
get { return PersonEntity.Age; }
set { PersonEntity.Age= value; RaisePropertyChanged("Age");
}
public void ChangePerson(Person newPerson)
{
//Some Validation..
PersonEntity = newPerson;
}
My TextBoxes are bound to Name and Age of the ViewModel.
If I change the _person object in the ViewModel, do I have to call for each Property a RaisePropertyChanged again or is there a way to do this automaticly (in my concret example I have about 15 Properties..)?
Thanks for any help.
Cheers
Joseph
You can indicate all properties have changed by using null or string.Empty for the property name in PropertyChangedEventArgs. This is mentioned in the documentation for PropertyChanged.
One other solution I used to tackle the problem of: first setting the value and then calling the PropertyChangedEventArgs is by adding a Set function in my ViewModelBase which looks like this:
public class ViewModelBase : INotifyPropertyChanged
{
protected bool Set<T>(ref T backingField, T value, [CallerMemberName] string propertyname = null)
{
// Check if the value and backing field are actualy different
if (EqualityComparer<T>.Default.Equals(backingField, value))
{
return false;
}
// Setting the backing field and the RaisePropertyChanged
backingField = value;
RaisePropertyChanged(propertyname);
return true;
}
}
Instead of doing this:
public string Name {
get { return PersonEntity.Name; }
set { PersonEntity.Name = value; RaisePropertyChanged("Name");
}
You can now achieve the same by doing this:
public string Name {
get { return PersonEntity.Name; }
set { Set(ref PersonEntity.Name,value);
}

Categories