gpt4 book ai didi

来自后台线程的 WPF NotifyIcon

转载 作者:行者123 更新时间:2023-12-04 19:03:15 28 4
gpt4 key购买 nike

我知道通常不应该从 UI 线程以外的线程触摸 UI 元素,但我是 WPF 的新手,我想知道是否可以改进我当前的工作实现。

我有一个仅由通知托盘图标组成的应用程序,我想从后台线程更新该图标。

这是我的 Program.cs 入口点:

static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

using (IconHandler notify = new IconHandler())
{
notify.Display();
Application.Run();
}

}
}

这是我的 IconHandler.cs 通知图标处理程序类:
class IconHandler : IDisposable
{

NotifyIcon ni;

public IconHandler()
{
ni = new NotifyIcon();
}

public void Display()
{
ni.MouseClick += new MouseEventHandler(ni_MouseClick);
ni.Icon = Resources.icon1;
ni.Visible = true;

new Thread(new ThreadStart(UpdateIcon)).Start();
}

public void UpdateIcon()
{
while (true)
{
// reference ni directly, it updates fine
}
}

public void Dispose()
{
ni.Dispose();
}

void ni_MouseClick(object sender, MouseEventArgs e)
{
// something useful
}
}

这有什么明显不正确的地方吗?这对我来说似乎有点可疑——这只是我的第一次尝试。它似乎适用于我想做的事情,有人对更好的实现有任何建议吗?我会遇到此设置的生命周期问题吗?

最佳答案

Is there anything blatantly incorrect about this? It seems a bit fishy to me - it was just my first attempt. It seems to work for what I want to do, does anyone have any suggestions for a better implementation? Will I run into lifecycle issues with this setup?


NotifyIcon 开始不是 WPF 控件,而是来自 Windows 窗体命名空间。因此,它具有正常的 C# 属性(例如 IconVisible ),这意味着您可以在非 UI 线程中更改图标属性,而不会引发异常。如果您使用了 WPF 控件,则它们具有依赖属性,并且在 UI 线程之外直接操作依赖属性将导致引发异常。

Will I run into lifecycle issues with this setup?


您目前是 不是 创建了 WPF 窗口或 WPF 控件。如果您的应用程序开发为您开始使用 WPF 和 UpdateIcon方法扩展为比您当前做的更多并访问这些 WPF 对象,然后是的,您将需要一种策略来处理来自非 UI 线程的更新。

您可以使用一些辅助方法隐藏一些这种跨线程访问。

示例 1 如果您的策略变为以编程方式从后台线程引用 WPF 控件,那么您可以使用诸如此类的辅助方法。

它首先检查调用是否在 UI 线程上,如果是,则直接更新控件,否则它将安排在稍后的时间点从 UI 线程调用方法(本身)。

我用过 BeginInvoke在这里,后台线程可以在 UI 线程实际调用该方法之前继续。如果要阻止后台线程,请使用 Invoke反而。

public void UpdateLabel(Label control, string text)
{
if (Application.Current.Dispatcher.CheckAccess())
control.Content = text;
else
Application.Current.Dispatcher.BeginInvoke(new System.Action(() => UpdateLabel(control, text)), DispatcherPriority.Normal);
}

示例 2
如果您的策略使用 Events在后台线程上引发以编程方式更新 WPF 控件,然后您可以隐藏一些跨线程调用作为引发事件的一部分,从而使 WPF 更新例程非常干净且易于阅读。

此事件的任何事件处理程序都可以在知道调用将来自 UI 线程的情况下进行编码,因此不会出现线程问题。

public void OnRaiseEvent(EventHandler handler, EventArgs args)
{
if (handler != null)
{
if (Application.Current.Dispatcher.CheckAccess())
handler(sender, new PropertyChangedEventArgs(propName));
else
Application.Current.Dispatcher.BeginInvoke(new System.Action(() => handler(sender, args)), DispatcherPriority.Normal);
}
}

示例 3
如果您 future 的策略充分利用 WPF with Binding 的优势(而不是以编程方式更新您的 WPF 控件),那么您可以将跨线程代码嵌入到数据绑定(bind)对象中。

例如,如果您的 XAML数据绑定(bind)到 MyProperty MyDataClass 实例的属性类并且该类实现了 INotifyPropertyChanged 接口(interface),您可以将跨线程代码放在数据类中,从而可以从任何线程更新数据。这是该类(class)的示例:-

public class MyDataClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

private string _myProperty;

public string MyProperty { get { return _myProperty;} set { PropertyChanged.SetValueAndNotify(this, ref _myProperty, value); } }
}

此类使用 SetValueAndNotify PropertyChanged 上的扩展方法事件。正是在这里我们隐藏了跨线程代码以简化代码的其他部分。这是此扩展方法的定义。

public static class PropertyChangedExtension
{
public static void SetValueAndNotify<T>(this PropertyChangedEventHandler handler, object sender, ref T destination, T source, [CallerMemberName] string propName = "notset")
{
// Is the new value different from the previous value? If there is no difference then there is nothing more to do
if (Equals(destination, source))
return;

// If we got to this point then the new value is different from the old value, so lets make the assignemnt and raise the property changed event
destination = source;

if (handler != null)
{
if (Application.Current.Dispatcher.CheckAccess())
handler(sender, new PropertyChangedEventArgs(propName));
else
Application.Current.Dispatcher.BeginInvoke(new System.Action(() => handler(sender, new PropertyChangedEventArgs(propName))), DispatcherPriority.Normal);
}
}
}

上面的例子使用 [CallerMemberName] C#5 中的属性以消除在为 INotifyPropertyChanged 参数提供属性名称时出现的任何键入错误。如果您没有使用最新的,那么您将需要修改 getter 和 setter,如下所示:-

public string MyProperty { get { return _myProperty;} set { PropertyChanged.SetValueAndNotify(this, ref _myProperty, value, "MyProperty"); } }

关于来自后台线程的 WPF NotifyIcon,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31569361/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com