UWP AutoGenerateProperty show two collections in DisplayMemberPathCollection - c#

I'm trying to modify a comboBox or catalog from showing the property "Name" from a set of collections to show two of them, like "Name" and "Age" for example.
I have already tried to add it as a second parameter as
[DisplayMemberPathCollection("Name","SecondString")]
and modifying the attribute so it takes two parameters.
//The autogenerated property in the model:
[AutoGenerateProperty]
[Display("User")]
[PropertyOrder(1)]
[DisplayMemberPathCollection("Name")]
[SelectedItemCollection("SelectedUser")]
//I changed it to this:
[AutoGenerateProperty]
[Display("User")]
[PropertyOrder(1)]
[DisplayMemberPathCollection("Name","Age")]
[SelectedItemCollection("SelectedUser")]
//The attribute modification I made to get two parameters:
public DisplayMemberPathCollectionAttribute(string first = "", string second = "")
{
DisplayMemberPath = first + second;
}
I would like to have those two fields displayed in the combo, but anything just doesn't seem to work and I haven't found anything helpfull yet

What you're doing was impossible. The DisplayMemberPath doesn't support concatenated field name. The correct way is to create a new field to concatenate the two fields and make the 'DisplayMemberPathCollection' refer to the new field.
For example, you could define a 'FullName' in your model class:
public class User:PropertyChangeBase
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyPropertyChanged();
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
NotifyPropertyChanged();
}
}
private string fullName;
public string FullName
{
get { return Name + " " + LastName; }
}
private int age;
public int Age
{
get { return age; }
set
{
age = value;
NotifyPropertyChanged();
}
}
}
The 'DisplayMemberPathCollection' looks like this: [DisplayMemberPathCollection("FullName")], then the comboBoxItem will show show the full name.

Related

Optimize the Algotithm

