WPF weird window switching on double-click - wpf

Does anybody know how to implement a double-click event handler that opens a new window in a way the new window becomes the front most window? (Just the behavior that is normally expected).
In WPF there is a strange behavior of windows when opening a second window in the double-click event handler. The second window opens but the first window, where the double-click-event was fired, becomes activated again immediately.
Opening a window in a click event handler, works as expected. The second window opens and remains the front window.
For demonstration purposes I created the following application. Two window classes with just a button control. To distinguish between click and double-click on the button control, the click-event works only if the left shift key is pressed.
After double-click
http://blog.mutter.ch/wp-content/uploads/2014/05/wpf_window1.png
After click (this is also the expected behavior for double-click)
http://blog.mutter.ch/wp-content/uploads/2014/05/wpf_window2.png
Main Window
<Window x:Class="WpfWindowSwitching.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="600">
<Grid>
<Button Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MouseDoubleClick="doubleClick"
Click="click">
<TextBlock FontWeight="Bold"
FontSize="22">
I am the first Window, double click this button...
</TextBlock>
</Button>
</Grid>
</Window>
The code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void doubleClick(object sender, MouseButtonEventArgs e)
{
openNewWindow();
}
private static void openNewWindow()
{
var window = new SecondWindow();
window.Show();
}
private void click(object sender, RoutedEventArgs e)
{
if (!Keyboard.IsKeyDown(Key.LeftShift)) return;
openNewWindow();
}
}
Second Window
<Window x:Class="WpfWindowSwitching.SecondWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecondWindow" Height="200" Width="600">
<Grid>
<Button Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Click="click">
<TextBlock FontWeight="Bold"
FontSize="22">
I am the second Window
</TextBlock>
</Button>
</Grid>
</Window>
The code behind:
public partial class SecondWindow : Window
{
public SecondWindow()
{
InitializeComponent();
}
private void click(object sender, RoutedEventArgs e)
{
this.Close();
}
}

After MouseDoubleClick event, MouseUp event is raised which gets handled on MainWindow. Hence secondary window gets activated momentarily and with subsequent event bubbling, main window gets activated.
In case you don't want that, you can explicitly stop event bubbling by setting e.Handled to True after mouse double click event. This way secondary window will remain activated.
private void doubleClick(object sender, MouseButtonEventArgs e)
{
openNewWindow();
e.Handled = true;
}

Related

WPF Loses Event Handler

I have a simple WPF app in .NET 4.7. The main Window listens for a "Key Up" event. If the Escape key is pressed, the Main Window closes itself. This works fine (at least it starts out working fine).
The main Window also contains a Button that opens another Window (call it X). X is just an empty Window. It has no event handlers. When X opens, the Button in the main Window is disabled. Once X closes, the Button is re-enabled.
Here's the problem: Once X is closed, the main Window's Key Up event handler seems to be gone...sort of. I can no longer press Escape to close the main Window unless I first give the focus to one of the buttons. If I just click on the main Window and press Escape, it will not close the Window. Prior to showing Window X, this was not the case.
It turns out that if I do not disable and re-enable the Button, the Key Up event handler continues to work after X is closed -- I can still reliably close the main Window by pressing the Escape key.
Obviously, I'm new to WPF. I'm wondering if there is something weird going on with Windows events.
Here is the the Main Window's XAML followed by it's code behind. I have also included Window X's XAML and code behind (btw, Window X is the WrapPanelWindow).
<Window x:Class="Wpf_01.MainWindow" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_01"
mc:Ignorable="d"
Title="Main Window" Height="300" Width="300" Background="#FF6289A4" KeyUp="Key_Up">
<StackPanel Margin="20,10,20,10">
<Button Content="Canvas Example"/>
<Button Content="WrapPanel Example" Click="Button_Click"/>
<Button Content="DockPanel Example"/>
<Button Content="Grid Example"/>
<Button Content="StackPanel Example"/>
</StackPanel>
Here is the Main Window's code behind:
namespace Wpf_01 {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
internal partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) {
Button clickedButton = sender as Button;
Window newWindow = null;
switch(clickedButton.Content) {
case "Canvas Example":
newWindow = new CanvasWindow();
break;
case "WrapPanel Example":
newWindow = new WrapPanelWindow();
break;
}
if(newWindow != null) {
clickedButton.IsEnabled = false;
newWindow.Show();
newWindow.Closed += (x, y) => clickedButton.IsEnabled = true;
}
} // Button_Clicked()
private void Key_Up(object sender, KeyEventArgs e) {
if(e.Key == Key.Escape)
Close();
} // KeyHandler()
}
}
Here is the WrapPanelWindow XAML and code behind:
<Window x:Class="Wpf_01.WrapPanelWindow" x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_01"
mc:Ignorable="d"
Title="Fun with WrapPanel Window" Height="200" Width="260">
<Grid>
</Grid>
</Window>
Finally, the WrapPanelWindow's code behind.
namespace Wpf_01 {
/// <summary>
/// Interaction logic for WrapPanelWindow.xaml
/// </summary>
internal partial class WrapPanelWindow : Window {
public WrapPanelWindow() {
InitializeComponent();
}
}
}
The main window is not getting the focus back when you close the CanvasWindow. When you disable the button, nothing else on the window is focusable.
If you select the App from the windows taskbar (minimize and restore) or switch programs and come back using Ctrl+Tab it works.
Try giving the clickedButton focus after you re-enable it:
newWindow.Closed += (x, y) =>
{
clickedButton.IsEnabled = true;
clickedButton.Focus();
};

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();
}
}

