What is the best approach for sorting a generic list when one of its objects property is changed?
I have the following example to help explain what is needed.
public class Sending
{
public Sending(int id, DateTime dateSent)
{
this.Id = id;
this.DateSent = dateSent;
}
public int Id { get; set; }
public DateTime DateSent { get; set; }
}
public class Operation
{
public List<Sending> ItemsSent = new List<Sending>();
public Operation()
{
ItemsSent.Add(new Sending(1, new DateTime(2010, 6, 2)));
ItemsSent.Add(new Sending(2, new DateTime(2010, 6, 3)));
ItemsSent[1].DateSent = new DateTime(2010, 6, 1);
}
}
What is the best way to trigger a sort on the list to sort by date after the DateSent property is set? Or should I have a method to update the property and perform the sort?
You could implement IComparable<Sending> on Sending and call Sort() on the ItemsSent. I would suggest to write a method to update an object and update the list manually.
public class Sending: IComparable<Sending>
{
// ...
public int CompareTo(Sending other)
{
return other == null ? 1 : DateSent.CompareTo(other.DateSend);
}
}
What you can do is you first implement INotifyChanged.
Then do some thing like this;
public class Sending : INotifyChanged
{
private int id;
private DateTime dateSent;
public Sending(int id, DateTime dateSent)
{
this.Id = id;
this.DateSent = dateSent;
}
public int Id { get; set; }
public DateTime DateSent
{
get
{
return this.dateSend;
}
set
{
this.dateSent = value;
OnPropertyChangerd("DateSent");
//CallYou List<Sending> Sort method;
}
}
So whenever a new value will set the sort method will sort the list.
Related
This question already has answers here:
How do I calculate someone's age based on a DateTime type birthday?
(74 answers)
Closed 7 months ago.
I have a class as below:
class Member
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
private int _age;
public int Age
{
get { return _age; }
private set { _age = DateTime.Now.Year - Birthday.Year; }
}
}
In the Main method, I assign member values and I want to get each member's age, but every result is zero, Why? How to solve this problem? Thanks!
class Program
{
static void Main(string[] args)
{
List<Member> memberList = new List<Member>(); //using System.Collections.Generic;
memberList.Add(new Member() { Name = "Andy", Birthday = new DateTime(1971, 7, 26)});
memberList.Add(new Member() { Name = "Mike", Birthday = new DateTime(1982, 1, 17)});
memberList.Add(new Member() { Name = "Lucy", Birthday = new DateTime(1993, 9, 28)});
foreach (var m in memberList)
{
Console.WriteLine(m.Age); //m.Age = 0
}
}
}```
You need to update your class like this because the setter are not doing the operation since youre returning the variable on the get and thats why you got a 0 :
public class Member
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
public int Age
{
get { return DateTime.Now.Year - Birthday.Year; }
}
}
after that change you can get the agre,btw you can calculate the age easier with a timespan to be more exact.
Please return the age from getter method like this.
class Member
{
public string Name { get; set; }
public DateTime Birthday { get; set; }
private int _age;
public int Age
{
get { return DateTime.Now.Year - Birthday.Year; }
private set { _age = DateTime.Now.Year - Birthday.Year; }
}
}
You can remove the calculation from setter method.
Please see the output-
I am doing a program for vacation in a company and the time that is allowed to be in a specific holiday.
I used an Abstract class with an abstract method :
public abstract class Abstract : TimeLength
{
public AbstractTest(string employeeCode, string employee, string typeOfHoliday, DateTime startDate, DateTime endDate) : base(startDate, endDate, "")
{
TypeOfHoliday = typeOfHoliday;
Employee = employee;
EmployeeCode = employeeCode;
}
public string EmployeeCode { get; set; }
public string Employee { get; set; }
public string TypeOfHoliday { get; set; }
public abstract bool HolidayValidation(string typeOfHoliday);
}
And I used multiple class that inherent from this abstract class like this two :
class MarriageVacation : Abstract
{
public MarriageVacation(string employeeCode, string employee, string typeOfHoliday, DateTime startDate, DateTime endDate) : base(employeeCode, employee, typeOfHoliday, startDate, endDate)
{
}
public override bool HolidayValidation(string typeOfHoliday)
{
if (Days() > (int)Holiday.MarriageVacation)
{
MessageBox.Show("Marriage Vacation Can only be 5 Days");
return false;
}
else
return true;
}
}
class Bereavement : Abstract
{
public Bereavement(string employeeCode, string employee, string typeOfHoliday, DateTime startDate, DateTime endDate) : base(employeeCode, employee, typeOfHoliday, startDate, endDate)
{
}
public override bool HolidayValidation(string typeOfHoliday)
{
if (Days() > (int)Holiday.Bereavement)
{
MessageBox.Show("Bereavement Can only be 5 Days");
return false;
}
else
return true;
}
}
I use Enum for holidays
and in the main I want to register this based on the users choice like this :
List<Abstract> holiday = new List<Abstract>();
if(CmbTypeHolidays.Text == Holiday.Bereavement.ToString())
{
var holid = new Bereavement(CmbEmpHolidays.Text.Split('-')[0], CmbEmpHolidays.Text.Split('-')[1], CmbTypeHolidays.Text, Convert.ToDateTime(StartDateHolidays.Value), Convert.ToDateTime(EndDateHolidays.Value));
if (holid.HolidayValidation(CmbTypeHolidays.Text))
{
holiday.Add(holid);
var bindingList = new BindingList<Abstract>(holiday);
dataGridHolidays.DataSource = bindingList;
controlPanelHolidays.Visible = false;
}
}
else if (CmbTypeHolidays.Text == Holiday.MarriageVacation.ToString())
{
var holid = new MarriageVacation(CmbEmpHolidays.Text.Split('-')[0], CmbEmpHolidays.Text.Split('-')[1], CmbTypeHolidays.Text, Convert.ToDateTime(StartDateHolidays.Value), Convert.ToDateTime(EndDateHolidays.Value));
if (holid.HolidayValidation(CmbTypeHolidays.Text))
{
holiday.Add(holid);
var bindingList = new BindingList<Abstract>(holiday);
dataGridHolidays.DataSource = bindingList;
controlPanelHolidays.Visible = false;
}
}
I wanted to know a better way to implement this solution or just to change the code that inserts data to the abstract List
You will need to set up a factory that maps holiday type name to the class implementing it:
private class HolidayConstructorArgs {
public string EmployeeCode {get;set;}
public string Employee {get;set;}
public string TypeOfHoliday {get;set;}
public DateTime From {get;set;}
public DateTime To {get;set;}
}
private static readonly IDictionary<string,Func<HolidayConstructorArgs,AbstractHoliday>> HolidayByTypeCode =
new Dictionary<string,Func<HolidayConstructorArgs,AbstractHoliday>> {
[$"{Holiday.Bereavement}"] = a => new Bereavement(a.EmployeeCode, a.Employee, a.TypeOfHoliday, a.From, a.To)
, [$"{Holiday.MarriageVacation}"] = a => new MarriageVacation(a.EmployeeCode, a.Employee, a.TypeOfHoliday, a.From, a.To)
};
Now you can get the factory from the dictionary, and use it to instantiate the object:
if (HolidayByTypeCode.TryGetValue(CmbTypeHolidays.Text, out var factory)) {
// This is where the "magic" happens:
// Func<> will invoke the appropriate constructor without a conditional
var holid = factory(
new HolidayConstructorArgs {
EmployeeCode = CmbEmpHolidays.Text.Split('-')[0]
, Employee = CmbEmpHolidays.Text.Split('-')[1]
, TypeOfHoliday = CmbTypeHolidays.Text
, From = Convert.ToDateTime(StartDateHolidays.Value)
, To = Convert.ToDateTime(EndDateHolidays.Value)
}
);
// ... The rest of your code remains the same
}
I made this changes based on the answers on this questions this is the Main class (Abstract) :
public class AbstractTest : TimeLength
{
public AbstractTest(string employeeCode, string employee, Holiday typeOfHoliday, DateTime startDate, DateTime endDate) : base(startDate, endDate, "")
{
TypeOfHoliday = typeOfHoliday;
Employee = employee;
EmployeeCode = employeeCode;
}
public string EmployeeCode { get; set; }
public string Employee { get; set; }
public Holiday TypeOfHoliday { get; set; }
public bool HolidayValidation(Holiday typeOfHoliday)
{
return Days() > (int)typeOfHoliday;
}
}
And in the Main i changed into this :
Holiday MyStatus = (Holiday)Enum.Parse(typeof(Holiday), CmbTypeHolidays.Text, true);
var holid = new AbstractTest(CmbEmpHolidays.Text.Split('-')[0], CmbEmpHolidays.Text.Split('-')[1], MyStatus, Convert.ToDateTime(StartDateHolidays.Value), Convert.ToDateTime(EndDateHolidays.Value));
if (!holid.HolidayValidation(MyStatus))
{
holiday.Add(holid);
var bindingList = new BindingList<AbstractTest>(holiday);
dataGridHolidays.DataSource = bindingList;
controlPanelHolidays.Visible = false;
}
else
{
MessageBox.Show($"{holid.TypeOfHoliday} Cant be more than {(int)MyStatus} Days");
}
For the typeOfHoliday i used Holiday type so it is easier to work with and the choice that the user makes i convert it to Enum Holiday
You have the same(almost) implementation for HolidayValidation and you dont use typeOfHoliday.
From what you have posted you might as well add the Holiday enum as parameter and property to base class(Abstract) and not have any inheritance at all.
Implement the HolidayValidation in the base class and use the Holiday property to compare to Days
I am looking to figure out if this is possible / the correct syntax to make it work. I want to do a null check on my List when adding a new item at the property level rather than doing a null check every time I go to add an item to the list. For example:
MyClass someClass = new MyClass()
{
MyClassID = 1,
Notes = null
};
//
//
Note newNote = new Note()
{
NoteID = 1,
Text = "Test note"
};
someClass.Notes.Add(newNote); // This line will throw an error because
// someClass.Notes is null
I want to solve this by doing something along the lines of the following code. However, I do not know if / what syntax makes this possible.
public class MyClass
{
public int MyClassID { get; set; }
public List<Note> Notes
{
get; set;
public void Add(Note note)
{
if (this.Notes == null)
this.Notes = new List<Note>();
this.Notes.Add(note);
}
}
}
public class Note
{
public int NoteID { get; set; }
public string Text { get; set; }
}
** I know I can do a null check on someClass.Notes == null and assign it to new List<Note>() before doing the .Add(newNote) but I'd like to do it at the property level so I don't have to copy those 2 lines of code every time I need to add a new note.
It's better if you init your list in constructor of MyClass
public class MyClass
{
public MyClass()
{
Notes = new List<Note>();
}
}
Let's introduce backing field private List<Note> m_Notes:
public class MyClass
{
public int MyClassID { get; set; }
// Empty list by default
private List<Note> m_Notes = new List<Note>();
public List<Note> Notes
{
get
{
return m_Notes;
}
set // If you really want "set" in the context
{
// Assign empty list if value is null
m_Notes = value ?? new List<Note>();
}
}
}
I am trying to sort list with clubs which have best result, I must use Sort Method, but it shows error, what I am doing wrong. I know it is a problem of Sort method but can't find a mistake, I made it work with lambda expression but I want to do it with sort method;
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// class with objects
Club barca = new Club("Barcelona", 1900, 100, 20);
Club Real = new Club("Real", 1910, 80, 70);
Club Manchester = new Club("Manchester", 1890, 75, 55);
Club Milan = new Club("Milan", 1880, 45, 65);
//new list of clubs
var myclublist = new List<IClub>();
///add clubs in list
myclublist.Add(barca);
myclublist.Add(Real);
myclublist.Add(Manchester);
myclublist.Add(Milan);
// sort method for list
myclublist.Sort();
//show clubs name with best results
foreach (var item in myclublist)
{
if (item.IsPositiveBallRatio() == true)
{
Console.WriteLine(item.ClubName());
}
}
}
// club class
public class Club : IClub, IComparable<Club>
{
public string Name { get; set; }
public int Year { get; set; }
public int Scoredgoals { get; set; }
public int Lossgoals { get; set; }
public Club(string name, int year, int scoredgoals, int lossgoals)
{
Name = name;
Year = year;
Scoredgoals = scoredgoals;
Lossgoals = lossgoals;
}
public int BallRatio()
{
int ratio;
ratio = Scoredgoals - Lossgoals;
return ratio;
}
public bool IsPositiveBallRatio()
{
if (Scoredgoals > Lossgoals)
{
return true;
}
else
return false;
}
public string ClubName()
{
string n;
n = Name;
return n;
}
public int CompareTo(Club other)
{
return BallRatio().CompareTo(other.BallRatio());
}
}
// inferface for club class
interface IClub
{
int BallRatio();
bool IsPositiveBallRatio();
string ClubName();
}
}
what I am doing wrong?
Why: IClub is not comparable to itself and there is no other information about the type available to the code at run-time for generic method. So it falls back to non-generic version of IComparable which is not implemented by your Club type.
Fixes:
either use list of Club instead of List<IClub> as Club is comparable to itself
implement non-generic IComparable on the Club:
public class Club : IClub, IComparable<Club> , IComparable
{
...
public int CompareTo(object obj)
{
return CompareTo(obj as Club);
}
}
make type you have in the list (IClub) to be comparable to itself - IClub : IComparable<IClub> to fix the issue if you really expect mixed IClub implementations in the array:
public class Club : IClub, IComparable<Club>
{
...
public int CompareTo(IClub other)
{
return CompareTo(other as Club);
}
}
public interface IClub : IComparable<IClub> {...}
See List.Sort for details.
Note: CompareTo in this post are sample-only and you need to add all type/null checks for them to work in real code.
Working on a C# application for Payroll. As a warning, I'm very new to all this, just been chucked in to it and enjoying it but it's a lot to absorb in a short amount of time. I'm currently learning about LINQ and classes. I'm grabbing invoice lines out of our account SQL server and paying personnel based on sales and some situations that can be determined from the data. See the sample below:
Ord_Inv_No Delivery_Date Trmnl_Key TrmnlGrp_Key
INV-00059754 2016-05-02 07:00:00 23 3
INV-00059839 2016-05-02 16:01:00 56 3
This is a very small portion of the data but it gives the gist of what I want to analyze. I have a class as shown below:
class SalesItem
{
public string DriverName { get; set; }
public DriverData DriverData { get; set; }
public int TerminalKey { get; set; }
public int TerminalGroupKey { get; set; }
public string DeliveryDate { get; set; }
public string CustomerLocation { get; set; }
public int? CustomerLocationKey { get; set; }
public bool? IsCredited { get; set; }
public string InvoiceNumber { get; set; }
public string TermianlGroupDesc { get; set; }
public SalesItem(string driverName, int terminalKey, int terminalGroupKey, string deliveryDate,
string customerLocation, int? customerLocationKey, bool? isCredited, string invoiceNumber, string terminalGroupDesc)
{
this.DriverName = driverName;
this.TerminalKey = terminalKey;
this.TerminalGroupKey = terminalGroupKey;
this.DeliveryDate = deliveryDate;
this.CustomerLocation = customerLocation;
this.CustomerLocationKey = customerLocationKey;
this.IsCredited = isCredited;
this.InvoiceNumber = invoiceNumber;
this.TermianlGroupDesc = terminalGroupDesc;
}
I'm trying to implement a way I can 1. store each SalesItem (each row) as a Collection for lack of a better word. I want to iterate through the collection of SalesItem and find out a few things, like when an Invoice row shows up twice with a different Trmnl_Key and two different Ord_Inv_No that have the exact same Date as one another and return the greatest rate of pay for the two. I had written some logic for this that was manipulating a DataTable that I was grabbing through SQLConnection/Command/Reader/Etc but all of this was very slow and cumbersome when dealing with a week of data and I couldn't get the comparison to work reliably. Further down the line, these rows would be flagged in a DataGrid in WPF for human verification of their pay in case of something funky coming in from the accounting software.
Right now, I'm trying to save the collection as shown below (which is just the current iteration, I have several days worth of trying that's failed.)
private void GetInformation(object sender, SelectionChangedEventArgs e)
{
ComboBox currentItem = sender as ComboBox;
int driverKey = Convert.ToInt32(currentItem.SelectedValue);
DriverData driver = new DriverData();
driver.SetDriverData(driverKey);
int years = driver.ServiceYears;
List<SalesItem> items = (from o in transportationDb._Payroll_Orders
where o.Ord_Driver_Key == driverKey &&
o.Delivery_Date >= new DateTime(2016, 5, 2) && o.Delivery_Date <= new DateTime(2016, 5, 8)
select new SalesItem(
o.Driver_Name,
o.Trmnl_Key,
o.TrmnlGrp_Key,
o.Delivery_Date.ToString(),
o.CustLoc_Description,
o.Ord_CustLoc_Key,
o.Credits, o.Ord_Inv_No, o.TrmnlGrp_Description )).ToList();
dataGrid.ItemsSource = items;
}
Any wisdom or direction would be much appreciated
Granted, this isn't much different than what you had, but try this and see if it works for you:
private void GetInformation(object sender, SelectionChangedEventArgs e)
{
ComboBox currentItem = sender as ComboBox;
int driverKey = Convert.ToInt32(currentItem.SelectedValue);
DriverData driver = new DriverData(); //What's this for?
driver.SetDriverData(driverKey); //What's this for?
int years = driver.ServiceYears; //What's this for?
var startDate=new DateTime(2016, 5, 2);
var endDate=new DateTime(2016, 5, 8);
var items = transportationDb._Payroll_Orders
.Where(o=>o.Ord_Driver_Key == driverKey)
.Where(o=>o.Delivery_Date >= startDate && o.Delivery_Date <= endDate)
.Select(o=>new SalesItem {
DriverName=o.Driver_Name,
...
});
dataGrid.ItemsSource = items;
}
If your Orders table has an index on Ord_Driver_key and Delivery_Date, then this query should be pretty quick.
I hope this helps in some way. I'm not too sure of what you're trying to achieve. A save function could look something like the following, but I don't know exactly what kind of connection you're using. Note that I'm checking to make sure the record doesn't exist already.
private void Save(SalesItem item)
{
if(!transportationDb._Payroll_Orders.Any(p => p.Ord_Inv_No == item.InvoiceNumber))
{
transportationDb._Payroll_Orders.Add(item.PayrollOrder);
transportationDb.SaveChanges();
}
else
{
// Already exists
}
}
Update your class to however you'd like, but I'd do it this way from the limited knowledge of what your object is suppose to look like.
class SalesItem
{
public SalesItem(_Payroll_Orders po == null, DriverData dd == null)
{
this._Payroll_Order = po != null ? po : new _Payroll_Orders();
this.DriverData = dd != null ? dd : new DriverData();
}
private _Payroll_Orders _Payroll_Order { get; set; }
public _Payroll_Orders PayrollOrder
{
get { return _Payroll_Order; }
set { _Payroll_Order = value; }
}
public string DriverName
{
get { return _Payroll_Order.Driver_Name; }
set { _Payroll_Order.Driver_Name= value; }
}
public DriverData DriverData { get; set; }
public int TerminalKey
{
get { return _Payroll_Order.Trmnl_Key; }
set { _Payroll_Order.Trmnl_Key= value; }
}
public int TerminalGroupKey
{
get { return _Payroll_Order.TrmnlGrp_Key; }
set { _Payroll_Order.TrmnlGrp_Key= value; }
}
public DateTime DeliveryDate
{
get { return _Payroll_Order.Delivery_Date; }
set { _Payroll_Order.Delivery_Date= value; }
}
public string CustomerLocation
{
get { return _Payroll_Order.CustLoc_Description; }
set { _Payroll_Order.CustLoc_Description = value; }
}
public int? CustomerLocationKey
{
get { return _Payroll_Order.Ord_CustLoc_Key; }
set { _Payroll_Order.Ord_CustLoc_Key = value; }
}
public bool? IsCredited
{
get { return _Payroll_Order.Credits; }
set { _Payroll_Order.Credits = value; }
}
public string InvoiceNumber
{
get { return _Payroll_Order.Ord_Inv_No; }
set { _Payroll_Order.Ord_Inv_No = value; }
}
public string TermianlGroupDesc
{
get { return _Payroll_Order.TrmnlGrp_Description; }
set { _Payroll_Order.TrmnlGrp_Description = value; }
}
}
then you can change your function here so you only have to select 'new SalesItem(o, driver)':
private void GetInformation(object sender, SelectionChangedEventArgs e)
{
ComboBox currentItem = sender as ComboBox;
int driverKey = Convert.ToInt32(currentItem.SelectedValue);
DriverData driver = new DriverData();
driver.SetDriverData(driverKey);
int years = driver.ServiceYears;
List<SalesItem> items = (from o in transportationDb._Payroll_Orders
where o.Ord_Driver_Key == driverKey &&
o.Delivery_Date >= new DateTime(2016, 5, 2) &&
o.Delivery_Date <= new DateTime(2016, 5, 8)
select new SalesItem(o, driver)).ToList();
dataGrid.ItemsSource = items;
}