Converting data from one class to another - c#

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
....

Related

Custom Selector Attribute - 'Customer' Cannot be found in the system

I made a custom selector that only displays the customers of the current user but when I select a customer I get the error: 'Customer' Cannot be found in the system.
The code for the Custom selector and how I implemented it on the DAC:
[PXNonInstantiatedExtension]
public class SO_SOOrder_ExistingColumn : PXCacheExtension<PX.Objects.SO.SOOrder>
{
#region CustomerID
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXForeignReference(typeof(Field<SOOrder.customerID>.IsRelatedTo<BAccount.bAccountID>))]
[SalesRepCustomer]
public int? CustomerID { get; set; }
#endregion
}
public class SalesRepCustomer : PXCustomSelectorAttribute
{
public SalesRepCustomer() : base(typeof(Customer.acctCD))
{
this.DescriptionField = typeof(Customer.acctCD);
}
protected virtual IEnumerable GetRecords()
{
foreach (Customer pc in PXSelect<Customer>.Select(this._Graph))
{
//Getting Current UserID
var cache1 = _Graph.Caches[BqlCommand.GetItemType(typeof(AccessInfo.userName))];
AccessInfo currentCacheObjecta = (AccessInfo)cache1.Current;
var userName = currentCacheObjecta.UserName;
SalesPerson person = PXSelect<SalesPerson, Where<SalesPerson.descr, Equal<Required<AccessInfo.userName>>>>.Select(_Graph, userName);
if (person != null)
{
CustSalesPeople custSalesPeople = PXSelect<CustSalesPeople, Where<CustSalesPeople.salesPersonID, Equal<Required<SalesPerson.salesPersonID>>, And<CustSalesPeople.bAccountID, Equal<Required<CustSalesPeople.bAccountID>>>>>.Select(_Graph, person.SalesPersonID, pc.BAccountID);
//return all customers related to this SalesPersonID in the CustSalesPeople table
if (!(custSalesPeople is null))
{
yield return pc;
}
}
else
{
//current user is not a sales person
//return all of the customers
yield return pc;
}
}
}
}
Screenshot of the selector on the Sales Order Screen:
Any help on this will be appreciated
You should have the constructor look something like
public SalesRepCustomer() : base(typeof(Customer.bAccountID))
{
this.DescriptionField = typeof(Customer.acctName);
this.SubstituteKey = typeof(Customer.acctCD);
}
The first type that you pass to the base constructor is the type of the value that will be used for the field.
In this case you want the customer id(an int) and you are currently using the AcctCD(a string) field. The description field would typically be the name of the customer account and the substitute key will make it so that the users see the AcctCD instead of the the customer ID(the actual value) which is just an integer.

C# Generic parameter from a string variable

