WPF, what kind of controls will handle MouseLeftButtonDown event? - wpf

I have a DataGridControl,and its cell's DataTemplate overwritten to TextBoxs,by clicking outside the cells but still on the DataGrid, I want the TextBox to lose Keyboard focus so that it can Commit the change, but it seems the DataGrid won't handle the MouseLeftButtonDown event, so I have to manually add a handler to the Grid,and in the handler:
e.Handled = true;
Keyboard.Focus( sender as UIElement );
to make the parent panel "focusable".
By using Snoop, I notice that controls like TextBox, Button are capable of handle MosueLeftButtonDown event, while Panels are not,event if set "Focusable" property to "True". Does anyone know the reason behind this, Thanks.
To simplify the situation: suppose we have a TextBox and a Button on a Grid:
<Grid Background="AliceBlue">
<TextBox Height="25" Margin="50" Text="abcd"/>
<Button Height="25" Margin="50,100,50,0"></Button>
</Grid>
when I click on the TextBox, it gets KeyBoard focus, when I click the blank area of the Grid, I want the TextBox to lose focus, the problem is Grid is not focusable compared with TextBoxes and Buttons.

The documentation for Panel shows that Panel can handle mouse events:
https://msdn.microsoft.com/en-us/library/system.windows.controls.panel_events(v=vs.110).aspx

Related

How to override PreviewKeyDown on a TextBox?

I have a handler for the PreviewKeyDown event on a TextBox inside a control I made, which checks to see if the user has pressed the down key. The event handler correctly handles the key press when the control sits inside a layout container like a grid. If however, I place the control inside a DataGrid's DataGridTempalteColumn, the control does not do what I need it to do.
I think the issue is that because the PreviewKeyDown is on a Tunneling strategy, the host DataGrid gets to handle the down arrow key press before my control does. For the down arrow the DataGrid moves the focus to the next row. The DataGrid doesn't seem to be setting the IsHandled to true, because it the event eventually gets down to my control, but it does nevertheless do its own thing on the event, which breaks things for me.
The issue isn't really with the DataGrid, but with the fact that my control has a tunneling PreviewKeyDown event from the TextBox. I'm looking for a way to override this default event on the TextBox. Perhaps there's something I can do with attached behaviors? Maybe I need to inherit from the TextBox and then override? So far I've not found anything that indicates how to handle a situation like this.
Below is the original text for this question that didn't yield any answers
I'm having difficulty using a custom autocomplete text box I made as a DataTemplate in a DataGridTemplateColumn.
<DataGrid.Columns>
<DataGridTemplateColumn Header="Material" Width="300">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<local:actextbox Text="{Binding Path=Description, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
MatchList="{Binding Path=DataContext.LaborTemplatesList, RelativeSource={RelativeSource AncestorType=UserControl, AncestorLevel=2}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Description}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
The actextbox class derives from user control and has event handlers to respond to certain key presses like so
private void myTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down & myPopup.IsOpen == true)
{
myPopUpList.SelectedIndex = 0;
ListBoxItem lbi = (ListBoxItem)myPopUpList.ItemContainerGenerator.ContainerFromItem(myPopUpList.SelectedItem);
lbi.Focus();
e.Handled = true;
}
}
The intent is that when an autocomplete popup is displayed, pressing down and up allows the user to navigate its contents. This works as expected when the control is placed in a hierarchy of layout containers; however, when it is part of the cell in a datagrid the expected behaviour is lost. Looks like the previewKeyDown is used by the DataGrid to apply its own interpretation of the down or up arrows, and while it does not set the event as handled, by the time the event gets down to my control focus is lost, and different row is selected.
I've looked online all over, and wasn't able to find any clues on how to handle this. Certainly, I've seen controls inside DataGridTemplateColumns handle all sorts of inputs, but how they accomplish this is lost on me.
OK, following some advice to use Snoop I figured out what was happening. In fact the issue was not that the DataGrid was doing something with the PreviewKEyDown event, but that I was moving focus away from the data grid cell that was presently being edited triggering a CellEditEnding event. This resulted in the behaviour I was observing.

Try to catch mouse event in HierarchicalDataTemplate

