How do you make properties with interdependent values? - c#

In WPF, I want to create three textboxes for the properties: FullName, FirstName and LastName. The text for these textboxes will be based on the listbox on the left (as shown in the program image below). I'm already done with getting text from the listbox to the textbox but I want to sync the values for the names such that:
changing the value for FullName, changes the listbox, FirstName and LastName box
and changing the value for FirstName and LastName changes the FullName and listbox
Here is what the program looks like:
(Program image)
OK thanks to Jared,I was able to solve the problem by making small changes to his answer but I dont like my solution lol can someone suggest a better revision to this program?
class Student : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return name;
}
set
{
FirstName = value.Split(" ".ToCharArray())?[0];
LastName = value.Split(" ".ToCharArray())?[1];
OnPropertyChanged("Name");
OnPropertyChanged("FirstName");
OnPropertyChanged("LastName");
}
}
private string firstName;
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
name = value + " " + LastName;
OnPropertyChanged("FirstName");
OnPropertyChanged("Name");
}
}
private string lastName;
public string LastName
{
get
{
return lastName;
}
set
{
lastName = value;
name = FirstName + " " + value;
OnPropertyChanged("LastName");
OnPropertyChanged("Name");
}
}
private Course _course;
public Course Course
{
get { return _course; }
set
{
_course = value;
OnPropertyChanged("Course");
}
}
private int _age;
public int Age
{
get { return _age; }
set
{
_age = value;
OnPropertyChanged("Age");
}
}
private DateTime _birthday;
public DateTime Birthday
{
get { return _birthday; }
set
{
_birthday = value;
OnPropertyChanged("Birthday");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

I believe what you'll need is a class such as the one below. You'd then bind your listbox to a collection of these, and the form elements to the currently selected item.
I added a space between first and last name. If you want that removed, you'll need to find another way of splitting the first and last names, such as checking capitalization.
Since FullName will be set regardless of which property is modified, I put all the FirePropertyChanged calls in its setter. I haven't tested this, but I assume it will work. :)
EDIT: Changed FullName setter to set the private variable of firstName and lastName instead of the public property.
public class Student : INotifyPropertyChanged
{
public string FullName
{
get
{
return FirstName + " " + LastName;
}
set
{
firstName = value.Split(" ".ToCharArray())?[0];
lastName= value.Split(" ".ToCharArray())?[1];
FirePropertyChanged("FullName");
FirePropertyChanged("FirstName");
FirePropertyChanged("LastName");
}
}
private string firstName;
public string FirstName
{
get
{
return firstName;
}
set
{
firstName = value;
FullName = value + " " + LastName;
}
}
private string lastName;
public string LastName
{
get
{
return firstName;
}
set
{
lastName = value;
FullName = FirstName + " " + value;
}
}
public void FirePropertyChanged (string PropertyName)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}

Related

Changing text of one item in a ComboBox

I have a ComboBox that contains a list of names: LastName + ", " + FirstName.
When a name is selected, it populates two text boxes with the First and Last names, respectively.
What I am trying to do is, if the name is changed in the text boxes, I want the change to be updated to the ComboBox as well without having to reload the entire thing. My ComboBox is NOT loaded directly from a database, so I cannot use RefreshItem()
Is this even possible?
You could implement the INotifyPropertyChanged interface and use a BindingSource as the DataContext of your ComboBox. Please refer to the following sample code.
Person.cs:
public class Person : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; NotifyPropertyChanged(); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; NotifyPropertyChanged(); }
}
public string FullName { get { return LastName + ", " + FirstName; } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Form1.cs:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<Person> people = new List<Person>()
{
new Person() { FirstName = "Donald", LastName = "Duck" },
new Person() { FirstName = "Mickey", LastName = "Mouse" }
};
BindingSource bs = new BindingSource();
bs.DataSource = people;
comboBox1.DataSource = bs;
comboBox1.DisplayMember = "FullName";
textBox1.DataBindings.Add(new Binding("Text", bs, "FirstName", false, DataSourceUpdateMode.OnPropertyChanged));
textBox2.DataBindings.Add(new Binding("Text", bs, "LastName", false, DataSourceUpdateMode.OnPropertyChanged));
}
}

Insert list object into database using stored procedure

