How does DataGrid.SelectedIndex behave when - wpf

I have a WPF data grid with multi select (SelectedMode = Extended). Each item has an IsSelected binding per https://stackoverflow.com/a/2615487/284795
<DataGrid
ItemsSource="{Binding Items}"
SelectionUnit="FullRow"
SelectionMode="Extended"
SelectedIndex="{Binding SelectedIndex}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
I'm curious. How do the properties SelectedIndex and SelectedItem on the DataGrid now behave? If two items are selected in the data grid, which one does SelectedItem point to?
Also, if all these bindings are two way, and I make a change to one from a view model, will the others be updated? (I'm observing a bug in my app perhaps because of this)

I had the same question a while ago, and I checked: I created a DataGrid with several items and set the SelectedMode = Extended, and I mde a binding to both: SelectedItem and SelectedIndex properties.
The result was this: When you select a single item, and then you select other items and make a multiselect, the SelectedItem and SelectedIndex properties will be the first item that you selected. So when you make multiselects the selected item will be the first one you selected.
Also all other selected items will be in the SelectedItems collection, that is read only (like in the question you pointed said) and it is not a dependency property, so you can't make bindings to it. So if you want take all selected items, you need to handle the selection changed event of the DataGrid and then, manually, add and remove the new and old items from the collection that you want to keep (selected items).
Hope my answer be clear enough, and it could helps you...

Related

listview checkbox binding wpf MVVM

I am trying to get the bool value of the checkbox present in the Listview. I am binding to a bool public property "Assignm" in the view model. I tried the below binding pattern but the problem is if i select one checkbox it selects all checkboxes and vice versa. I think this is because relativesource is listview and it works on complete listview. I also tried changing the relative source to ListviewItem but that didn't trigger anything. Can someone help me please. Do i need to change something here ?
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Tag="{Binding MU_Identifier}" IsChecked="{Binding DataContext.Assignm, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}">
</CheckBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
Because your binding for IsChecked property is Assignm property which seems to be one property of your view model.
If there is a boolean property named Assignm for the data model of the DataSource of ListView, then just change the binding like this: {Binding Assignm}, as Tag property does.
All your items are bounded to a single property, so when one item changes a property in your context it changes on other items.
To provide correct work all your items from ItemsSource should have property IsChecked.
Check this Example

Combobox trigger not working to set selected index

I have a situation where i need to set SelectedIndex of my combobox to 0 in case the selectedItem gets deleted from the ItemsSource collection. A small sample replicates this situation well -
Xaml
<Grid>
<ComboBox x:Name="cmb" ItemsSource="{Binding Names}" VerticalAlignment="Top">
<ComboBox.Style>
<Style TargetType="ComboBox">
<Style.Triggers>
<Trigger Property="SelectedValue" Value="{x:Null}">
<Setter Property="SelectedIndex" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
<Button VerticalAlignment="Bottom" Width="100" Height="20" Click="Button_Click"/>
</Grid>
I haven't binded my SelectedItem or SelectedIndex to any value. On load, trigger works fine and set the SelectedIndex to 0 but on button click, i am deleting the current selectedItem from Names collection. Hence, the selected value is set to null. But the trigger not fires in that case and combobox comes as blank.
I know it can be solved in code behind by listening to SelectionChanged event and setting the SelectedIndex back to 0 there in case currentItem is null. But, i can't use that approach because of some restrictions. I want some pure xaml approach. Thanks in advance..
EDIT
Setting IsSynchronizedWithCurrentItem to True for combobox, prevents combobox to be empty but on removing the selectedItem from ItemSource collection, it set the next available value to be selected but i want my first item to be selected.
I think it's because you can't have the same value inside the trigger as what the trigger is based on, and essentially SelectedValue, 'SelectedItem', and SelectedIndex are just different ways to refer to the same item.
You'll note if you add a background color to the trigger, it gets applied, so the trigger is definitely working.
I'm not sure what restrictions you have which you say are preventing you from using SelectionChanged, however I often see people say stuff like that because they are new to the MVVM design pattern, and think it means an application should have no code-behind at all. That is incorrect.
Using MVVM means that there should be no business logic in the code-behind. UI logic is perfectly acceptable in the code-behind a View, and I would consider this to be UI logic because the only thing it is doing is altering the UI based on a UI value.
Since you are anyway deleting the selected item in the button_Click, Could you not accomplish what you want by setting the selectedIndex value to zero inside the Button_Click(sender, eventargs) handler after you delete the current selected item!

WPF Datagrid selecteditem = null in MVVM

I'm trying to work with a datagrid using the MVVM pattern. The problem is that whenever I change the VM property which is binded to SelectedItem to null, the View doesn't "deselect" the currently selected item. This is my binding in xaml:
<DataGrid Grid.Column="0" Grid.Row="0"
ItemsSource="{Binding Path=Users}"
AutoGenerateColumns="False"
CanUserAddRows="False"
IsReadOnly="True"
SelectedItem="{Binding Path=SelectedUser, Mode=TwoWay}">
The SelectedItem binding works from the view to the VM thus in the SelectedUser property I always have the selected object. The problem is that in the VM I'm doing some stuff which sometimes changes the SelectedUser property to null so I would expect the datagrid to deselect the row as well. Instead, it remains selected and if I try to click on the same row, the property doesn't update. If I click on any other row, the property changes as expected.
Is there a way to make the datagrid deselect if it's binded property is set to null? Also I'm looking for a MVVM solution as I don't want to write code behind. I can solve this by writing code behind so don't waste time offering such solutions :)
l.e.: this is my property in the VM:
public RPLUser SelectedUser
{
get
{
return selectedUser;
}
set
{
selectedUser = value;
OnPropertyChanged("SelectedUser");
}
}
Thanks in advance!
I recommend to check the Output Window in visual studio and see if any Binding is failing.
Are you sure when you select something, the selection updates into the SelectedUser property?
Did u put a breakpoint in setter of SelectedUser and see that it is hitting when you select something on the datagrid?
The reasons for this Binding to break could be many ...
SelectedUser is of different type than individual Users.
SelectedUser does not match by reference with any items in Users.
How and where are you setting null?
The following code in my case works perfectly fine...
<tk:DataGrid MaxHeight="200" AutoGenerateColumns="False"
ItemsSource="{Binding}"
SelectedItem="{Binding MySelItem,
ElementName=MyDGSampleWindow,
Mode=TwoWay}"
IsReadOnly="True">
<tk:DataGrid.Columns>
<tk:DataGridTextColumn Header="Key"
Binding="{Binding Key,
Mode=OneWay}"/>
<tk:DataGridTextColumn Header="Value"
Binding="{Binding Value,
Mode=OneWay}"/>
</tk:DataGrid.Columns>
</tk:DataGrid>
When I set MyDGSampleWindow.MySelItem as null, the datagrid propertly deselects. Perhaps you might need to give us more input on how are you actually setting the value as null.
Did you try setting IsSynchronizedWithCurrentItem="True" in the xaml properties for the DataGrid? AFAIK, this will allow you to unselect it by setting the SelectedUser to null.
I cannot test it at the moment, but you could also try to add this in the setter of your property:
set
{
selectedUser = value;
OnPropertyChanged("SelectedUser");
ICollectionView collectionView = CollectionViewSource.GetDefaultView(Users);
collectionView.MoveCurrentTo(selectedUser);
}
(For ICollectionView to do anything, you will need to have IsSynchronizedWithCurrentItem set)
Like I said, I cannot test this right now. Also, the setter of the property is probably not the best place to put it. Maybe create an event handler for the PropertyChangedevent locally and put that logic there.
Let me know if it helps, else I'll see if I can run a short test...
Yeah may need to add the XAML UpdateSourceTrigger to update the UI.
SelectedItem="{Binding SomeProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
The DataGrid will not Deselect it automatically as DataGridRow's IsSelected property should be set to False.
You can do that by Setting a style on DataGrid.. like
<Style x:Key="dataGridRowStyle"
BasedOn="{StaticResource {x:Type WPFToolkit:DataGridRow}}"
TargetType="{x:Type WPFToolkit:DataGridRow}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected}" />
</Style>
The IsSelected property should be the of the object i.e in your case RPLUser should have a property Isselected
Then before you set the SelectedUser to null... just do SelectedUser.IsSelected=False
And dont forget to attach this style to the DataGridRowStyle in Datagrid
I am using WPFToolkit you can modify the style if you are targetting .NET 4.0

