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