So, i have a method
public void AddToSearch(List<FullName> fullNames)
{
foreach (var fullName in fullNames)
{
if (fullName.Surname != null)
_sb.Append(fullName.Surname.Trim() + " ");
if (fullName.Name != null)
_sb.Append(fullName.Name.Trim() + " ");
if (fullName.Patronymic != null)
_sb.Append(fullName.Patronymic.Trim());
fullNamesList.Add(_sb.ToString().TrimEnd());
_sb.Clear();
}
it takes a list of FullName and by using StringBuilder instance converts each element into a string(which format is "$Surname $Name $Patronymic"). At the end i put the result into my list. The Question is - how can i optimize all of that "Trim" stuff. It bothers me that i use it in multiple occassions and i am pretty sure it effects the time.
how can i optimize all of that "Trim" stuff
Very simple, simply don't call Trim() on those strings. What spaces are you worried about? Who's entering those values in your business objects? Because short of solar flares randomly flipping bits enough to append spaces to your strings, you're in full control from beginning to end, so simply don't add the spaces.
You also don't need the two string builders, just insert in your main one. There's no need for yet another Trim() here either, because simply decrementing the Length property of your string builder is a constant operation (it literally decrements one integer with guaranteed no extra allocations).
the strings normalization process should be done in the data layer (in application or database) for stored strings. While dynamic strings such as user input, needs to be normalized as soon as you get them to prepare them for the next task.
For your current code, you can modify the FullName class, adjust the setters to trim the value before it's been stored, and override the ToString to return the full name.
Example :
public class FullName
{
public string Name
{
get => Name;
set => Name = value?.Trim();
}
public string Surname
{
get => Surname;
set => Surname = value?.Trim();
}
public string Patronymic
{
get => Patronymic;
set => Patronymic = value?.Trim();
}
public override string ToString()
{
return $"{GetValueOrEmpty(Surname)}{GetValueOrEmpty(Name)}{GetValueOrEmpty(Patronymic, false)}";
}
private string GetValueOrEmpty(string name, bool addSpaceAfter = true)
{
if(!string.IsNullOrWhiteSpace(name))
{
return name + (addSpaceAfter ? " " : string.Empty);
}
return string.Empty;
}
}
Then, you can do this :
fullNamesList.AddRange(fullNames.Select(x=> x.ToString()));
UPDATE :
Thanks to #olivier-jacot-descombes, the above code is missing the use of backing fields, which will avoid causing overflow exception by the properties infinite recursions. The following adjustments will do the trick.
public class FullName
{
private string _name;
private string _surname;
private string _patronymic;
public string Name
{
get => _name;
set => _name = value?.Trim();
}
public string Surname
{
get => _surname;
set => _surname = value?.Trim();
}
public string Patronymic
{
get => _patronymic;
set => _patronymic = value?.Trim();
}
public override string ToString()
{
return $"{GetValueOrEmpty(Surname)}{GetValueOrEmpty(Name)}{GetValueOrEmpty(Patronymic, false)}";
}
private string GetValueOrEmpty(string name, bool addSpaceAfter = true)
{
if(!string.IsNullOrWhiteSpace(name))
{
return name + (addSpaceAfter ? " " : string.Empty);
}
return string.Empty;
}
}
Try and extension something like this.
public static class Helper
{
public static StringBuilder AppendValue(this StringBuilder builder,string value)
{
if(!string.IsNullOrEmpty(value))
{
builder.Append(value.Trim());
return builder;
}
}
}
call as follows:
sb.AppendValue(fullName.Name);
sb.AppendValue(fullName.Surname);
...
You will get the StringBuilder back with the value if it is not empty otherwise nothing will be added to it.

Get and Set properties' initializing

When I write the code like the methods below my fields get initialized correctly and the application works fine.
private string username;
private string password;
public string Password
{
get { return password; }
set { password = value; }
}
public string Username
{
get { return username; }
set { username = value; }
}
public Authenticate()
{
this.username = "njabulo";
this.password = "12345";
}
Before writing it like this I had written the code in the following fashion and the fields didn't get initialized:
private string username;
private string password;
public string Password
{
get { return password; }
set { password = "njabulo"; }
}
public string Username
{
get { return username; }
set { username = "12345"; }
}
I would like to know what exactly is causing the error in the second method. I think the value on the set property stands for anything that may be thrown at the property and I am giving it an actual value.
There is no reason to Set to a literal value, you may as well do
get { return "njabulo"; }
If you are using C# 6 then you can initialize like:
public string Password {get; set;} = "njabulo";
Then it will initialize, but not always stay that value if you set it later.
When you define a property, with getter or setter, it means that the code for getter or setter only execute when any of these actions occurred.
In the second example you haven't called the setter yet.and there is no reason to specify setter with a content value.
The first example is fine bcoz you have done the followings
1.Defined properties with back end fields.
2.initialised back end fields
But in the second one you haven't made initialisation.
The purpose of set is to allow setting the value of that property, like this:
var x = new WhateverYourClassIsNamed();
x.Username = "ABC";
You would normally write the property like this:
public string Username
{
get { return username; }
set { username = value; }
}
That way if someone calls
x.Username = "newusername";
then when the set method is called, value is "newusername"; That's how you can set a property on your class. You don't have to declare a variable named value. That automatically refers to whatever value is used when you call set.
If you do this:
set { username = "12345"; }
Then it doesn't matter what value you try to set. You could call
x.Username = "99999"
or any other value, but it's always going to set username to "12345".
Usually when we say "initialize" we mean values that are set when the class is first created. If that's what you had in mind you could do this:
private string username;
private string password = "12345"; //This set whenever you create
//a new instance of the class
public string Password
{
get { return password; }
set { password = value; }
}
public string Username
{
get { return username; }
set { username = value; }
}
or do as Crowcoder suggested,
public string Password {get; set;} = "12345";
That's a newer, more convenient syntax that does the same thing.
The correct initialization is the first method, or make it shorter using automatic property. The second method, before anybody call "set", your Password or Username is still null :
public string Password { get; set; }
public string Username { get; set; }
public Authenticate()
{
Username = "njabulo";
Password = "12345";
}
I add more because of your comments above (compare value), you can use it like this:
public class Authenticate
{
private string _password;
private string _username;
public Authenticate()
{
_password = "mypassword";
_username = "myusername";
}
public string Password
{
get { return _password; }
set
{
if (_password != value) // Compare it here
_password = value;
}
}
public string Username
{
get { return _username; }
set
{
if (_username != value) // Compare it here
_username = value;
}
}
}

Returning a value from textbox in a class

I have a text box in my form. I have a class TicketUser where i have accessors set to return a a value in my label. Im trying to get the value from the text box inside the label. I'm trying to get firstName to return a value of txtfirstName.Text from my form. Can someone please guide me in the right direction please. Im pretty new to asp.net
public class TicketUser
{
public string firstName;
public string lastName;
public string username;
TicketForm form = new TicketForm();
//property accessors
public string CreateAccountMessage
{
get
{
return "Congratulations" + firstName + "Your account has been created. Your username is" + username;
}
set
{
CreateAccountMessage = value;
}}
//CreateAccount method
public string CreateAccount()
{
return CreateAccountMessage;
}}}
I hope i got you correct because you need to some details about what you are trying to do and what are the results.
Make and Object of TicketUser in the code behind file of the form and set
t = new TicketUser();
t.firstName=txtfirstName.Text;
t.lastName=txtlastName.Text;
t.userName=txtUsername.Text;
Change this:
public string firstName = "";
public string lastName = "";
public string username = "";

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 :-)

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