Constant focus for the tool window

Is there a way to create a tool window in WinForms, if as long as the hosting form has focus, the tool window also works? An example of this in Paint.NET:

Focused tool window

I use C # as the base language of the application, in .Net 4.0.

+6
source share
4 answers

The source code for the old version of Paint.Net is available in openpdn Fork of Paint.NET 3.36.7

I tried to extract their methods from this source code into the most concise working example I could put together:

Eject Class:

internal static class Win32 { public const int WM_ACTIVATE = 0x006; public const int WM_ACTIVATEAPP = 0x01C; public const int WM_NCACTIVATE = 0x086; [DllImport("user32.dll", SetLastError = false)] internal static extern IntPtr SendMessageW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal extern static bool PostMessageW(IntPtr handle, uint msg, IntPtr wParam, IntPtr lParam); } 

Basic form:

 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private bool ignoreNcActivate = false; protected override void WndProc(ref Message m) { base.WndProc(ref m); switch (m.Msg) { case Win32.WM_NCACTIVATE: if (m.WParam == IntPtr.Zero) { if (ignoreNcActivate) { ignoreNcActivate = false; } else { Win32.SendMessageW(this.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero); } } break; case Win32.WM_ACTIVATEAPP: if (m.WParam == IntPtr.Zero) { Win32.PostMessageW(this.Handle, Win32.WM_NCACTIVATE, IntPtr.Zero, IntPtr.Zero); foreach (Form2 f in this.OwnedForms.OfType<Form2>()) { f.ForceActiveBar = false; Win32.PostMessageW(f.Handle, Win32.WM_NCACTIVATE, IntPtr.Zero, IntPtr.Zero); } ignoreNcActivate = true; } else if (m.WParam == new IntPtr(1)) { Win32.SendMessageW(this.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero); foreach (Form2 f in this.OwnedForms.OfType<Form2>()) { f.ForceActiveBar = true; Win32.SendMessageW(f.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero); } } break; } } protected override void OnShown(EventArgs e) { base.OnShown(e); Form2 f = new Form2(); f.Show(this); } } 

Always active Form2 (if the application is not active):

 public partial class Form2 : Form { internal bool ForceActiveBar { get; set; } public Form2() { InitializeComponent(); this.ShowInTaskbar = false; this.ForceActiveBar = true; } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == Win32.WM_NCACTIVATE) { if (this.ForceActiveBar && m.WParam == IntPtr.Zero) { Win32.SendMessageW(this.Handle, Win32.WM_NCACTIVATE, new IntPtr(1), IntPtr.Zero); } } } } 

There is no need to set TopMost to true for Form2, since it must belong to the main form when it is displayed. In addition, Form2 is not a child form of MDI.

+2
source

Tool windows in Paint.NET are just those tool windows. In Win32 terms, you achieve this by creating a window with the WS_EX_TOOLWINDOW extended window style:

The window is intended for use as a floating toolbar. The tool window has a title bar that is smaller than a normal title bar, and the window title is drawn using a smaller font. The tool window does not appear on the taskbar or in the dialog box that appears when the user presses ALT + TAB.

In WinForms, this is controlled by the FormBorderStyle property. Set it to FormBorderStyle.FixedToolWindow or FormBorderStyle.SizableToolWindow in the form constructor.

You also need to make sure that you specify the owner window of the tool window. Its owner should be your main form, the one for which it serves as a tool palette. You usually do this when showing the form, using the Show method overload, which allows you to specify the owner window.

Finally, another cool effect that Paint.NET has (I think if I remember correctly) is that tool windows can never get focus. You can interact with them by clicking on the buttons to select tools, but you cannot set focus on a floating palette. He always returns to the main window. A naive attempt to imitate this behavior may be to reset focus in one of the focus change notifications (for example, the Activate event), but this is not a good idea for many reasons. A better solution would be to add an extended WS_EX_NOACTIVATE style. I do not know of any property that provides this function in WinForms, but you can set it manually during window creation by overriding the CreateParams property. For instance:

 public class MyForm : Form { // ... other code ... protected override CreateParams CreateParams { get { const int WS_EX_NOACTIVATE = 0x08000000; CreateParams cp = base.CreateParams; cp.ExStyle |= WS_EX_NOACTIVATE; return cp; } } } 
+2
source

I don't know if Windows Forms has a built-in function for this, but you can achieve what you want with the code below:

For the main form:

 private ToolForm m_toolForm; private void MainForm_Load(object sender, EventArgs e) { m_toolForm = new ToolForm (); m_toolForm.Show(); } private void MainForm_Resize(object sender, EventArgs e) { switch (WindowState) { case FormWindowState.Minimized: m_toolForm.Hide(); break; case FormWindowState.Maximized: m_toolForm.Show(); break; } } 

For the tool form: You donโ€™t need any code, just set the โ€œTopMostโ€ property to true.

0
source

For those using DevExpress RibbonForm, use the solution in the link below to solve the focus problem. The zip file is in the commentary on the solution, and not in the solution itself:

http://www.devexpress.com/Support/Center/Question/Details/Q498321

0
source

Source: https://habr.com/ru/post/945667/


All Articles