【问题标题】:MVVM on Winforms: disabled buttons can be hovered (using async/await)Winforms 上的 MVVM:禁用的按钮可以悬停(使用 async/await)
【发布时间】:2017-12-14 10:27:04
【问题描述】:

由于我不熟悉 WPF,我想尝试在 Winforms 中使用 MVVM 模式。我有一个表单,单击按钮会启动一个异步方法,该方法通过 API 检索 msgs。

该操作在我的视图模型中运行,其中还有控制某些 UI 行为的属性。视图模型通过 Fody Weaver Property Changed 实现 INotifyOnPropertyChanged,并有一个 BindingSource 接收视图模型本身作为 DataSource。

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 而不是异步等待。他没有这个问题。他的按钮正确禁用,禁用时不显示悬停效果。

这个半禁用的按钮可能是什么原因?

【问题讨论】:

  • 我也已经在 Winforms 中实现了 MVVM,没有您遇到的问题...您可以发布一个小代码示例,以便我们重现您的问题...

标签: c# winforms mvvm async-await


【解决方案1】:

一个简单的 ReactiveUI 示例:

1.- 创建一个新的 Winforms 项目 (.net 4.5)

2.- 添加 reactiveui-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 属性的任何控件,只需使用将引发命令的事件的名称。

【讨论】:

  • 这很好用。感谢您提供广泛的示例。但是我从来没有使用过 observables,所以如果你有更多的示例链接会有所帮助!
  • 例如,我想知道如何绑定到按钮文本。 BindCommand 似乎会自动引发 Click 事件并更改按钮的 Enabled 属性。我能说一下绑定了哪些属性和事件吗?
  • 检查编辑。 Observables 很棒,只要把 IEnumerable(包括 linq)想成拉取集合,而 IObservable 想成推入集合。
【解决方案2】:

问题出在 WinForms 本身。它只是不适合我知道但无论如何想试一试的 MVVM 模式。

我使用后台工作程序尝试了我的异步方法。按钮的问题是一样的。仅当您从异步方法中/从不同线程更改视图模型属性时才会发生这种情况。

我注意到的另一件事是,使用视图模型属性和数据绑定根本无法控制可见性。仅当您通过进度报告进行设置时,启用的属性才有效。有点可惜,不过我还是想学WPF...

更新:我在 ReactiveUI 之外发现的唯一解决方法是使用 IProgress&lt;Action&gt;。在异步方法中,我在 progress.Report(new Action() =&gt; { ... }) 中更改了我的 ViewModel 的属性,其余的由数据绑定完成。 INotifyPropertyChanged 也需要在 ViewModel 中实现(nuget PropertyChanged.Fody 将在此处提供帮助)。

【讨论】:

  • 你可以在Winforms中完美地做MVVM,可能你的实现还不够好。试试 ReactiveUI,我的使用 MVVM 和 ReactiveUI 的 Winforms 项目运行良好。我可以毫无问题地绑定 Enabled 和 Visible 属性。
  • 而且您正在通过异步方法更新这些属性,是吗?
  • 异步和同步方法。
  • 好的,那么我的回答对我的回答投了反对票,这对于没有 ReactiveUI 的 WinForms 来说实际上是正确的,您能否提供自己的回答和工作代码示例来支持您的主张!我很想使用 ReactiveUI,但他们的网站在 WinForms 方面似乎并不完整。
  • 我没有对你投反对票 ;) 我会尝试将示例上传到 github。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-31
  • 1970-01-01
  • 2015-01-21
  • 2012-12-26
  • 1970-01-01
相关资源
最近更新 更多