I have a collection NameList which contains multiple objects of PersonName class.
I want to insert that NameList using a stored procedure into a table tbl_namelist in a single insert operation. But before the insert, it must delete all records from tbl_namelist.
public class NameList : ObservableCollection<PersonName>
{
public NameList() : base()
{
Add(new PersonName("Willa", "Cather"));
Add(new PersonName("Isak", "Dinesen"));
Add(new PersonName("Victor", "Hugo"));
Add(new PersonName("Jules", "Verne"));
}
}
public class PersonName
{
private string firstName;
private string lastName;
public PersonName(string first, string last)
{
this.firstName = first;
this.lastName = last;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}

update the properties of a copy of an instance wpf

I have two listboxes. One source listbox which is binded to ObservableCollection<Person> MyNetwork The other listbox is my target listbox which is binded to ObservableCollection<Person> Crew.Each time I drop the item in the target listbox I create a new instance of the sourceItem.
Now I would like to update the properties of new istances, but it doesn't seem to work. Is there a way to make the copies of the sourceItems to update when I change one of the sourceItems(FirstName) properties. I'm pretty new to WPF and MVVM and wonder if this is possible or is there a work around to achieve this?
Here what I have so far:
in the ViewModel
Source ListBox:
private ObservableCollection<Person> _myNetwork = new ObservableCollection<Person>();
public ObservableCollection<Person> MyNetwork
{
get { return _myNetwork; }
set { _myNetwork = value; RaisePropertyChanged(); }
}
Target ListBox:
private ObservableCollection<Person> _crew = new ObservableCollection<Person>();
public ObservableCollection<Person> Crew
{
get { return _crew; }
set { _crew = value; RaisePropertyChanged("Crew");}
}
void IDropTarget.Drop(IDropInfo dropInfo)
{
Person sourceItem = dropInfo.Data as Person;
if (dropInfo.Data is Person)
{
Person person = new Person(sourceItem.FirstName,
sourceItem.LastName,
sourceItem.Profession);
Crew.Add(person);
}
}
The Model:
public Person(string FirstName, string LastName, string Profession)
{
_firstName = FirstName;
_lastName = LastName;
_profession = Profession;
}
private string _firstName;
public string FirstName
{
get { return this._firstName; }
set { this._firstName = value; RaisePropertyChanged("FirstName"); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; RaisePropertyChanged("LastName"); }
}
private string _profession;
public string Profession
{
get { return _profession; }
set { _profession = value; RaisePropertyChanged("Profession"); }
}
I would recommend using a library that wraps the PropertyChanged event so it's easier to update your properties when you need to call them.
Once example is Caliburn for WPF. You can use NotifyOfPropertyChange(() => FirstName) from within your code to update the FirstName property however you need to (it doesn't have to just be used in the setter).
Here is a good article on how to use it.
Example:
using Caliburn.Micro;
namespace CaliburnMicroExample
{
public class ShellViewModel : PropertyChangedBase
{
private string _message;
public string Message
{
get { return _message; }
set
{
_message = value;
NotifyOfPropertyChange(() => Message);
}
}
public ShellViewModel()
{
Message = "Hello World";
}
}
}

ListBox toBind Dictionary<string, Object>

Is there a way to Bind Dictionary to a ListBox?
e.g:
I Have this Class:
class Person
{
private string _firstName;
private string _lastName;
private int _age;
public Person(string firstName, string lastName, int age)
{
this._firstName = firstName;
this._lastName = lastName;
this._age = age;
}
public string FirstName
{
get { return _firstName; }
set { this._firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { this._lastName = value; }
}
public int Age
{
get { return _age; }
set { this._age = value; }
}
}
and I use it as the value of my dictionary:
private Dictionary<string, Person> _persons = new Dictionary<string, Person>();
and i want to Bind it to listBox ... but I need to choose what Item to Bind ...
e.g: I want to Bind Person, then all Person only will appear in DisplayMember of listBox ...
i tried this answer:
stackoverflow.com/questions/854953/datagridview-bound-to-a-dictionary of Chris
but the output displayed on listbox perItem was something like:
{ Key = key, Value = ObjectValue }
Question: Is there another way to do this? or how to fix this atleast edit the item of listbox?
Updated:
I just saw this answer a while ago:
stackoverflow.com/questions/1506987/how-to-bind-dictionary-to-listbox-in-winforms
and it seems working! But my Problem now was How about if I want to Display Data From Values ... what to put on listBox.DisplayMember ?
There isn't a way to specify something like "Person.FirstName" in the DisplayMember of a listbox in winforms.
What you could do is override Person's ToString method or rather than binding a dictionary you could bind a List<Person>...
Or have a class just for binding and the bind List<CustomPerson>.
public class CustomPerson
{
public string Key { get; set; }
public Person Person { get; set; }
public string DisplayValue
{
get { return Person.FirstName; }
}
}
Then set DisplayMember to DisplayValue.

Data binding to custom object

I have a simple custom class (Person), which I want to bind to a label as a whole (not to separate properties of this class). The label should just present whatever the Person.ToString() returns (in this case FirstName + LastName).
How do I properly bind it using the person as a Source.
How do I make sure that any change in one of the properties of the Person will be reflected in the label?
public class Person : INotifyPropertyChanged {
private string firstName;
public string FirstName {
get {
return firstName;
}
set {
firstName = value;
OnPropertyChanged("FirstName");
}
}
private string lastName;
public string LastName {
get {
return lastName;
}
set {
lastName = value;
OnPropertyChanged("LastName");
}
}
public override string ToString() {
return FirstName + " " + LastName;
}
private void OnPropertyChanged(string name) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public Window1() {
myPerson = new Person() {
FirstName = "AAA",
LastName = "BBB"};
InitializeComponent();
}
public Person MyPerson {
get {
return myPerson;
}
set {
myPerson = value;
}
}
Label Content="{Binding Source=MyPerson}"
Create a new property FullName which returns the full name and raise PropertyChanged for FullName in the setters of FirstName and LastName as well. You should never bind to the object itself.

Categories