WPF ListView handle single and double click differently - c#

I have a list view with some items, and a Style that specifies that when an item is clicked, the IsSelected property will change. See an excerpt of the code below.
Currently this selects/highlights an item and performs an action related to that item when clicked on in the list. Now I want to change the behaviour so that this happens on double click instead of single click. In addition, when an item is single clicked, that item should also be selected/highlighted, but the action related to that item is not performed, so that the user can select several items by single clicking them (similar to how you would do ctrl-click usually) and then performing all actions at once when the desired items are selected and some button clicked.
I should also mention that I am using the MVVM pattern, so there is no code in the code-behind file. Any code would go in the ItemListViewModel.cs file.
I suspect I would have to use Command in some way? I also found an attribute for ListView called MouseDoubleClick="", however this would not replace the single click functionality, only come in addition to it.
Does anyone have an idea how to make this work?
<ListView Grid.Row="0" x:Name="ItemsList"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
ScrollViewer.IsDeferredScrollingEnabled="True"
ItemsSource="{Binding FilteredItems}"
SelectionMode="Extended">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="IsSelected" Value="{Binding Selected, Mode=TwoWay}"/>
</Style>
</ListView.Resources>
</ListView>

For the selection behavior, take a look at SelectionMode. You can set ListView.SelectionMode to Multiple, which sounds like the behavior you're describing.
For the double-click, you can use the MouseDoubleClick event, or you can try using a MouseBinding which lets you use ICommands instead of events (the page on InputBinding has some good examples).
You'll have to test to see how everything plays together, though. Handling both single and double click can be tricky, and you might be forced to handle the PreviewMouseDown event and do everything manually (worst case scenario).

Related

Customize DragUI of ListViewItem (UWP)

ListView offers a DragItemsStarting event that comes with according event args. However, unlike DragStartingEventArgs - common to other elements - it does not offer a DragUI, as far as I can tell. My only option is to use DragOver event which is very annoying.
So, I instead said I'll make the ListViewItem's content draggable. However, that backfired because now the Click event doesn't get through anymore, or only very rarely. Simply put, I either can customize the DragUI and not click my ListItems, or the DragUI looks bad, but I retain my functionality.
Is it possible to get a custom DragUI and have the click be handled by the ListView?
You can still utilize DragStarting, but you have to add it to the item content itself within your DataTemplate:
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid CanDrag="True" DragStarting="ItemDragStartingHandler">
...
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If that causes the click event not working properly, you could try customizing the ListViewItem container itself, which might work. Right-click the ListView in the designer or the Document Outline window, select Edit Additional Templates, and then Edit Generated Item Container. From the last menu select Edit a Copy...
You will get a Style which might be quite long, but within it you will find the the following:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ListViewItemPresenter DragStarting="ItemDragStartingHandler" x:Name="Root" ...>
You can apply DragStarting event to the ListViewItemPresenter and that might satisfy your needs.

How to Change border of selecteditem

I have one main window.xaml, inside which i have referred Viewmodel and another xaml page called Template.xaml.
Mainwindow.xaml will be some thing like below:
<Window ...>
<Window.Resorce ../>
<Window.DataContext>
<local:Viemodel/>
</Window.DataContext>
<Grid...
<local:Template/>
</Grid>
</Window>
And this is my Tempaltes.Xaml:
<ItemsControl HorizontalAlignment="Left" ItemsSource="{Binding Path=ControlsList}" KeyboardNavigation.IsTabStop="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="950"
Margin="28"
KeyboardNavigation.IsTabStop="False"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Name="Mybutton" Height="200" Background="Red" Command="{Binding Path=SupportButton}">
.....
In tha above block i have used binding in 3 places.
1. Item Controls
2. Button
3. Image
And here the command binding was not working for "MyButton". What may the reason behind this?
I need to change the background of button once the particular item is chosen. How to achieve that?
Under the button properties there is additionally a tab for actions or action methods. You'll want to find the one that is a button clicked event.
Once you have created an event handler from that action you will have the ability to change the background or foreground of the button.
Not 10% sure what you're trying to achieve here, but perhaps a ListBox would work better for you?
It'll automatically change the background of the item while it's selected and remove the background when another item is selected. If you want to perform an action when the selected item changes you could bind to the SelectedItem property.
Just a thought...
It's difficult to tell from the question but it sounds like using a ToggleButton may be appropriate in your case. If you go that route, you could simply bind the IsChecked property to your View Model, and your property setter could perform any command-like logic.
And of course you could style the ToggleButton any way you like, so the IsChecked state could look relatively different from the Normal state.

GridView in UWP

