How can I set the focused control after a Dialog Box? - wpf

I'm working on an XBAP app where Users primarily use the Keyboard for Navigation. When I display a MessageBox, I can hit Enter to close it but then the main application doesn't seem to regain focus. I have to manually click the mouse on the screen to put focus back on the application.
Is there a way around this?
Edit
I can verify that the app still has Logical Focus, but it just doesn't have Keyboard Focus

I found a hack that works, although I don't like it because I feel it ties my Views to my ViewModel
I'm using an IsFocused AttachedProperty to bind a control to a boolean property behind the View. The same View is also subscribing to a DisplayError event that displays a MessageBox error and reset the IsFocused property afterwards so it updates the UI. Last change made was to update my ViewModels to publish errors to the EventAggregator instead of handling themselves with a MessageBox, which is probably better anyways.
I suppose it works, even if I don't like it

Not sure if this will help your situation but in my circumstance it was ok for me to set focus back to main window, which was able to be accomplished with
App.Current.MainWindow.Focus();
Just be sure main window is properly initialized, which may not be the case if a splash screen or some login window or something initially grabbed the main window role (ie by StartupUri) and then nothing else was updated thereafter.
This worked for me since I was handling all keyboard events at the main window level to drive updates to my view model.

using System.Runtime.InteropServices;
using System.Windows.Interop;
public class Interop
{
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
public static IntPtr GetWindowHandle(Window window)
{
return new WindowInteropHelper(window).Handle;
}
}
// In main window, when the MessageBox is closed
IntPtr window = Interop.GetWindowHandle(this);
IntPtr focused = Interop.GetForegroundWindow();
if (window != focused)
{
Interop.SetForegroundWindow(window);
}
http://tech.avivo.si/2009/11/how-to-focus-window-in-wpf-when-it-gets-out-of-focus/

Related

WPF MessageBox not working

