C# 如果不同的属性发生变化,则订阅属性上的不同事件
我有一个类步骤,它有一个任务集合,即列表。 步骤具有属性状态、时间。任务也具有相同的属性。当任何任务的时间或状态发生更改时,都需要更新步骤的状态和时间值。 为此,我将向Step类中的每个任务添加处理程序C# 如果不同的属性发生变化,则订阅属性上的不同事件,c#,wpf,inotifypropertychanged,C#,Wpf,Inotifypropertychanged,我有一个类步骤,它有一个任务集合,即列表。 步骤具有属性状态、时间。任务也具有相同的属性。当任何任务的时间或状态发生更改时,都需要更新步骤的状态和时间值。 为此,我将向Step类中的每个任务添加处理程序 private void AddHandlers() { foreach (Task tsk in Tasks) { tsk.PropertyChanged += HandleStatusChang
private void AddHandlers()
{
foreach (Task tsk in Tasks)
{
tsk.PropertyChanged += HandleStatusChanged;
tsk.PropertyChanged += HandleTimeChanged;
}
}
private void HandleStatusChanged(object sender, EventArgs e)
{
UpdateStepStatusFromTasks();
}
private void HandleTimeChanged(object sender, EventArgs e)
{
UpdateStepTimesFromTasks();
}
private void UpdateStepTimesFromTasks()
{
// logic for calculating Time for Step
}
private void UpdateStepStatusFromTasks()
{
// logic for calculating Status for Step
}
以下是任务中的属性已更改事件处理程序
公共事件属性更改事件处理程序属性更改
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
我的问题是,即使我只更改任务时间,它也会调用处理程序状态和时间,因为它们订阅了任务上相同的属性更改事件
如何根据从调用的属性将属性更改事件分为两部分,并确保只调用相应的处理程序,而不同时调用这两个处理程序
对不起,如果这听起来很傻,但我有点初学者WPF
问候,,
P每个事件都有添加或删除的“访问器”。类似于属性的get/set。此访问器可以向您显示事件的性质。每个事件都有一个InvocationList,它表示在引发事件时将通知的对象集合。使用此访问器,您可以更好地控制哪些内容得到通知,哪些不得到通知。当您订阅事件时,订阅的对象将被插入到调用列表中
由于您为两个事件订阅相同的对象,因此将触发它两次 您唯一能做的就是检查更新的属性的名称public void ChangedHandler(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName=="Time"){//do something}
else if (e.PropertyName == "Date") {doSomething}
}
由于您正在处理WPF,我在这里看到一个奇怪的模式。您正在从各种方法引发事件。您应该从要为其发出通知的属性引发事件,该属性绑定到控件
public class MyVM
{
private string _status = "status1";
public string Status
{
get
{
return _status;
}
set
{
if(_status!=value)
{
_status =value
OnPropertyChanged("Status");
}
}
}
}
您可以使用诸如“nameof”、基类或MethorVeawers之类的各种工具来改进这一点,例如每个事件都有添加或删除的“访问器”。类似于属性的get/set。此访问器可以向您显示事件的性质。每个事件都有一个InvocationList,它表示在引发事件时将通知的对象集合。使用此访问器,您可以更好地控制哪些内容得到通知,哪些不得到通知。当您订阅事件时,订阅的对象将被插入到调用列表中
由于您为两个事件订阅相同的对象,因此将触发它两次
您唯一能做的就是检查更新的属性的名称
public void ChangedHandler(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName=="Time"){//do something}
else if (e.PropertyName == "Date") {doSomething}
}
由于您正在处理WPF,我在这里看到一个奇怪的模式。您正在从各种方法引发事件。您应该从要为其发出通知的属性引发事件,该属性绑定到控件
public class MyVM
{
private string _status = "status1";
public string Status
{
get
{
return _status;
}
set
{
if(_status!=value)
{
_status =value
OnPropertyChanged("Status");
}
}
}
}
您可以使用诸如“nameof”、基类或MethorVeawers(如等)之类的方法对此进行改进。您需要检查传入的参数以获取属性的名称 首先摆脱你的双重订阅
private void AddHandlers()
{
foreach (Task tsk in Tasks)
{
tsk.PropertyChanged += HandlePropertyChanged;
}
}
然后为事件使用正确的签名,以便获得正确类型的事件参数
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
现在我们有了PropertyChangedEventArgs
而不仅仅是EventArgs
,我们可以检查属性并调用所需的方法
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "Status":
UpdateStepStatusFromTasks();
break;
case "Time":
UpdateStepTimesFromTasks();
break;
}
}
由于需要处理更多的属性,您可以将它们添加到switch语句中
注意:您可以使用a作为保存任务的集合,而不是手动订阅每个
任务,然后您可以订阅该事件,如果列表中的任何项引发属性更改,则将引发该事件(确保启用并选中等于ListChangedType.ItemChanged
).您需要检查传入的参数以获取属性名称
首先摆脱你的双重订阅
private void AddHandlers()
{
foreach (Task tsk in Tasks)
{
tsk.PropertyChanged += HandlePropertyChanged;
}
}
然后为事件使用正确的签名,以便获得正确类型的事件参数
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
现在我们有了PropertyChangedEventArgs
而不仅仅是EventArgs
,我们可以检查属性并调用所需的方法
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "Status":
UpdateStepStatusFromTasks();
break;
case "Time":
UpdateStepTimesFromTasks();
break;
}
}
由于需要处理更多的属性,您可以将它们添加到switch语句中
另外,不必手动订阅每个任务
您可以使用a作为保存任务的集合,然后您可以订阅事件,如果列表中的任何项发生了属性更改(请确保启用并选中等于ListChangedType.ItemChanged
),则会引发该事件。,这里很明显的一点是,您将两个处理程序附加到``事件,因此所有内容都将被处理两次。它只需要订阅一次
但是,我宁愿使用微软的反应式扩展(Rx)——NuGet“Rx Main”——来处理事件,而不是使用大量复杂的方法来处理到处都是的代码。在学习了一些基本操作符之后,它确实使处理事件变得更加容易
用过于简单的术语来说,Rx是事件的LINQ。它允许您使用查询来处理事件,而不是枚举。它创建可观察的对象
首先,我将创建这个可观察的:
var tpns = // IObservable<{anonymous}>
from t in Tasks.ToObservable()
from ep in Observable.FromEventPattern<
PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => t.PropertyChanged += h,
h => t.PropertyChanged -= h)
select new { Task = t, ep.EventArgs.PropertyName };
这些应该很容易理解
现在订阅每个(基本上类似于附加到事件):
您会注意到每个订阅都是一个IDisposable
。您只需在订阅上调用.Dispose()
,所有底层事件处理程序都将为您分离,而不是使用-=
操作符从事件中分离
现在,我建议更改AddHandlers
方法以返回IDisposable
。然后,调用AddHandlers
的代码可以处理这些处理程序(如果需要的话),以确保您可以在退出之前进行清理
因此,完整的代码如下所示:
private IDisposable AddHandlers()
{
var tpns = // IObservable<{anonymous}>
from t in Tasks.ToObservable()
from ep in Observable.FromEventPattern<
PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => t.PropertyChanged += h,
h => t.PropertyChanged -= h)
select new { Task = t, ep.EventArgs.PropertyName };
IObservable<Task> statusChanges =
from tpn in tpns
where tpn.PropertyName == "Status"
select tpn.Task;
IObservable<Task> timeChanges =
from tpn in tpns
where tpn.PropertyName == "Time"
select tpn.Task;
IDisposable statusSubscription =
statusChanges
.Subscribe(task => UpdateStepStatusFromTasks());
IDisposable timeSubscription =
timeChanges
.Subscribe(task => UpdateStepTimesFromTasks());
return new CompositeDisposable(statusSubscription, timeSubscription);
}
private IDisposable AddHandlers()
{
var tpns=//IObservable
来自t i