I are facing issue with GridView Control. We had a working Windows Store App on 8.1 where GridView left and right mouse clicks had different functionality. In the case of left mouse click, we used to use “ItemClick” event which performs navigation to another XAML page. On right click of GridItem, it gets selected and shows the appbar, we have used “SelectionChanged” event for this.
We are now migrating our existing windows store app to UWP Application, we have used same gridView Code, we find significant difference in functionality and look & feel, we don’t see GridView Item Selected like above picture. We see “ItemClick” and “SelectionChanged” are working together. The flow is something like that on left click on the item, the control goes to SelectionChanged event and then ItemClick. We were not able to differentiate actions like Left Mouse Click and Right Mouse click, since both events are getting fired up upon clicking on left click/tapping. We have different functionality on left and right clicks of mouse.
Need help on how to mimic windows 8.1 functionality in UWP.
My requirement was the I wanted to use Right Click/Long Tapped to select an item and take an action accordingly from App Bar Buttons and on Left Click/Tap should redirect me to the next XAML Page. The problem I was facing was the on Right Click, I wasnt able to detect that which items of GridView has been clicked and how can I add that into SelectedItem.
What I did was, I introduced extra Grid in DataTemplate of GridView. Within this Grid, I added RightTapped event.
The sample code snippet is
<GridView x:Name="ItemGridView"
ItemsSource="{Binding Source={StaticResource ItemsViewSource}}"
IsItemClickEnabled="True"
SelectionMode="Single" ItemClick="ItemGridView_ItemClick"
SelectionChanged="ItemGridView_SelectionChanged">
<GridView.ItemTemplate>
<DataTemplate>
<Grid RightTapped="Grid_RightTapped">
<Border Background="White" BorderThickness="0" Width="210" Height="85">
<TextBlock Text="{Binding FileName}" />
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The event name is Grid_RightTapped. This helped me detect that from which GridViewItem, I got the long tap/right click.
The code-behind for this is:
private void Grid_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
Song selectedItem = (sender as Grid).DataContext as Song;
//the above line will get the exact GridViewItem where the User Clicked
this.ItemGridView.SelectedItem = selectedItem;
//the above line will add the item into SelectedItem and hence, I can take any action after this which I require
}
}
The reason we are doing this way is, because now we can add clicked item into the GridView SelectedItem using Right Click. Now in UWP, clicked items are added into SelectedItem using left click only. And with left click, I can navigate to another page using ItemClick event.
You are correct, there has been a change in the interaction model behavior. According to MSDN article How to change the interaction mode (XAML)
For selection, set IsItemClickEnabled to false and SelectionMode to
any value except ListViewSelectionMode.None and handle the
SelectionChanged event (ItemClick is not raised in this case).
For invoke, set IsItemClickEnabled to true and SelectionMode to
ListViewSelectionMode.None and handle the ItemClick event
(SelectionChanged is not raised in this case).
Another combination is to set IsItemClickEnabled to false and
SelectionMode to ListViewSelectionMode.None. This is the read-only
configuration.
A final configuration, which is used least often, is to set
IsItemClickEnabled to true and SelectionMode to any value except
ListViewSelectionMode.None. In this configuration first ItemClick is
raised and then SelectionChanged is raised.
You seem to be using the last option - IsItemClickEnabled is set to true and SelectionMode is set to something that's not None. According the Microsoft, this is used least often so maybe it would be a good idea to rethink this design?
Since you haven't shared any code that you already tried, I will just throw in one idea: maybe playing around with Tappedand RightTapped event handlers could help you differentiate between the two more easily?
To identify left and right click, for right click you can use RightTapped event
<GridView x:Name="categoryItemsGV"
Margin="5,5,0,0"
IsItemClickEnabled="True"
ItemClick="categoryItemsGV_ItemClick"
IsRightTapEnabled="True"
RightTapped="categoryItemsGV_RightTapped"
SelectionMode="Single"
SizeChanged="categoryItemsGV_SizeChanged"
ItemsSource="{Binding}">
and .cs code is below:
private void categoryItemsGV_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
var tablemod = (sender as GridView).SelectedItem;
}
From RightTapped the item over which the mouse was right clicked can be obtained from e.OriginalSource
<GridView x:Name="myGridView" VerticalAlignment="Center">
<GridView.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Reset"/>
<MenuFlyoutSeparator/>
<MenuFlyoutItem Text="Repeat"/>
<MenuFlyoutItem Text="Shuffle"/>
</MenuFlyout>
</GridView.ContextFlyout>
</GridView>
Private Sub myGridView_RightTapped(sender As Object, e As RightTappedRoutedEventArgs) Handles myGridView.RightTapped
myGridView.SelectedItem = e.OriginalSource
End Sub
Now that RightClick has selected the desired item, further action like delete, copy can be executed on it.

How to correctly use a button in a listbox itemtemplate / datatemplate?

