永久关注工具窗口

Dar*_*lus 6 c# winforms

有没有办法在WinForms中创建一个工具窗口,只要托管表单有焦点,工具窗口也可以?这方面的一个例子是在Paint.NET中:

聚焦工具窗口

我在.Net 4.0下使用C#作为后端应用程序语言.

Lar*_*ech 3

旧版本 Paint.Net 的源代码可在Paint.NET 3.36.7 的 openpdn Fork上找到

我尝试将他们的方法从源代码中提取到我能收集到的最简洁的工作示例中:

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);
}
Run Code Online (Sandbox Code Playgroud)

基本形式:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

始终处于活动状态 Form2(除非应用程序未处于活动状态):

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);
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Form2 无需将 TopMost 设置为 true,因为显示时它应归主窗体所有另外,Form2不是MDI 子窗体。