【问题标题】:C# make form hide and then show againC#使表单隐藏然后再次显示
【发布时间】:2018-02-28 06:02:18
【问题描述】:

我正在尝试使 Windows 窗体应用程序能够重复显示然后隐藏自身。

这个想法是创建一个小应用程序,每次按下 Num Lock 或 Caps Lock 等修饰键时,它都会在屏幕上覆盖图像。我检测到键盘键工作正常,但我没有太多运气来创建一个我可以显示然后重复隐藏的表单。

我的看法,(如果我错了,请纠正我)有两种可能的方法可以使表单表现得像我想要的那样:

  • Program.cs中正常启动表单,然后在Form1.cs中保持隐藏和显示表单以及显示图像的逻辑
    • 表单会调用this.Hide()this.Show() 来隐藏和显示自己,但是每当我尝试这样做时,我都无法让this.Hide() 隐藏表单;表单在所有打开的窗口顶部仍然可见。
  • Program.cs中保留隐藏和显示表单的逻辑,然后在Form1.cs中保留显示图像的逻辑
    • 我一直在玩弄 the code from this guide 来显示和隐藏来自 Program.cs 的表单的类包装器,但事实证明,form.Showdialog() 阻止任何进一步的代码执行,直到表单关闭.

我自己一直在玩代码,上述方法都没有奏效。我是否以错误的方式思考这个问题? WFA 可以按照我想要的方式行事吗?

这里的代码有点乱,我不确定这里的哪些部分最相关,但我会尽我所能把它包括在这里:

Program.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace KeyboardIndicators {
  // ApplicationContext wrapper
  public abstract class TrayIconApplicationContext : ApplicationContext {
    private NotifyIcon lockIcon;
    private ContextMenu lockIconContext;

    protected TrayIconApplicationContext() {
      // Wire up the ApplicationExitHandler to ApplicationExit events
      Application.ApplicationExit += this.ApplicationExitHandler;

      lockIconContext = new ContextMenu {

      };

      // Create lockIcon tray icon and make it visible
      lockIcon = new NotifyIcon {
        ContextMenu = lockIconContext,
        Text = Application.ProductName,
        Icon = new Icon("icon.ico"),
        Visible = true
      };

    }

    protected NotifyIcon LockIcon { get { return lockIcon; } }
    protected ContextMenu LockIconContext { get { return lockIconContext; } }


    // ApplicationExit event handler
    private void ApplicationExitHandler(object sender, EventArgs e) {
      this.OnApplicationExit(e);
    }

    // Performs cleanup to end the application
    protected virtual void OnApplicationExit(EventArgs e) {
      // TODO(Neil): Add meaningful thread cleanup here soon
      if (lockIcon != null) {
        lockIcon.Visible = false;
        lockIcon.Dispose();
      }
      if (lockIconContext != null)
        LockIconContext.Dispose();
    }
  }

  // TrayIconApplicationContext wrapper for Form1 to control the activation of the form window
  class FormApplicationContext : TrayIconApplicationContext {
    public FormApplicationContext() {
      // Add Exit menu item
      MenuItem exit = new MenuItem("E&xit");
      this.LockIconContext.MenuItems.Add(exit);
      exit.Click += this.ExitContextMenuClickHandler;

      //KeyboardIndicators indicators = new KeyboardIndicators();
      //indicators.RunListener();

      {
        using(Form form = new Form1("NumLock", true))
          form.ShowDialog();
      }
    }

    private void ExitContextMenuClickHandler(object sender, EventArgs eventArgs) {
      this.ExitThread();
    }
  }

  public class KeyboardIndicators {
    class LockState {
      // Is the numlock key on?
      public bool Num;
      // Is the capslock key on?
      public bool Caps;
      // Is the scroll lock key on?
      public bool Scroll;
    }

    public void RunListener() {
      try {
        // Store the old keyboard lock state
        LockState prevState = new LockState() {
          Num = Control.IsKeyLocked(Keys.NumLock),
          Caps = Control.IsKeyLocked(Keys.CapsLock),
          Scroll = Control.IsKeyLocked(Keys.Scroll)
        };

        while (true) {
          // Store the new keyboard lock state
          LockState newState = new LockState() {
            Num = Control.IsKeyLocked(Keys.NumLock),
            Caps = Control.IsKeyLocked(Keys.CapsLock),
            Scroll = Control.IsKeyLocked(Keys.Scroll)
          };

          //TODO(Neil): Handle simultaneous presses better, i.e. queue the balloon tips
          if (newState.Num != prevState.Num) {
            Form1 form = new Form1("NumLock", newState.Num);
          } else if (newState.Caps != prevState.Caps) {
            Form1 form = new Form1("CapsLock", newState.Caps);
          } else if (newState.Scroll != prevState.Scroll) {
            Form1 form = new Form1("ScrollLock", newState.Scroll);
          }

          // Set the previous lock state to the new one in prep for the next iteration
          prevState = newState;

          // Sleep for 500ms
          Thread.Sleep(500);
        }
      } catch (ThreadAbortException) { /* No need to do anything, just catch the ThreadAbortException.*/ }
    }
  }

  static class Program {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);

      //Application.Run(new Form1("NumLock", true));
      Application.Run(new FormApplicationContext());
    }
  }
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace KeyboardIndicators {
  public partial class Form1 : Form {
    public Form1(String activatedModifier, bool lockState) {
      InitializeComponent();
      ShowForm(activatedModifier);

      //this.Show();
      //this.Hide();
    }

    public void ShowForm(String activatedModifier) {
      PictureBox pictureBox = new PictureBox();

      Image myBitmap = Image.FromFile("cube.png");
      Size bitmapSize = new Size(myBitmap.Width, myBitmap.Height);

      switch (activatedModifier) {
        case "NumLock":
          break;
        case "CapsLock":
          break;
        case "ScrollLock":
          break;
      }

      this.Size = bitmapSize;
      pictureBox.ClientSize = bitmapSize;

      pictureBox.Image = myBitmap;
      pictureBox.Dock = DockStyle.Fill;
      this.Controls.Add(pictureBox);
      this.FormBorderStyle = FormBorderStyle.None;
    }

    protected override CreateParams CreateParams {
      get {
        CreateParams createParams = base.CreateParams;
        createParams.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT

        return createParams;
      }
    }
  }
}

