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

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.

Related

Wpf Mouse event set on canvas, but targets child object

I have a Canvas that contains a Rectangle. On that canvas, I bind a mousedown event to a command on the ViewModel. In that command, I am being passed the MouseEventArgs, but there the Target element is either the Canvas or the Rectangle. Where can I find in the MouseEventArgs the Canvas this event was fired from?
My code is more or less:
<Canvas Background="White">
<i:EventTrigger EventName="MouseLeftButtonDown">
<local:InteractiveCommand Command="{Binding CmdMouseLeftButtonDown}"/>
</i:EventTrigger>
<Rectangle Width="50" Height="50" />
</Canvas>
And in the ViewModel:
ICommand CmdMouseLeftButtonDown => new DelegateCommand<MouseEventArgs>(e =>
{
e.??? // <= Where do I find the Canvas here, whether I click on the Rectangle or Canvas?
}
Please do not answer with some hackish solution like e.MouseDevice.Target.Parent. This needs to work however complicated the element in the canvas is. It could contain another canvas for instance.
A view model is not supposed to have a reference to a UI element such as a Canvas or a Rectangle at all in the first place. This effectively breaks the MVVM pattern and that's why it makes no sense to pass the sender argument to the command.
You might as well get rid of the EventTrigger and invoke the command programmatically from the code-behind of the view:
<Canvas Background="White" MouseLeftButtonDown="Canvas_MouseLeftButtonDown">
<Rectangle Width="50" Height="50" Fill="Red" />
</Canvas>
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var yourViewModel vm = DataContext as YourClass;
vm.CmdMouseLeftButtonDown.Execute(sender as Canvas); //<-- pass the Canvas as a command argument or create a new command argument type that holds a reference to the Canvas
}
This is certainly not any worse than your current approach as far as the MVVM pattern is concerned. You are still invoking the very same command from the very same view and MVVM is not about eliminating code. It is about separation of concerns.
Your MouseEventArgs.Source will reference to the Canvas in any case but the MouseEventArgs.OriginalSource will referece to the Rectange if you have clicked on its area. It will be the control determined by pure hit testing.
Set <Canvas Background="Transparent" ... />
as answered in the following question by #Rob Fonseca-Ensor:
WPF: Canvas mouse events not firing on empty space

WPF, what kind of controls will handle MouseLeftButtonDown event?

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

How to implement popup in Windows Phone

I'm implementing a templated control, which should work as virtual keyboard button - when you hold it, it displays a popup with additional options to choose.
I've implemented the popup more less in the following way:
<Grid>
<Border>Content</Border>
<Grid x:Name="gPopup" Visibility="Collapsed">
<StackPanel x:Name="spSubItems" Orientation="Horizontal" />
</Grid>
</Grid>
I show the popup by changing visibility to visible and setting negative margins for top and bottom. However, when I do that, and when the popup is actually larger than the control, the control is being resized to match its size - despite fact, that it is not inside:
How can I implement the popup, such that it won't expand the container it's on? And such that the container will still match size of its contents?
Edit: In response to comments and answers
I'm not sure if I'm understood correctly. Here's an image with explanation:
I'd like to keep the original container's size the same after showing the popup. I'm unsure how WrapPanel or DockPanel could help me with that.
The solution is simply to use Popup instead of positioned Grid.
Sample- Create a grid
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!-- Setting a Rectangle having transparent background which set the
Visibility of popup -->
<Rectangle Name="popupRect" Fill="#80000000" Visibility="Collapsed"/>
<!—Here in the above Code we are just filling the rectangle With the transparent BackGround -->
<!—Creating A Border -->
<Border Name="popupBorder" Background="{StaticResource PhoneChromeBrush}" BorderBrush="Red" BorderThickness="2"
HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Collapsed">
<!-- Creating A grid Inside the Border and Rectangle -->
</Grid>
Create event for which popup should appear(for both cancel and appear)-
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
popupRect.Visibility = Visibility.Collapsed;
popupBorder.Visibility = Visibility.Collapsed;
}
private void popupButton_Click(object sender, RoutedEventArgs e)
{
popupRect.Visibility = Visibility.Visible;
popupBorder.Visibility = Visibility.Visible;
}
It will work, I guess.
Like spook says, put your gPopup Grid in a Popup element and show it by opening the popup. This won't affect the main visual tree.
The reason the embedded grid embiggens the border is that the outer grid has to expand to hold pGrid and the border expands to fill the outer grid.

