簡體   English   中英

Winforms上的MVVM:可以將禁用的按鈕懸停(使用異步/等待)

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

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