【问题讨论】:

  • 我注意到您尝试在Form1 构造函数中调用this.Hide()(代码已被注释掉)。这不起作用see this SO answer。如果在Shown 事件处理程序中调用this.Hide(),是否可以隐藏表单?
  • @Marius 所以我明白了。通过事件处理程序,您的意思是 ShowForm()?
  • 不,我的意思是 Shown 事件(请参阅msdn)。您可以调用它,例如像这样this.Shown += (s, e) =&gt; this.Hide();
  • @Marius 那个特定的代码只会让它无法显示,虽然......
  • 当然可以。只是不要从表格中进行检测。在主程序中执行它们,并在检测到密钥时仅显示适用的形式。并且要么使用 ShowDialog 并每次创建一个新的表单对象,要么创建一次表单并在该对象上使用显示/隐藏。

标签: c# winforms


【解决方案1】:

作为示例,我创建了一个 Winforms 应用程序,如果按下这些键中的任何一个,它会显示“NUM”和/或“CAPS”,否则表单会被隐藏。 密钥的检测基于C# Low Level Keyboard Hook

Program.cs

using System;
using System.Windows.Forms;

namespace WinFormsKeyHook
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

Form1.cs

注意:在 Designer 中为 Form1 添加一个名为“label1”的标签。

using System.Collections.Generic;
using System.Windows.Forms;

namespace WinFormsKeyHook
{
    public partial class Form1 : Form
    {
        private static bool _caps;
        private static bool _num;

        public Form1()
        {
            InitializeComponent();

            KeyboardHook kh = new KeyboardHook();
            kh.KeysToObserve.AddRange(new List<Keys> { Keys.CapsLock, Keys.NumLock });
            kh.InstallHook();
            kh.KeyDown = key => ProcessKeyDown(key);

            _caps = Control.IsKeyLocked(Keys.CapsLock);
            _num = Control.IsKeyLocked(Keys.NumLock);
        }

        private void ProcessKeyDown(Keys key)
        {

            if (key == Keys.CapsLock)
            {
                _caps = !_caps;
            }
            if (key == Keys.NumLock)
            {
                _num = !_num;
            }

            this.ShowState(_num, _caps);
        }

        internal void ShowState(bool num, bool caps)
        {
            if (!num && !caps)
            {
                this.Hide();
                return;
            }

            this.label1.Text = "";
            this.label1.Text += num ? "NUM " : "";
            this.label1.Text += caps ? "CAPS" : "";

            if (!this.Visible)
            {
                this.Show();
            }
        }
    }
}

KeyboardHook.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WinFormsKeyHook
{
    public class KeyboardHook
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private IntPtr _hookID = IntPtr.Zero;

        public List<Keys> KeysToObserve { get; set; } = new List<Keys>();

        public Action<Keys> KeyDown;

        public void InstallHook()
        {
            _hookID = SetHook(HookCallback);
        }

        ~KeyboardHook()
        {
            UnhookWindowsHookEx(_hookID);
        }

        public IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        public IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
             if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                var key = (Keys)vkCode;
                Console.WriteLine(key);
                KeyDown(key);
            }

            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
    }
}

【讨论】:

  • KeyboardHook 类的具体用途是什么?是不是表单阻塞在线程和KeyboardHook 需要引起中断来更改表单文本?
  • 在我看来,在while 循环中使用Thread.Sleep() 不是好的做法see this SO post for a discussion。我认为您想要实现的是对键盘输入做出反应,这可以通过挂钩事件来更好地反映。您还可以使用每 500 毫秒触发一次的计时器来代替键盘挂钩。我只是想给你一个可行的例子,因为我已经知道它是如何工作的,所以我用更少的时间来处理键盘钩子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-16
  • 2017-12-13
  • 2017-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多