Comparing two object state,before and after update - c#

first things first.
I have the following classes:
class Employee
{
private int employeeID;
private string firstName;
private string lastName;
private bool eligibleOT;
private int positionID;
private string positionName;
private ArrayList arrPhone;
public IList<Sector> ArrSector {get; private set;}
//the constructor method takes in all the information of the employee
public Employee(int empID, string fname, string lname, bool elOT, int pos, string posname)
{
employeeID = empID;
firstName = fname;
lastName = lname;
eligibleOT = elOT;
positionID = pos;
positionName = posname;
arrPhone = new ArrayList();
ArrSector = new List<Sector>();
}
//the constructor method takes in the employee id, the first name and the last name of the employee
public Employee(int empid, string firstname,string lastname)
{
employeeID = empid;
firstName = firstname;
lastName = lastname;
}
//overtides the first name and the last name as a string.
public override string ToString()
{
return firstName +" "+lastName;
}
public int EmployeeID
{
get { return employeeID; }
set { employeeID = value; }
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
public bool EligibleOT
{
get { return eligibleOT; }
set { eligibleOT = value; }
}
public int PositionID
{
get { return positionID; }
set { positionID = value; }
}
public string PositionName
{
get { return positionName; }
set { positionName = value; }
}
public ArrayList ArrPhone
{
get { return arrPhone; }
set { arrPhone = value; }
}
// The function assigns all the variables associated to the employee to a new object.
public static object DeepClone(object obj)
{
object objResult = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
//Memento pattern is used to save the employee state.
//The changes will be rolled back if the update button not clicked
public class Memento : IMemento
{
private Employee originator = null;
private int employeeID;
private string firstName;
private string lastName;
private bool eligibleOT;
private int positionID;
private string positionName;
private ArrayList arrPhone;
private IList<Sector> arrSector;
public Memento(Employee data)
{
this.employeeID = data.EmployeeID;
this.firstName = data.FirstName;
this.lastName = data.LastName;
this.eligibleOT = data.EligibleOT;
this.positionID = data.PositionID;
this.positionName = data.PositionName;
this.arrPhone = data.ArrPhone;
this.originator = data;
this.arrSector = Extensions.Clone<Sector>(data.ArrSector);
}
}
I am using C sharp in winforms. the front end of my application has a listbox on the left end side which has the first name of the employee.on the left hand side, there are different textboxes which correspond to the employee selected in the list box. I have coded it in such a way that everytime i select an employee, its attributes, like the employee id, name, position, etc are displayed in these fields.
if the user changes any attribute of the employee, he has to click an update button to make the changes to the database.
now the real problem, when the user changes any field of the selected employee, and selects another employee without clicking the update button, i want to show a pop up box to tell the user that if he selects another employee , all the changes will be lost.
for this reason i have created the momento class to hold the previous state of the employee.
i have also tried overloading the == operator
public static bool operator ==(Employee e, Memento m)
{
return ((e.employeeID == m.employeeID) &&
(e.firstName == m.firstName) &&
e.lastName == m.lastName &&
e.eligibleOT == m.eligibleOT &&
e.positionID == m.positionID &&
e.positionName == m.positionName &&
e.arrPhone == m.arrPhone &&
e.ArrSector == m.arrSector);
}
public static bool operator !=(Employee e, Memento m)
{
return (e.employeeID != m.employeeID);
}
my idea was to compare the two object...
but m not successfull. how do i do it??how do i show the popup if changes are made.?where do i place the code to show the popup?

One word of warning...it's generally not a good idea to have different logic in your == and != operators. It's somewhat unintuitive to be able to have both == and != be false at the same time.
if(!(a == b) && !(a != b))
{
// head explodes
}
That aside, I'm guessing that you have your Employee class referenced as an object (or other parent class) in your comparison code. Maybe something like this:
if(listBox1.SelectedItem != currentMemento)
{
...
}
If this is the case, then the compiler isn't binding the != to your custom implementation. Cast listBox1.SelectedItem to Employee in order to force that.
if((Employee)listBox1.SelectedItem != currentMemento)
{
...
}
There are, however, many other approaches that you could take to solve this issue:
Make the implementation entirely on the GUI side, with a bool that gets set to true when the data in the text fields changes, then check that flag when changing employees
Implement the IComparable or IEquatable interfaces
Override the Equals method on the Employee and/or Memento class
(If you go with the second option, it's generally recommended that you complete the third as well)
Example
Here's an example of what you could do (I'm assuming you have a ListBox named listBox1 and you've attached to the SelectedIndexChanged event with the listBox1_SelectedIndexChanged function):
private Employee lastSelectedEmployee;
private Memento selectedMemento;
void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Employee selectedEmployee = (Employee)listBox1.SelectedItem;
if(lastSelectedEmployee != null && lastSelectedEmployee != selectedEmployee)
{
if(/*changes exist*/)
{
if(/*cancel changes*/)
{
listBox1.SelectedItem = lastSelectedEmployee;
return;
}
}
}
lastSelectedEmployee = selectedEmployee;
selectedMemento = //create the memento based on selectedEmployee;
}
You'll have to provide your own logic for the areas I've left comments, but the idea should be pretty straightforward.

Have a look at the IComparable interface. It requires you to implement the method you need t make such a comparison. KB article, Hopefully it turn English for you, on my PC it turns always German.
-sa

Related

Converting data from one class to another

I'm trying to convert data from one ExtensionMethods class to another class called ComboBoxViewItem
ExtensionMethods:
public static class ExtensionMethods
{
public static int GetDisplayItemId(this ComboBox combobox)
{
if (combobox.SelectedItem == null)
return 0;
else
return ((DisplayItems)combobox.SelectedItem).Id; //Error here
}
}
ComboBoxViewItem
class ComboBoxViewItem<T>
{
private string name;
public T Item { get; set; }
public ComboBoxViewItem(T item, string name)
{
this.Item = item;
this.name = name;
}
public ComboBoxViewItem(T item)
{
var prop = item.GetType().GetProperty("Name");
if (prop == null)
throw new ArgumentException("This object does not have a Name property, please use the other contructor.");
if (prop.PropertyType != typeof(string))
throw new ArgumentException("The property Name MUST be of type string. Please use the other contructor instead.");
this.Item = item;
this.name = (string)prop.GetValue(item);
}
public override string ToString()
{
return name;
}
}
Step 1 Now what I'm trying to do is load data from my WCF service into one of my comboboxes like this:
public async Task LoadCompanies()
{
using (MKCServiceClient service = new MKCServiceClient())
{
var companies = await service.GetCompaniesAsync();
foreach (var company in companies)
cmbQuoteCompany.Items.Add(new ComboBoxViewItem<Company>(company));
cmbQuoteCompany.SelectedIndex = 0;
}
} //Coding works fine and loads data into the combobox
Step 2 I want to add that data that was selected in the combobox to be added else where in another table using this method below:
private async void btnQuoteAdd_Click(object sender, RoutedEventArgs e)
{
using (MKCServiceClient service = new MKCServiceClient())
{
quoteInformation = await service.GetQuoteAsync(new QuoteData
{
CompanyId = cmbQuoteCompany.GetDisplayItemId(), //I use ExtensionMethods class here
BranchId = cmbQuoteBranch.GetDisplayItemId(), //Here
CustomerId = cmbQuoteContact.GetDisplayItemId(), //Here
CustomerRFQ = txtQuoteCustomerRFQ.Text,
Date = dpQuoteDate.Text,
Item = txtQuoteItem.Text,
Material = txtQuoteMaterial.Text,
Description = txtQuoteDescription.Text,
Quantity = Convert.ToInt32(txtQuoteQTY.Text)
});
await service.FinalizeQuoteAsync(finalizeQuote);
}
}
My end goal is to get the Id of the selected item in my combobox and then insert it into my database.
After I call the btnQuoteAdd_Click method, my application crashes and gives me the following error
Unable to cast object of type
'MKCWorkflowApplication.ComboBoxViewItem`1[MKCWorkflowApplication.WorkflowService.Company]'
to type 'MKCWorkflowApplication.DisplayItems'.
The reason why i'm posting this issue here is because i've been given this coding from a friend who knows a lot more that C# than me and we are no longer in contact, so I don't know how to get past this issue :(
So if anyone could figure out what is going on, please help! Thank you.
Your extension method GetDisplayItemID expects a combobox filled with instances of the classDisplayItem not filled with instances of a ComboBoxViewItem and thus the internal cast fails. A possible workaround could be written if your classes Company, Quote and Customer provide an indentical named property ID.
You could rewrite your ComboBoxViewItem<T> class in this way
class ComboBoxViewItem<T>
{
public string Name;
public int ID;
public T Item { get; set; }
public ComboBoxViewItem(T item, string name, int id)
{
this.Item = item;
this.Name = name;
this.ID = id;
}
public ComboBoxViewItem(T item)
{
var prop = item.GetType().GetProperty("Name");
if (prop == null)
throw new ArgumentException("This object does not have ...");
if (prop.PropertyType != typeof(string))
throw new ArgumentException("The property Name MUST be of type...");
this.Name = (string)prop.GetValue(item);
prop = item.GetType().GetProperty("ID");
if (prop == null)
throw new ArgumentException("This object does not have ...");
if (prop.PropertyType != typeof(int))
throw new ArgumentException("The property ID MUST be of ...");
this.ID = (int)prop.GetValue(item);
this.Item = item;
}
public override string ToString()
{
// C# 6.0 string interpolation
//return string.Format($"{ID}, ({Name})");
// C# Standard string formatting
return string.Format("{0}, ({1})", ID, Name);
}
}
Now you could retrieve the ID from your comboboxes using this syntax without using the old extension method or defining a new one.
CustomerId = (cmbQuoteContact.SelectedItem as ComboBoxViewItem<Customer>).Item.ID
....

Adding a bool for each property

I'm building a c# class that works with two different data sources. It will load a data source and take a configuration set from a function. Then I want to do several tasks on all properties within the object.
for example.
public String StreetAddress
{
get { return _streetAddress; }
set
{
if (value.Length <= 64)
_streetAddress = value;
else
_streetAddress = value.Substring(0, 1024).Trim();
}
}
public String City
{
get { return _city; }
set
{
if (value.Length <= 128)
_city = value;
else
_city = value.Substring(0, 128).Trim();
}
}
public String State
{
get { return _state; }
set
{
if (value.Length <= 128)
_state = value;
else
_state = value.Substring(0, 128).Trim();
}
}
So that holds the data from one side. I was hoping to be able to store and set a change flag on each property. So if we take State for example. If the person is moved from Texas to Illinois I want to set a bool within that property to note the change then be able to loop over all changes before saving the object to the DB. But I don't see any way to assign another state variable within that property. Is the best way to write another object on top of this to control it or is there another more creative way to store multiple strings within the one property?
If you'd like an OOP way of doing the thing, you can:
Define an interface and a class for holding your property, such as:
interface IPropertySlot
{
bool IsDirty { get; }
void ResetIsDirty();
object UntypedValue { get; }
}
class PropertySlot<T>:IPropertySlot
{
public T Value { get; private set; }
public bool SetValue(T value)
{
if (!Equals(_value, Value))
{
Value = value;
IsDirty = true;
return true;
}
return false;
}
public bool IsDirty { get; private set; }
public void ResetIsDirty()
{
IsDirty = false;
}
public object UntypedValue
{
get { return Value; }
}
}
Store your properties inside your class in a dictionary from String (for name of property) to IPropertySlot and get/set them through a pair of methods:
void SetProperty<T>(string name, T value)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
((PropertySlot<T>)property) .SetValue(value);
}
T GetProperty<T>(string name)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
return ((PropertySlot<T>)property).Value;
}
Finding the changed properties later is just a matter of going over the _properties.Values and finding which of them are IsDirty.
This approach also gives you a way to add more functionality to your properties in an OO manner (such as raising PropertyChanged/PropertyChanging events, mapping it to DB fields, etc.).
In such a situation I'd prefer an approach external to the Dto implementation.
Implement some unit that would take two instances of a class, and determine all the differences.
Map each property to compare:
static PropertyManager<Dto> manager = new PropertyManager<Dto>()
.Map(x => x.City)
.Map(x => x.StreetAddress);
Use two instances to compute difference:
var a = new Dto{ StreetAddress = "Foo", City = "Bar" };
var b = new Dto{ StreetAddress = "Foo", City = "Baz" };
var differences = manager.ComputeDifferences(a,b).ToList();
if( differences.Any() )
{
Console.WriteLine("Instances differ");
}
foreach (var diff in differences)
{
Console.WriteLine(diff);
}
This sample code prints out:
Instances differ
x.City
Here is a complete code example:
https://dotnetfiddle.net/4sNeoN

call methods from my class.cs

i was need to write 2 methods in my student class which do the following
hasPassed() Should return True if the student has a year mark >= 40 or
false if the marks is <40
toString() Should return a single string containing a summary of the
student details held within the class
e.g.
“12345 Basil Fawlty, 23/08/1946”
here's the code i have for the above to methods, is what i have correct for what its asking for the above?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CourseWork
{
public class Student
{
private static string firstname;
private string secondname;
private string dateofbirth;
private string course;
private int matricnumber;
private double yearmark;
public bool hasPassed()
{
if (yearmark >= 40)
return true;
else
return false;
}
public void toString()
{
firstname = "Basil";
secondname = "Fawlty";
dateofbirth = "23/08/1946";
course = "MA Hotel Management";
matricnumber = 12345;
yearmark = 55;
}
public Student()
{
}
public string FirstName
{
get { return firstname; }
set { firstname = value; }
}
public string SecondName
{
get { return secondname; }
set { secondname = value; }
}
public string DateOfBirth
{
get { return dateofbirth; }
set { dateofbirth = value; }
}
public string Course
{
get { return course; }
set { course = value; }
}
public int MatricNumber
{
get { return matricnumber; }
set
{
if (value <= 99999 && value >= 10000)
{
matricnumber = value;
}
else
{
Console.WriteLine("Invalid Matric Number: {0}", value);
}
matricnumber = value;
}
}
public double YearMark
{
set
{
if (value <= 100 && value >= 0)
{
yearmark = value;
}
else
{
Console.WriteLine("Invalid Year Mark: {0}", value);
}
yearmark = value;
}
}
}
i then need the above methods to be used in a get button that does the following
Get: Uses the values of the Student class methods to update the text boxes. The
Student.hasPassed() method should be used to update the pass/fail label. The
Student details summary should be updated by using Student.toString ().
but I'm having trouble coding it and i cant seam to call hasPassed() method or toString() method from my student class
so I've doing something wrong but cant see what it is
any ideas how to go about fixing this?
i have a set button that basically lets me save update vaules in the student class, though i dont think thats saving them correcty, but wont know until i get the Get button working i have used Student student = new student() in the set button in the get button i need to use the toString method to show the eg 12345 Basil Fawlty, 23/08/194 in the txt boxes and in a label, i then need to use hasPassed() method in the Get button so that when a yearmark is >= 40 another label says Pass or fail if < 40
I dont completely read your question because there are lots of errors.
For example
public void toString()
{
firstname = "Basil";
secondname = "Fawlty";
dateofbirth = "23/08/1946";
course = "MA Hotel Management";
matricnumber = 12345;
yearmark = 55;
}
where is your object?
you should create an object like this:
Student stu = new Student();
Be careful and ask your question more understandable!
Have a look :
https://stackoverflow.com/questions/902994/how-to-ask-programming-questions-correctly
The firstName variable is static. This will make all instances of Student share the same first name, which is not correct. Each Student object should have it's own first name.
The class's instance variables are private and have no way of being set. You probably want to create a constructor that takes these variables as arguments.
public Student(string firstName, string secondName, ...)
{
this.firstName = firstName;
this.secondName = secondName;
...
}
The hasPassed() method is correct. You can verify that the behavior is working by instantiating an instance of the Student class and calling hasPassed() on the instantiated object.
double goodYearMark = 85;
Student goodStudent = new Student("Basil", "Fawlty", ..., goodYearMark);
Console.WriteLine("Good Student Passed? " + goodStudent.hasPassed());
double badYearMark = 35;
Student badStudent = new Student("Bad", "Student", ..., badYearMark);
Console.WriteLine("Bad Student Passed? " + badStudent.hasPassed());
The ToString() method should return a string value. Every object in .NET has a ToString() method, and you can override the default behavior using the override keyword.
See the MSDN documentation for the Object.ToString Method.
public override string ToString()
{
return string.format("{0} {1}, {2}", firstName, secondName, dateOfBirth);
}
The code examples above may not compile because I typed them directly into the response window, but hopefully they will be useful as guidance. Hope this helps!
Read the toString requirement one more time, you're doing this wrong. What happens to your existing values when you call toString in your code now?
Also, check the two last property setters. Currently you're not preventing the user setting an invalid value.
You also need to create an instance of your class, and set initial values on it that you can return from toString.
Good luck, you're almost there :-)

I want to Sort the List but it Doesn't work

I have a One Product Class:
public class Product
{
private string firstname;
private string lastname;
private string email;
public Product()
{
}
public Product(string firstname, string lastname, string email)
{
this.Firstname = firstname;
this.Lastname = lastname;
this.Email = email;
}
public string Firstname
{
get
{
return firstname;
}
set
{
firstname = value;
}
}
public string Lastname
{
get
{
return lastname;
}
set
{
lastname = value;
}
}
public string Email
{
get
{
return email;
}
set
{
email = value;
}
}
public virtual string GetDisplayText(string sep)
{
return Firstname + sep + Lastname + sep + Email;
}
}
I make a One more class where i am doing a ICompare
public class PersonSort : IComparer<Product>
{
public enum CompareType
{
Email
}
private CompareType compareType;
public PersonSort(CompareType cType)
{
this.compareType = cType;
}
public int Compare(Product x, Product y)
{
if (x == null) throw new ArgumentNullException("x");
if (y == null) throw new ArgumentNullException("y");
int result;
switch (compareType)
{
case CompareType.Email:
return x.Email.CompareTo(y.Email);
default:
throw new ArgumentNullException("Invalid Compare Type");
}
}
}
Then i call in ProductList Class
List<Product> person;
public void Sort()
{
person.Sort(new PersonSort(PersonSort.CompareType.Email));
}
Then this method call in Form:
private ProductList products = new ProductList();
private void button4_Click(object sender, EventArgs e)
{
products.Sort();
}
but it show me null exception: Object reference not set to an instance of an object.**
Can you please help me.How to fix it?
You have a null reference somewhere. Make sure that the list is initialized. Also, is Product.Email properly set?
You may want to use StringComparer instead. Replace
return x.Email.CompareTo(y.Email);
with
return StringComparer.Ordinal.Compare(x.Email, y.Email);
List<Product> person;
Where is this given a value? You haven't included the code where you make person a list and add items to it (or add items to a list and then assign it to person, etc.). A bug there could cause the issue.
public int Compare(Product x, Product y)
{
if (x == null) throw new ArgumentNullException("x");
if (y == null) throw new ArgumentNullException("y");
This is a bad idea, because it's part of the documentation of IComparer<T>.Compare that it is okay to pass in null, and null arguments evaluate as less than any other argument. While I don't think it is used with List<T>.Sort() it remains that methods that use comparers can depend upon passing in null being safe. Hence:
public int Compare(Product x, Product y)
{
if(ReferenceEquals(x, y))//either both null or both the same instance
return 0;
if(x == null)
return -1;
if(y == null)
return 1;
That could be related.
Finally, if an Email field was null it could throw at
return x.Email.CompareTo(y.Email)
Best thing to do is to have code in the constructor and setter so that it's impossible for that to ever happen. Delete the parameter-less constructor, add a null-check to the other constructor and to the checker so it throws ArgumentNullException when something creates a bogus Product rather than later on.
You can also add a check to the comparer:
if(x.Email == null || y.Email == null)
throw new Exception("Cannot compare a user with null email");
Which won't fix the bug, but would help you track it down.
Based on the code provided, person in ProductList is not initialized. That being said, if you include the call stack of the exception in your question, you'll get a definitive answer.
List<Product> person;
to
List<Product> person = new List<Product>();

Wp7 ListBox ItemSource ObservableCollection IndexOUtofRange and Items are not updated

I have the following issue: I am creating a Windows Phone 7 application and I am using a ListBox which is bound to an ObservableCollection people. The implementation of this you see below:
public class Person
{
private string _id { get; set; }
private string _name { get; set; }
public Person(string Id, string Name, string Title)
{
_id = Id;
_name = Name;
}
public string Id
{
get { return _id; }
set
{
_id = value;
FirePropertyChangedEvent("Id");
}
}
public string Name
{
get { return _name; }
set
{
_name = value;
FirePropertyChangedEvent("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The people Collection is filled with Person objects. They are created in the following function... listValues is my ListBox.
void svc_GetHierachyCompleted(object sender, HCMobileSvc.GetHierachyCompletedEventArgs e)
{
var data = e.Result.ToArray();
listValues.ItemsSource = null;
people.Clear();
int i = 0;
foreach(var item in data)
{
if (i == 0)
{
// Manager
mgrField1.Text = item[1].ToString();
mgrField2.Text = item[2].ToString();
i++;
}
else
{
// Untergebenen hinzufügen
people.Add(new Person(item[0].ToString(), item[1].ToString(), item[2].ToString()));
}
}
// Update List
listValues.ItemsSource = people;
}
Now I have a DataTemplate with two textblocks bound to both properties Id and Name. When the SelectionChanged event is fired I try to rebuild the entire list (so I call the function above again) using the following code:
string id = people[listValues.SelectedIndex].Id;
MessageBox.Show(id);
CreateHierachy(id);
The CreateHierachy just only queries a WebService which then goes into the method above. The problem is, as soon as I select a value in the ListBox I get the following error:
ArgumentOutOfRangeException {"\r\nParameter name: index"}
The error is caused by the line listValues.SelectedIndex.
I absolutely have no idea why that happens. What I know is that the MessageBox shows me the correct SelectedIndex value. What I also know is that when I remove the line people.Clear() that the error goes away but the ListBox does not get Updated.
Any ideas where the problem might be?
Thanks!!!
Bye,
WorldSignia
You should check here for SelectedIndex being >= 0:
if (listValues.SelectedIndex >= 0)
string id = people[listValues.SelectedIndex].Id;

Categories