WPF Binding IsSelected to ViewModel doesn't set items that haven't been shown in the List

I have a ViewModel that has an IsSelected property which I bind in my ListView.ItemContainerStyle XAML to an IsSelected property in my view model.
I bring up the application and populate the view model collection (which is shown in my ListView) with a lot of items, say about 2000. Then I select everything in the list via Ctrl-A. The items in my view model collection only get the IsSelected set for the items that are visible in the ListView. If I page down through the list the IsSelected gets set for whatever items get shown. If I page through all the items then all the items in my view model have the IsSelected property set to true.
Here is my XAML for binding the IsSelected in the list view to my view model:
<ListView Margin="5" ItemsSource="{Binding FilteredComparisonList}" x:Name="comparisonListView">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Source filename" DisplayMemberBinding="{Binding ImageFile.BaseFilename}" Width="Auto" />
</GridView>
</ListView.View>
</ListView>
Why isn't IsSelected for all the items in my view model set to true when I select all the items in the ListView?
The MVVM way to do this would be to override the Ctrl-A shortcut with your own SelectAll command (Create a SellectAll command with a shortcut of Ctrl-A). The implementation will set IsSelected on the view models.
Your IsSelected property in your view needs to have a two way binding to your view model so that the items appear selected in your view.
I dont think turning off virtualisation is nessesary.
Hope this helps.
This is happening because of the ListView's built-in virtualization. If you're not familiar with that, what it basically means is that the items don't become real until they are in view. You can turn off the ListView's virtualization with the following property:
VirtualizingStackPanel.IsVirtualizing="False"
But beware this will have an adverse effect on your ListView's performance. For 2,000 items it won't be severe, but it might be noticeable.
The ListBoxItem's are virtualised, they do not exist until they are generated (when they are brought into view). However the underlying ListView is derived from ListBox which has a SelectedItems property. The SelectedItems are a list of all the selected items not a list of the ListBoxItems that are selected. When the ListBoxItem is brought into view its IsSelected property is set based on if it's item (the item it displays) is in the collection of SelectedItems.
This is a propblem for your view model, especially if you need the virtualisation to be on (which you probably do for 2000 or so items). I would be interested in the answer to the question "How do i bind my view models IsSelected property based on if an item is in a ListBox's SelectedItems collection?"
the answer probably involves a converter
I had the same problem and that's what helped me finally:
Put in your ListView:
VirtualizingStackPanel.VirtualizationMode="Standard"
I'll post this answer that I found on a different forum since it helped me with the issue.
I have defined an interface ISelectable for my ViewModels to implement
public interface ISelectable
{
bool IsSelected { get; set; }
}
Then in MyCustomListView I have done this:
public class MyCustomListView : ListView
{
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<ISelectable>())
item.IsSelected = true;
foreach (var item in e.RemovedItems.OfType<ISelectable>())
item.IsSelected = false;
base.OnSelectionChanged(e);
}
}
Alternatively, you could do subscribe to the SelectionChanged event of the ListView and use the same code above.

WPF - prevent ListBox item selection

I would like to prevent selection of ListBoxItems in my ListBox. My DataTemplate has a checkbox and this should be the only thing the user can click or select.
How can I do that?
Thanks!
This is almost a duplicate question. In fact, you're asking two questions here:
Either style your ListBoxItem so that it doesn't show selection (look elsewhere on SO for that answer), or replace ListBox with ItemsControl if you don't need the other features that ListBox provides.
Bind your checkbox's IsChecked property to the parent ListBoxItem.IsSelected property:
<CheckBox
IsChecked="{Binding
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ListBoxItem},
Path=IsSelected}"
/>
When your user will try to (un)check your checkboxes then item become 'active' in some way. And focused style will be applied. As far as i know there is no way to disable selection(because if you did your checkboxes will not work) but you can override focused(or selected) style of your listbox items

Resources