有没有办法在WinForms中创建工具窗口,只要托管表单具有焦点,工具窗口也可以?这方面的一个例子是在Paint.NET中:
我在.Net 4.0下使用C#作为后端应用程序语言。
答案 0 :(得分:2)
Paint.NET中的工具窗口就是那个工具窗口。在Win32术语中,您可以通过创建具有WS_EX_TOOLWINDOW
扩展窗口样式的窗口来实现此目的:
该窗口旨在用作浮动工具栏。工具窗口的标题栏比普通标题栏短,窗口标题使用较小的字体绘制。当用户按下ALT + TAB时,工具窗口不会出现在任务栏或对话框中。
在WinForms中,这由FormBorderStyle
属性控制。在表单的构造函数中将其设置为FormBorderStyle.FixedToolWindow
或FormBorderStyle.SizableToolWindow
。
您还需要确保为工具窗口指定所有者窗口。它的主人应该是你的主要形式,它作为工具调色板。您通常在显示表单时执行此操作,使用允许您指定所有者窗口的Show
方法的重载。
最后,Paint.NET的另一个很酷的效果(我想,如果我没记错的话)是工具窗口永远不会真正获得焦点。您可以与它们进行交互,单击按钮以选择工具,但实际上您无法将焦点设置为浮动调色板。它总是回到主窗口。模仿此行为的天真尝试可能是重置焦点更改通知之一(例如,Activate
事件),但出于多种原因这不是一个好主意。更好的解决方案是添加WS_EX_NOACTIVATE
扩展窗口样式。我不知道在WinForms中公开此功能的任何属性,但您可以通过覆盖CreateParams
属性在窗口创建期间手动设置它。例如:
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;
}
}
}
答案 1 :(得分:2)
旧版Paint.Net的源代码可在openpdn Fork of Paint.NET 3.36.7
获得我尝试将他们的方法从源代码中提取到我能够集中讨论的最简洁的工作示例中:
Pinvoking课程:
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);
}
基本表格:
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);
}
}
始终激活Form2(除非app未激活):
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);
}
}
}
}
没有必要为Form2设置TopMost为true,因为它在显示时应由主窗体拥有。此外,Form2 不是 MDI子表单。
答案 2 :(得分:0)
我不知道Windows Forms是否具有内置功能,但您可以使用以下代码实现您想要的功能:
主要表单:
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;
}
}
对于工具表单: 您不需要任何代码,只需将属性“TopMost”设置为true即可。
答案 3 :(得分:0)
对于使用DevExpress RibbonForm的用户,请使用以下链接中的解决方案来解决焦点问题。 zip文件是对解决方案的评论,而不是解决方案本身:
http://www.devexpress.com/Support/Center/Question/Details/Q498321