winforms SystemEvents.TimeChanged未触发

5us2dqdw  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(100)

"我想要的"
每当Windows中的时区更改时,我都想做一些事情。

到目前为止

为此,我实现了事件SystemEvents.TimeChanged,如下所示:
在constructor中:

SystemEvents.TimeChanged += SystemEvents_TimeChanged;

字符串
事件主体:

void SystemEvents_TimeChanged(object sender, EventArgs e)
{
    MessageBox.Show("Test1");}

有什么问题

在Windows中更改时间或时区时,不会激发该事件。

我所尝试的

当我在一个干净的WinForms应用程序中编写上述代码时,一切都按预期工作。但在我的应用程序中却不是这样,因为它包含了很多其他代码。我没有看到我有任何其他事件,这应该阻止上述事件的触发。

我的问题是

有没有人知道是什么原因导致上面的代码在我的应用程序中没有被触发,但当我创建一个只包含上面代码的新项目/应用程序时,却能按预期工作?

更新1

发生这种情况是因为我在调用

Application.Run(new FormMain());


然后SystemEvents会粘在启动画面创建的线程上,即使这个线程在应用程序加载后被终止。
现在的问题是,是否有一种方法可以告诉SystemEvents,当应用程序加载时,现在应该使用“正确的”UI线程?

91zkwejq

91zkwejq1#

这个答案是关于问题更新1部分的,因此在原始问题中提供的代码示例是有效的。
我花了一些时间来弄清楚这一点。因此,我没有对您的代码进行全面概述,我为此解决方案即兴创作了2个WinForms(一个作为splash,另一个作为main),当然这只是说明概念的例子。
据我所知,当你启动你的软件,它开始与飞溅部分作为单独的线程,当飞溅完成后,比FormMain启动后。你可以做得更好,使用ApplicationContext。您创建了自己的上下文类,它是从ApplicationContext扩展而来的,在该类中,您使用各自的逻辑声明了Splash和FormMain。现在,在您的情况下,您需要确保FormMain在Splash或类似的东西之后的某个时候开始(我不知道你的软件是如何工作/流程的)。
在上下文类中,你创建了订阅和取消订阅SystemEvents.TimeChanged的方法,这样你就可以收听时间的变化。为了演示的目的,我还创建了一个BindingList来演示时间的变化。
现在让我们展示一些代码:

public static void Main()
{
    // use own created context
    MainApplicationContext context = new MainApplicationContext();
    Application.Run(context);
}

// just quick way to demonstrate how we collect time changes
public static BindingList<string> Logs { get; private set; }

private class MainApplicationContext : ApplicationContext
{
    private int _formCount;
    
    public MainApplicationContext()
    {
        Logs = new BindingList<string>();

        _formCount = 0;

        // splash screen
        var splash = new FormSplash();
        splash.Closed += OnFormClosed;
        splash.Load += OnFormOpening;
        splash.Closing += OnFormClosing;
        _formCount++;
        splash.Show();

        // For demo, make some logic that close splash when program loaded.
        Thread.Sleep(2000);

        var main = new FormMain();
        main.Closed += OnFormClosed;
        main.Load += OnFormOpening;
        main.Closing += OnFormClosing;
        _formCount++;

        splash.Close();
        main.Show();
    }

    private void OnFormOpening(object sender, EventArgs e)
    {
        SystemEvents.TimeChanged += SystemEvents_TimeChanged;
    }

    private void OnFormClosing(object sender, CancelEventArgs e)
    {
        SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
    }

    private void OnFormClosed(object sender, EventArgs e)
    {
        _formCount--;
        if (_formCount == 0)
        {
            ExitThread();
        }
    }
    
    private void SystemEvents_TimeChanged(object sender, EventArgs e)
    {
        var text = $"TimeChanged, Time changed;" +
            $" it is now {DateTime.Now.ToLongTimeString()}";
        Logs.Add(text);
    }
}

字符串
现在在我们的FormMain中,创建列表框,将其命名为LogListBox

public FormMain()
{
    InitializeComponent();
    Load += ListChanged;
}

// This keep list of time changes events updated if changed this
// could be log or some thing else.
private void ListChanged(object sender, EventArgs e)
{
    LogListBox.DataSource = Program.Logs;
}


下面是它的工作原理:x1c 0d1x
文件:

puruo6ea

puruo6ea2#

在单独的线程中显示启动画面之前,您可以从主线程调用下面的方法,将此线程与侦听系统事件的系统线程关联起来。

private static void InitializeSystemEvents()
{
    var timerId = SystemEvents.CreateTimer(1);
    SystemEvents.KillTimer(timerId);
}

字符串
这样,您以后就可以从运行在主线程中的主Form订阅SystemEvents

**更新:**问题的根源在于启动画面关闭时,监听系统事件的系统线程被杀死。因此,解决此问题的另一种方法是在应用程序的整个生命周期内保持启动画面打开(但隐藏)。

相关问题