【发布时间】: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()阻止任何进一步的代码执行,直到表单关闭.
- 我一直在玩弄 the code from this guide 来显示和隐藏来自
我自己一直在玩代码,上述方法都没有奏效。我是否以错误的方式思考这个问题? 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) => this.Hide(); -
@Marius 那个特定的代码只会让它无法显示,虽然......
-
当然可以。只是不要从表格中进行检测。在主程序中执行它们,并在检测到密钥时仅显示适用的形式。并且要么使用 ShowDialog 并每次创建一个新的表单对象,要么创建一次表单并在该对象上使用显示/隐藏。