从不同 Class 向多个表单发送更新事件 - WPF
Sending Update Events to Multiple Forms from a different Class - WPF
结构
我有一个简单的表单,它会触发一个非常定期检查更新的计时器。在加载时启动的表单构造函数如下所示:
public MainWindow()
{
InitializeComponent();
otherWindow = new TheOtherWindow();
if (Meta.hasUpdate)
{
updateImage.Source = new BitmapImage(new Uri("/MyProject;component/Images/updateTrue.gif", UriKind.Relative));
}
Thread updateMonitor = new Thread(() =>
{
UpdateManager updater = new UpdateManager();
updater.StartUpdateMonitor();
});
updateMonitor.IsBackground = true;
updateMonitor.Start();
}
Meta class 包含一些非常基本的信息,存储在多个地方引用但有时会更新的各种字符串。在那个结构中是这样的:
class Meta
{
...
private static bool hasUpdate = false;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
}
}
另一部分是 UpdateManager class,其中包括一个每 5 分钟检查一次更新的小例程。
class UpdateManager
{
Timer timer;
public void CheckForUpdates(Object source, ElapsedEventArgs e)
{
if (!isUpToDate())
{
timer.Stop();
Meta.SetHasUpdate(true);
Application.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("A new update is now available!);
}));
}
}
public void StartUpdateMonitor()
{
float updateInterval = 300000;
timer = new Timer(updateInterval); // Milliseconds between checks.
timer.Elapsed += CheckForUpdates;
timer.AutoReset = true;
timer.Enabled = true;
}
}
问题
简而言之,我想在达到 Meta.SetHasUpdate() 时触发一个事件,然后将其广播到应用程序中的所有表单,目的是更改一个小图标以指示更新可用。
我这样做的尝试以我了解到实施 INotifyPropertyChanged 不能很好地与静态成员配合而告终。这是我尝试实现的...
class Meta : INotifyPropertyChanged
{
...
private static bool hasUpdate = true;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
NotifyPropertyChanged();
}
private static void NotifyPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(null, new PropertyChangedEventArgs("hasUpdate"));
}
}
}
由于这些成员需要从多个表单中读回,我不能在不传递对象的情况下使它们不是静态的,我不想这样做。
在这种情况下,如何触发多个表单可以从 Meta class 接收的事件?我需要考虑不同的结构,还是我误解了 INotifyPropertyChanged?
虽然可以有很多方法来解决这个问题,(想想你的 Meta class 的 DI 到你的每个页面的 ViewModels 并对 INPC 做出反应......这比单例方法更受欢迎),一种方法要考虑的是使用消息而不是事件。消息(在大多数 MVVM 框架中提供)是在松散耦合的组件之间进行通信的好方法。如果您利用像 MVVM Light 这样的 MVVM 库,那么这很容易,因为它包含一个 Messenger 实现。这种方法的主要优点是您想要接收通知的表单不一定需要保留对源的引用,就像您使用基于事件的方法一样。
只需让所有感兴趣的表单注册一条消息,并在收到时做出相应反应。
例如,使用 MVVM Light,我们可以利用在更新 INPC 属性 时自动广播消息的优势。
private bool hasUpdate;
public bool HasUpdate
{
{
return hasUpdate;
}
set
{
// the last bool param indicates whether or not to broadcast a message to all interested parties.
Set(nameof(HasUpdate), ref hasUpdate, value, true);
}
}
然后在应用程序的一个完全独立/不相关的部分(通常在 ViewModel 中),我们可以这样做以表明我们对这样的更新感兴趣:
Messenger.Default.Register<PropertyChangedMessage<bool>>(this, m => ReceiveHasUpdatedMessage(m));
然后在接收 lambda 中:
private void ReceiveHasUpdatedMessage(PropertyChangedMessage<bool> m)
{
// react accordingly.
}
这只是 MVVM Light 提供的 Messenger
的一个简单用例。您几乎可以做任何您想做的事情。这里的前提是使用这种方法可以将感兴趣的各方从要求对发射器的硬引用中分离出来。
结合大家非常有帮助的建议,我整理了以下代码。上面是MVVM的解决方案,虽然我没有测试。如果您没有使用 MVVM,这就是我所做的。
UpdateManager class 是一样的。 Meta 具有以下结构:
class Meta
{
private static bool hasUpdate = false;
public static event PropertyChangedEventHandler StaticPropertyChanged;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
StaticNotifyPropertyChanged();
}
private static void StaticNotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
}
然后,对于我想知道这种变化的任何形式,我插入以下代码:
public partial class SomeForm : Window
{
public SomeForm()
{
InitializeComponent();
Meta.StaticPropertyChanged += MethodThatTriggersOnUpdate;
...
}
private void MethodThatTriggersOnUpdate(object sender, EventArgs e)
{
myImage.Dispatcher.BeginInvoke(
(Action)(() => myImage.Source = new BitmapImage(
new Uri("/MyProject;component/Images/myNewImage.gif", UriKind.Relative))));
}
...
}
结构
我有一个简单的表单,它会触发一个非常定期检查更新的计时器。在加载时启动的表单构造函数如下所示:
public MainWindow()
{
InitializeComponent();
otherWindow = new TheOtherWindow();
if (Meta.hasUpdate)
{
updateImage.Source = new BitmapImage(new Uri("/MyProject;component/Images/updateTrue.gif", UriKind.Relative));
}
Thread updateMonitor = new Thread(() =>
{
UpdateManager updater = new UpdateManager();
updater.StartUpdateMonitor();
});
updateMonitor.IsBackground = true;
updateMonitor.Start();
}
Meta class 包含一些非常基本的信息,存储在多个地方引用但有时会更新的各种字符串。在那个结构中是这样的:
class Meta
{
...
private static bool hasUpdate = false;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
}
}
另一部分是 UpdateManager class,其中包括一个每 5 分钟检查一次更新的小例程。
class UpdateManager
{
Timer timer;
public void CheckForUpdates(Object source, ElapsedEventArgs e)
{
if (!isUpToDate())
{
timer.Stop();
Meta.SetHasUpdate(true);
Application.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("A new update is now available!);
}));
}
}
public void StartUpdateMonitor()
{
float updateInterval = 300000;
timer = new Timer(updateInterval); // Milliseconds between checks.
timer.Elapsed += CheckForUpdates;
timer.AutoReset = true;
timer.Enabled = true;
}
}
问题
简而言之,我想在达到 Meta.SetHasUpdate() 时触发一个事件,然后将其广播到应用程序中的所有表单,目的是更改一个小图标以指示更新可用。
我这样做的尝试以我了解到实施 INotifyPropertyChanged 不能很好地与静态成员配合而告终。这是我尝试实现的...
class Meta : INotifyPropertyChanged
{
...
private static bool hasUpdate = true;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
NotifyPropertyChanged();
}
private static void NotifyPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(null, new PropertyChangedEventArgs("hasUpdate"));
}
}
}
由于这些成员需要从多个表单中读回,我不能在不传递对象的情况下使它们不是静态的,我不想这样做。
在这种情况下,如何触发多个表单可以从 Meta class 接收的事件?我需要考虑不同的结构,还是我误解了 INotifyPropertyChanged?
虽然可以有很多方法来解决这个问题,(想想你的 Meta class 的 DI 到你的每个页面的 ViewModels 并对 INPC 做出反应......这比单例方法更受欢迎),一种方法要考虑的是使用消息而不是事件。消息(在大多数 MVVM 框架中提供)是在松散耦合的组件之间进行通信的好方法。如果您利用像 MVVM Light 这样的 MVVM 库,那么这很容易,因为它包含一个 Messenger 实现。这种方法的主要优点是您想要接收通知的表单不一定需要保留对源的引用,就像您使用基于事件的方法一样。
只需让所有感兴趣的表单注册一条消息,并在收到时做出相应反应。
例如,使用 MVVM Light,我们可以利用在更新 INPC 属性 时自动广播消息的优势。
private bool hasUpdate;
public bool HasUpdate
{
{
return hasUpdate;
}
set
{
// the last bool param indicates whether or not to broadcast a message to all interested parties.
Set(nameof(HasUpdate), ref hasUpdate, value, true);
}
}
然后在应用程序的一个完全独立/不相关的部分(通常在 ViewModel 中),我们可以这样做以表明我们对这样的更新感兴趣:
Messenger.Default.Register<PropertyChangedMessage<bool>>(this, m => ReceiveHasUpdatedMessage(m));
然后在接收 lambda 中:
private void ReceiveHasUpdatedMessage(PropertyChangedMessage<bool> m)
{
// react accordingly.
}
这只是 MVVM Light 提供的 Messenger
的一个简单用例。您几乎可以做任何您想做的事情。这里的前提是使用这种方法可以将感兴趣的各方从要求对发射器的硬引用中分离出来。
结合大家非常有帮助的建议,我整理了以下代码。上面是MVVM的解决方案,虽然我没有测试。如果您没有使用 MVVM,这就是我所做的。
UpdateManager class 是一样的。 Meta 具有以下结构:
class Meta
{
private static bool hasUpdate = false;
public static event PropertyChangedEventHandler StaticPropertyChanged;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
StaticNotifyPropertyChanged();
}
private static void StaticNotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
}
然后,对于我想知道这种变化的任何形式,我插入以下代码:
public partial class SomeForm : Window
{
public SomeForm()
{
InitializeComponent();
Meta.StaticPropertyChanged += MethodThatTriggersOnUpdate;
...
}
private void MethodThatTriggersOnUpdate(object sender, EventArgs e)
{
myImage.Dispatcher.BeginInvoke(
(Action)(() => myImage.Source = new BitmapImage(
new Uri("/MyProject;component/Images/myNewImage.gif", UriKind.Relative))));
}
...
}