A am writing a WinRT application which utilises a Gridview to display some data. The Gridview has a SelectionMode of Extended so that as the user navigates the grid with the cursor keys the selected item moves with them (plus I have multi-select functionality)
The problem I'm experiencing is that if you navigate the grid using the cursor keys and have Ctrl pressed down, the selected item remains where is was and only the focus changes. My DataTemplate doesn't show the focused item so it's quite confusing to the user.
Is there anyway I can change this behaviour so that navigating the grid with Ctrl held down works in the same way as if it wasn't being held down?
The solution was quite simple in the end. Just create a GotFocus handler like this one:
private void SdxGridView_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is GridViewItem && !((GridViewItem)e.OriginalSource).IsSelected)
{
SelectedItems.Clear();
((GridViewItem)e.OriginalSource).IsSelected = true;
}
}
Related
I want to be able to highlight the selected item on my page because it's busy and there is a lot going on. The user will not have access to their mouse, only keyboard so currently they tab through buttons quickly and enter in to what they need to do (it's a fast data entry sort of app if you must know).
I want to be able to highlight the selected button (so when you tab through currently it will select a button but it isn't very noticeable, it just has a slight border around it when selected).
I know that you can use a focusEnter and focusLeave event, but I would like to avoid that if at all possible just because there are so many buttons on the page that I would have to have a ton of repetitive events with almost the same code.
You can and should use just two common event handlers for Enter and Leave events for all your buttons!
Use the sender param to access the buttons:
private void buttons_Leaveobject sender, EventArgs e)
{
((Button)sender).BackColor = SystemColors.Control;
((Button)sender).ForeColor = SystemColors.ControlText;
}
private void buttons_Enter((object sender, EventArgs e)
{
((Button)sender).ForeColor = SystemColors.Control;
((Button)sender).BackColor = SystemColors.ControlText;
}
Use your own ideas about how to highlight the focussed button; this is a bit excessive imo..:
Of course Button with FlatAppearance can do the highlighting all by themselves as they have separate Colors for their states.
I would suggest creating your own class derived from Button, and then handling the background painting yourself. That would allow you to play with the background look/color and/or the border effects.
I am creating a WinRT-flavored Windows Phone 8.1 app. It uses a GridView to show many tiles that users can tap to execute commands. Users can also hold a tile to enter ReorderMode for the GridView. I enable reordering when the user holds a tile by setting ReorderMode. Since each tile contains a button with a command bound to it, I also set IsEnabled = false for the button in each tile. This allows the user to reorder tiles without triggering commands.
My problem is re-enabling the button in each tile when the user exits ReorderMode. By default, a user can tap the back button to exit ReorderMode, but I have not found any event to listen to. Is there an event that fires when ReorderMode is disabled that I can listen for to re-enable the button in each tile? Subscribing to HardwareButtons.BackPressed does not work.
Subscribing to the back button doesn't work as the GridView eats that event before you see it. You have to bind to ReorderMode: ReorderMode="{Binding ReorderProperty, Mode=TwoWay}"
Then in the ReorderProperty setter, have code that handles the change.
I've came across this problem recently and my solution for reordering items with drag and drop with nice user experience was to:
Enable dragging when GridView item is double-tapped:
private void GridView_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{
if (sender is GridView)
{
(sender as GridView).ReorderMode = ListViewReorderMode.Enabled;
}
}
Disable reordering when Grid looses focus:
private void GridView_LostFocus(object sender, RoutedEventArgs e)
{
if (sender is GridView)
{
(sender as GridView).ReorderMode = ListViewReorderMode.Disabled;
}
}
My grid view XAML code was:
<GridView
ItemsSource="{Binding MyObservableCollection}"
AllowDrop="True"
CanDragItems="True"
CanReorderItems="True"
DoubleTapped="GridView_DoubleTapped"
LostFocus="GridView_LostFocus" >
Of course it is a solution for Windows Phone 8.1 GridView.
background
I have custom control, containing a Popup which contains a ListBox
Requirement
I need to close the popup and do some logic with the selected item when the user chooses an item. User chooses an item when :
he click's on the item with the mouse.
he selected and item through keyboard navigation (up/down keys) and clicks enter
Problem
I have implemented all the above, but my problem is with the events to listen to inorder to execute my logic.
If I execute my logic on the SelectionChanged event, It will not fire when the user clicks on the selected item, so I'm missing my first scenario.
If I execute my logic on the PreviewMouseLeftButtonDown It will fire before the selection changed so I don't know what the user has chosen. This is also why I can't use both.
I thought of listening on the ListBoxItem events to do this (How to capture a mouse click on an Item in a ListBox in WPF?) or firing a command from an implicit ListBoxItem style (WPF Interaction triggers in a Style to invoke commands on View Model) but they didn't work for me.
The best idea that I came up with, is to create some kind of a "post selection" MouseButtonDown event via behaviours or actions, but I am not sure how, or if this is even the way to go.
Any Idea how to create such a thing? Or is there a better solution for this?
The answer is to Bind to the ListBox.SelectedItem property and to handle the PreviewKeyDown event of your control. This way, you'll always know which item is the selected one and when the Enter key is hit:
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.
Register("SelectedItem", typeof(YourDataType), typeof(YourControl),
new UIPropertyMetadata(null, OnSelectedItemPropertyChanged));
public YourDataType SelectedItem
{
get { return (YourDataType)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
private static void OnSelectedItemPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
// User has selected an item
}
...
private void Control_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Return)
{
// User pressed Enter... do something with SelectedItem property here
}
}
UPDATE >>>
Ok, I think I understand your problem a bit better now. The simplest solution would be if you could slightly alter the requirement so that;
An item is selected with either the mouse or keyboard up/down keys
An item is chosen with the Enter key
That way, you'd always know the selected item when the user chooses. However, if you can't do that, can you handle the PreviewMouseLeftButtonUp instead of the PreviewMouseLeftButtonDown event? I'm not 100% sure, but I think that that would occur after the selection has been made.
When I am having a value item selected in my WPf DropDown Combo Box then navigating using keys Left and right arrow keys result in firing of selected changed event for each item.
How to overcome this problem
The most easy and suitable way I found to overcome this problem is as follows:
rather than using SelectedIndexChanged event I used on DropDownClosed event and all code that is wriiten earlier inside selected index changed moved to this event under a if condition that checks whether a item is selected or not. Like this.
private void OnCmbOperatorsListDropDownClosed(object sender, EventArgs e)
{
if (cmbOperatorsList.SelectedIndex != -1)
InsertText(cmbOperatorsList.SelectedValue.ToString());
//Do whatever u want with selected item
}
So in this way when i navigate through Arrow keys SelectedIndexChagned event will not fired or since i haven't used that event so it will not create any problem.
As per my knowledge this is not possible straight away. I could have implemented this in a kind of "selection simulated" manner.
Handle arrow keys on combobox dropdown in PreviewKeyDown event by setting e.Handled = true. So that usual navigation based selection wont happen.
Inthese handlers based on Keys, change the Background and Foreground of the previous or next item from the drop down list so that it will look as if its selected and highlighted.
Then perform selection of the item which curently has the "simulated selection background - foreground" when dropdown closes. After dropdown closure, revert the background and foreground style.
But thats just my way of doing it.
You can use the PreviewKeyDown event like
private void combo_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key.Equals(Key.Left) || (e.Key.Equals(Key.Right)))
{
((ComboBox)sender).SelectionChanged -= combo_SelectionChanged;
}
}
and if u want to attach that event you can add this PreviewMouseDown event.
This is what i tried and may not be a proper method of doing such cases
If the user click on a item in the listbox, the listboxItems_SelectedIndexChanged is called. But, even if the user miss an item and randomly clicks inside the listbox (not on items) the listboxItems_SelectedIndexChanged is still called.
How can I change this? I only want action on item click.
Note: removing the ability to navigate the application with keyboard is not a option.
I guess that in some cases you don't have enough list items in your control, therefore you have some space that you can click on and then SelectedIndexChanged is fired.
I guess you cannot dynamically resize the control to always fit the number of list items or else you wouldn't be asking this question.
Now, what should happen when the user click (selects) the same list item? Should some logic happen even though the selected index is the same (so when it was clicked the first time the same logic happend)?
If you require that selecting the same index more than once should be ignored then you could use the following hack:
Keep a variable at the form scope (the form containing the listbox control) and each time the selection index changes set that variable. Then use it later to check if the same selection has been made to ignore handling the event. Here is an example:
private int _currSelIdx = -1; // Default value for the selected index when no selection
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex == _currSelIdx)
return;
Console.WriteLine(listBox1.SelectedIndex);
_currSelIdx = listBox1.SelectedIndex;
}
It ain't pretty, but hey...whatever works!
Maybe SelectedIndexChanged is not the right place to put your logic, since it is triggered even when you change the selection with the keyboard.
I would use MouseClick instead, checking if the click occurred over the selected item, i.e. something like this:
private void listBox1_MouseClick(object sender, MouseEventArgs e)
{
if (listBox1.SelectedIndex < 0 || !listBox1.GetItemRectangle(listBox1.SelectedIndex).Contains(e.Location))
MessageBox.Show("no click");
else
MessageBox.Show("click on item " + listBox1.SelectedIndex.ToString());
}
This link may help, instead of double click, implement the same for single click
i want to detect an item double click in a winforms listbox control. [how to handle click on blank area?]