簡體   English   中英

如果屬性不同,則訂閱有關屬性的不同事件也會發生變化

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM