C# ListBox update on item change - c#

I have a list of custom objects which I have added to a ListBox control in my WinForms C# 4.0 application.
When the user selects a particular element in the ListBox, the properties of that object come up in the window next to the ListBox in various input fields. The user can change these and click 'Save' which will modify the data members of the objects to correspond with the changes the user has made.
The function does work. The values are saved to the object, and when the user selects the element again, their changes are confirmed to be saved correctly.
What isn't working is the update of the text in the ListBox. For example if we have a list of staff in the ListBox, and we can see "John Smith" there, we can click his name - edit his name to "John Smithe" and click OK. The ListBox still shows "John Smith", however if we click on his name, then in the TextBoxes on the right we can see that his name has correctly been changed to "John Smithe".
I have tried calling the Refresh() method on the ListBox but this didn't work.
I can fix it by removing the item from the ListBox and adding it again. This works, and it's not really an issue because the items are stored in separate lists anyway so I have no risk of losing any of my staff.
But is this really the best way to do it? Is there a more elegant way to update the text in the ListBox without removing/adding the item again?

Do the objects in the ListBox implement INotifyPropertyChanged?
Update:
It seems that you can solve the problem with a couple of steps:
Set the DisplayMember property of the ListBox to a property on your objects that provides whatever it is you want to appear in the list. I will assume this property is named DisplayText for this answer.
Have the objects implement INotifyPropertyChanged.
In the setters of all the properties that influence the value of DisplayText, raise the NotifyPropertyChanged event with DisplayText for the property name.
You should then be good to go.

Following the tutorial I reference above I made a quick and dirty example of using a BindingList. Hopefully it's helpful to you.
public partial class Listbox_Databinding : Form
{
BindingList<Person> People = new System.ComponentModel.BindingList<Person>();
public Listbox_Databinding()
{
InitializeComponent();
People.Add(new Person("John", "Smith"));
People.Add(new Person("John", "Jacob"));
lstSelectPerson.DataSource = People;
}
private void lstSelectPerson_SelectedIndexChanged(object sender, EventArgs e)
{
txtLast.Text = ((Person)lstSelectPerson.SelectedItem).Last;
}
private void btnUpdate_Click(object sender, EventArgs e)
{
((Person)lstSelectPerson.SelectedItem).Last = txtLast.Text;
}
}
public class Person : INotifyPropertyChanged
{
public Person(string first, string last)
{
First = first;
Last = last;
}
public override string ToString()
{
return Last + ", " + First;
}
string p_first;
string p_last;
public string First
{
get { return p_first; }
set
{
p_first = value;
OnDisplayPropertyChanged();
}
}
public string Last
{
get { return p_last; }
set
{
p_last = value;
OnDisplayPropertyChanged();
}
}
void OnDisplayPropertyChanged()
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("DisplayName"));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}

Related

Binding Label's Text to a Variable in Xamarin forms c#

