Prevent focus switch to main form on fly out IsOpen=false; - wpf

I have a MetroWindow. It has a Flyout. The Flyout has a button. When the Flyout button is pressed, a new MetroWindow is shown and the flyout is dismissed.
What occurs is:
User pressed button on Flyout.
The button-press opens a new non-modal form and sets the Flyout's IsOpen to false.
The non-modal form opens on top of everything.
The flyout closes.
The main form gets the focus after the flyout closes - hiding the non-modal form.
I would like the focus-change to the main form to not occur when the flyout closes.
I have a method that works, but it's a bit laggy because it has to wait for the flyout animation to finish.
Is there a better way? I do not want to make the new window to be modal or AlwaysOnTop.
private void SearchResultClose(object sender, RoutedEventArgs e)
{
m_EvtResultClosed.Set();
}
private void SearchResultOpenChange(object sender, RoutedEventArgs e)
{
if (foSearchResult.IsOpen)
{
m_EvtResultClosed.Reset();
}
}
and
<controls:Flyout Position="Top" Header="" x:Name="foSearchResult"
Height="275" ClosingFinished="SearchResultClose" IsOpenChanged="SearchResultOpenChange">
and
private void OpenPersonCard(object sender, RoutedEventArgs e)
{
var selected_person = SearchPersonResultsVM.View.CurrentItem as Editable<Person>;
if (selected_person != null)
{
var card = new PersonFileWindow();
card.Person = selected_person;
foSearchResult.IsOpen = false;
// Wait for it to close, and then show the form.
Dispatcher.BeginInvoke(new Action(async () =>
{
await Task.Run(() =>
{
m_EvtResultClosed.WaitOne();
});
card.Show();
}), null);
}
}
where
private AutoResetEvent m_EvtResultClosed = new AutoResetEvent(false);

See the function IsOpenedChanged in MahApps' Flyout.cs and we see it calls Focus() on close whether its animated or not. It is also comment :
// focus the Flyout itself to avoid nasty FocusVisual painting (it's visible until the Flyout is closed)
But I do not know what it is trying to resolve exactly.
https://github.com/MahApps/MahApps.Metro/blob/1.2.4/MahApps.Metro/Controls/Flyout.cs
The simplest hack is adding Focusable="False" to your flyout as:
<controls:Flyout Focusable="False" Position="Top" Header="" x:Name="foSearchResult"Height="275" ClosingFinished="SearchResultClose" IsOpenChanged="SearchResultOpenChange">
It works fine here and I can't tell any nasty FocusVisual painting as commented in the source.

By default, when opened, the Flyout steals focus for itself, or if Focusable==false, for some control within it, even the close button. In any case, focus is not restored to the previous element.
So, just setting Focusable="False" probably won't solve the problem.
Instead, Flyout provides the AllowFocusElement property to suppress this behavior:
<controls:Flyout AllowFocusElement="False" ... >

Related

How to wait for click then close the Splashscreen in WPF application?

I have a WPF Application and there's a button which shows an image (Splash Screen) containing the company logo and the name of the developers of the application. I want this image be shown until the user interacts with whatever. When user clicks or enters a keyboard key, the image must close. Refer to the comment line in my code.
private void Info_BeforeCommandExecute(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
SplashScreen SS = new SplashScreen("Images/InfotecSplashScreenInfo.png");
SS.Show(false);
// Need to do something here to wait user to click or press any key
SS.Close(TimeSpan.FromSeconds(1));
}
You could add a handler for the Keyboard class using AddKeyDownHandler:
private void Info_BeforeCommandExecute(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
SplashScreen SS = new SplashScreen("Images/InfotecSplashScreenInfo.png");
SS.Show(false);
KeyEventHandler handler;
handler = (o,e) =>
{
Keyboard.RemoveKeyDownHandler(this, handler);
SS.Close(TimeSpan.FromSeconds(1));
};
Keyboard.AddKeyDownHandler(this, handler);
}
This would allow you to close the splash screen after the user presses a key.
Reed's answer is no doubt easiest, but you could also avoid using SplashScreen altogether and just use a custom Window to give you full control.
YourCode.cs
private void Info_BeforeCommandExecute(object sender, DevExpress.Xpf.Bars.ItemClickEventArgs e)
{
SplashWindow SS = new SplashWindow();
SS.ShowDialog(); // ShowDialog will wait for window to close before continuing
}
SplashWindow.xaml
<Window x:Class="WpfApplication14.SplashWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SplashWindow" Height="350" Width="525">
<Grid>
<!-- Your Image here, I would make a Custom Window Style with no border and transparency etc. -->
<Button Content="Click Me" Click="Button_Click"/>
</Grid>
</Window>
SplashWindow.xaml.cs
public partial class SplashWindow : Window
{
public SplashWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Once user clicks now we can close the window
// and/or add keyboard handler
this.Close();
}
}

