How to bind to a ListBox in a ViewModel - c#

Here's the deal: I have to take a SelectedItem from a Listbox, that I got from this question and add it to a ListBox in another UserControl. The ViewModels and models are all setup, I just need to know how to refer to the ListBox that is getting the items.
This would be under ViewModel A -- the ViewModel that controls the user control with the ListBox that receives the items.
//This is located in ViewModelA
private void buttonClick_Command()
{
//ListBoxA.Items.Add(ViewModelB -> SelectedListItem);
}
I don't understand how to get ListBoxA.
Would it be an ObservableCollection of strings?
For further clarification: ListBoxA, controlled by ViewModelA will be receiving values from ListBoxB in ViewModelB. I have included a property for ViewModelB in ViewModelA

You need to have a property in ViewModelA that can be any type that implements IEnumerable. I will use a list:
public const string MyListPropertyName = "MyList";
private List<string> _myList;
/// <summary>
/// Sets and gets the MyList property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public List<string> MyList
{
get
{
return _myList;
}
set
{
if (_myList == value)
{
return;
}
RaisePropertyChanging(MyListPropertyName);
_myList = value;
RaisePropertyChanged(MyListPropertyName);
}
}
Then in your Listbox, you need to set the ItemsSource to this list
<ListBox ItemsSource="{Binding MyList}">
.......
</ListBox>
Now in your constructer, fill MyList with the data you want to display, and on the Add Command, you want to put
MyList.Add(ViewModelB.myString);
ViewModelB.myString assuming from your previous question that in ViewModelB you have a property myString bound to the SelectedItem of ListBoxB, and you have a reference to the instance of ViewModelB in ViewModelA.
This should do it, let me know
Update:
You should be using an ObservableCollection in VMA since the collection will be added to.

Related

WPF XAML Binding in another project

How can i bind a listbox for exemple to a method which is in another project like this :
Project1(BDD)
Class1
Project2(GUI)
class2
I want to bind a listbox itemSource in the second project(GUI), with a class created in my first project(BDD).
How can i bind a listbox for exemple to a method which is in another project?
Put simply, we don't do that in WPF. Instead, you should add a reference to the other project, add a using declaration and then instantiate an object from that class. Then (assuming that your method returns a collection of some sort) you should set the output of the method to a collection property that you then data bind to the ItemsSource property.
Here's a very basic example:
private ObservableCollection<int> numbers = new ObservableCollection<int>();
public ObservableCollection<int> Numbers
{
get { return numbers; }
set { numbers = value; NotifyPropertyChanged("Numbers"); }
}
...
Numbers = new YourClassFromOtherProject().GetData();
...
<ItemsControl ItemsSource="{Binding Numbers}" ... />

Problems WPF binding through a contained object

I'm trying to use WPF binding to an object that is contained by the DataContext object and it is not working.
If I place all binding elements in the DataContext ViewModel object, everything works, but if I separate out the data list and data elements into a separate class that is contained by the ViewModel class, the ListBox data will work, but the binding to the individual data elements is not working.
With my experimentation, I assume that the bound object needs to be directly bound to the DataContext ViewModel class. I can do that and it works, but it's not as object oriented or reusable as I would like it to be. I've separated out the data list for the ListBox into it's own class and I'm assuming that since it is an ObservableCollection it works regardless of it being attached to the contained object. Since the individual data elements of the objects are only notified through OnPropertyChanged, no matter what I've tried, I can't get the WPF form to recognize the data, even though the DataContext.CurrentRecord shows the correct data. I can only assume that the OnPropertyChanged notification are not going where I need them to go.
Partial Code for DataContext ViewModel is as follows:
public ObservableCollection<ItemModel> Items
{
get { return ItemListMgr.Items; }
set { ItemListMgr.Items = value; }
}
public ItemModel CurrentItem
{
get { return ItemListMgr.CurrentItem; }
set { ItemListMgr.CurrentItem = value; }
}
Corresponding code in Contained ItemListMgr object is as follows:
public readonly ItemListModel _ItemList;
public ObservableCollection<ItemModel> Items
{
get { return _ItemList.Items; }
set
{
_ItemList.Items = value;
OnPropertyChanged("Items");
}
}
private ItemModel _currentItem;
public ItemModel CurrentItem
{
get { return _currentItem; }
set
{
_currentItem = value;
OnPropertyChanged("CurrentItem");
}
}
The "Items" object is the list that gets displayed and even using the contained object "ItemListMgr", this part still works great. As the user scrolls through the list CurrentItem is set to the active item in "Items", but even though the WPF data entry elements are bound to the CurrentItem elements, they will not change when I scroll through the list.
The Xaml code to bind a text box is as follows:
Text="{Binding CurrentItem.ItemName, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}"
The Xaml code binds to the DataContext object, which is using a simple pass through to the contained ItemListMgr class. I've tried to add the OnPropertyChange to the pass through elements, but that didn't help and I would guess only added to the confusion.
As this is a large amount of code and fairly complex, I hope that I've given enough key elements for you to understand what I'm doing. Hopefully there is a way to allow this type of binding as it will greatly enhance what I'm doing.
Whatever help you can give will be greatly appreciated,
Paul

