[英]subscribing to different events on Property changed if different properties
我有Step類,其中有Task的集合,即List。 步驟具有屬性Status,Time。 任務也具有相同的屬性。 每當任何任務的時間或狀態更改時,都需要更新“狀態”和“步驟時間”的值。 為此,我要在Step類的每個任務中添加處理程序。
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
}
這是任務公共事件PropertyChangedEventHandler PropertyChanged中的屬性更改事件處理程序;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
我的問題是,即使我僅更改Task Time,它也會同時調用處理程序的狀態和時間,因為它們訂閱了任務上相同的屬性更改事件。
我如何基於從調用的Property來對Property更改的事件進行分叉,並確保僅調用相應的處理程序,而不能一起調用?
抱歉,這聽起來很傻,但是我有點像WPF的初學者。
問候,P
每個事件都有“訪問器”添加或刪除。 類似屬性的獲取/設置。 該訪問器可以向您顯示事件的性質。 每個事件都有一個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”,baseClasses或諸如FODY之類的MethorVeawers等各種方法來改進此功能
您需要檢查傳入的args的參數以獲取屬性的名稱。
首先擺脫雙重訂閱。
private void AddHandlers()
{
foreach (Task tsk in Tasks)
{
tsk.PropertyChanged += HandlePropertyChanged;
}
}
然后為事件使用正確的簽名,以便獲得正確類型的事件args。
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
現在我們有了PropertyChangedEventArgs
而不是EventArgs
我們可以檢查PropertyName
屬性並調用所需的方法。
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch(e.PropertyName)
{
case "Status":
UpdateStepStatusFromTasks();
break;
case "Time":
UpdateStepTimesFromTasks();
break;
}
}
當您需要處理更多屬性時,可以將它們添加到switch語句中。
PS,您可以使用BindingList<Task>
作為保存任務的集合,而不是手動預訂每個Task
,然后可以訂閱ListChanged
事件,如果列表中的任何項目引發PropertyChanged(確保啟用RaiseListChangedEvents
並檢查ListChangedEventArgs.ListChangedType
等於ListChangedType.ItemChanged
)。
因此,這里很明顯的事情是您將兩個處理程序附加到``事件上,因此所有內容都被處理了兩次。 它只需要訂閱一次。
但是,與其使用大量的代碼在各處彈跳來制作許多復雜的方法,我更喜歡使用Microsoft的Reactive Extensions(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 };
此查詢基本上獲取Tasks
列表,並將每個任務的所有PropertyChanged
事件轉換為單個可觀察對象,該可觀察對象在任務具有屬性更改時返回每個Task
,並返回更改的任務的PropertyName
。
現在,很容易創建更多可觀察的對象,它們通過PropertyName
過濾並返回Task
:
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());
您會注意到每個訂閱都是一個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);
}
唯一的新事物是CompositeDisposable
,它將兩個IDiposable
訂閱連接到一個IDisposable
。
這種方法的優點是,現在大多數代碼都位於一個方法中。 這樣一來,至少在經過少量學習后,它就易於理解和維護。 :-)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.