c# design question - standalone GUI application - c#

It's a pleasure to see how much knowledge people have on here, it's a treasure of a place.
I've seen myself writing code for DataGridView events - and using DataSource to a backend prepared DataTable object.
Sometimes the user can remove rows, update them etc. and the underlying data will need validation checks again.
Let's assume we have a person class
class Person {
public string FirstName { get; set; }
}
Let's say some other part of the code deals with creating an array of Person.
class Processor {
public static Person[] Create()
{
....
....
return person[];
}
}
And this information would appear on a DataGridView for user viewing.
I've tried something like this:
public static DataTable ToTable(List<Person> list)
{ ... }
And had this method in the Person class .. which I would think it'd belong to. Then I would bind the DataGridView to that DataTable and the user will then see that data and do their tasks.
But I've thought of using BindingList<> which I'm not so educated on yet.. would I still have the same capability of sorting the DataGridView like it does with DataTable as a DataSource? Would BindingList be implemented by a container class like "PersonCollection" or would the Person class implement itself? I would like to fire some events to be able to modify the collection in a clean way without having to reset datasources, etc. Where the user experience could really be affected.
I understand that modifying the DataSource DataTable is the good way. But sometimes I need to fire methods in the corresponding class that that specific row refers to, and had an ugly extra hidden column which would hold a reference to the existing object somewhere else (the Person reference).
If you guys know a better design solution, I would be more than happy to hear it.
Thanks in advance,
PS. After reading "The Pragmatic Programmer", I just can't stop thinking critically about code!
Leo B.