User control is not hiding in wpf

I created a UserControl like Popup which is displayed when user clicks on menu item.
If user clicks side that user control should be collapsed.
It works fine for me when user clicks side other than any control.
If I click on datagrid or listbox it is not hiding.
Here is my code:
<src:AddNewItemPopUp x:Name="PopUp" Margin="111,47,620,230" Panel.ZIndex="1" Visibility="Collapsed"/>
I took a button in click event I set PopUp visibility property to true
In my user control I have grid. In the mousedown event of grid I have written following code...
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
if (PopUp.Visibility == Visibility.Visible)
{
PopUp.Visibility = Visibility.Collapsed;
}
}
If I click on any control like a Button, DataGrid, ListBox that are placed in Grid Popup is not collapsed.
First Set Grid's Background Property, for example grid.Backgroung=Brushes.Transparent or in Xaml Backgroung = "Transparent"
Second Handle PreviewMouseDown event instead of MouseDown event.
The first one makes the mouse event to fire, when mouse is directly over the grid.
The second one makes the mouse event fire, when mouse is over an UIElement in the grid.
Try:
Visibility="Hidden"
that is:
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
if (PopUp.Visibility == Visibility.Visible)
{
PopUp.Visibility = Visibility.Hidden;
}
}
and also see:
http://msdn.microsoft.com/en-us/library/system.windows.visibility.aspx

PopUp StaysOpen when control goes

In a Usercontrol Grid(Grid1) I have a textBox, two Buttons (Search and Save Buttons) and Two Popups(Popup1 and Popup2). Inside Popup2 there is a textBox and one button(Search). I wanted to hide the popups(both 1 and 2) when when the user click outside the Grid1. Rightnow I can hide the Popups but NOT able to click the button inside Popup2. As soon as I Click the SearchButton inside the Popup2 it hides Popups.
I have set the StaysOpen Property for both Popups to a bool prop like this: StaysOpen="{Binding PopupStaysOpen}"
Thanks.
-Menon
private void Grid1_LostFocus(object sender, RoutedEventArgs e)
{
(this.DataContext as ViewModel).PopupStaysOpen = false;
}

Finding out where is my mouse when I handle the MouseLeave event?

I am trying to get a popup to show when the mouse enter a button.
It should then hide if the user leaves the popup.
But for a good user experience, the popup should also stay open if the user goes back on the button.
So when the mouse gets out of the popup, the mouse leave event is triggered, but I don't want to handle it if the mouse entered the button at the same moment.
Is there any way of knowing if the mouse is over another control in the mouse leave event?
I have tried many different approach without success. Tell me if you have a different approach to the problem too!
Set a timer when the MouseLeave event on your popup is fired, along with a boolean, perhaps called closingPopup, to true. The timer will set closingPopup to false and hide the popup. On the MouseEnter event of the button, if closingPopup is true, then stop the timer and set the boolean to false.
That or a variant of that should get you what you want. You can give it a 1/4 second delay that way. Or any amount that seems responsive.
Edit: You should probably also add the same check to MouseLeavefor the button. (Glad I could help!)
This can be achieved by starting a timer when the mouse leaves the button, when the timer ticks, close your popup. If the mouse re-enters the button, stop the timer.
private DispatcherTimer timer = new DispatcherTimer();
public MainPage()
{
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
MyPopUp.IsOpen = false;
timer.Stop();
}
private void Button_MouseEnter(object sender, MouseEventArgs e)
{
timer.Stop();
MyPopUp.IsOpen = true;
}
private void Button_MouseLeave(object sender, MouseEventArgs e)
{
timer.Start();
}
With the following markup:
<Button MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave">
<Popup x:Name="MyPopUp">
<Grid Width="100" Height="20" Background="Red">
<TextBlock Text="Hi"/>
</Grid>
</Popup>
</Button>