I have a Silverlight application that displays a list of items in a ListBox. Each item represents a different 'page' of my application so I have a style that is applied to the ItemContainerStyle property that looks like this:
<Style x:Key="navigationItemContainerStyle" TargetType="ListBoxItem">
<Setter Property="Margin" Value="5,3"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Cursor="Hand">
<VisualStateManager.VisualStateGroups>
<!-- code omitted --!>
</VisualStateManager.VisualStateGroups>
<Border x:Name="contentBorder"
Background="{StaticResource navigationHighlightBrush}"
CornerRadius="3"
Opacity="0"/>
<ContentControl x:Name="content"
Margin="10,5"
Content="{Binding}"
Foreground="DarkGray"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The style is very simple. It simply displays the Border when the ListBoxItem's visual state is equal to 'Selected'. Notice that the content is hosted by a ContentControl as I want to be able to change the Foreground property when the item is in the 'Selected' state.
This works brilliantly as can be seen by the screenshot below:
Now I want the selected item to invoke navigation so the idea I had was to create a DataTemplate that sets the content of each item to a HyperLinkButton:
<DataTemplate x:Key="navigationListBoxItemTemplate">
<HyperlinkButton Content="{Binding}"
Background="Transparent"/>
</DataTemplate>
Now this doesn't work as the ItemTemplate hosts it's content in a ContentControl rather than a ContentPresenter so I have had to update the ListBoxItem template to use a ContentPresenter instead.
<ContentPresenter x:Name="content" Margin="10,5"/>
I now get the following result:
When I click on the HyperLinkButton the ListBoxItem is now no longer selected (I can click just outside the HyperLinkButton and the ListBoxItem becomes selected). What I really want is for the ListBoxItem to become selected when the HyperLinkButton is clicked. The HyperLinkButton has no concept of selection so I cannot bind to the ListBoxItem's IsSelected property.
Note: The actual navigation works perfectly, the problem is purely with the appearance of the ListBoxItems.
So my questions are:
Can I make it so that when the HyperLinkButton is clicked, the ListBoxItem becomes selected like the first image?
I will also need some way of changing the foreground of the HyperLinkButton when it is selected as this is no longer done in the items template due to me swapping the ContentControl fro a ContentPresenter.
Note: I could probably solve this problem by binding the SelectedItem property of the ListBox to my viewModel and handling the navigation there negating the requirement to have a HyperLinkButton hosted by each ListBoxItem but I am interested in knowing if it is possible using styles and templates to achieve my desired result.
Update
I have tried a couple of things to try and resolve this but so far have been unsuccessful. The first thing I tried was applying a new style to the HyperLinkButton control in my DataTemplate which essentially removes all of the default look and feel from the control but my application still behaves in the way described above.
The second thing I tried was setting the IsHitTestVisible property to false. This allows me to click 'through' the HyperLinkbutton and select the ListBoxItem but this means that the navigation is now no longer invoked.
The ListBoxItem is not selected because the Button (whatever type it is) marks the MouseLeftButtonDown event as Handled. Therefore the required event does not bubble up to the parent ListBoxItem.
Evidently, your ListBoxItem is getting focus from the click (I assume that is the border in your final image), so this must happen regardless.
Under the covers, the ListBoxItem will have a standard LeftMouseButtonDown event handler set up to deal with Selection, and it must have a call to AddHandler to deal with setting focus.
You can achieve something similar by adding your own handler for the event, like so:
listboxitem.AddHandler(UIElement.MouseLeftButtonDownEvent, new System.Windows.Input.MouseButtonEventHandler(MyMouseLeftButtonDownEventHandler), true);
The final parameter instructs the handler to handle handled events...
Attaching this handler I leave to you, but using a behavior is probably the most straight forward. You could event derive a type from Button and have it walk up the Visual Tree to find and select the ListBoxItem... The possibilities are endless.

How to use Pivot Effectively?

I have following data structure in my WP7 app. And I'm generating three PivotItems through databinding, contents of binding. Interesting part is when Binding happens for Pivot items contents(Items) are queried for three time and again selection changes.
Is there anything I'm doing wrong?
Code:
<controls:Pivot Title="{StaticResource ApplicationName}" ItemsSource="{Binding Folders}" SelectedItem="{Binding SelectedFolder, Mode=TwoWay}" Name="_pivot">
<controls:Pivot.ItemTemplate>
<DataTemplate>
<ListBox DataContext="{Binding Source={StaticResource Locator}}" ItemsSource="{Binding ThingsListViewModel.Items}" />
</DataTemplate>
</controls:Pivot.ItemTemplate>
I have three folders items, when Pivot control is created ThingsListViewModel.Items property executed thrice, and once every time selection changes.
I'm expecting ThingsListViewModel.Items to execute only selection chage on Pivot control.
I think you need to listen to the LoadedPivotItem and Loaded events of the pivot. The Loaded event will always load the first PivotItem (LoadedPivotItem) will not be called. The LoadedPivotItem is called when the user swipes to another PivotItem.
Based on these events you should then run your queries for the currently SelectedPage. You may also want a flag to indicate once you have loaded the data for each Pivot to avoid running the queries again.

Categories