ListView row remains selected after selecting new row - c#

The following xaml code produces a ListView with three columns. The ListView ItemsSource is an observablecollection. The first column shows the name of the object in a particular row. The second and third columns show buttons associated to the object in a particular row.
<Grid Width="497" Height="260">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="309*" />
<ColumnDefinition Width="188*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="478*" />
<RowDefinition Height="4*" />
</Grid.RowDefinitions>
<GroupBox Header="ObservableCollection Object List" HorizontalAlignment="Left" Width="497" BorderBrush="Black" BorderThickness="2" Grid.ColumnSpan="2">
<ListView ItemsSource="{Binding Path=CollectionList}" Name="QueueListView">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
</ListView.Resources>
<ListView.SelectedItem>
<Binding Path="SelectedObjectFromList" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" >
</Binding>
</ListView.SelectedItem>
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="Object Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="160" Header="Property Information">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Get Property Info" Command="{Binding Path=GetObjProperties}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="180" Header="Transfer">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Transfer Object" Command="{Binding Path=TransferObjHere}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</GroupBox>
</Grid>
The SelectCurrentItem event handler binds the ListView.SelectedItem to the SelectedObjectFromList property in my view model when the user clicks the "Transfer Object" or "Get Property Info"button. I an use this property to expose the selected object to my view model.
Here is my SelectCurrentItem handler c# code in code behind:
protected void SelectCurrentItem(Object sender, KeyboardFocusChangedEventArgs e)
{
ListViewItem viewItem = (ListViewItem)sender;
viewItem.IsSelected = true;
}
This works great! When the user has clicked a button, the SelectedObjectFromList property is updated from the ListView observablecollection object for that row. (No need to manually click the ListView row to set the property before clicking the button.)
One problem: As I click buttons in the list, the recently selected rows still appear to be selected (they are highlighted in the GUI).
I tried to solve the problem by setting the isFocused property of the ListView:
protected void SelectCurrentItem(Object sender, KeyboardFocusChangedEventArgs e)
{
ListViewItem Item = (ListViewItem)sender;
Item.IsSelected = true;
Item.IsFocused = true;
}
Of course that gives me a StackOverflow exeption :). Does anyone have example code to update the ListView.Selection in the GUI in this case? Thanks in advance.

ListViews allow you to have multiple items selected at once. When you click on a 2nd item, you're not resetting the IsSelected property of the old selected item to False, so it is staying selected
You can try either setting the ListView's SelectionMode to Single
<ListView SelectionMode="Single" ... />
or try getting the old SelectedItem and unselecting it when you're selecting the new one
protected void SelectCurrentItem(Object sender, KeyboardFocusChangedEventArgs e)
{
var temp = myListBox.SelectedItem as ListViewItem;
if (temp != null)
temp.IsSelected = false;
ListViewItem viewItem = (ListViewItem)sender;
viewItem.IsSelected = true;
}

Related

Binding some checkbox into combobox to a listView

I have an establishment collection that I display in a combobox. One of the properties is "IsSelected", which allows me to select multiple items in a combobox.
<ComboBox Name="CmbEtabTout"
ItemsSource="{Binding EtablissementsUtilisateur}"
Grid.IsSharedSizeScope="True"
Grid.Column="2"
Grid.ColumnSpan="3"
Grid.Row="2"
Height="25"
Width="250">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition SharedSizeGroup="AgentA" Width="auto" />
<ColumnDefinition Width="5" />
<ColumnDefinition SharedSizeGroup="AgentB" Width="auto" />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsSelected}" Grid.Column="0"/>
<TextBlock Text="{Binding IdEtablissement}" Grid.Column="1"/>
<TextBlock Text="{Binding Nom}" Grid.Column="3"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ListView x:Name="LVAgent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Agents}" Grid.ColumnSpan="2" Margin="150,0,42,0" Grid.Column="2" Grid.Row="4" Grid.RowSpan="5" >
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"
Command="{Binding DataContext.SelectAgentCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
<CheckBox IsChecked="{Binding SelectAllAgents}"
IsEnabled="True"/>
</GridViewColumn>
<GridViewColumn Header="Matricule"
Width="110"
DisplayMemberBinding="{Binding Matricule}"/>
<GridViewColumn Header="Nom"
Width="120"
DisplayMemberBinding="{Binding Nom}"/>
<GridViewColumn Header="Prénom"
Width="120"
DisplayMemberBinding="{Binding Prenom}"/>
</GridView>
</ListView.View>
</ListView>
My combobox collection :
private ObservableCollection<Etablissement> _EtablissementsUtilisateur;
public ObservableCollection<Etablissement> EtablissementsUtilisateur
{
get
{
return _EtablissementsUtilisateur;
}
set
{
if (value != _EtablissementsUtilisateur)
{
_EtablissementsUtilisateur = value;
RaisePropertyChanged(nameof(EtablissementsUtilisateur));
}
}
}
I'm trying to find out how to bind these comboboxes to refresh a list: if I choose three establishments, the list displays the agents of these three establishments. With a command maybe?
<CheckBox IsChecked="{Binding IsSelected}" Grid.Column="0"/>
Look like this :
Since I already bind my checkbox with "IsSelected" (used for SelectAll), I don't know how to bind to refresh the list of Agent behind, when I check, without having to press a button like "validate".
Edit : My problem now is if I want to do something like this, for exemple :
<CheckBox IsChecked="{Binding IsSelected}" Command="{Binding }" Grid.Column="0" />
I can only bind to the Etablissement Class and not to the ViewModel. (because of the itemSource of combobox i think)
the goal is, "when any checkbox is checked or unchecked, if I choose three establishments, the list displays the agents of these three establishments".
"when any checkbox is checked or unchecked" => property changed event handler
"if I choose three establishments" => if statement
"the list displays the agents of these three establishments" => method call
assuming Etablissement : INotifyPropertyChanged, we could add the event handler to each Etablissement.PropertyChanged. the other option is to add the handler to CheckBox.Checked and CheckBox.Unchecked.
bonus: event handlers can be async, so if "method call" is async, you're right at home to await it, meaning your UI remains responsive and does not lock up.
i would add the handlers inside a Loaded event for your UserControl or perhaps the ComboBox
Loaded += delegate
{
PropertyChangedEventHandler propertyChanged = delegate
{
//if number of checked items != 3
//return;
//update agents
};
foreach (var etablissement in EtablissementsUtilisateur)
etablissement.PropertyChanged += propertyChanged;
}