WPF ListBox drag & drop interferes with ContextMenu?

I'm implementing drag & drop from a ListBox, but I'm seeing some strange behaviour with a ContextMenu elsewhere in the window. If you open the context menu and then start a drag from the ListBox, the context menu closes but won't open again until after you perform another drag.
Does this make sense? Anybody got any ideas what might be going on?
<ListBox Grid.Row="0" ItemsSource="{Binding SourceItems}" MultiSelectListboxDragDrop:ListBoxExtension.SelectedItemsSource="{Binding SelectedItems}" SelectionMode="Multiple" PreviewMouseLeftButtonDown="HandleLeftButtonDown" PreviewMouseLeftButtonUp="HandleLeftButtonUp" PreviewMouseMove="HandleMouseMove"/>
<ListBox Grid.Row="1" ItemsSource="{Binding DestinationItems}" AllowDrop="True" Drop="DropOnToDestination" />
<Button Grid.Row="2">
<Button.ContextMenu>
<ContextMenu x:Name="theContextMenu">
<MenuItem Header="context 1"/>
<MenuItem Header="context 2"/>
<MenuItem Header="context 3"/>
</ContextMenu>
</Button.ContextMenu>
Button with context menu
</Button>
...
public partial class Window1
{
private bool clickedOnSourceItem;
public Window1()
{
InitializeComponent();
DataContext = new WindowViewModel();
}
private void DropOnToDestination(object sender, DragEventArgs e)
{
var viewModel = (WindowViewModel)e.Data.GetData(typeof(WindowViewModel));
viewModel.CopySelectedItems();
}
private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var sourceElement = (FrameworkElement)sender;
var hitItem = sourceElement.InputHitTest(e.GetPosition(sourceElement)) as FrameworkElement;
if(hitItem != null)
{
clickedOnSourceItem = true;
}
}
private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
{
clickedOnSourceItem = false;
}
private void HandleMouseMove(object sender, MouseEventArgs e)
{
if(clickedOnSourceItem)
{
var sourceItems = (FrameworkElement)sender;
var viewModel = (WindowViewModel)DataContext;
DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
clickedOnSourceItem = false;
}
}
}
It seemed to be something to do with the mouse capture!?
The normal sequence of events during a drag goes something like this...
The PreviewMouseLeftButtonDown
handler gets called and
ListBox.IsMouseCaptureWithin is
false.
The PreviewMouseMove handler
gets called. By this time
ListBox.IsMouseCaptureWithin is true.
During the PreviewMouseMove handler
DragDrop.DoDragDrop gets called and
sometime during this the mouse
capture is released from the ListBox.
But, what seems to happening for a drag started when the context menu is open is...
The PreviewMouseLeftButtonDown
handler gets called and
ListBox.IsMouseCaptureWithin is
false.
The PreviewMouseMove handler gets
called. But this time
ListBox.IsMouseCaptureWithin is
still false.
Sometime after the end of the
PreviewMouseMove handler the
ListBox then gets the mouse capture
(ListBox.IsMouseCaptureWithin
becomes true)
The result of this is that after the drag, the ListBox still has the mouse capture so any clicks on the button to open the context menu are actually going to the listbox not the button.
Adding the following code to the start of the PreviewMouseLeftButtonDown handler seems to help by swallowing up the click that closes that context menu rather than trying to start a drag from it...
if (!contextMenuCloseComplete)
{
sourceElement.CaptureMouse();
return;
}
...with the contextMenuCloseComplete bool getting set in handlers for the context menu's Closed and Opened events.
Does that make sense? Does anyone understand where this mouse capture behaviour is coming from?

Resources