亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何從 ViewModel 正確執行異步方法?

如何從 ViewModel 正確執行異步方法?

C#
一只甜甜圈 2022-10-23 13:58:05
出于這個問題的目的,我有一個簡單Window的以下 XAML:<StackPanel>    <TextBox Text="{Binding MyText}" />    <CheckBox IsChecked="{Binding IsChecked}">Check</CheckBox></StackPanel>每當用戶在TextBox或中輸入文本時CheckBox,我想執行一個緩慢的任務(例如將模型的狀態保存到磁盤)。這是視圖模型:public class ViewModel : ViewModelBase  // using GalaSoft.MvvmLight{    private string _myText;    public string MyText    {        get => _myText;        set        {            if (Set(ref _myText, value))                Save();        }    }    private bool _isChecked;    public bool IsChecked    {        get => _isChecked;        set        {            if (Set(ref _isChecked, value))                Save();        }    }    private async void Save()    {        var id = Guid.NewGuid();        Debug.WriteLine($"Starting save {id}");        await Task.Delay(100);  // Simulate slow task        Debug.WriteLine($"Finished save {id}");    }}該Save方法模擬一個緩慢的任務,例如保存到磁盤。出于調試目的,它會在執行此操作之前和之后輸出一個唯一 ID。此外,該方法是異步的,因為我不希望 UI 在操作期間凍結。問題是,在用戶在 中鍵入內容TextBox,然后檢查 后CheckBox,屬性會很快更新。這會導致以下調試示例輸出:Starting save 6ea6c102-cbe7-472f-b8b8-249499ff7f64Starting save c77b4478-14ca-4243-a45b-7b35b5663d49Finished save 6ea6c102-cbe7-472f-b8b8-249499ff7f64Finished save c77b4478-14ca-4243-a45b-7b35b5663d49如您所見,第一個保存操作(來自)在第二個保存操作(來自)開始MyText之前沒有完成。IsChecked這讓我有點害怕,因為我想,數據可能會以錯誤的順序保存并損壞。是否有處理此類問題的良好做法?我想了幾個可能的解決方案。Delay=100首先是在TextBox綁定中使用類似的東西。這將導致在Save用戶停止輸入 100 毫秒后調用該方法。由于各種原因,這是一個丑陋的解決方案。第二種是使用SemaphoreSlim. 在Save方法內部,我可以用try/將代碼括起來finally以使用此處所述的信號量。這實際上有效,但我不確定這是否是處理此問題的最佳方法。
查看完整描述

3 回答

?
慕桂英546537

TA貢獻1848條經驗 獲得超10個贊

是否有處理此類問題的良好做法?

如果您希望兩個保存都發生,那么使用鎖(或SemaphoreSlim)對它們進行序列化是一種方法。如果您想阻止第二次保存開始,那么通常的方法是在保存過程中禁用這些控件,例如,通過IsBusy數據綁定到您的 UI 的屬性。

注意事項:

  • IsBusy同步設置您的屬性。這會立即禁用控件。

  • IsBusy在 a中取消finally設置,以確保它始終未設置,即使發生錯誤也是如此。


查看完整回答
反對 回復 2022-10-23
?
三國紛爭

TA貢獻1804條經驗 獲得超7個贊

正如您所說,我還認為鎖是最好的方法。


我推薦使用 Stepehen Cleary straigthforward 解決方案:https ://github.com/StephenCleary/AsyncEx


因此,您的代碼看起來像


private readonly AsyncLock _lock = new AsyncLock();

private async void Save()

{

    var id = Guid.NewGuid();


    using (await _lock.LockAsync())

        {

            // It's safe to await while the lock is held

            Debug.WriteLine($"Starting save {id}");


            await Task.Delay(100);  // Simulate slow task


            Debug.WriteLine($"Finished save {id}");

        }

}

這并不會真正影響代碼的可讀性,并且您最終會得到一個異步方法隊列。


查看完整回答
反對 回復 2022-10-23
?
鳳凰求蠱

TA貢獻1825條經驗 獲得超4個贊

這是 Rx 的絕佳樣本。如果你不想使用ReactiveUI(它可以很容易地與 MVVMLight 相鄰),你所需要的只是一個屬性改變的信號。


使用 RxUI:


this.WhenAnyValue(x => x.MyText, x => x.IsChecked) // this you will need to emulate if you don't want RxUI

.Throttle(TimeSpan.FromMilliseconds(150)) // wait for 150ms after last signal, if there isn't any, send your own further into pipeline

.Synchronize()

.Do(async _ => await Save()) // we have to await so that Synchronize can work

.Subscribe();

這將在最后一次 MyText 更改或 IsChecked 更改后等待 150 毫秒,然后執行一次保存。


此外,RxUI 有非常聰明的 ICommand 實現,它支持開箱即用的異步工作,包括在工作期間禁用命令。


查看完整回答
反對 回復 2022-10-23
  • 3 回答
  • 0 關注
  • 148 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號