[英]MVVM on Winforms: disabled buttons can be hovered (using async/await)
由於我不熟悉WPF,因此我想嘗試在Winforms中使用MVVM模式。 我有一個表單,單擊按鈕即可啟動一個異步方法,該方法通過API檢索味精。
該操作在我的視圖模型中運行,其中還有控制某些UI行為的屬性。 視圖模型通過Fody Weaver屬性Changed實現INotifyOnPropertyChanged,並具有BindingSource,該綁定源將視圖模型本身作為數據源接收。
BindingSource bsViewModel = new BindingSource();
bsViewModel.DataSource = this; // this being the view model the BindingSource lives in
VM屬性通過DataBinding.Add()綁定到控件的屬性。除此故障外,此方法工作正常。 按鈕文本正確更新,但是只有當我將鼠標懸停在按鈕上時(或在放置這些按鈕的UserControl中調用Refresh();),按鈕的顏色才會更新。
一位同事在WinForms中將相同的技術用於BindingSource和MVVM,但使用BackgroundWorker而不是異步等待。 他沒有那個問題。 他的按鈕正確禁用,禁用時不顯示懸停效果。
這半個禁用按鈕的原因可能是什么?
一個簡單的ReactiveUI示例:
1.-創建一個新的Winforms項目(.net 4.5)
2.-添加reactui-winforms nuget軟件包(7.4.0)
3.-添加一個名為“ MainViewModel”的新類,並添加以下引用:
using System;
using System.Reactive;
using System.Threading.Tasks;
using ReactiveUI;
4.-這是類代碼:
public class MainViewModel : ReactiveObject
{
private bool canExecute;
private string textButton;
public MainViewModel()
{
this.CanExecute = true;
this.TextButton = "Press me"
// the command creation, this.WhenAnyValue(x => x.CanExecute) is an observable that will change the enabled/disabled command when CanExecute changes
this.AsyncProcess = ReactiveCommand.CreateFromTask(AsyncProcessImpl, this.WhenAnyValue(x => x.CanExecute));
}
public bool CanExecute
{
get { return this.canExecute; }
set { this.RaiseAndSetIfChanged(ref this.canExecute, value); }
}
public string TextButton
{
get { return textButton; }
set { this.RaiseAndSetIfChanged(ref textButton, value); }
}
public ReactiveCommand<Unit, Unit> AsyncProcess { get; }
private async Task AsyncProcessImpl()
{
// changing CanExecute to false will disable the button
this.CanExecute = false;
this.TextButton = "Wait..."
await Task.Delay(TimeSpan.FromSeconds(5)); // just for testing
// CanExecute back to true, the button will be enabled
this.CanExecute = true;
this.TextButton = "Press me";
}
}
5.-打開Form1表單並添加一個名為“ btnStart”的按鈕。
6.-打開后面的Form1代碼,並添加對ReactiveUI的引用:
using ReactiveUI;
7.-將類更改為:
public partial class Form1: Form, IViewFor<MainViewModel>
{
public Form1()
{
InitializeComponent();
// ViewModel initialization
this.ViewModel = new MainViewModel();
// binding creation when the form is activated
this.WhenActivated(dispose => {
// the command binding will be disposed when the form is deactivated, no memory leaks
dispose(this.BindCommand(this.ViewModel, x => x.AsyncProcess, x => x.btnStart));
dispose(this.OneWayBind(this.ViewModel, x => x.TextButton, x => x.btnStart.Text);
});
}
public MainViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get { return this.ViewModel; }
set { this.ViewModel = value as MainViewModel; }
}
}
8.-運行應用程序
編輯:如果要更改哪個事件將引發命令,只需在BindCommand方法中添加最后一個參數:
dispose(this.BindCommand(this.ViewModel, x => x.AsyncProcess, x => x.btnStart, "TheEventNameYouWant"));
您可以將命令綁定到具有Disable屬性的任何控件,只需使用將引發該命令的事件的名稱即可。
問題出在WinForms本身。 它只是不會使我自己意識到但還是想嘗試一下的MVVM模式消失。
我嘗試了使用后台工作程序的異步方法。 按鈕的問題是相同的。 僅當您從異步方法內/從其他線程更改視圖模型屬性時,才會發生這種情況。
我注意到的另一件事是,使用視圖模型屬性和數據綁定根本無法控制可見性。 僅當您通過進度報告進行設置時,啟用的屬性才起作用。 真是可惜,但我還是想學WPF ...
更新:我在ReactiveUI之外發現的唯一解決方法是使用IProgress<Action>
。 在異步方法中,我在progress.Report(new Action() => { ... })
更改ViewModel的屬性,其余的由數據綁定完成。 另外,還需要在ViewModel中實現INotifyPropertyChanged(nuget PropertyChanged.Fody將在此處提供幫助)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.