I am new to wpf and MVVM, and I've spent all day trying to get the value of a ComboBox to my ViewModel on SelectionChanged. I want to call a function in the selection changed process. In mvvm, what is the solution for it?
In MVVM, we generally don't handle events, as it is not so good using UI code in view models. Instead of using events such as SelectionChanged, we often use a property to bind to the ComboBox.SelectedItem:
View model:
public ObservableCollection<SomeType> Items { get; set; } // Implement
public SomeType Item { get; set; } // INotifyPropertyChanged here
View:
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding Item}" />
Now whenever the selected item in the ComboBox is changed, so is the Item property. Of course, you have to ensure that you have set the DataContext of the view to an instance of the view model to make this work. If you want to do something when the selected item is changed, you can do that in the property setter:
public SomeType Item
{
get { return item; }
set
{
if (item != value)
{
item = value;
NotifyPropertyChanged("Item");
// New item has been selected. Do something here
}
}
}
Related
So I've got a basic view model that functions like so:
// The ICollectionVIew is what my ListBox binds to.
public ICollectionView UserView { get; set; }
// <signup> is a model that's populated from a database representing a signup table
private ObservableCollection<signup> _signup;
public ObservableCollection<signup> Signup
{
get
{
return _signup;
}
set
{
if (_signup != value)
{
value = _signup;
}
OnPropertyChanged("Signup");
}
}
// This is the constructor for the ViewModel
public registrationVM()
{
// entity context Fills up the Model
context.signups.Load();
// The below code fills up the ObservableCollection
var query = context.signups;
_signup = new ObservableCollection<signup>(query);
// And the below code fills up the ICollectionView using the ObservableCollection
UserView = CollectionViewSource.GetDefaultView(_signup);
}
So now instead of binding to the ObservableCollection, I can bind to the ICollection.
<ListBox ItemsSource="{Binding UserView}" DisplayMemberPath="firstName" SelectedItem="{Binding SelectedUser}"/>
This works perfectly in terms of loading my information. But then now comes the issue of navigating. I bound my button Commands to the ViewModel,
<Button x:Name="next" Command="{Binding Next}"/>
And in it's execution Method:
private object Next_CommandExecute(object param)
{
// 'UserView' Is the ICollectionView I declared earlier
return UserView.MoveCurrentToNext();
}
The problem is the button's function doesn't do anything. Same goes for the 'previous' Button. The on screen selected record doesn't change so I'm guessing there's something I'm doing wrong. What exactly is what I've been failing to figure out. Does anyone see where I'm going wrong?
As mentioned in my comment, you need to set IsSynchronizedWithCurrentItem = true on your ListBox
ListBox ItemsSource="{Binding UserView}"
DisplayMemberPath="firstName"
IsSynchronizedWithCurrentItem = true
SelectedItem="{Binding SelectedUser}"/>
i have a ObservableCollection with some data. They are displayed as Master (ListBox) and Detail (some Labels). I use binding and IsSynchronizedWithCurrentItem to show the correct details to the selected master item. This works all fine. Now i want to edit some details (load different image). I implemeted this a a Button Command in the ViewModel.
But how do i know which item is selected (UI) in the ViewModel-layer ?
Thanks for help
I don't really find the IsSynchronizedWithCurrentItem property that useful in MVVM scenarios.
Just expose another SelectedItem property in the ViewModel.
public ItemType SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
// your logic here
}
}
You need to bind a value or Enumerable of values of your ViewModel to the ListBox's SelectedItems property.
SelectedItems="{Binding VMProperty}"
http://msdn.microsoft.com/en-us/library/system.windows.controls.listbox.selecteditems(v=vs.110).aspx
If you only want one item selected you need to set:
SelectionMode="Single"
SelectedItem="{Binding VMProperty}"
If I understood you right, what you are looking for is the following
<ListBox SelectedItem="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"/>
Plus under your ViewModel you have to declare the following
public YourObject ObjectName { get; set; }
Simple as that!
The normal way to detect which item in the collection is currently selected in the UI is to data bind a property of the same type as the items in the collection to the ListBox.SelectedItem property:
<ListBox ItemsSource="{Binding SomeCollection}"
SelectedItem="{Binding SomeProperty}" />
Now, whenever the user selects a new item, the SomeProperty setter will be called:
public YourDataType SomeProperty
{
get { return someProperty; }
set
{
someProperty = value;
// The value has just changed
}
}
I need to create an Auto search control which will show the results as rows as this one http://www.devexpress.com/Products/NET/Controls/WPF/Editors/lookup-editor.xml. However, I dont need the graphics and checkboxes here. A simple listview like appearance will work.
Please suggest how to create the user control using WPF.
Here has a nice article on Sorting, Filtering and Grouping ListView.
Basically you set CollectionViewSource to ListCollectionView. You can then use the Filter Property to filter the ListView.
If you're using an MVVM approach you could do the following:
Bind your search Textbox Text member, ListView's ItemsSource and SelectedItem to the ViewModel
Set 'UpdateSourceTrigger=PropertyChanged' on the TextBox's binding
In the setter of the property that the TextBox is bound to add logic that searches the ItemsSource collection and sets the SelectedItem bound property.
Something like this:
XAML:
<TextBox Text="{Binding Path=SearchTerm, UpdateSourceTrigger=PropertyChanged}"/>
<ListView ItemsSource="{Binding Path=SourceCollection}" SelectedItem="{Binding Path=SelectedSearchItem, Mode=TwoWay}" />
Code:
public class ViewModel : INotifyPropertyChanged
{
public string SearchTerm
{
get { return searchTerm; }
set {
searchTerm = value;
SelectedSearchItem = SourceCollection.FirstOrDefault(foo => foo.Name.Contains(searchTerm));
}
}
public Foo SelectedSearchItem
{
get { return selecedSearchItem; }
set {
selectedSearchItem = value;
// Raise PropertyChanged
}
}
public ObservableCollection<Foo> SourceCollection { get; set;}
}
I am trying to implement a Master detail view in WPF MVVM.
In my viewmodel I have a observable collection of "Causes". Each Cause has an observable collection of "Solutions".
I bound an editable Datagrid to the Causes and it is working fine. But when a user selects a row in the DataGrid I want to allow the user to see its associated solutions in the TabControl.
How should I go about doing this? Should I create a property CurrentCause in the Viewmodel and bind it to the SelectedItem. In the TabControl can I bind to CurrentCause.Solutions.
Will this be an optimal approach? Please advise. Thanks!!
You could bind the ItemsSource of the TabControl to be the SelectedItem of the DataGrid using element binding.
<TabControl ItemsSource="{Binding ElementName=myDataGrid, Path=SelectedItem.Solutions}">
You can set IsSynchronizedWithCurrentItem to True and do something like this.
I would bind your DataGrid to a list of Causes, the SelectedItem to CurrentCause in your Model and the TabControl to Solutions. Then you have everything nicely tied up in your MVVM.
private Cause _currentCause;
public Cause CurrentCause
{
get { return _currentCause; }
set
{
if (_currentCause == value) return;
CurrentSolution = _currentCause.Solutions; //However you do this...
_currentCause = value;
RaisePropertyChanged("CurrentCause");
}
}
private ObservableCollection<Cause> _causes;
public ObservableCollection<Cause> Causes
{
get { return _causes; }
set
{
_causes = value;
RaisePropertyChanged("Causes");
}
}
private ObservableCollection<Solution> _solutions;
public ObservableCollection<Solution> Solutions
{
get { return _solutions; }
set
{
_solutions = value;
RaisePropertyChanged("Companies");
}
}
<dg:DataGrid ItemsSource="{Binding Causes}" SelectedItem="{Binding CurrentCause}"...
Being a new to WPF/XAML/MVVM, I've got a question.
In my View, I have 2 listboxes, which derive from ItemsControl.
On my viewmodel, I'd like to expose 2 ItemsControl properties such that I can bind my listbox to this view model property... this way I can implement a command that, from the view model, lets me move the currently selected item from ListBox1 to ListBox2.
Imagine all the really cool stuff not shown the following snippets:
view model code:
public int MyStuff1SelectedIndex { get{...} set{...} }
public int MyStuff2SelectedIndex { get{...} set{...} }
public ItemsControl MyStuffItemsControl1 { set; private get; }
public ItemsControl MyStuffItemsControl2 { set; private set; }
view XAML:
<ListBox Name="x:MyStuffListBox1" SelectedIndex="{Binding MyStuff1SelectedIndex}.... />
<ListBox Name="x:MyStuffListBox2" SelectedIndex="{Binding MyStuff2SelectedIndex}...../>
given that, I want my viewmodel to be able to have a command which could move items from one list box to another, w/ code such as the following:
public void MoveItemCommandExecute(...)
{
var sourceItem = MyStuff1ItemsControl.MagicGetItemExtensionMehod(MyStuff1SelectedIndex);
MyStuff1ItemsControl.MagicRemoveItemExtensionMethod(MyStuff1SelectedIndex);
MyStuff2ItemsControl.MagicAddItemExtensionMethod(sourceItem);
}
so, basically, what would the binding XAML look like ? I am trying to set a property on the view model from the view...
thanks!
You'll want to rethink this approach. Typically, you would bind your two listboxes ItemsSource properties to two ObservableCollection<T> properties on your view model, where T is the type of object in your list.
<ListBox x:Name="MyStuffListBox1" ItemsSource="{Binding MyList1}" SelectedItem="{Binding SelectedList1Item}" />
<ListBox x:Name="MyStuffListBox2" ItemsSource="{Binding MyList2}" SelectedItem="{Binding SelectedList2Item}" />
Note: I would use x:Name in your XAML, rather than the Name attribute.
public ObservableCollection<Thing> MyList1 { get; set; }
public ObservableCollection<Thing> MyList2 { get; set; }
// these properties should raise property changed events (INotifyPropertyChanged)
public Thing SelectedList1Item { get {...} set {...} }
public Thing SelectedList2Item { get {...} set {...} }
// constructor
public MyViewModel()
{
// instantiate and populate lists
this.MyList1 = new ObservableCollection(this.service.GetThings());
this.MyList2 = new ObservableCollection(this.service.GetThings());
}
You can then format what is displayed in the lists using DisplayMemberPath or defining an ItemTemplate on each list.
You can swap items between the lists by using the standard Collection methods on the ObservableCollection type - http://msdn.microsoft.com/en-us/library/ms668604.aspx
You shouldn't be implementing controls in your view model. That makes it a view, not a model of a view. The properties on your view model should be ObservableCollection<T>, and you should be binding the ItemsSource of items controls to those properties.
If you do this, your XAML might look like this:
<ListBox ItemsSource="{Binding List1}" SelectedItem="{Binding SelectedItem1, Mode=TwoWay}"/>
<ListBox ItemsSource="{Binding List2}" SelectedItem="{Binding SelectedItem2, Mode=TwoWay}"/>
<Button Command="{Binding MoveItemFromList1ToList2Command}">Move</Button>
List1 and List2 are of type ObservableCollection<T>, SelectedItem1 and SelectedItem2 are of type T (whatever type you've decided T should be), and MoveItemFromList1ToList2Command is a RoutedCommand that has these two handlers defined:
public bool CanMoveItemFromList1ToList2
{
{ get { return SelectedItem1 != null; }
}
public void MoveItemFromList1ToList2()
{
List2.Add(SelectedItem1);
List1.Remove(SelectedItem1);
}
I'd have to test this code to be sure, but I don't think you need to bother with property-change notification in the MoveItemFromList1ToList2 method; when you remove the item from List1, the ObservableCollection will notify the ListBox that the item's been removed, and the ListBox will set SelectedItem to null, updating SelectedItem1 in the view model. (And, of course, that will make the code break if you remove it from the first collection before adding it to the second.)