I'm fairly new to wpf and this website. Please give me some mercy if I show some mistakes.
My HierarchicalDataTemplate ,which is my treeview item, consists of multiple components: two textblocks , image , and checkbox with some stack panels for the layout. My MouseEventHandler, which is to catch when user click on textboxes, image , or checkbox , is TreeViewItem.Selected. But when I click on the tiny space between those components, it doesn't trigger TreeViewItem.Selected.
My first initial thought was that I might need to specify event handler on the stack panel which was for the layout of my HierarchicalDataTemplate . However, it didn't rise the event ,even though I made event handler on stack panel specifically.
Could you give me some guidance?
ps. I used binding for IsSelected property but it never notified to change its property
Set Background="Transparent" for topmost layout container inside your HierarchicalDataTemplate.
The following Grid doesn't raise MouseLeftButtonDown event:
<Grid MouseLeftButtonDown="handler" Width="200" Height="200">
</Grid>
But the following does:
<Grid MouseLeftButtonDown="handler" Width="200" Height="200" Background="Transparent">
</Grid>
This is because in the 1st case it doesn't have background and there's nothing to raise MouseLeftButtonDown event. So the event will be raised only if user clicks on some element inside that Grid.

Disable button when datagrid is in edit mode

I have usercontrol with a DataGrid binded to an observablecollection of items and two buttons: save changes and discard changes.
My problem is that when the user is editing a datagridrow the buttons remain clickable but aren't executed.
Is there a way to disable the buttons when the DataGrid is in edit mode?
I tried this code with no success:
<Button Content="SaveChanges" Command="{Binding Path=CmdSaveChanges}"
IsEnabled="{Binding ElementName=MyDataGrid, Path=IsEditing, Converter={StaticResource InverseBooleanConverter}}" />
You are binding to an IsEditing property of an element named MyDataGrid which presumably is a DataGrid. However, DataGrid has no such property.
Although DataGridCell has an IsEditing property, there is no easy way to get the currently editing cell. DataGrid.CurrentCell will not give you the DataGridCell, but only a DataGridCellInfo.
You are perhaps better of with attaching handlers to the DataGrid's BeginningEdit and CellEditEnding events.

"Modal Dialog" in WPF - make overlay block key events

I'm creating a WPF application containing a "Main-Content" - Layer containing a TabControl and a "Dialog" - Layer containing an ItemsControl.
The XAML looks like this:
<Grid>
<TabControl>
..Some Tabs
</TabControl>
<ItemsControl>
<ContentControl Content={Binding Dialog1Property} />
<ContentControl Content={Binding Dialog2Property} />
</ItemsControl>
</Grid>
Usually "Dialog1Property" and "Dialog2Property" are null which means the ItemsControl is invisible. Whenever I assign a Control to one of them, it is shown in front of the TabControl which is exactly what I want. If I assign a gray Rectangle with an opacity of 0.7 to one of the Dialog - Properties it creates a Gray overlay.
If I click on the Tab, which is slightly visible through the overlay, nothing happens - the Rectangle blocks Mouse Events. It is, however, still possible to focus the TabControl behind the overlay using the Tab-Key and therefore it is also possible to switch tabs even though a Dialog is shown.
Is there an easy way to tell the rectangle to somehow block key events as it allready does with Mouseclicks?
Regards
BBRain
Yes, on your Rectangle, subscribe to the event PreviewKeyDown.
<Rectangle Opacity="0.7" Fill="Green" PreviewKeyDown="Rectangle_PreviewKeyDown" />
In its handler, simply set e.Handled = true;
private void Rectangle_PreviewKeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
Since routed events prefixed with "Preview..." are tunneling, the elements under your rectangle won't recieve the input.

How do I set focus to a control within the ItemTemplate when the ListBoxItem is selected?

I have a ListBox which presents objects using a DataTemplate. The DataTemplate contains a TextBox. When the user selects an item in the ListBox, I would like to set focus to the TextBox for the selected item.
I have been able to partially achieve this by handling ListBox.SelectionChanged, but it only works when the user clicks on the ListBox to select the item - it does not work if the user tabs into the ListBox and uses the arrow keys to select the item even though TextBox.Focus() is invoked.
How can I set focus to the TextBox when the user uses the keyboard to select an item?
Here is the markup for the ListBox:
<ListBox Name="lb1" SelectionChanged="ListBox_SelectionChanged" ItemsSource="{Binding Items}" >
<ListBox.ItemTemplate>
<DataTemplate >
<TextBox></TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the handling code:
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBoxItem lbi = (ListBoxItem)this.lb1.ItemContainerGenerator.ContainerFromItem(this.lb1.SelectedItem);
Visual v = GetDescendantByType<TextBox>(lbi);
TextBox tb = (TextBox)v;
tb.Focus();
}
One way to do this is to replace the tb.Focus() from your SelectionChanged event handler with:
tb.Dispatcher.BeginInvoke(DispatcherPriority.Input, new ThreadStart(delegate()
{
tb.Focus();
}));
This works because calling BeginInvoke on the dispatcher causes the specified code to run when the dispatcher is available - i.e. after WPF has finished handling the events internally.
The catch is that, after you first press down arrow when a list item has focus, the next list item will became selected, its textbox will become focused and you will not be able to move selection again with the down arrow. You'll probably want to write some code to handle this case too.

Resources