WPF: using Commands bound in a UserControl

I'm doing a sample with MVVM and have a problem with commands. I have an Article class (with ID, Name, Price, etc.), an ArticleViewModel that represents the view model, and a user control (ArticleControl) that allows to input the data for the article, with bindings to the properties of the ArticleViewModel. This user control has a biding for a save command.
<UserControl.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</UserControl.CommandBindings>
This is how the command is defined:
public class Commands
{
private static RoutedUICommand _save;
public static RoutedUICommand Save
{
get { return _save; }
}
static Commands()
{
InputGestureCollection saveInputs = new InputGestureCollection();
saveInputs.Add(new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S"));
_save = new RoutedUICommand(
"Save",
"Save",
typeof(Commands),
saveInputs);
}
}
And the command binding handlers:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
double baseprice = 0;
double.TryParse(ArticleBasePrice.Text, out baseprice);
e.CanExecute =
!string.IsNullOrEmpty(ArticleID.Text) &&
!string.IsNullOrEmpty(ArticleName.Text) &&
!string.IsNullOrEmpty(ArticleDescription.Text) &&
baseprice > 0;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
ArticleViewModel avm = (ArticleViewModel)DataContext;
if (avm != null && avm.Save())
{
ArticleID.Text = String.Empty;
ArticleName.Text = String.Empty;
ArticleDescription.Text = String.Empty;
ArticleBasePrice.Text = String.Empty;
}
}
Now, I put this user control on a window. When I hit Ctrl+S the command is executed. However, I also put a Save button on that window, next to this user control. When I click it I want to execute the same command (and I don't want to do another command binding in the window where the user control is hosted).
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave"
Content="Save" Width="100"
HorizontalAlignment="Left"
Command="{???}"/> <!-- what should I put here? -->
</StackPanel>
But I do not know how to refer that saveCmd defined in the user control. I tried different things, some are completely wrong (they throw exception when running the app), some don't have any effect.
Command="{StaticResource saveCmd}"
Command="{StaticResource local:ArticleControl.saveCmd}"
Command="{x:Static local:Commands.Save}"
Any help is appreciated. Thank you.
The reason why the Save button will not cause the commandbindings of your other control to execute is because the Save button is outside the user control and therefore the command system will not look for a commandbinding in that control. The Command execution strategy is a bit like a bubbling event and will start from the focused item (the Button) and go up the visual tree until it finds the CommandBindings.
You can either implement the command binding in the parent control or set the CommandTarget property of the Save button to the user control.
Another approach is to set the FocusManager.IsFocusScope=True on the button or the container of the button. If you do this I suggest you read up on what IsFocusScope does but in a nutshell it will leave the input focus on whatever control has the focus when you press the button, instead of making the button the new input focus. This is generally used for toolbars or menu like structures.
Based on Patrick's suggestions, this is what I did:
Put the command binding in the user control and implemented the handlers in the code-behind as shown in the original message.
Used Command, CommandTarget and FocusManager properties on the button to point to the binding from the user control (ArticleUserControl is the x:Name of the user control).
This is how the XAML for the window looks:
<Window x:Class="MVVMModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMModel"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left"
Command="local:Commands.Save"
CommandTarget="{Binding ElementName=ArticleUserControl}"
FocusManager.IsFocusScope="True" />
</StackPanel>
</Window>
I think you just have to move your CommandBinding to a Resource Dictionary, so that it's available outside your UserControl!
Here is what I did to work, though I'm not particularly happy with the solution. If anyone knows a better approach, please do let me know.
I moved the logic for the commands handler in a separate, static class:
static class CommandsCore
{
public static bool Save_CanExecute(ArticleControl ac)
{
double baseprice = 0;
double.TryParse(ac.ArticleBasePrice.Text, out baseprice);
return
!string.IsNullOrEmpty(ac.ArticleID.Text) &&
!string.IsNullOrEmpty(ac.ArticleName.Text) &&
!string.IsNullOrEmpty(ac.ArticleDescription.Text) &&
baseprice > 0;
}
public static void Save_Executed(ArticleControl ac)
{
ArticleViewModel avm = (ArticleViewModel)ac.DataContext;
if (avm != null && avm.Save())
{
ac.ArticleID.Text = String.Empty;
ac.ArticleName.Text = String.Empty;
ac.ArticleDescription.Text = String.Empty;
ac.ArticleBasePrice.Text = String.Empty;
}
}
}
I kept the command binding in the user control as it was
<UserControl.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</UserControl.CommandBindings>
But in the handlers I called the two methods I just defined above.
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = CommandsCore.Save_CanExecute(this);
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandsCore.Save_Executed(this);
}
And then I did the same from the window where the control is used.
<Window x:Class="MVVMModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMModel"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left"
Command="local:Commands.Save"/>
</StackPanel>
</Window>
and the handlers
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = CommandsCore.Save_CanExecute(articleControl);
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandsCore.Save_Executed(articleControl);
}
And this works, the Save button is enabled only when the fields are filled in appropriately and the command is executed correctly when clicking the button.