What do I need to do to get a ListView's selection behavior in WPF to catch up with WinForms?

There are basically two things I am trying to activate or implement:
1. Clearing selection when background is clicked:
Typically, when one clicks in a blank area of a list view type control, such as windows explorer, any selected items become unselected. This is not happening for me in either multiple or extended selection mode. Do I have to manually handle the mouse click event to clear the selection, or is it perhaps not behaving as expected because I've applied a background to the control?
2. Selection rectangle with automatic scrolling:
Before porting my application to WPF, the standard WinForms listview allowed me to drag a selection rectangle and it would select any items it intersected. If there were items scrolled out of view in any direction, dragging in that direction would result in the control automatically scrolling into that area as I dragged the mouse, so I could selected items that are out of view. Does the WPF ListView implement this feature, or am I going to have to implement it myself? Someone posted a non-trivial implementation involving hittests in the comments on this page (http://social.msdn.microsoft.com/Forums/vstudio/en-US/191af722-e32b-4e6d-a00b-9ad2b53ea3b9/listview-dragging-a-selection-box-around-items?forum=wpf), but it doesn't even support the autoscrolling and I'm having a hard time believing Microsoft just left this feature out.
Really ListView has no default appearance and you had to do set even basic selection with trigger.
Wow, this colors and displays the SelectedIndex without a single style or trigger.
All in XAML
<Window.Resources>
<sys:String x:Key="MyString">Hello</sys:String>
<x:Array x:Key="MyStringArray" Type="sys:String">
<sys:String>Hello</sys:String>
<sys:String>World</sys:String>
<sys:String>Continent</sys:String>
<sys:String>Universe</sys:String>
</x:Array>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<ListView ItemsSource="{StaticResource MyStringArray}" x:Name="lv" SelectionMode="Single" LostFocus="lv_LostFocus">
</ListView>
<TextBlock Text="{Binding ElementName=lv, Path=SelectedIndex}" />
<Button Content="Take Focus"/>
</StackPanel>
</Grid>
private void lv_LostFocus(object sender, RoutedEventArgs e)
{
lv.SelectedIndex = -1;
}

Why I cannot drop files from explorer to FlowDocumentReader and how to fix it?

I'm trying to implement a piece of functionality that will let the user to drag files into an application to be opened in the FlowDocumentReader.
My problem is that is though I have AllowDrop=true on the FlowDocumentReader, the cursor does not change to the "drop here" icon but changes instead to "drop is not allowed" icon.
This happens only to the FlowDocumentReader, all other parts og the UI (window itself, other controls) work as expected. The FlowDocumentReader actually receives the events, and it is possible to handle the drop, but the user does not have a visual indication that he can release the mouse here.
I also cannot hide the "drop is not allowed" cursor by setting Cursor=Cursors.None
Need to handle DragOver event in FlowDocument to allow dropping here.
xaml:
<!--
<FlowDocumentReader x:Name="fdr" Background="White">
<FlowDocument x:Name="doc" AllowDrop="True" DragEnter="doc_DragOver" Drop="doc_Drop" Background="White"/>
</FlowDocumentReader>
-->
<FlowDocumentReader x:Name="fdr" Background="White">
<FlowDocument x:Name="doc" AllowDrop="True" DragOver="doc_DragOver" Drop="doc_Drop" Background="White"/>
</FlowDocumentReader>
code behind:
private void doc_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.All;
e.Handled = true;
}
private void doc_Drop(object sender, DragEventArgs e)
{
}
I couldn't find any direct way to solve this, so here is what I have ended up with:
I placed a grid on top of the FlowDocumentReader. This grid has a sold color, opacity of 0 (transparent) and Visibility=Collapsed. The purpose of this grid is to serve as a drop target.
When FlowDocument within the FlowDocumentReader received the DragEnter event, I switch the grid's visibility to Visible. The grid starts receiving drag events and the cursor stays in the "drop here" form.
When grid receives Drop or DragLeave events, its visibility is turned back to Collapsed to allow the FlowDocument receive mouse events
<FlowDocumentReader x:Name="fdr" Grid.Row="1" Background="White">
<FlowDocument x:Name="doc" DragEnter="doc_DragEnter" Background="White"/>
</FlowDocumentReader>
<Grid x:Name="dtg" Grid.Row="1" Background="White" Opacity="0"
Drop="dtg_Drop" DragLeave="dtg_DragLeave" Visibility="Collapsed"/>

Resources