Create a business object class. Implement INotifyPropertyChanged. Look at the code below:
public class Employee:INotifyPropertyChanged
{
public Employee(string Name_, string Designation_, DateTime BirthDate_)
{
this.Name = Name_;
this.Designation = Designation_;
this.BirthDate = BirthDate_;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
[DisplayName("Employee Name")]
public string Name
{
get { return this._Name; }
set
{
if (value != this._Name)
{
this._Name = value;
NotifyPropertyChanged("Name");
}
}
}
private string _Name = string.Empty;
[DisplayName("Employee Designation")]
public string Designation
{
get { return this._Designation; }
set
{
if (value != this._Designation)
{
this._Designation = value;
NotifyPropertyChanged("Designation");
}
}
}
private string _Designation = string.Empty;
public DateTime BirthDate
{
get { return this._BirthDate; }
set
{
if (value != this._BirthDate)
{
this._BirthDate = value;
NotifyPropertyChanged("BirthDate");
}
}
}
private DateTime _BirthDate = DateTime.Today;
[DisplayName("Age")]
public int Age
{
get
{
return DateTime.Today.Year - this.BirthDate.Year;
}
}
}
Create your custom collection:
public class EmployeeCollection:BindingList<Employee>
{
public new void Add(Employee emp)
{
base.Add(emp);
}
public void SaveToDB()
{
//code to save to db
}
}
Set the data source:
_employeeStore = new EmployeeCollection();
this.dataGridView1.DataBindings.Add("DataSource", this, "EmployeeStore");
Now if you want to add an employee to your datagridview,
Employee employee = new Employee(textBoxName.Text, textBoxDesignation.Text, dateTimePicker1.Value);
_employeeStore.Add(employee);
This is very clean. You just play with business object and don't touch the UI.

Havent read you question fully, bbut you might want to take a look at my Project ModelShredder, which provides a convinient and fast ToDataTable method

Related

MVVM: ViewModels for bidirectional model elements

Assume I have following Model structure:
class Team {
public string Name {get;set; }
public List<Player> players {get;set;}
}
class Player {
public int Age {get;set;}
public string Name {get;set;}
public Team Team {get;set;}
}
I wish to create Viewmodels for this model. However, I also would like to avoid duplicating all properties from Player in the TeamVM and vice versa (for this simple example this would be feasable, but in reality rather cumbersome).
Looking at the literature and online articles, it seems that the "Pure" way would be to create a ViewModel for each Model and to have a ViewModel only return other ViewModels and never Models. This is all fine, but my problem is: how do you create these viewmodels without getting into a recursion trap. Assume I do it like this:
public class TeamVM: ViewModel<Team> {
private ObservableCollection<PlayerVM> _players;
public TeamVM(Team t): base(t) {
_players = new ObservableCollection();
foreach (Player p in t.players) {
_players.Add(new PlayerVM(t));
}
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
public ObservableCollection<PlayerVM> Players {
get { return _players; }
}
}
and
public class PlayerVM : ViewModel<Player> {
private TeamVM _teamVM;
public PlayerVM(Player p): base(p) {
_teamVm = new TeamVM(p.Team);
}
public int Age {
get { return _modelElement.Age; }
set { _modelElement.Age = value; NotifyPropertyChanged(); }
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
public TeamVM Team {
get { return _teamVM; }
set { _teamVm = value; NotifyPropertyChanged(); }
}
}
Obviously, the above can never work, since it creates recursion: creation of a TeamVM results in the creation of PlayerVMs which in turn spawn TeamVMs again etc.
Right now, I have solved this, by adding an intermediate class as follows:
public class TeamMinimalVM: ViewModel<Team> {
public TeamVM(Team t): base(t) {
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
}
public class TeamVM: TeamMinimalVM {
private ObservableCollection<PlayerVM> _players;
public TeamVM(Team t): base(t) {
_players = new ObservableCollection();
foreach (Player p in t.players) {
_players.Add(new PlayerVM(t));
}
}
}
And then having PlayerVM depend on TeamMinimalVM instead of TeamVM. This means that in the views, you would be able to do: {Binding Player.Team.Name} but not {Binding Player.Team.Players.Name}, which is kind of ok for me I guess since I don't think it's a great idea to do this anyway.
My question now is: is there a better/more "standard" way to do "Pure" VMs of bidirectional model elements? I do not want to clone properties of one type in the other (there are too many), nor do I want to expose Model elements directly.
Finally, the ViewModel class I use is this one (just for completeness, but it is not essential to the question I think.)
public class ModelElementViewModel<T> : ObservableObject where T : class
{
private bool _modelElementChanged;
private T _modelElement;
public ModelElementViewModel(T element)
{
_modelElement = element;
}
/// <summary>
/// The underlying model element for this viewmodel. Protected as one should not bind directly to model elements from the gui.
/// </summary>
internal T ModelElement {
get { return _modelElement; }
set {
if (_modelElement != value)
{
_modelElement = value;
ModelElementChanged = false;
NotifyAllPropertiesChanged();
}
; }
}
/// <summary>
/// Property that can be used to see if the underlying modelelement was changed through this viewmodel (note that an external
/// change to the model element is not tracked!)
/// </summary>
public bool ModelElementChanged {
private set
{
if (_modelElementChanged != value)
{
_modelElementChanged = value;
NotifyPropertyChanged();
}
}
get
{
return _modelElementChanged;
}
}
protected override void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
ModelElementChanged = true;
base.NotifyPropertyChanged(propertyName);
}
}
Edit:
What wasn't clear from my original question is that Players are not used exclusively by teams. I want following three scenarios to work:
I want to be able to create a view for a single player that displays all player information
I want to be able to create a view for a team, displaying the information of that team and a table of all players with their statistics
I also want to be able, for example, to have a Playersbook view, which consists of a table displaying all known players with their teamname for example.
Your classes have a clear hierarchy: teams aggregate players. Teams are owners, players are owned. Therefore, when creating a player VM, you can pass team VM as a constructor argument.
The obvious limitation of this is now you can't have players without teams. Possible solutions are: enforcing players to always be owned by some team; supporting null as a team VM and setting a proper value later; creating a "null team" object and using it for team-less players.
In cases like these, when there's a clear aggregation hierarchy, I use my OwnedObservableCollection<T, TOwner>. With it, I can create create a collection _players = new OwnedObservableCollection<PlayerVM, TeamVM>(this) in a team, then just add and remove players to and from the teams by using just Add and Remove.

Serializer Error

I have custom class with following properties:
Class Person
readonly public string Name;
readonly public string FamilyName;
readonly public string UserName;
private List<Person> Team = new List<Person>();
public Person Leader { get; private set; }
public bool HasTeam { get; private set; }
I am getting error on serializer because "Object has Leader property that has no public set." However I need to keep it private, as change of Leader will cause errors. Do you know any way around? Or I need to make it public and keep in mind that I cannot set it?
Thank you,
Michael
So, as mentioned, you could make it so, that it's it can be set only once, but i wouldn't see it as a good option (maybe you should rather rethink how you would like to store this information?)
public class Person
{
private Person leader;
public Person Leader
{
get
{
return leader;
}
set
{
if (Object.Equals(leader, value))
{
return;
}
if (leader != null)
{
throw new InvalidOperationException("Leader can be set only once!");
}
leader = value;
}
}
}
this would allow you to save/load the values, and it wouldn't allow it to be set afterwards. However, this is just working around the problem.
In case you don't have to save it specifically to XML, you could use a binary formatter, that saves the entire Person object (no matter if it contains private fields / properties)

ErrorProvider with custom GetHashCode

I have a form responsible of creating (and saving) new Patients. On this form I am using an ErrorProvider to show error icons on invalid fields (in this case just "LastName"). So, as usual => errorProvider.DataSource = patient;
Everything works fine when my model uses default GetHashCode(). But when I try to override this method using a custom hash code (I want to use this model with ISet collections) the control does not work properly. Now, I understand that custom hash codes should be used just for immutable objects. But the point is, how can I fill the fields of these objects if the ErrorProvider behaviour relays on GetHashCode to work properly? Is it necessary to implement a Dirty mechanism that switches between default hash code (during object initialization) and custom hash?
Code sample:
public class Patient : IDataErrorInfo, INotifyPropertyChanged
{
public string lastName;
public virtual string LastName
{
get { return lastName; }
set
{
if (lastName == value) return;
lastName = value;
NotifyPropertyChanged("LastName");
}
}
#region IDataErrorInfo Members
string IDataErrorInfo.Error { get { return null; } }
string IDataErrorInfo.this[string propertyName]
{
get { return this.GetValidationError(propertyName); }
}
#endregion // IDataErrorInfo Members
protected string GetValidationError(string propertyName)
{
if (ValidatedProperties.IndexOf(propertyName) < 0)
return null;
string error = null;
switch (propertyName)
{
case "LastName":
if (LastName == null)
error = "null";
break;
default:
break;
}
return error;
}
public virtual event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public override int GetHashCode()
{
unchecked
{
int result = 17;
result = 23 * result + ((LastName != null) ? LastName.GetHashCode() : 0);
return result;
}
}
}
Each field that is used in GetHashCode function must be immutable. I would not recommend to implement two versions of GetHashCode, because it should be persistent and repeatable. I know one possible way how to solve this problem. If you know that some object will be changed, then you can delete it from set before editing operation and add again into set when all modifications are done. In this case you can skip overriding of GetHashCode and use SortedSet with a specified comparer that implements IComparer interface.
Update
Normally, I would recommend to use HashSet if you don't need sorted set as a result. SortedSet employs binary search tree and it seems that it doesn't use GetHashCode function. SortedSet is a bit slower than HashSet. SortedSet performance is about O(log n), because it has to find a space for inserting element in the sorted set. HashSet takes only O(1).
IComparer helps to find whether two objects are equal (there is no need to call Equals) or tells which of them is less than or greater than other. I wrote a bit of code for testing of SortedSet functionality.
Code
public class Foo
{
public Foo(string something)
{
Something = something;
}
public string Something { set; get; }
}
public class BySomething : IComparer<Foo>
{
private readonly CaseInsensitiveComparer _comparer = new CaseInsensitiveComparer();
public int Compare(Foo x, Foo y)
{
return _comparer.Compare(x.Something, y.Something);
}
}
Test
[TestMethod]
public void SortedSetTest()
{
var first = new Foo("Doe");
var second = new Foo("Floyd");
var third = new Foo("Floyd");
var set = new SortedSet<Foo>(new BySomething());
set.Add(first);
set.Add(second);
set.Add(third);
Assert.AreEqual(set.Count, 2);
}

IDataErrorInfo calling bound object rather than DataContext

I have a model:
public class Product
{
public int Rating { get; set; }
...
}
and a View Model:
public class ProductViewModel: IDataErrorProvider
{
public int Temperature { get; set; }
public Product CurrentProduct { get; set; }
public string this[string columnName]
{
get
{
if (columnName == "Rating")
{
if (CurrentProduct.Rating > Temperature)
return "Rating is too high for current temperature";
}
return null;
}
}
}
My view has an instance of ProductViewModel as the DataContext. The view has the field:
<TextBox Text={Binding Path=CurrentProduct.Rating, ValidatesOnDataErrors=True} .../>
By default, validation occurs on the IDataErrorProvider of the bound object (Product), not the DataContext (ProductViewModel). So in the above instance, ProductViewModel validation is never called. This is just a simple example but illustrates the problem. The model doesn't (and shouldn't) know about Temperature, so the design dictates that the VM should perform the validation on that field.
Yes, I could hack it and replicate the bound properties of the model directly in the ViewModel, but I would have thought there must be an easier way to redirect the call to the VM rather than the model?
If you want your viewmodel to validate a property named "Rating" by IDataErrorInfo, then your viewmodel must actually have a property called Rating and you must bind to it, which would mean to replicate the bound properties of the model in the viewmodel.
Anyway this blog article could be interesting for you (Validating Business Rules in MVVM). The author adds a Validation delegate to the model that the viewmodel can set. This allows you to validate your model using data that it does not known, like the Temperature in your example.
I've encountered that problem before, and my solution is to expose a validation delegate from my Models which is checked when validating the class, and the ViewModel can use this to hook addition validation to the class that is unrelated to Model itself
For example, I would use code that looked something like this from the ViewModel to attach a validation delegate to the Model anytime its set
public class ProductViewModel
{
public int Temperature { get; set; }
private product _currentProduct;
public Product CurrentProduct
{
get { return _currentProduct; }
set
{
if (value != _currentProduct)
{
if (_currentProduct != null)
_currentProduct.RemoveValidationDelegate(ValidateProduct);
_currentProduct = value;
if (_currentProduct != null)
_currentProduct.AddValidationDelegate(ValidateProduct);
RaisePropertyChanged("CurrentProduct");
}
}
}
// Product Validation Delegate to verify temperature
private string ValidateProduct(object sender, string propertyName)
{
if (propertyName == "Rating")
{
if (CurrentProduct.Rating > Temperature)
return "Rating is too high for current temperature";
}
return null;
}
}
The actual code that adds the ValidationDelegate to the Model is pretty generic, so I typically have it in a BaseViewModel so all Models can have this functionality without me having to type it out for each one
#region IDataErrorInfo & Validation Members
#region Validation Delegate
public delegate string ValidationDelegate(
object sender, string propertyName);
private List<ValidationDelegate> _validationDelegates = new List<ValidationDelegate>();
public void AddValidationDelegate(ValidationDelegate func)
{
_validationDelegates.Add(func);
}
public void RemoveValidationDelegate(ValidationDelegate func)
{
if (_validationDelegates.Contains(func))
_validationDelegates.Remove(func);
}
#endregion // Validation Delegate
#region IDataErrorInfo for binding errors
string IDataErrorInfo.Error { get { return null; } }
string IDataErrorInfo.this[string propertyName]
{
get { return this.GetValidationError(propertyName); }
}
public string GetValidationError(string propertyName)
{
string s = null;
foreach (var func in _validationDelegates)
{
s = func(this, propertyName);
if (s != null)
return s;
}
return s;
}
#endregion // IDataErrorInfo for binding errors
#endregion // IDataErrorInfo & Validation Members
I also have this approach outlined in my blog post here if you want to see another example.

How do I create a Null Object in C#

Martin Fowler's Refactoring discusses creating Null Objects to avoid lots of
if (myObject == null)
tests.
What is the right way to do this? My attempt violates the "virtual member call in constructor" rule.
Here's my attempt at it:
public class Animal
{
public virtual string Name { get; set; }
public virtual string Species { get; set; }
public virtual bool IsNull
{
get { return false; }
}
}
public sealed class NullAnimal : Animal
{
public override string Name
{
get{ return "NULL"; }
set { }
}
public override string Species
{
get { return "NULL"; }
set { }
}
public virtual bool IsNull
{
get { return true; }
}
}
Go look up the amount of pain that interesting concepts, such as DbNull, have caused and think about if this is actually a good idea.
Protip: if you are constantly checking for null references, you probably should rethink the API a bit to help preclude null objects closer to the top of the stack.
Protip II: having something throw an exception when there is an unexpected null is actually fine and dandy. Things should go boom if you have nulls where there shouldn't be null.
I tend to agree with Wyatt Barnett's answer in that you should show restraint when creating these kinds of "null" objects. That said, there are some nice reasons for doing so. On occasion.
I also tend to agree with Supertux's answer in that the whole point of a null object is to not need to check whether or not it is null, so you should lose the IsNull property. If you really feel you need the IsNull property, then read Wyatt's response again and reconsider.
And thank you CraigTP for the nice links for more info. Good stuff.
Now I will assume that in your real code you actually have a constructor that is attempting to set the values of Name or Species (whatever your real code equivalent might be called). Otherwise, why would you get the "virtual member call in constructor" warning/error? I've run into a couple of similar problems when using the newfangled MyProperty { get; set; } shortcut myself (particularly when used in structs, and don't get me started about serialization versioning). Your solution is to not use the shortcut, but instead do it the old-fashioned way.
public class Animal {
protected Animal() { }
public Animal(string name, string species) {
_Name = name;
_Species = species;
}
public virtual string Name {
get { return _Name; }
set { _Name = value; }
}
private string _Name;
public virtual string Species {
get { return _Species; }
set { _Species = value; }
}
private string _Species;
}
public sealed class NullAnimal : Animal {
public override string Name {
get { return String.Empty; }
set { }
}
public override string Species {
get { return String.Empty; }
set { }
}
}
This solves the problem of setting your virtual properties in the constructor. Instead, you are setting your private field values (something you don't have the ability to reference if you use the shortcut). For extra credit, compile both methods, and use the Reflector to look at the resulting assemblies.
The more I use the { get; set; } shortcut, the more I dislike it.
The point of the Null Object pattern is that it doesn't require a null check to prevent a crash or error.
For example if you tried to perform an operation on the Species property and it was null - it would cause an error.
So, you shouldn't need an isNull method, just return something in the getter that won't cause the app to crash/error e.g.:
public class Animal
{
public virtual string Name { get; set; }
public virtual string Species { get; set; }
}
public sealed class NullAnimal : Animal
{
public override string Name
{
get{ return string.Empty; }
set { ; }
}
public override string Species
{
get { return string.Empty; }
set { ; }
}
}
You only use this approach if it is appropriate. Your example of an Animal object might not be a good example because it doesn't present an appropriate case where you would use this approach. For example:
Animal animal = new Animal();
if (animal.tail == null)
{
//do nothing because wagging a tail that doesn't exist may crash the program
}
else
{
animal.wagTail();
}
In this example, you should build the Animal object so that if the animal doesn't have a tail, it can successfully handle the wagTail() command without crashing.
Class Animal
{
Tail tail;
void wagTail()
{
if (this.tail == null)
{
//do nothing
}
else
{
this.tail.doTheWag();
}
}
}
Now you don't need to do a null check, but can just call animal.wagTail() regardless of whether the animal has a tail or not.
I'd like to mention here some interesting detail. Look at your class. Does it has any logic in it? This is not a class in its sense, this is a data structure. What you are trying to do is apply null object pattern to something it is not applicable to. Data structures is closer to value types, than to classes. There fore null check can be right in place to solve your problem.
Null object pattern is not something you should always follow. Null object pattern is a thing you can use to avoid Liskov's substitution principle violation, to represent a class that does nothing, because null is not appropriate substitution for a class as it is a value, but not a class.
But things are different with value types and data structures. Null is value! So in this case null check is the right thing to do.

Categories