I can't binding DataGrid to ObservableCollection

I can't understand what's happening here. I have two public properties in my ViewModel:
public ObservableCollection<vw_ClientesFull> MyClients { get; set; }
private IEnumerable<vw_ClientesFull> _clients;
public IEnumerable<vw_ClientesFull> Clients
{
get
{
return _clients;
}
set
{
_clients= value;
OnPropertyChanged("Clients");
}
}
Then I have a method to refresh both of them:
private void RefreshClientes()
{
this.serviceClient.Clientes_ListarCompleted += (s, e) =>
{
Clients = e.Result;
MyClients = new ObservableCollection<vw_ClientesFull>(Clients);
};
this.serviceClient.Clientes_ListarAsync(_sRazonSocial, VendedorSel, TransporteSel, _nID, bInactivos);
}
Them i bind my dataGrid to show the information. If I do:
ItemsSource="{Binding Path=Clients}"
If works perfect, but if i do:
ItemsSource="{Binding Path=MyClients}"
Nothing is show! Why? Doesn't ObservableCollection fire onPropertyChange Automaticaly?
Thanks for the help!!!
UPDATE
So if i need to fire the OnPropertyChange manualy, why this work without it?
public ObservableCollection<Vendedores> Vendedores { get; set; }
private void CargarVendedores()
{
Vendedores = new ObservableCollection<Vendedores>(this.serviceClient.GetVendedores());
this.VendedorSel = this.Vendedores.FirstOrDefault();
}
If i bind a combobox like this:
ItemsSource="{Binding Path=Vendedores}"
Work without the OnPropertyChange! Why!
This problem is due to a misconception. ObservableCollection does not raise PropertyChanged, (which happens when the entire property is reassigned) when you replace it, but rather CollectionChanged (which is raised when items are added or removed). You still need to raise PropertyChanged if you plan to reassign the whole object.
Yes, ObservableCollection implements INotifyPropretyChanged. However, it isn't going to help you here :)
ObservableCollection is special because it implements INotifyCollectionChanged. In other words, it raises an event when items are added to or removed from the underlying collection. It also implements INotifyPropertyChanged, so anything bound to a property of the collection will get updated.
You are changing the variable itself though (setting to a new instance no less). This requires that the "instance" of the ObservableCollection property raise the event. In other words, you need:
private ObservableCollection<vw_ClientesFull> myClients;
public ObservableCollection<vw_ClientesFull> MyClients
{
get { return myClients; }
set
{
myClients = value;
OnPropertyChanged("MyClients");
}
In your update, the binding hasn't fired yet (you set in the constructor) so it gets the correct list. Subsequent changes to the list wouldn't work, however.
You must raise the PropertyChanged event when you set the value of MyClients, same as you've already done for Clients.

How to bind datatable value in combobox which has static value?

I have a ComboBox with few static values.
<ComboBox Name="cmbBoxField" Grid.Column="4" Grid.Row="2" Style="{StaticResource comboBoxStyleFixedWidth}" ItemsSource="{Binding}" ></ComboBox>
MVVMModle1.cmbBoxField.Items.Add(new CustomComboBoxItem("Text Box", "0"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Pick List", "1"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Check Box", "2"));
MVVMModle1.cmbBoxFieldType.Items.Add(new CustomComboBoxItem("Radio Button", "3"));
When I am saving the data in Database table it is getting saved.
((CustomComboBoxItem)(MVVMModle1.cmbBoxField.SelectedValue)).Value.ToString();
Now when I am trying to Edit my form and binding the value again to combobox it is not showing the value.
MVVMModle1.cmbBoxField.SelectedValue = dtDataList.Rows[0]["ControlList"].ToString().Trim();
Someone please help me in this. How to bind selected value to the combobox?
There are quite a few problems with your code here:
You are setting the ItemsControl.ItemsSource property to the default binding (bind to the current data context), which is incorrect unless the DataContext is any type that implements IEnumerable, which it probably isn't.
If this is correct because the DataContext is, for example, an ObservableCollection<T>, then you still have an issue because you are adding items statically to the ComboBox instead of whatever the ItemsSource is.
Also, the type of items you are adding are CustomComboBoxItem, which I'm going to assume inherits from ComboBoxItem. Either way, you can't say the SelectedValue is some string since the values in the ComboBox are not strings.
You should really not have a collection of CustomComboBoxItem's, but instead a custom class that is in itself it's own ViewModel.
Now that that's been said, here is a suggested solution to your problem:
<ComboBox ItemsSource="{Binding Path=MyCollection}"
SelectedValue="{Binding Path=MySelectedString}"
SelectedValuePath="StringProp" />
public class CustomComboBoxItem : ComboBoxItem
{
// Not sure what the property name is...
public string StringProp { get; set; }
...
}
// I'm assuming you don't have a separate ViewModel class and you're using
// the actual window/page as your ViewModel (which you shouldn't do...)
public class MyWPFWindow : Window, INotifyPropertyChanged
{
public MyWPFWindow()
{
MyCollection = new ObservableCollection<CustomComboBoxItem>();
// Add values somewhere in code, doesn't have to be here...
MyCollection.Add(new CustomComboBoxItem("Text Box", "0"));
etc ...
InitializeComponent();
}
public ObservableCollection<CustomComboBoxItem> MyCollection
{
get;
private set;
}
private string _mySelectedString;
public string MySelectedString
{
get { return _mySelectedString; }
set
{
if (String.Equals(value, _mySelectedString)) return;
_mySelectedString = value;
RaisePropertyChanged("MySelectedString");
}
}
public void GetStringFromDb()
{
// ...
MySelectedString = dtDataList.Rows[0]["ControlList"].ToString().Trim();
}
}
You could alternatively not implement INotifyPropertyChanged and use a DependencyProperty for your MySelectedString property, but using INPC is the preferred way. Anyways, that should give you enough information to know which direction to head in...
TL;DR;
Take advantage of binding to an ObservableCollection<T> (create a property for this).
Add your items (CustomComboBoxItems) to the ObservableCollection<T>.
Bind the ItemsSource to the new collection property you created.
Bind the SelectedValue to some string property you create (take advantage of INPC).
Set the SelectedValuePath to the path of the string property name of your CustomComboBoxItem.
Can you use cmbBoxField.DataBoundItem()? If not target the source from the selected value, i.e. Get the ID then query the source again to get the data.
(CustomComboBoxItem)MVVMModle1.cmbBoxField.DataBoundItem();
When you bind a datasource it is simpler to do it like this:
private List GetItems(){
List<CustomComboBoxItem> items = new List<CustomComboBoxItem>();
items.Add(new CustomComboBoxItem() {Prop1 = "Text Box", Prop2 = "0"});
//...and so on
return items;
}
Then in your main code:
List<CustomComboBoxItem> items = this.GetItems();
MVVMModle1.cmbBoxField.DisplayMember = Prop1;
MVVMModle1.cmbBoxField.ValueMember = Prop2;
MVVMModle1.cmbBoxField.DataSource = items;
This will then allow your selected value to work, either select by index, value or text
var selected = dtDataList.Rows[0]["ControlList"].ToString().Trim();
MVVMModle1.cmbBoxField.SelectedValue = selected;

WPF - Checkbox bound to whether or not ObservableCollection contains a value?

I currently have a checkbox that is bound to a property that checks an ObservableCollection for a specific value. If the value exists, then the property returns true.
My property and the property that exposes the ObservableCollection are both readonly.
This approach works fine when I first load my model, but when it add additional items into the ObservableCollection the checkbox that is bound to the property does not update.
Here's the code for my property:
public bool IsMeasure11
{
get //readonly
{
return this.Charges.Any(t => t.IsMeasure11);
}
}
And here's the code for my ObservableCollection:
public ObservableCollection<DACharge> Charges
{
get //readonly
{
if (_charges == null)
{
_charges = new GenericEntityCollection<DACharge>(_DACase.Id).ToList().ToObservableCollection();
}
return _charges;
}
}
And the XAML for the Checkbox:
<CheckBox Content="M11" Name="chkM11" IsChecked="{Binding IsMeasure11, Mode=OneWay}">
Thanks in advance,
Sonny
Your property does not implment INotifyPropertyChanged so there is no way for the binding engine to know to update the bound property.
An ObservableCollection<T> does not update the property which it is being returned within; in this instance the public ObservableCollection<DACharge> Charges property.
You will need to register for the ObservableCollection.CollectionChanged event and when an item is added/removed, etc...fire a PropertyChanged event as displayed in the previous link for the Charges property.
You need to invalidate the binding of IsMeasure11 (i.e. invoke the PropertyChanged event of INotifyPropertyChanged) whenever the Charges collection changes. One way of doing this would be to subscribe to the CollectionChanged event of the Charges collection.
// somewhere, maybe the constructor of your view model
this.Charges.CollectionChanged += ChargesChanged;
private void ChargesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.NotifyOfPropertyChanged(() => IsMeasure11);
}
You need to make your "add item" action to fire up the INotifyChanged Interface's PropertyChanged event as described here: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
If not then WPF will never know when the value changes as the binding isnt sourced at the ObservableCollection but to another property that accesses it instead.
Hope it helps.

Categories