C#静态方法中的窗体建模

本文关键字:窗体 建模 静态方法 | 更新日期: 2023-09-27 17:58:24

我的C#程序遇到了一些问题。我想创建秒表(从某个值开始倒计时),即当按下某个键时开始计时。为了处理按键,我使用低级键盘挂钩。但是这个类有静态方法,所以如果我想从不同的类调用一个非静态的方法,我必须创建一个新的实例。随着倒计时,我想每隔一个刻度(秒)更改TextBox元素的Text属性。问题是,当我必须在静态方法中创建Countdown类的新实例时,如何在每个tick(在Countdown类中)更改TextBox的属性,从而TextBox将不再响应以前的TextBox。我的代码运行得很好,键可以识别,计时器正在倒计时,并在单独的MessageBox中显示秒值(用于调试),但它不会更改表单中的文本。

如果它能帮助你理解我上面写的内容,我可以给你我的代码。请在评论中这样说。

感谢您提前提供帮助。

代码:

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

namespace stopwatch2
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    private void Form1_Load(object sender, EventArgs e)
    {
        InterceptKeys.InterceptInit();
    }
    private void Form1_Closing(object sender, CancelEventArgs e)
    {
        InterceptKeys.Unhook();
    }
    public void changeText(string text)
    {
        MessageBox.Show(text); //for debug
        textBox1.Text = text;
    }

    class InterceptKeys
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;
        public static void InterceptInit()
        {
            _hookID = SetHook(_proc);
        }
        public static void Unhook()
        {
            UnhookWindowsHookEx(_hookID);
        }
        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }
        private delegate IntPtr LowLevelKeyboardProc(
            int nCode, IntPtr wParam, IntPtr lParam);
        public static IntPtr HookCallback(
            int nCode, IntPtr wParam, IntPtr lParam)
        {

            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                Countdown timer = new Countdown(); //creating new instance
                if ((Keys)vkCode == Keys.Home)
                {
                    timer.StartTimer();
                }
                if ((Keys)vkCode == Keys.End)
                {
                    timer.StopTimer();
                }
            }
            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);

    }
   public partial class Countdown : Form1
    {
        public System.Windows.Forms.Timer timer1;
        public int counter = 60;
        public void StartTimer()
        {
            timer1 = new System.Windows.Forms.Timer();
            timer1.Tick += new EventHandler(timer1_Tick);
            timer1.Interval = 1000; // 1 second
            timer1.Start();
            changeText(counter.ToString());
        }
        public void timer1_Tick(object sender, EventArgs e)
        {
            counter--;
            if (counter == 0)
                counter = 60;
            changeText(counter.ToString());
        }
        public void StopTimer()
        {
            timer1.Stop();
        }
     }
  }
}

C#静态方法中的窗体建模

因此,首先,您不希望Countdown扩展Form1。这会给你一种错觉,以为你可以访问Form1的成员,但你就是做不到。每次创建一个新的Countdown实例时,您都会创建一个全新的表单,该实例具有自己的文本框和自己的。。。每件事

更糟糕的是,每次触发钩子处理程序时,都会创建一个新的Countdown事件,因此不会在之前启动的Countdown的同一实例上停止计时器。

InterceptKeys也不应该是Form1的内部类,它应该是自己文件中自己的独立类。

老实说,我甚至不认为Countdown类应该存在;它的方法应该属于另外两个类中的一个。

让我们从InterceptKeys开始。您可以看到,90%的类只是创建挂钩,并确定它是否是您"关心"的事件。然后,当我们关心的事情发生时,我们只有一点点代码(10%)来做任何需要发生的事情。

我们有一种方法可以更有效地处理这一问题,以解决"关注点分离"问题。InterceptKeys类只需要处理设置键盘挂钩并过滤掉我们不关心的代码的样板代码。我们想把最后10%的代码移出这个类。活动是一个很好的方式来做到这一点。从逻辑上讲,这里发生了两个"事件",一个是当按下主页键时,另一个是按下结束键时。因此,我们将从在InterceptKeys内部创建以下两个事件开始:

public static event Action HomePressed;
public static event Action EndPressed;

现在,我们可以只触发这两个事件,而不是在适当的位置调用另一个类的方法。只需更换:

Countdown timer = new Countdown(); //creating new instance
if ((Keys)vkCode == Keys.Home)
{
    timer.StartTimer();
}
if ((Keys)vkCode == Keys.End)
{
    timer.StopTimer();
}

带有:

if ((Keys)vkCode == Keys.Home)
{
    if(HomePressed != null) HomePressed();
}
if ((Keys)vkCode == Keys.End)
{
    if(EndPressed != null) EndPressed();
}

那么,现在InterceptKeys有两个事件,现在呢?现在我们转到Form1中初始化InterceptKeys的位置,并处理事件。在我们这样做之前,我们首先要获取Countdown中的所有内容,并将其放在Form1中,只需移动整个内容。有了所有这些方法,我们可以做到这一点:

private void Form1_Load(object sender, EventArgs e)
{
    InterceptKeys.InterceptInit();
    InterceptKeys.HomePressed += ()=> StartTimer();
    InterceptKeys.EndPressed += ()=> StopTimer();
}

现在,无论何时按下这两个键中的一个,都会在该表单的现有方法上调用适当的方法。除此之外,我们现在已经将操纵表单显示的所有代码移动到该表单的定义中,同时确保所有讨厌的键盘挂钩都在它自己的小世界中消失。"分忧",一切都在自己合适的地方。

附带说明一下,您应该真正使timer1counter成为私有字段,而不是公共字段。你没有公开使用它们,这很好,但你不想在未来这样做。您可以根据需要创建提供对这些字段的有限访问权限的方法(这就是您目前正在做的)。

只剩下一件事了。每次按下主页时,您都可能不想启动新的计时器。旧计时器仍然存在,因此您将有两个、三个或更多计时器。更有可能的是,您只想重新启动现有的计时器。这很容易做到。

StartTimer中,您不能创建新的计时器,而是可以操作现有的计时器。删除除以外的所有内容

timer1.Start();
changeText(counter.ToString());

然后在表单首次加载时创建并配置Timer

private void Form1_Load(object sender, EventArgs e)
{
    timer1 = new System.Windows.Forms.Timer();
    timer1.Tick += new EventHandler(timer1_Tick);
    timer1.Interval = 1000; // 1 second
    InterceptKeys.InterceptInit();
    InterceptKeys.HomePressed += ()=> StartTimer();
    InterceptKeys.EndPressed += ()=> StopTimer();
}