I have two classes, Customer and Country. Customer has a property called HomeCountry, which i have decorated with a custom attribute called "Lookup" and takes a string parameter "Country". The purpose is, when I am using the Customer class, the item in HomeCountry must exist in the Country class (which happens to be a list).
I am using reflection to iterate the Customer class, it finds the attribute and i want it to check the value in the list of country items. So far I have:
foreach (PropertyInfo _pi in object.GetType().GetProperties()) {
IEnumerable<Attribute> _attrs = _pi.GetCustomAttributes();
foreach (Attribute _a in _attrs) {
Object obj = Activator.CreateInstance(_type, null);
// what goes here?
}
}
I have a method:
public T Populate<T>(params string[] _parameters)
I think i want to do
List<obj> v = populate<obj>();
or
List<typeof(obj)> v = populate<typeof(obj)>();
but obviously nothing works! Can anybody help me ?
Thanks
OK i will try and provide a full example:
I have a CUSTOMER_ORDER class:
public class CUSTOMER_ORDER {
public CUSTOMER_ORDER() {}
[Key(0)]
public string OrderNumber {get;set;}
public MPCCOM_SHIP_VIA ShipVia {get;set;}
}
Then the MPCCOM_SHIP_VIA class:
public class MPCCOM_SHIP_VIA {
public MPCCOM_SHIP_VIA() {}
[Key(0)]
public string ID {get;set;}
public string Description {get;set;}
}
I have a method called Populate< T > which takes a class and then uses reflection to loop all the properties and build a select statement, executes it, and then returns the data and populates the object:
public T Populate<T>(params string[] #Parameters)
{
Type _t = typeof(T);
dynamic _o = Activator.CreateInstance(typeof(T), null);
SqlBuilder _sb = new SqlBuilder();
_sb.Table = string.Format("{0}.{1}", _Owner, _t.Name.ToString());
foreach (PropertyInfo p in _t.GetProperties(Utilities.BindingFlags))
{
if (p.GetMethod.IsPrivate == false) _sb.Fields.Add(p.Name.ToString());
IEnumerable<Attribute> _attrs = p.GetCustomAttributes();
foreach (Attribute _a in _attrs)
{
if (_a.TypeId.ToString().Equals(typeof(Key).FullName))
{
int _position = ((Key)_a).Position;
try
{
string _parameter = #Parameters[_position];
_sb.Where.Add(string.Format("{0} = '{1}'", p.Name, _parameter));
}
catch {}
}
}
}
using (OleDbCommand _cmd = new OleDbCommand())
{
_cmd.Connection = this._cn;
_cmd.CommandText = _sb.SQL;
if (_trn != null) _cmd.Transaction = _trn;
_cmd.CommandType = System.Data.CommandType.Text;
using (OleDbDataReader _reader = _cmd.ExecuteReader())
{
if (_reader.Read())
{
for (int x = 0; x < _reader.FieldCount; x++)
{
foreach (PropertyInfo p in _t.GetProperties(Utilities.BindingFlags))
{
if (p.GetMethod.IsPrivate == false)
{
if (p.Name.Equals(_reader.GetName(x).ToString()))
{
dynamic _val = _reader.GetValue(x);
if (p.ReflectedType.BaseType.Name.Equals(""))
{
// what goes here!
}
try
{
p.GetSetMethod(true).Invoke(_o, new object[] { _val });
}
catch { }
break;
}
}
}
}
}
else
{
throw new DatabaseObjectNotFound(_t.Name.ToString(), string.Join(",",#Parameters));
}
}
}
return (T)_o;
}
So, as i read an order, the source DB gets the key to the MPCCOM_SHIP_VIA in the respective field, i want to call the same Populate method against the MPCCOM_SHIP_VIA object with the key. I hope this makes more sense demonstrating what i want to do. And thanks
After some hunting around, this is the answer i was looking for...
MethodInfo method = typeof(class).GetMethod("Populate");
method = method.MakeGenericMethod(p.PropertyType);
_val = method.Invoke(class, new object[] { _prms });
I guess my issue was i was asking the wrong question!

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>();

difficulty inserting a name to an inserted object of a checkedlistbox

I am abit new in C# and i am trying to insert an object to a CheckedListBox,
so this inserted item will have a title inside the checked list (my object contains a string field inside it which I want to be displayed in the CheckedListBox).
for example this is my class:
public class InfoLayer
{
private string LayerName;
private List<GeoInfo> GeoInfos;
public InfoLayer()
{
LayerName = "New Empty Layer";
GeoInfos = new List<GeoInfo>();
}
public InfoLayer(string LayerName)
{
this.LayerName = LayerName;
GeoInfos = new List<GeoInfo>();
}
public InfoLayer(string LayerName,List<GeoInfo> GeoInfosToClone):this(LayerName)
{
foreach (GeoInfo item in GeoInfosToClone)
{
GeoInfos.Add((GeoInfo)((ICloneable)item).Clone());
}
}
public GeoInfo SearchElement(long id)
{
foreach (GeoInfo info in GeoInfos) // foreach loop running on the list
{
if (info.INFOID == id)
return info; // return the item if we found it
}
return null;
}
public GeoInfo SearchElement(string name)
{
foreach (GeoInfo info in GeoInfos)
{
if (info.INFONAME.CompareTo(name)==0)
return info;
}
return null;
}
public override string ToString()
{
string toReturn = "";
for (int i = 0; i < GeoInfos.Count; i++) // for loop running on the list
{
toReturn += String.Format("{0}\n",GeoInfos[i].ToString()); // piping another geoinfo
}
return toReturn;
}
public string LAYERNAME{get{return LayerName;}}
my class also contains a tostring overrider inside her (not what i want to display)
thanks in advance for your help.
Override ToString() in your class, the class that the object is an instance of.
Edit:
You don't want to display the contents of ToString(). You want to display the LayerName, don't you? Perhaps you should display the values with Databinding instead. Then you can set DisplayMember to your new LAYERNAME property.
I believe this is what you are trying to achieve:
checkedListBox1.Items.Add(yourObject.stringField);
((MyObjectType)checkedListBox1.Items(index)).Name = "whatever"
You will have to know the index of the object you want to change.
You'll just have to override the ToString method in your class so that it returns this Name property value.
public overrides string ToString() {
return Name;
}
It will then display its name when added to your CheckedListbox.

Comparing two object state,before and after update

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

Categories