In my WPF I've tried System.Windows.MessageBox.Show, System.Windows.Forms.MessageBox.Show, and Xceed.Wpf.Toolkit.MessageBox.Show (from the Wpf Toolkit).
Every one of these methods shows the message box exactly how I want it too. The problem is that none of the buttons work. I click OK, I click cancel, I click the X, none of the buttons do anything. And with the toolkit message box I can't move the message box either. The buttons don't depress either. It's as if they're disabled, but I have no idea why.
EDIT: I'm using Prism and MEF to compose the application. Inside my module I have a view that is being displayed in a region in my Shell Window. The view is a UserControl with a button.
<UserControl>
<Grid>
<Button content="click me" Click="Button_OnClick"/>
</Grid>
</UserControl>
In the code behind I have the OnClick method.
private void Button_OnClick(object sender, RoutedEventArgs e)
{
System.Windows.MessageBox.Show("test");
}
The message box gets displayed, I can move it, The 'X' glows on mouse over, but neither the 'X' nor the 'OK' button, do anything.
I can provide more code as needed, I just don't want to have to include my whole application...
FIXED
The main WPF window had a borderless behavior attached to it that was processing windows messages (WndProc) and it wasn't processing the WM_NCACTIVATE message properly.
NOT WORKING:
case UnsafeNativeConstants.WM_NCACTIVATE:
handled = true;
break;
WORKING:
case UnsafeNativeConstants.WM_NCACTIVATE:
retVal = UnsafeNativeMethods.DefWindowProc(hwnd, UnsafeNativeConstants.WM_NCACTIVATE, new IntPtr(1), new IntPtr(-1));
handled = true;
break;
internal static UnsafeNativeMethods
{
[DllImport("user32", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr DefWindowProc([In] IntPtr hwnd, [In] int msg, [In] IntPtr wParam, [In] IntPtr lParam);
}
Try running your app in the debugger and pausing when the dialog is visible to ensure the message pump/loop is still running and is not deadlocked. That's the only reason I can think of that your UI could be unresponsive.

Who is responsible for Window View lifecylce in MVVM pattern?

It stays for me unclear who creates and disposes windows in WPF.
As we know window can be closed by clicking the cross sign in the upper right corner and we can stop closing only by setting Cancel = true in Closing event handler.
For me it is quite natural for the ViewModel to decide if View is allowed to close. And I think it is VM whow request the instantiation of the appropriate window.
In our project we created IViewManager:
public interface IViewManager
{
void ShowView();
void CloseView();
}
public interface IWindowedViewModel : IDisposable
{
bool IsEnabled {get;set;}
WindowState WindowState {get;set;}
Visibility Visibility {get;set;}
bool IsActive {get;set;}
bool CanBeClosed();
}
So our VMs communicate with the view via bindings and indirectly via IViewManager.
In the examples of MVVM applications I've seen so far VM is quite passive and is not responsible for View's lifecyle. That's why I have some doubts about our design.
I wan't to be sure we are not missing something important concerning MVVM pattern.
For that you will have to Modify your Window to have bool type Depedency Property like CanSave that will be bound to your ViewModel(this Property will say whether ther are any pending changes or not).Now if CanSave is true you will show user a DialogBox which says "Do you want to Save pending changes" and if User Click Yes fire the Same Command as that is for your Save Button , If user clicks No Just simply close the window.And if CanSave is false simply close the window.I hope this will give you an idea.
Another way to do that is Make Window Close Button Custom and bind it to ViewModel through Command and handle it From there.

ShowDialog() behind the parent window

I am using ShowDialog() with WindowStyle = WindowStyle.SingleBorderWindow; to open a modal window in my WPF (MVVM) application, but it lets me navigate to parent window using the Windows taskbar (Windows 7).
I've found an answer here: WPF and ShowDialog() but it isn't suitable for me because I don't need an "always on top" tool window.
Thanks in advance
Try setting the Owner property of the dialog. That should work.
Window dialog = new Window();
dialog.Owner = mainWindow;
dialog.ShowDialog();
Edit:
I had a similar problem using this with MVVM. You can solve this by using delegates.
public class MainWindowViewModel
{
public delegate void ShowDialogDelegate(string message);
public ShowDialogDelegate ShowDialogCallback;
public void Action()
{
// here you want to show the dialog
ShowDialogDelegate callback = ShowDialogCallback;
if(callback != null)
{
callback("Message");
}
}
}
public class MainWindow
{
public MainWindow()
{
// initialize the ViewModel
MainWindowViewModel viewModel = new MainWindowViewModel();
viewModel.ShowDialogCallback += ShowDialog;
DataContext = viewModel;
}
private void ShowDialog(string message)
{
// show the dialog
}
}
I had this problem but as the Window was being opened from a view model I didn't have a reference to the current window. To get round it I used this code:
var myWindow = new MyWindowType();
myWindow.Owner = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
You can use: myWindow.Owner = Application.Current.MainWindow;
However, this method causes problems if you have three windows open like this:
MainWindow
|
-----> ChildWindow1
|
-----> ChildWindow2
Then setting ChildWindow2.Owner = Application.Current.MainWindow will set the owner of the window to be its grandparent window, not parent window.
When the parent window makes (and shows) the child window, that is where you need to set the owner.
public partial class MainWindow : Window
{
private void openChild()
{
ChildWindow child = new ChildWindow ();
child.Owner = this; // "this" is the parent
child.ShowDialog();
}
}
Aditionally, if you don't want an extra taskbar for all the children... then
<Window x:Class="ChildWindow"
ShowInTaskbar="False" >
</Window>
Much of the reason for the MVVM pattern is so that your interaction logic can be unit tested. For this reason, you should never directly open a window from the ViewModel, or you'll have dialogs popping up in the middle of your unit tests.
Instead, you should raise an event that the View will handle and open a dialog for you. For example, see this article on Interaction Requests: https://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx#sec12
The problem seems to be related to Window.Owner, and indeed if you judge by previous knowledge that you might have of the Win32 API and WinForms, a missing owner would be the typical cause of such a problem, but as many have pointed out, in the case of WPF that's not it. Microsoft keeps changing things to keep things interesting.
In WPF you can have a dialog with a specific owner and you can still have the dialog appear in the taskbar. Because why not. And that's the default behavior. Because why not. Their rationale is that modal dialogs are not kosher anymore, so you should not be using them; you should be using modeless dialogs, which make sense to show as separate taskbar icons, and in any case the user can then decide whether they want to see different app windows as separate icons, or whether they want to see them grouped.
So, they are trying to enforce this policy with complete disregard to anyone who might want to go against their guidelines and create a modal dialog. So, they force you to explicitly state that you do not want a taskbar icon to appear for your dialog.
To fix this problem, do the following in the constructor of your view class:
ShowInTaskbar = false;
(This may happen right after InitializeComponent();
This is equivalent to Xcalibur37's answer, though the way I figure things, since WPF forces you to have both a .cs file and a .xaml file, you might as well put things that are unlikely to change in the .cs file.
Add "ShowInTaskbar" and set it to false.
Even if this post is a bit old, I hope it is OK that I post my solution.
All the above results are known to me and did not exactly yield the desired result.
I am doing it for the other googlers :)
Lets say f2 is your window that you want to display on top of f1 :
f2.Owner = Window.GetWindow(this);
f2.ShowDialog();
That's it , I promise it will not disappear !
HTH
Guy

Setting Default Keyboard Focus On Loading A UserControl

I have an MVVM setup with a mainwindow that contains a ContentControl.
I set this to a particular viewmodel which then maps to a view.
A view is a usercontrol.
I want to be able to set the default keyboard focus to a default element in the usercontrol(View) when it loads so the application can eventually be driven just by using up, down, left, right and enter.
Some of my failed attempts are setting
FocusManager.FocusedElement="{Binding ElementName=DefaultElement}"
in my content control tag. This sets the logical focus but not the keyboard focus
I'd rather keep the solution in xaml if possable but have tried placing the following in code behind.
Keyboard.Focus(DefaultElement);
This does not work but if I popup a message box first it does. I'm a little confused as to why.
MessageBox.Show(Keyboard.FocusedElement.ToString());
Keyboard.Focus(DefaultElement);
EDIT::::
I just placed this in my onloaded event of my user control. It seems to work but can anyone see any issues that might arrise at this priority level. I.E a circumstance when the action will never run?
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
Keyboard.Focus(DefaultElement);
}));
It seems that this wpf the you have to implement a workaround on a case by case basis. The solution that seemed to work best, most of the time for me was to insert the focus code inside the dispatcher when OnVisible was changed. This sets the focus not only when the View/Usercontrol loads but also if you a changing Views by way of Visibility. If you Hide and then Show a ContentControl that is mapped to your ViewModels then the Loaded event won't fire and you'll be forced to Mouse input, or tabbing (Not so good if you want to navigate your app with a remote control).
VisibilityChanged will always fire however. This is what I ended up with for my listbox.
private void ItemsFlowListBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
ItemsFlowListBox.Focus();
ItemsFlowListBox.ScrollIntoView(ItemsFlowListBox.SelectedItem);
}));
}
}
I had the same symptom for a WPF UserControl hosted in a Winforms application. Just wanted to note I was about to try this solution when I found a normal TabIndex in the Winforms app fixed it
Per How to set which control gets the focus on application start
"The one with the minimum tab index automatically gets the focus
(assuming the TabStop property is set to true). Just set the tab
indices appropriately."
It's a tricky one with no easy answer. I'm currently doing this, although I'm not sure I like it:
public MyView()
{
InitializeComponent();
// When DataContext changes hook the txtName.TextChanged event so we can give it initial focus
DataContextChanged +=
(sender, args) =>
{
txtName.TextChanged += OnTxtNameOnTextChanged;
};
}
private void OnTxtNameOnTextChanged(object o, TextChangedEventArgs eventArgs)
{
// Setting focus will select all text in the TextBox due to the global class handler on TextBox
txtName.Focus();
// Now unhook the event handler, since it's no longer required
txtName.TextChanged -= OnTxtNameOnTextChanged;
}
And in case you're wondering what the global class handler does, it's this:
protected override void OnStartup(StartupEventArgs e)
{
...
// Register a global handler for this app-domain to select all text in a textBox when
// the textBox receives keyboard focus.
EventManager.RegisterClassHandler(
typeof (TextBox), UIElement.GotKeyboardFocusEvent,
new RoutedEventHandler((sender, args) => ((TextBox) sender).SelectAll()));
which auto selects TextBox text when receiving keyboard focus.

How to make a "popup" (hint, drop-down, popup) window in Winforms?

How can i make, what i will call, a "popup" window" in WinForms?
Since i used my own made-up word "popup", let me give examples of this so-called "popup" window:
a tooltip window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
a popup menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
a drop-down window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
A main menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
Update A popup window not make itself the "active" window when interacted with using a mouse or keyboard (the "owner" window remains the active window):
The attributes that i'm looking for in this mythical "popup" are that it:
can extend outside the boundaries of its parent form (i.e. is not a child window)
doesn't appear in the taskbar (i.e. Window's heuristics of which windows should appear doesn't kick in, nor does it have WS_EX_APPWINDOW extended window style)
is not modal (i.e. doesn't disable its "owner")
doesn't steal focus
is always on-top of of it's "owner"
does not become the "active" window when interacted with (the owner remains active)
Windows applications are already managing to create such windows. How can i do it in a WinForms application?
Related questions
How do i achieve all the above in native code?
How do i create a popup window in Delphi?
i have this native code to show a "popup" window - what P/Invokes are required to perform the same actions in .NET?
i have a set of P/Invoke's in .NET - can i reuse a regular WinForm, overriding certain methods, to achieve the same effect?
i have WinForm that i'm showing as a "popup" by overriding certain methods - is there a built-in Control that can act as a popup for me?
How to simulate a drop-down window in WinForms?
Attempt#1
i tried the Show(onwer) + ShowWithoutActivation method:
PopupForm dd = new PopupForm ();
dd.Show(this);
with PopupForm:
public class PopupForm: Form
{
public PopupForm()
{
InitilizeComponent();
}
private void InitilizeComponent()
{
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = false;
}
protected override bool ShowWithoutActivation
{ get { return true; } }
}
Very nearly solved the problem, but then i discovered was reminded of another property of "popup" windows: they do not take focus from their "owner" form become active when interacted with by mouse or keyboard.
You want an owned window. In your main form:
private void showPopup_Click(object sender, EventArgs e)
{
PopupForm popupForm = new PopupForm();
// Make "this" the owner of form2
popupForm.Show(this);
}
PopupForm should look like this:
public partial class PopupForm : Form
{
private bool _activating = false;
public PopupForm()
{
InitializeComponent();
}
// Ensure the popup isn't activated when it is first shown
protected override bool ShowWithoutActivation
{
get
{
return true;
}
}
private const int WM_NCACTIVATE = 0x86;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
protected override void WndProc(ref Message m)
{
// The popup needs to be activated for the user to interact with it,
// but we want to keep the owner window's appearance the same.
if ((m.Msg == WM_NCACTIVATE) && !_activating && (m.WParam != IntPtr.Zero))
{
// The popup is being activated, ensure parent keeps activated appearance
_activating = true;
SendMessage(this.Owner.Handle, WM_NCACTIVATE, (IntPtr) 1, IntPtr.Zero);
_activating = false;
// Call base.WndProc here if you want the appearance of the popup to change
}
else
{
base.WndProc(ref m);
}
}
}
And make sure that PopupForm.ShowInTaskbar = false.
I was curious as to how combobox dropdowns and menus work, so I did some more research.
There are two basic approaches.
Create the popup as an overlapped window, owned by the main window
This method is required if the popup has embedded controls, or if you want the popup to behave as a modeless dialog.
If the user is going to interact with child controls in the popup window, it must receive activation. (So the various techniques for blocking activation, such as handling WM_MOUSEACTIVATE are red herrings.) And when it receives activation, Windows will deactivate the main window. The fix for this is to send a WM_NCACTIVATE message to the parent to update its visual appearance without changing its activation status. This is the approach used by the .Net ToolStrip, and my other answer illustrates it with code.
Create the popup as a child of the Desktop window
This method is used by combobox dropdowns and (I guess) menus, but you can't embed child controls so it's not widely applicable.
The popup is a child window so it doesn't interfere with activation. It is always on top. Mouse capture is used to detect clicks outside the popup and dismiss it.
However, this isn't straightforward to implement. Activation remains with the main application, so it keeps the focus and receives keystrokes. This seems to preclude embedded controls in the popup because they can't receive focus. The combobox handles this by forwarding keystroke messages to its dropdown list. (Note that menus, combobox dropdowns, etc. are all entirely owner-draw in the sense that they have no embedded windows.)
Create your "popup" window as a child window of desktop, then show it without activating it.
hWnd = CreateWindowEx(..., WS_CHILDWINDOW | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS, ..., GetDesktopWindow(), ...);
SetWindowPos(hWnd, HWND_TOPMOST, ..., SWP_NOACTIVATE);
After doing this, your original window remains activated even if you click on the "popuped" window. The "popup" window can have its own children controls. You can click the button on it. But if it is an edit control, you cannot edit it, I don't know why. Maybe because there is already a cursor on your original window, blinking.

Resources