So Im trying to creat a simple app like a shopping app. so I have categories and multiple items for each category, and when you get to choose an item then you will have the posibility to increase how many you need or delete the item. For exemple I chosed three items, so my cart have 3 items where each one have an Add button and a delete button. When I hit the add button the number of the items shown should increase and so on.
so what I've done so far is creating a JSON file that having all my categories, and once I hit a category I get to deserialize another JSON file that have all my items, so the items shown depends on the category I chosed of course.
Now each time i choose an item it get added to the cart and shown on the bottom page with a + and - buttons and so on.
so I created a category class to deserialize my json, and an objets class to deserialize my Item's json. I implememted the INotifyChangedProperty in the objets class so that I can keep showin whenever the number of a chosen item get increased, so basicly thats my ViewModel, but I guess that it's like that I need a ViewModel of each created item ? so I guess what I really need to use is the ObservableCollection ..
I hope I explained everything well, and waiting for your feedbacks about if Im doing it right or wrong and how should i proceed to get what I want. thank you so much
the problems is that to set the bindingcontext to my "Objets" Class I have to put the arguments in it, and then my Label well get a precised value ... what should I do ?
I do one sample about your model, you can take a look:
<ContentPage.Content>
<StackLayout>
<Label x:Name="label1" />
<Button
x:Name="btn1"
Clicked="Btn1_Clicked"
Text="change value" />
</StackLayout>
</ContentPage.Content>
public partial class Page15 : ContentPage
{
public Objets model { get; set; }
public Page15()
{
InitializeComponent();
model= new Objets("test 1", 1.001f, " test11111", 12);
this.BindingContext = model;
label1.SetBinding(Label.TextProperty, "nbr_objet");
}
private void Btn1_Clicked(object sender, EventArgs e)
{
model.nbr_objet = 20;
}
}
public class Objets : INotifyPropertyChanged
{
public string Designation { get; set; }
public float Prix { get; set; }
public string imageUrl { get; set; }
private int Nbr_Objet;
public int nbr_objet
{
get { return Nbr_Objet; }
set
{
Nbr_Objet = value;
RaisePropertyChanged("nbr_objet");
}
}
public Objets(string Designation, float Prix, string imageUrl, int Nbr_Objet)
{
this.Designation = Designation;
this.Prix = Prix;
this.imageUrl = imageUrl;
this.Nbr_Objet = Nbr_Objet;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Update:
but I guess that it's like that I need a ViewModel of each created item ? so I guess what I really need to use is the ObservableCollection ..
You said that you have three categories, and each category have many items, If you display these in ListView, category is used as Group header, and I suggest you can use the same model for different item for different categories, then add in Observablecollection, because it have implemented INotifyPropertyChanged interface.
About ListView group, you can take a look:
https://github.com/xamarin/xamarin-forms-samples/tree/master/UserInterface/ListView/Grouping
If you still have another question, I suggest you can create new thread to ask, because this thread is very long.
Please remember to mark the helpful reply as answer, thanks.
to set a binding programatically
// set the BindingContext for the page
this.BindingContext = new MyViewModel();
// Title is a public property on MyViewModel
myLabel.SetBinding(Label.TextProperty, "Title");
in order for the UI to update when the VM is changed, the VM needs to implement INotifyPropertyChanged
This is some guidance that might help with your problem. Your code is messy and I think that is causing your confusion (you have several things named very similarly).
int Nbr_Objet;
public int nbr_objet { get{...} set {...}}
this.Nbr_Objet= Nbr_Objet;
this shows me that you are setting your member variable Nbr_Objet directly, when you do that the property change notification doesn't fire - you need to assign the value through the public nbr_objet for that to happen.
I'd suggest you define the binding in XAML, and make sure you bind to the property nbr_objet, not the private member variable (field) Nbr_Objet.
If you want to avoid confusion, follow the C# coding standard and name your member variable _nbrObjet, and camel case your property name public int NbrObjet { get {....

bind a combo-box to a List with "static" content and show content from database on WPF with MVVM

i am having a combo box and i want to show when you click on it the Gender "Female" or "Male" , the same time i want to read from my database which "Gender" has the selected Employee. So, generally i want to read and show on my combo-box the "sex" from my database but when you click on it i want also to show the two different options that you will have ("Female" or "Male"). The problem is now that i know how to bind to a combo-box so i can show the content on it from an Observable collection, but i do not know first how i can bind a property on it and how i can show also the same time the two different choices that some one can choose.
Thanks in advance!
Keep your ObservableCollection binding. It sounds as if that's working fine and it is responsible for providing all the options you want exposed in the dropdown of the ComboBox.
It sounds like you want to then show by default the Sex property that was obtained from your database.
Presumably, you will have some Sex property on your DataContext that is implementing INotifyPropertyChanged
private string _sex;
public string Sex
{
get { return _sex; }
set
{
if (_sex != value)
{
_sex = value;
OnPropertyChanged("Sex");
}
}
}
private void WhateverMethodYouHaveGettingDataFromDB()
{
//... do whatever it needs ...
Sex = // get sex from database...
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Now you have a bindable property that represents the Sex that you obtained from your database.
To push this onto the default exposed value in your ComboBox, simply bind it to the SelectedValue property.
<ComboBox ItemsSource="{Binding --your observable collection--}"
SelectedValue="{Binding Sex}"/>

Binding ListBox.SelectedItem to Property

This might be a duplicate question, but I'm unable to find a good answer. All the answers like Binding WinForms ListBox to object properties don't work on my WinForm. I'll explain.
I have a list of Firms that I show in a ListBox. I would like when the SelectedItem changes, that it updates a property on my model. So that I can read the Firms properties.
// the classes
public class Firm
{
public string Name { get; set; }
public int Id { get; set; }
// more properties ...
}
public class MyModel : INotifyPropertyChanged
{
private Firm _firm = new Firm();
public Firm Firm
{
get { return _firm; }
set
{
if (Equals(value, _firm)) return;
_firm = value;
OnPropertyChanged();
}
}
// more properties and OnPropertyChanged() ...
}
// the form
private MyModel Model;
public void MyForm(List<Firm> firms)
{
lstFirm.DataBindings.Add("SelectedItem", Model, "Firm",
true, DataSourceUpdateMode.OnPropertyChanged);
lstFirm.DisplayMember = "Name";
lstFirm.ValueMember = "Id";
lstFirm.DataSource = firms;
}
public void lstFirm_SelectedIndexChanged(object sender, EventArgs e)
{
// Do something with Model.Firm
}
The problem is that Model.Firm null is. Does anybody have an idea what I need to do to make a databinding between the ListBox and the Model? I bind other stuff on my WinForm (such as TextBoxes to String properties) and those work nicely.
From what I can see, your code never sets Model.Firm... Where's the constructor for MyModel? If you don't provide one, Model.Firm will stay null unless you explicitly set it. Here's an example constructor:
public MyModel(Firm firm)
{
_firm = firm;
}
Also, Equals() doesn't do what you think it does. Instead of if (Equals(value, _firm)) return;, use this: if (value == _firm) return;
Ok, so after a weekend of testing, I figured it out.
I was debuging in the SelectedIndexChanged event and didn't see the change in my Model.Firm just yet. But as the SelectedItemChanged event is only internal, I couldn't use that and that's where the databinding on SelectedItem applies the values to databound items.
Now the reason why the change isn't visible yet, is because the SelectedItemChanged is only fired after the SelectedIndexChanged is executed. So internally in the ListBox control, it probably looks like
this.SelectedIndex = value;
this.SelectedItem = FindItem(value);
this.SelectedIndexChanged(/*values*/);
this.SelectedItemChanged(/*values*/); // Apply databinding changes
So it's quite normal that you don't see the changes, before the change has occured. And I didn't know this, so I was kinda stumped why the SelectedItem (who was displaying the changed value) wasn't copied over to the databound model property.
So I didn't have to change anything major to get it all working. :)

How do I use INotifyPropertyChanged in WinRT?

I'm a total newbie, just learning the basics of DataContext and the MVVM model. I've now got a grid bound to a view model object which implements INotifyPropertyChanged, however it appears that UpdateSourceTrigger (which all the WPF tutorials tell me to use) is not available for WinRT / Metro Style apps!
How do I implement INotifyPropertyChanged then?
I'm at the end of my tether here. I've spend nearly the whole day on the most basic of app examples, simply trying to get a grid to update after I click something. The only way I've managed to do this so far is to create an entirely new instance of the view model and reassign the DataContext which I know is wrong
UPDATE:
I have made some progress, but things have gotten very weird. I have a view model, with a generic list of items. The items list is wired up with a PropertyChangedEventHandler. If I replace the entire collection with a new one, the listview updates.
model.Items = new List<DataItem>{ new DataItem{ Title = "new item" }};
This results in a one item list with the above item. However, if I try adding an item, nothing happens
model.Items.Add(new DataItem{ Title = "added item" });
I also tried creating a method which added an item and specifically fired PropertyChanged, but that also doesn't work
Here's where it gets weird. Next I tried this code.
model.Items.Add(new DataItem { Title = "added item" });
model.Items = new List<DataItem> { new DataItem { Title = "new item" }};
This results in a two item list:
- new item
- added item
How can this be? The code says, "add one item" then "replace the whole list" but it executes in the reverse order?
UPDATE 2:
I've switched to ObservableCollection as suggested, which has actually solved the original problem. I can now add an item and it shows up on the list.
However, the new weird behaviour is still in effect. Items added before the collection is reset are appended to the end of the new collection. Why is my code executing in reverse order?
You need to implement the interface and send out the notification once the given property you care about changes.
public event PropertyChangedEventHandler PropertyChanged;
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CustomerName"));
}
}
}
}
Keep in mind that for a collection, you should use an ObservableCollection as it will take care of the INotifyCollectionChanged being fired when an item is added or removed.
I would suggest to scale your sample back as far as possible. Don't start with a DataGrid but rather a simple TextBoxand Button, where the Button forces a change in your ViewModel which will then reflect on the UI.
Code taken from here.
It's best to implement a parent class which implements it like this:
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And then in your subclass (i.e. ViewModel) in your property do something like this:
public class MyViewModel : NotifyPropertyChangedBase
{
private string _name;
public string Name {
get{ return _name; }
set{
_name = value;
RaisePropertyChanged("Name");
}
}
}

