静态方法中的C#形式修改

静态方法中的C#形式修改,c#,winforms,static,textbox,instance,C#,Winforms,Static,Textbox,Instance,我的C#程序遇到了一些问题。我想创建秒表(从某个值开始倒计时),即在按下某个键时开始计时。我使用低级键盘挂钩来处理按键。但是这个类有静态方法,所以如果我想从不同的类调用一个方法,那不是静态的,我必须创建一个新实例。在倒计时时,我希望每隔一个刻度(秒)更改TextBox元素的Text属性。问题是,当我必须在静态方法中创建一个新的Countdown类实例时,如何更改TextBox每个勾号(在Countdown类中)的属性,因此TextBox将不再响应以前的TextBox。我的代码工作得非常好,按键可

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

如果它能帮助你理解我上面写的东西,我可以给你我的代码。在评论中就这么说吧

提前谢谢你的帮助

守则:

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();
        }

     }

  }

}

因此,首先,您不希望
Countdown
扩展
Form1
。这会给您一种错误的印象,即您可以访问
表单1
的成员,但您就是不能。每次创建一个新的
倒计时
实例时,您都在创建一个全新的表单,它有自己的文本框和…所有内容

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

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

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

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

我们有一种方法可以更有效地处理这一问题,以解决“关注点分离”问题。InterceptKeys类只需要处理设置键盘挂钩的样板代码,并过滤掉我们不关心的那些。我们想把最后10%的代码移出这个类。活动是一种很好的方式。从逻辑上讲,这里有两个“事件”,一个是当按下home键时,另一个是当按下end键时。因此,我们将首先在
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();
}
与:

那么,现在
InterceptKeys
有两个事件,现在呢?现在我们转到
InterceptKeys
Form1
中初始化的地方,并处理事件。在我们这样做之前,我们首先要抓住
倒计时
中的所有内容,并将其放在
表单1
中,只需移动整个内容。通过所有这些方法,我们可以做到这一点:

private void Form1_Load(object sender, EventArgs e)
{
    InterceptKeys.InterceptInit();
    InterceptKeys.HomePressed += ()=> StartTimer();
    InterceptKeys.EndPressed += ()=> StopTimer();
}
现在,只要按下这两个键中的一个,就会在该窗体的现有方法上调用相应的方法。除此之外,我们现在已经将操纵表单显示的所有代码移动到表单的定义中,同时确保所有讨厌的键盘钩子都在它自己的小世界中关闭。“关注点分离”,一切都在它自己合适的地方

另一方面,您应该真正将
timer1
counter
设置为私有字段,而不是公共字段。你没有公开使用它们,这很好,但你不想在将来这样做。您可以创建方法,根据需要提供对这些字段的有限访问(这就是您当前正在做的)

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

StartTimer
中,您不能创建新的计时器,而是可以操作现有的计时器。从它的身体上移除所有东西,除了:

timer1.Start();
changeText(counter.ToString());
然后,只需在首次加载表单时创建并配置
计时器

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();
}

请务必显示(相关)代码。如果不查看代码,很难准确了解问题所在。您正在实现什么样的计时器?这可能是重复的:尝试使用System.Windows.Forms.Timer,而不是System.Timers.Timer或System.Threading.Timer。我已经添加了代码。感谢您的快速回复。:)我绝对爱你!:)非常感谢您的大力帮助@不客气。在将来进行winform编程时,请注意应用这些相同类型的技术。老实说,这是我在c#中的第一个代码,基本上是第一个使用OOP的代码。我以前只用C和PHP写过一些东西。我现在将扩展计时器,因此倒计时时间也将由热键设置。再次感谢您的帮助。:)@sobol6803是的,我可以告诉你这些步骤是新的,这就是为什么我花时间和精力彻底解释每个步骤,而不是仅仅发布一段代码或链接,以帮助你在将来重新生成这些步骤。
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();
}