xaml UWP set focus/selection to first listview item

<ListView x:Name="listview" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.ZoomMode="Enabled"
ItemsSource="{Binding YourCollection}" DoubleTapped="listview_DoubleTapped" Tapped="listview_Tapped" SelectionChanged="listview_SelectionChanged"
GotFocus ="StackPanel_GotFocus" IsItemClickEnabled="True" ItemClick="ListView_ItemClick"
Margin="162,539,-103,11" Style="{StaticResource ListViewStyle1}" ScrollViewer.VerticalScrollBarVisibility="Disabled" Grid.RowSpan="2">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="130" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate>
<StackPanel Orientation="Vertical" Height="130" Width="192" >
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Height="108" Width="192" HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding Name}" TextAlignment="Center" Height="22" Width="192" Foreground="White" FontFamily="Assets/GothamLight.ttf#GothamLight"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Need to set the focus or selection to first item of the listview items. The listview contains an array of items, where the focus needs to be targeting first item during start and then retain the last chosen item.
There are multiple options here depending on your coding style. It looks like you're using code behind from our event handlers and binding to a view model class with YourCollection property, so I'll give you both examples. :)
Using code-behind
Update your XAML file to handle the Loaded event and name your ListView:
<Page Loaded="Page_Loaded">
...
<ListView Name="MyListView" ItemsSource="{Binding YourCollection}">
...
</ListView>
Then add the following code your Page_Loaded handler:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
if (MyListView.Items.Count > 0)
MyListView.SelectedIndex = 0;
}
Using view model
Provide a SelectedItem property in your view model (wherever you are defining YourCollection):
private YourItem_selectedItem = null;
public Dumb SelectedItem
{
get { return _selectedItem; }
set { SetProperty<YourItem>(ref _selectedItem, value); }
}
Then bind your ListView to the selected item, as you did with your ItemsSource:
<ListView Name="MyListView" ItemsSource="{Binding YourCollection}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
Finally, just set your SelectedItem after you've loaded your collection items.
This method also has the benefit of replacing your SelectionChanged and ItemClick events. You won't need them because the control changes SelectedItem by default in those situations.
I found a further solution that does not require the Page_Loaded handler nor the property SelectedItem in the ViewModel.
<ListView Name="yourCollectionListView"
ItemsSource="{Binding YourCollection}"
SelectedItem="{Binding RelativeSource={RelativeSource Self}, Path=ItemSource[0]}"/>
Of course you should ensure the list has at least one item. With VisualState.StateTriggers you can hide the ListView if it is empty.
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState.StateTriggers>
<StateTrigger IsActive="{Binding YourCollection.Count, Converter={StaticResource EqualToParam}, ConverterParameter={StaticResource Zero}}"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="yourCollectionListView.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
Define the zero value in the page's resources:
<x:Int32 x:Key="Zero">0</x:Int32>
EDIT
It is even possible to achieve this by binding the following to the SelectedItem property:
SelectedItem="{Binding Path=[0]}"