Passing c# ListBox Item to method

Small programming problem here, hopefully someone can point me in the right direction.
Im using a list here on a WP7 page. (Silverlight ListBox).
My list is populated by an XML file. Each List item has three text boxes populated as per the code below.
I need to pass one of the selected ListBox items as text to the following method to poulate the phone number.
This is my c# code which populates the listbox, and then the phonecall method.
private void planning(object sender, ManipulationStartedEventArgs e)
{
XElement _xml = XElement.Load("contacts/contacts.xml");
{
contacts.Items.Clear();
foreach (XElement value in _xml.Elements("channel").Elements("item"))
{
ContactsItem _item = new ContactsItem();
_item.Title = value.Element("title").Value;
_item.Web = value.Element("web").Value;
_item.Phone = value.Element("phone").Value;
contacts.Items.Add(_item);
}
}
}
private void phone_number(object sender, MouseButtonEventArgs e)
{
Microsoft.Phone.Tasks.PhoneCallTask phonecall = new Microsoft.Phone.Tasks.PhoneCallTask();
phonecall.PhoneNumber = //value here
phonecall.Show();
}
With my accompanying class:
public class ContactsItem
{
private string _title;
private string _web;
private string _phone;
public string Title
{
get { return _title; }
set { _title = value; }
}
//etc etc....
The idea is, when you click on the binded 'Phone' text in the list box, it will pass that value to the phonecall method. When the text box with the phone number is clicked, it calls that phone_number method.
Hope you understand. Many thanks.
Look into the sender's DataContext property (you may have to do some type casting) in the phonecall method (I mean the handler which handles the ListBoxItem's click event). If the ListBox is bound correctly, the ContactsItem will be the menu item's DataContext.
Also, as a side note: Learn to follow .NET's naming standards (CamelCase for methods), it will make your life much easier in the long run, especially if you were to collaborate with other propgrammers.
And you can use this to generate simple properties with basic getters/setters:
public string Name {get; private set;}
Assuming "contacts" is your ListBox, you could get the phone number by doing
((ContactsItem)contacts.SelectedItem).Phone

Categories