DialogResult WPF

I am reading one book which says
Rather than setting the DialogResult
by hand after the user clicks a
button, you can designate a button as
the accept button (by setting
IsDefault to true). Clicking that
button automatically sets the
DialogResult of the window to true.
Similarly, you can designate a button
as the cancel button (by setting
IsCancel to true), in which case
clicking it will set the DialogResult
to Cancel.
This is the MainWindow:
<Window x:Class="WpfApplicationWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="400" Height="400">
<StackPanel>
<Button Name="BtnShowDialogStatus" Click="BtnShowDialogStatus_Click">DIALOG RESULT</Button>
</StackPanel>
</Window>
Code for click event:
private void BtnShowDialogStatus_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(new NewWindow().ShowDialog().ToString());
}
And this is the Dialog box which I am opening on the click event:
<Window x:Class="WpfApplicationWPF.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<StackPanel>
<Button Name="BtnDEfault" IsDefault="True" Click="BtnDEfault_Click">DEFAULT BUTTON</Button>
<Button Name="BtnCancel" IsCancel="True" Click="BtnCancel_Click">CANCEL BUTTON</Button>
</StackPanel>
</Window>
This is the code for it:
private void BtnDEfault_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
I can see it returning the DialogResult only as false no matter I click the default or cancel button.
IsDefault ties the button to the Enter key, so that pressing the Enter key will fire the Click event. It does not mean that the Yes button will return true for the DialogResult.
Refer to the links.It will clear up things for you
http://blog.wpfwonderland.com/2010/03/22/getting-a-dialogresult-from-a-wpf-window/
http://www.wpftutorial.net/Dialogs.html
Hope it helps...
change your code to
private void BtnDEfault_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
this.Close();
}
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
this.Close();
}
hope this helps
To my understanding setting IsDefault as true and IsCancel as false only enables you to assign what event should occur i.e. the window will fire close event on 'Escape' key for IsCancel and for Enter key for IsDefault=true.
You will need to set the Dialog result from your button click / command action handlers.
Using net 5 this seems to be all the code needed to open a ShowDialog window, and close it.
From the window you have opened.
<Button Margin="10" IsDefault="True" Click="Ok_OnClick" >OK</Button>
<Button Margin="10" IsCancel="True">Cancel</Button>
private void Ok_OnClick(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
From function to open window.
var requester = new DeleteRequester();// a wpf window
var showDialog = requester.ShowDialog( );
if (showDialog != null && showDialog.Value)
the only reason it is checked for null is to get rid of the blue nag line from re-sharper.
It seems whenever you change the "DialogResult" The window is going to close and the value gets returned. Kind of make sense, why would you change the value if you weren't done.
With what ever your doing with the window, you simply need to close the window to return a false result, or set the DialogResult to true to close the window with a true result.
Simple and basic:
If(ItWorked){DialogResult = true;}// closes window returns true
If(ItsJunk){Close();}// closes window returns false
If(ItsJunk){DialogResult = false;}//closes window returns false

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