Unable to find selected index in ListView for windows phone

I am facing a trouble in getting item's index in Listview. I have a child element inside data template like the following:
<ListView x:Name="ptype_gw" Width="300" HorizontalAlignment="Left" ItemsSource="{Binding PropertyTypes}" >
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding title}" Style="{ThemeResource menu_heading_green}" Grid.Row="0" />
<GridView Grid.Row="1" ItemsSource="{Binding childs}" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<RadioButton Name="PropertyTypeRadio" Content="{Binding title}" Tag="{Binding}" GroupName="Types" Checked="RadioButton_Checked" Background="White" Foreground="Black" BorderBrush="Black" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now i Need to get the index of selected item whenever radio button is checked. I have tried the following code:
var item = (sender as FrameworkElement).DataContext;
int index = ptype_gw.Items.IndexOf(item);
But it didn't helped me.
That’s very simple.
Bind the SelectedItem of list to a property in view model. Say SelectedItem. When user selects an item, the SelectedItem property will hold the details of current selected item.
Now handle the radio button checked or unchecked event as per your requirement. When the event occurs, check the SelectedItem property.
I am talking about MVVM case.
If you are writing code in your xaml.cs itself, then get the selected property of listbox directly like this in the radio button checked event.
ListBoxName.SelectedItem
ListBoxName.SelectedIndex

Bind Popup on a Button in a GridView Column

I've a little Problem. On my Window, i've a GridView and on every Row of the GridView is a Button (The Button is Part of the GridViewColumn.CellTemplate). Now i want, that when i press the Button, a Popup should be open under the pressed Button.
But how i can bind the Popup on the Button in a Grid because the Button is dynamic (for every Row one Button).
<Popup Name="popup_Zuordnungen">
<controls:Anlagenzuordnung Grid.Row="3" x:Name="VertragsAnlagenPopup" Margin="0,20,0,0"> </controls:Anlagenzuordnung>
</Popup>
<ListView Grid.Row="1" Name="lv_Leistungserbringer" DataContextChanged="lv_Leistungserbringer_DataContextChanged" SelectionChanged="lv_Leistungserbringer_SelectionChanged" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:VertragsLeistungserbringerZuordnung}}, Path=DataSource}" Height="150" VerticalAlignment="Bottom">
<ListView.View>
<GridView>
<GridViewColumn Header="ID">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ID}" Width="40" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Bezeichnung">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Beschreibung}" Width="500" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Zuordnungen anzeigen" Name="cmd_Zuordnungen"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The Button is in the last Column.
Cab anywhere help me?
This is a Window with code-behind?
In that case, you can simply add a click event to your button (in the DataTemplate), and handle the popup placement in the event handler:
XAML:
...
<DataTemplate>
<Button Content="Zuordnungen anzeigen" Name="cmd_Zuordnungen"
Click="cmd_Zuordnungen_Click" />
</DataTemplate>
Code:
private void cmd_Zuordnungen_Click(object sender, RoutedEventArgs e)
{
popup_Zuordnungen.IsOpen = false;
popup_Zuordnungen.PlacementTarget = (Button)sender;
popup_Zuordnungen.IsOpen = true;
}

Remove ListItem from nested ListView on Button Click

I have a nested Listbox (collection of objects in the main object list) that needs to delete the underlying items.
When the item is removed, I could reset the ItemsSource of the main list, but the main list will have tons of items and each time a item is removed from its underlying collection the main scrollbar would be also reset, making users willing to kill me in a very painful way.
My question: How can i find the container of the item which button has been clicked and how can i find the item itself so i can kill the #&!$*&#$# (cursing onomatopoeia)?
Here's the XAML exemple of my lists:
<ListView Name="mainList">
<ListView.View>
<GridView>
<GridViewColumn Header="Column 1" />
<GridViewColumn Header="Column 2" />
<GridViewColumn Header="Column 3" />
<GridViewColumn Header="Collection column">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding BindingCollectionProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Grid.Column="0" Text="{Binding Item.Property}" />
<TextBlock Grid.Column="1" Text="{Binding Item.AnotherProperty}" />
<Button Content="remove" Grid.Column="2" Click="DeleteClickEvent" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
How's my DeleteClickEvent(object sender, RoutedEventArgs e) should be like?
You could have a command and pass it your item as parameter instead of a click handler:
<Button ... Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type YOURCUSTOMCONTROL}}, Path=DataContext.YOURCOMMAND}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />
If you want to keep your event handler you could use VisualTreeHelper.GetParent twice on the sender.
ListBoxItem item = VisualTreeHelper.GetParent(VisualTreeHelper.GetParent((DependencyObject)sender)) As ListBoxItem;
BindingCollectionProperty.Remove(item);

Categories

Resources