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.
Related
I have a combobox that is editable and a textbox.
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="86,149,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="282,150,0,0" IsEditable="True" PreviewMouseDown="ComboBox_PreviewMouseDown"/>
I don't understand why ComboBox_PreviewMouseDown does not trigger, when the focus is on the textbox and I click on the combobox. It just highlights the text in the combobox and sets the focus. Clicking in the combobox when it already has the focus, PreviewMouseDown fires.
Is that what's happening here? Why is a PreviewMouseDown in an unfocused combobox not working?
When ComboBox.IsEditable is set to True, the ComboBox internally sets the focus (and keyboard focus) to the edit TextBox to make it instantly available for text input. This makes total sense as the intention when clicking the edit TextBox is always to enter or edit some text. Otherwise, the user would have to click the TextBox twice to make it receive focus for text input (keyboard focus).
So, to prevent focus stealing, the author marked the MouseDown event as handled i.e. RoutedEventArgs.Handled is set to true. (This is the reason why most non-preview events are marked handled by most controls).
Also, the author wanted to prevent the moving of the caret when clicked into the edit TextBox for the first time (to give it focus): the PreviewMouseDown event's RoutedEventArgs.Handled will only be set to true, if the edit TextBox has no keyboard focus and the drop-down panel is closed. (That's why the second click into the TextBox will pass through to be handled by an added event handler).
To achieve the behavior you expect, you have to handle the UIElement.PreviewGotKeyboardFocus event or the attached Keyboard.PreviewGotKeyboardFocusevent on the ComboBox.
Alternatively register the event handler using the UIElement.AddHandler method and set the handledEventsToo parameter to true:
this.MyComboBox.AddHandler(
UIElement.PreviewMouseDownEvent,
new RoutedEventHandler(MyComboBox_PreviewMouseDown),
true);
I ran into this same issue myself. A simple and effective workaround is to wrap your ComboBox in a lightweight ContentPresenter, then attach your PreviewMouseDown handler to that, like so:
<ContentPresenter x:Name="MyComboBoxWrapper"
PreviewMouseDown="MyComboBoxWrapper_PreviewMouseDown">
<ContentPresenter.Content>
<ComboBox x:Name="MyComboBox" />
</ContentPresenter.Content>
</ContentPresenter>
Additionally, since this control gets the PreviewMouseDown event before the ComboBox does, you not only can use it to pre-process events before the ComboBox even sees them, but you can cut off the ComboBox entirely by setting the event arg's handled property to 'true.'
Works like a charm! No subclassing or other tricks needed and it only requires a lightweight control in the tree!
Notes
As some may have considered, technically you could attach the PreviewMouseDown event to any ancestor of your ComboBox, but you then may have to include logic in that handler to determine if you're actually clicking on the ComboBox vs something else.
By using an explicit ContentPresenter (an incredibly lightweight element that itself doesn't have any rendering logic. It simply hosts other elements), you now have a dedicated PreviewMouseDown handler just for this control. Plus, it makes it more portable should you need to move it around since the two items can travel together.
I have a Pivot control. I want a special behavior -- everytime the user taps the header of currently chosen PivotItem, I want to react to that. However, this reaction can not happen when the tapped header does not belong to the currently selected pivot item.
My plan was as follows:
For each PivotItem create a custom header and associate its tap event with a handler:
<phone:PivotItem DataContext="{Binding}" ContentTemplate="{StaticResource MyTemplate}" Content="{Binding}" x:Name="itemA">
<phone:PivotItem.Header>
<TextBlock x:Name="headerA" Text="A" Tap = "HeaderA_Tapped"/>
</phone:PivotItem.Header>
</phone:PivotItem>
And in the handler, test whether the tapped item is currently selected, if yes, react:
protected void HeaderA_Tapped(object sender, GestureEventArgs e)
{
if (mainPivot.SelectedItem.Equals(itemA))
{
//selected item is the same pivotItem that reported tapping event
react();
}
}
It seemed pretty straightforward, but after giving it a try I found out that the tap event was reported only AFTER the selection changed event. In cases, where the user taps currently not selected pivotItem header, the pivot will change the selection accordingly (default behavior that I want to keep), and only then it reports the tap event. However, that is too late for my code, because in that moment the tapped header and currently selected item are already the same.
Is there any way how I can detect whether the tap initiated a selection change? Or is there a way to revert the order of events? I guess currently the WP event model sinks the event from the root of Visual Tree down to leafs -> therefore the Pivot gets to handle it sooner, and only then it gets to header TextBlock.
I think you should track Pivot SelectedIndex and update SelectedIndex in Selection_Changed event by using a Timer.
Some think like this:
int selectedIndex;
func Selection_ChangedEvent()
{
//use a timer to update selectedIndex
//something like: Timer.do(selectedIndex = Pivot.SelectedIndex, 100)
//fire after a specify time. adjust this for the best result.
}
And in your tapped event
HeaderA_Tapped()
{
if(selectedIndex == "index of item A in Pivot")
{
react();
}
}
Create Pivot selection changed event
<phone:Pivot Title="MY APPLICATION" x:Name="MainPivot" SelectionChanged="Pivot_SelectionChanged">
Add the event handlers to cs file
private void Pivot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (MainPivot.SelectedIndex)
{
case 0:
// do something
break;
case 1:
//do something
break;
}
}
For more details see this search
https://www.google.co.in/?gfe_rd=cr&ei=Y1XdVZruLqfG8AfUuquIAQ&gws_rd=ssl#q=wp8+c%23+pivot+selectionchanged
https://social.msdn.microsoft.com/forums/windowsapps/en-US/1baf74fa-0ddd-4226-a02d-a7fc9f80374d/pivot-static-header-like-twitter-app
OK, so after some read of my Windows Phone 8 Development Internals book by Andrew Whitechapel and Sean McKenna (do not ask why I did not do it sooner) I got a working solution.
I am not going into full fledged detailed discussion of events, just to point out conclusions relevant to my issue. It seems that in the WP8 event model there are at least two types of events, lower level routed events and logical touch gesture events. The routed events are routed through visual tree from the most concrete element (in my case the header TextBlock control) to the root (therefore Pivot should have get it later). Logical events are not routed, they appear only on a single element (e.g., my TextBlock control), and therefore their handlers have to be registered on the particular element. I assume that the logical events are raised by the element based on the lower level routed event. Applied to my case, it seems that first the lower-level MouseLeftButtonDown (or button up) event was raised, routed to Pivot that used it to change selection, and only then my TextBlock control created the logical Tap event. This would result in behavior that I observed on my emulator. I am not sure about the way how the routed events are really routed and how the logical events are created, so if I made a mistake in my conclusions, please correct me. However, I was able to solve my problem.
Using aforementioned assumptions I decided to listen to lower-level routed events instead of logical tap gesture. Using this MouseLeftButtonUp event I was notified before the new PivotItem was selected, thus giving me chance to check whether the raised MouseLeftButtonUp was originated by currently selected PivotItem.
And so finally the solution:
My XAML Pivot definition (notice that now I am handling MouseLeftButtonUp events on the TextBlock controls, instead of logical Tap events):
<phone:Pivot Title="Pivot" Name="mainPivot">
<phone:PivotItem DataContext="{Binding A}" ContentTemplate="{StaticResource MyTemplate}" Content="{Binding A}">
<phone:PivotItem.Header>
<TextBlock Text="A" MouseLeftButtonUp="HeaderHandlerA"/>
</phone:PivotItem.Header>
</phone:PivotItem>
<phone:PivotItem DataContext="{Binding B}" ContentTemplate="{StaticResource MyTemplate}" Content="{Binding B}">
<phone:PivotItem.Header>
<TextBlock Text="B" MouseLeftButtonUp="HeaderHandlerB"/>
</phone:PivotItem.Header>
</phone:PivotItem>
</phone:Pivot>
Since I have a different handler for each PivotItem header, I can simply test selected index with a constant. The corresponding handlers in the code-behind:
private void HeaderHandlerA(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// HeaderHandlerA is allways called within the first PivotItem,
// therefore I can just test whether selected index is 0 (index of the first PivotItem)
if (mainPivot.SelectedIndex == 0)
{
OnSelectedItemTapped();
}
// else the "tapped" header was not the selected one and I do nothing
}
private void HeaderHandlerB(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (mainPivot.SelectedIndex == 1)
{
OnSelectedItemTapped();
}
}
Maybe not the most elegant solution, but it works...
I'm working on my first Universal App (I'm beginner with it).
In app I have few places, where I want to select ListViewItem, and remove/edit (http://i.imgur.com/ZRkIQHm.png) it by some button. I'm not a designer, so I want to choose some simple, good-looking solution how to display these buttons.
I was inspired by Windows 10, there are few places, where you click on ListViewItem, and this selected item shows some buttons (Printer setting, WiFi connection, Bluetooth connection) - like on this image - http://i.imgur.com/WjerA5F.png.
I've spend lot of time with googling, trying, and now I have no idea how to do it. I was closest probably with using VisualStateManager in ListViewItemContainer.
Here is code example (in c# is filled ItemsSource by some ObservableCollection<>):
<ListView Name="dbList" Grid.Column="0" SelectionChanged="dbList_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Name="XXX">
<TextBlock Text="{Binding}"/>
<Button Name="removeDatabaseButton" Click="removeDatabaseButton_Click" Content="Remove" Tag="{Binding}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Thanks!
PS: Sorry for my english :)
The answer is quite complicated:
You need to modify your list Type Class which should implement the INotifyPropertyChanged interface which has the property Visible.
private Visibility _visible;
public Visibility Visible //Binding to this in Listbox - Button Visibility
{
get { return _visible; }
set { _visible = value; } //Call OnPropertyChanged method
}
Now create a listview_itemclick() method. Call the listview.selectedindex and set the visibility of the listitem property "Visible" to Visible - the event PropertyChanged will fire and the ui gets notified that the visibility of this specific button should change.
Helping links:
INotifyPropertyChangedInterface
Playlist to MVVM tutorials - containing INotifyPropertyChanged
I have two TabItem's contained inside a TabControl.
Each TabItem contains serveral TextBox's.
When TabControl's OnSelectionChanged event is fired, as well as selecting the new TabItem, it is also setting focus on the first TextBox contained inside the newly selected item.
Is there any way to prevent this from happening?
Setting IsTabStop="False" on the TextBox will achieve this, but unfortunately also prevents the TextBox from being 'tabbed' into.
In your tab control, handle the focus event for each of the tabs like this:
<TabItem GotFocus="TabItem_OnGotFocus">
Then just remove focus using:
private void TabItem_OnGotFocus(object sender, RoutedEventArgs e)
{
Keyboard.ClearFocus();
}
Just add a container to your content as Grid, Stackpanel, Border, etc. and set it Focusable. When Tab selection change, the focus is set to the container and you can also use the tab key.
<TabItem Header="myHeader">
<StackPanel Focusable="True">
...
</StackPanel>
</TabItem>
#shannon it answers to your question about MVVM
When I tab into a ListBox control, the first item gets focused. When I have a label and set the target property to the ListBox (as shown in the code below) and then use the dedicated Alt shortcut then it will focus not the first item but the listbox itself (listbox border becomes dotted). What is the best way to avoid this unwanted behavior? Is there a way to disable focusing on the listbox itself and only allow focusing on the items?
Example code:
<Label Content="_Label" Margin="0,10,0,88" Name="MyLabel" Target="{Binding ElementName=MyListBox}" Height="Auto" />
<ListBox Width="100" Name="MyListBox" Margin="46,0,639,0" />
Behavior:
By setting Target you explicitly asked focus to move to listBox. In case you want to put it on first listBox item, you have to do it manually.
One way would be to hook GotFocus event and set focus to next available item using TravelRequest object which wil put it on first listBox item.
XAML:
<ListBox Width="100" Name="MyListBox" Margin="46,0,639,0"
GotFocus="MyListBox_GotFocus"/>
Code behind:
private void MyListBox_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource == sender)
{
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
MyListBox.MoveFocus(request);
}
}