WPF ListView ignores SelectedItem-change - wpf

I am working with a ListView in WPF which is bound an observable collection. I then bound the SelectedItem-Property to a property of the ViewModel.
When I select an item in the ListView via GUI "SelectedItem" is changed.
When I change "SelectedItem" in the ViewModel, the ListView only changes, when I set the SelectedItem to NULL.
When I set any other (valid!) object (like the first entry of the ObservableCollection) the ListView just ignores it.
Furthermore: When I want to "Veto" a SelectedItem Change (because data is not saved), the ListView highlights the new selected item instead of the SelectedItem-Property of the ViewModel.
I already tried to change the Binding to Mode=TwoWay - doesn't work as well (otherwise the "NULL" change to SelectedItem wouldn't work as well)
This is the code from the view:
<ListView ItemsSource="{Binding Configurations}" SelectedItem="{Binding SelectedUserConfiguration}" SelectionMode="Single">
<ListView.View>
<GridView>
<GridViewColumn Header="User Configuration" DisplayMemberBinding="{Binding ConfigurationName}" Width="200" />
</GridView>
</ListView.View>
</ListView>
And my ViewModel:
public ObservableCollection<UserConfigurationViewModel> Configurations { get; private set; }
private UserConfigurationViewModel _selectedUserConfiguration;
public UserConfigurationViewModel SelectedUserConfiguration
{
get {
return this._selectedUserConfiguration;
}
set
{
if (this._selectedUserConfiguration != null && this._selectedUserConfiguration.WasChanged)
{
if (ask-user)
{
this._selectedUserConfiguration.Reset();
this._selectedUserConfiguration = value;
}
}
else
{
this._selectedUserConfiguration = value;
}
NotifyOfPropertyChange(() => this.SelectedUserConfiguration);
}
}

In order to select the selected item in any collection control from code, the selected item must be an actual item from the collection that is bound to the ItemsSource property. This can be achieved easily enough with LinQ if your collection items have at least one unique property:
SelectedUserConfiguration = Configurations.Where(c => c.UniqueProperty ==
valueOfItemToSelect).FirstOrDefault();
If your data type objects do not have a unique property, you can simply add an int Id property for this purpose.

I was just having the same issue with a TabControl.
I've tried to create a int property and bind it to SelectedIndex, but no success.
Strangely enough this did the trick:
In your ListView set IsSynchronizedWithCurrentItem="True"
Set a ListView.Resources like this:
<ListView.Resources>
<Style TargetType="ListViewItem" x:Key="ListViewTemplate">
<Setter Property="IsSelected" Value="True" />
</Style>
</ListView.Resources>

Related

Databinding to ListView Item in DataTemplate

I have a user control (WaypointInfoControl) that I wrote that has a dependency property named TheGraphic as shown here:
public Graphic TheGraphic
{
get { return (Graphic)GetValue(TheGraphicProperty); }
set { SetValue(TheGraphicProperty, value); }
}
public static readonly DependencyProperty TheGraphicProperty =
DependencyProperty.Register("TheGraphic", typeof(Graphic), typeof(WaypointInfoControl), new PropertyMetadata(default(Graphic)));
I have a viewmodel that has a Waypoints property defined like this:
private ObservableCollection<Graphic>_Waypoints = new GraphicCollection();
public ObservableCollection<Graphic> Waypoints
{
get { return _Waypoints; }
set { RaiseAndSetIfChanged(ref _Waypoints, value); }
}
In my xaml, I have a ListView that I want to populate with Waypoints:
<ListView ItemsSource="{Binding Waypoints}" >
<ListView.ItemTemplate >
<DataTemplate >
<controld:WaypointInfoControl TheGraphic="????" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
How do I bind TheGraphic to the individual item in the ListView that it represents?
Your ItemsSource is bound to a collection of Graphic objects which means that the DataContext for each item in your ListView will be a single Graphic object. Since the DependencyProperty that you are looking to bind to is looking for the Graphic object you will just want to bind to the entire DataContext, you achieve this by using the binding markup extension without specifying a path (this just causes the binding to pull in the entire DataContext which in your case is the Graphic object that you are looking for).
So this should work:
<ListView ItemsSource="{Binding Waypoints}" >
<ListView.ItemTemplate >
<DataTemplate >
<controld:WaypointInfoControl TheGraphic="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Why are my TreeViewItems acting like RadioButtons?

I have a WPF TreeView for which I've implemented a small model class behind the scenes. I bind a list of them to the TreeView's ItemsSource when creating the control. (I've pared the code here down a bit for the sake of simplicity, but it should be reproducable.)
public class TreeViewItemModel
{
public ObservableCollection<TreeViewItemModel> Children { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
public TreeViewItemModel()
{
Children = new ObservableCollection<TreeViewItemModel>();
IsSelected = false;
}
}
public partial class MainWindow : Window
{
public ObservableCollection<TreeViewItemModel> MyTree { get; set; }
public MainWindow()
{
InitializeComponent();
// Add some dummy values
List<TreeViewItemModel> items = new List<TreeViewItemModel>();
for (int i = 0; i < 10; i++) items.Add(new TreeViewItemModel() { Name = ("Node" + i) });
MyTree = new ObservableCollection<TreeViewItemModel>(items);
DataContext = this;
}
}
My TreeViewItems themselves contain checkboxes. Now, what I'd like to do is to bind IsSelected to the checkbox so that at the end of the day I (hopefully) have a list of TreeViewItemModel classes with IsSelected set to whether or not the checkbox is checked.
To that end, I have this style:
<Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
and this TreeView declaration:
<TreeView ItemsSource="{Binding MyTree}" >
<TreeView.Resources>
<DataTemplate DataType="{x:Type UI:TreeViewItemModel}">
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsSelected}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
This almost works. I can create a list of items programmatically and they get bound to the TreeView, check off items in my TreeView, and when I check them in C# IsSelected is set appropriately.
Except for one thing: my TreeViewItems all act like RadioButtons. I click one, and it sets IsSelected to true. I rejoice! But then I click on another... and it deselects the first TreeViewItem! I can never have more than one selected at a time.
But... why?! I don't understand at all. They're all bound to different items on the backend, so why would setting IsSelected change the state of another item?
:'(
In your Style for TreeViewItem you bind TreeViewItem.IsSelected to IsSelected property of your view model which basically means that CheckBox will be checked if TreeViewItem is selected. It happens so because WPF TreeView does not support multi selection.
You can easily add multi selection by changing TreeViewItem content into CheckBox or ToggleButton, exactly what you're trying to achieve, but then you cannot bind TreeViewItem.IsSelected to your view model.
What currently happens is
you click to select one item
previous TreeViewItem.IsSelected is set to false
this is passed to your view model by IsSelected
which is then passed back to CheckBox.IsChecked
new TreeViewItem.IsSelected is set to true
and so on
Remove Style for TreeViewItem and leave only CheckBox.IsChecked to IsSelected binding
On a side note you don't need StackPanel when you want to show just one element like CheckBox
You try removing your style? You should then see multiple selections

selectedvalue of combobox in datagrid not getting set - silverlight mvvm model

Below is the XAML snippet for my combo-box in a datagrid.
<data:DataGridTemplateColumn Header="Entry Mode">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=EntryModeCombo,Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="Id" SelectedValue="{Binding Path=selectedEntryMode,Mode=TwoWay}" ></ComboBox>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
Entrymode is an entity in the system and the Id and Name properties of this entity are used to set the DisplayMemberPath and SelectedValuePath of the combo.
public class A
{
private ObservableCollection<EntryMode> _EntryModeCombo;
public ObservableCollection<EntryMode> EntryModeCombo
{
get { return _EntryModeCombo; }
set
{
_EntryModeCombo = value;
RaisePropertyChanged("EntryModeCombo");
}
}
private string _selectedEntryMode;
public string selectedEntryMode
{
get { return _selectedEntryMode; }
set
{
_selectedEntryMode = value;
RaisePropertyChanged("selectedEntryMode");
}
}
}
In my viewModel, I am making an observable collection of the class A, and using that to bind a grid. All works well in the ADD mode, but in the edit mode, when I try to set the selected value of the combobox in the grid, it does not work. The population of the combo-box happens, but it remains unselected. Not sure why the selectedEntryMode property is getting set, but not affecting the combo selection in the grid.
Any suggestions will be appreciated.Thanks.
SelectedValue can only be used for getting value. not setting. use SelectedItem insted

ListView's ItemsSource binding not pushing changes to source

I am using WPF and Mvvm and my ListView has its ItemSource bound to an ICollectionView. How do I handle selected item change event?
Originally I had a DataGrid's ItemSource bind to the same ICollectionView and setup the collection's CurrentChanged event. Everything works fine, but not the case for a ListView.
All you have to do, as Thomas mentioned is to bind the SelectedItem Attribute of the listbox to a property in the viewmodel. To make it clear, here's an example.
Here's my view
<Grid x:Name="LayoutRoot" Background="White">
<ListView ItemsSource="{Binding Contacts}" SelectedItem="{Binding SelectedContact, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And here's my ViewModel
public class MainViewModel: ViewModelBase
{
ObservableCollection<ContactViewModel> contacts;
ContactViewModel selectedContact;
public ContactViewModel SelectedContact
{
get { return selectedContact; }
set {
selectedContact = value;
base.OnPropertyChanged("SelectedContact");
}
}
public ObservableCollection<ContactViewModel> Contacts
{
get { return contacts; }
set {
contacts = value;
base.OnPropertyChanged("Contacts");
}
}
}
Everytime you try changing the selection in the listbox you'll step into the setter of the SelectedContact.
set
{
contacts = value;
base.OnPropertyChanged("Contacts");
}
Through this, you'll know that the selected contact has changed.
Using the property SelectedContact, you'll also know which of the item in your collection is selected.
You can also bind a Collection property in the ViewModel to the SelectedItems attribute of the ListView if you want to implement multiple selection.
Just bind the SelectedItem of the ListView to a property of your ViewModel

WPF - Extend ListView with checkable AND selectable ListViewItems

I already read many examples on extending ListViews with checkboxes bound with IsSelected. But I want something more.
I want a seperation between the checked and selected state, so i get a ListBox that has a single selected item, but can have multiple checked items.
Unfortunately ListViewItem does not have a property for checked and I dont see a possibility to get the ListView to work with a custom CheckableListViewItem.
Of course i could use a List of objects with a checked property as ItemSource, but I dont think thats a good way to go. Checked or not is a matter of the list or item-container, not of the object listed in it. Beside that I dont want all my classes like user, role, group to have counterparts like checkableUser, checkableRole and checkableGroup.
The behaviour i want can be easyly accomblished for the UI with a
<DataTemplate x:Key="CheckBoxCell">
<StackPanel Orientation="Horizontal">
<CheckBox />
</StackPanel>
</DataTemplate>
and a
<GridViewColumn CellTemplate="{StaticResource CheckBoxCell}" Width="30"/>
But without a binding on the checkbox i cant check if it is checked or not.
Is there any way to accomplish something like that? The perfect solution for me would be to have listView1.SelectedItem, listView1.CheckedItems and maybe a listView1.UncheckedItems and of course listView1.CheckItem and listView1.UncheckItem.
Thanks for any help.
Ok, I got it.
Wasn't much to do, but becouse I'm new to the whole WPF stuff, its been some work to figure out.
Here is the solution:
public class CheckableListViewItem : ListViewItem
{
[Category("Appearance")]
[Bindable(true)]
public bool IsChecked { get; set; }
}
public class CheckableListView : ListView
{
public IList CheckedItems
{
get
{
List<object> CheckedItems = new List<object>();
for (int i=0;i < this.Items.Count; ++i)
{
if ((this.ItemContainerGenerator.ContainerFromIndex(i) as CheckableListViewItem).IsChecked)
CheckedItems.Add(this.Items[i]);
}
return CheckedItems;
}
}
public bool IsChecked(int index)
{
if (index < this.Items.Count) return (this.ItemContainerGenerator.ContainerFromIndex(index) as CheckableListViewItem).IsChecked;
else throw new IndexOutOfRangeException();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
if (item is CheckableListViewItem) return true;
else return false;
}
protected override DependencyObject GetContainerForItemOverride()
{
return new CheckableListViewItem();
}
}
Insert into your XAML under Window.Resources (clr = my class namespace):
<DataTemplate x:Key="CheckBoxCell">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=IsChecked,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type clr:CheckableListViewItem}}}" />
</StackPanel>
</DataTemplate>
And this as your CheckableListView:
<clr:CheckableListView SelectionMode="Single" [...] >
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource CheckBoxCell}"
Width="30"/>
[...]
</GridView>
</ListView.View>
</clr:CheckableListView>
Maybe this helps someone with the same problem.
In order to do this, you will have to create a custom ListBox and custom ListBoxItem controls to use within your application. Otherwise, you would have to add it to the items in your lists as a generic object ICheckable<T> (where T is User or Role) and have an ICheckableCollection<ICheckable<T>> for your items instead of adding a checkable to your model objects.

Resources