【问题标题】:Update property asynchronously c# MVVM异步更新属性 c# MVVM
【发布时间】:2020-06-18 17:42:53
【问题描述】:

XAML 绑定到一个 ViewModel MainVM,它加载了另外两个子 ViewModel SubVM1SubVM2。我正在使用asynchronous command presented by John Thiriet。我的意图:使用 MainVM 中定义的命令,使用来自 SubVM1 的异步函数来更新来自 SubVM2 的属性。 (XAML)

<Window.Resources>
    <local:MainVM x:Key="main"/>
    <local:SubVM2 x:Key="subvm2"/>
</Window.Resources>
<Grid>
    <Label Content="{Binding MyValue}" DataContext="{StaticResource subvm2}" FontSize="40"/>
    <Button Command="{Binding MyCommand}" DataContext="{StaticResource main}" Width="100" Height="100" Content="GO"/>
</Grid>

主虚拟机

class MainVM : VMBase
{
    private SubVM1 _subvm1;
    private SubVM2 _subvm2;
    private bool _isBusy;

    public SubVM1 Subvm1
    {
        get => _subvm1;
        set
        { _subvm1 = value; OnPropertyChanged("Subvm1"); }
    }
    public SubVM2 Subvm2
    {
        get => _subvm2;
        set
        { _subvm2 = value; OnPropertyChanged("Subvm2"); }
    }
    public bool IsBusy
    {
        get => _isBusy;
        set
        { _isBusy = value; OnPropertyChanged("IsBusy"); }
    }

    public IAsyncCommand MyCommand { get; set; }

    public MainVM()
    {
        Subvm1 = new SubVM1();
        Subvm2 = new SubVM2();
        MyCommand = new AsyncCommand(Increment, MuchBusiness);
    }

    private async Task Increment()
    {
        try
        {
            IsBusy = true;
            await Subvm1.MyAsyncFunc(Subvm2.MyValue);
        }
        finally
        {
            IsBusy = false;
        }
    }

    private bool MuchBusiness()
    {
        return !IsBusy;
    }

}

子虚拟机1

class SubVM1 : VMBase
{
    public async Task MyAsyncFunc(double MyValue)
    {
        await Task.Run(() =>
        {
            int i = 0;
            while (i < 20)
            {
                MyValue = i;
                i++;
                Console.WriteLine(MyValue);
            }
        });
    }
}

子虚拟机2

class SubVM2 : VMBase
{
    private double _myValue;
    public double MyValue
    {
        get => _myValue;
        set
        { _myValue = value; OnPropertyChanged("MyValue"); }
    }

    public SubVM2() { }
}

我尝试使用this article from Stephen Cleary。这真的很有启发性,但我只知道我做错了。我似乎无法弄清楚如何使用他提供的代码。另外,我找不到通常真正帮助我理解的源代码。 我也读过dispatchers,但同样,没有源代码 有 this solution 旨在修改 OnPropertyChanged,但这是来自 win8 应用程序,我无法从那里推断。

【问题讨论】:

  • 请注意,除了调用 await Task.Run() 之外什么都不做的 public async Task 方法不应该是异步的:public Task MyAsyncFunc(double MyValue) { return Task.Run(() =&gt; { ... }); }
  • @Clemens 即使我不想退货?在“真实”代码中,此 Async 函数运行一个负载平台,我希望在单独的线程上处理该操作。
  • await MyAsyncFunc() 已经在等待任务了。
  • 哦,是的,对不起,昨天读到这里很累。在 await Task.Run 前后确实有一堆 UI 相关的更新。

标签: c# wpf asynchronous mvvm


【解决方案1】:

Detailed solutionlink to get the file related to the article

我最终使用了 MVVMLight。该库中的所有函数都以GS

开头

XAML

<Window.Resources>
    <local:ViewModelLocator x:Key="Locator"/>
</Window.Resources>
<Grid DataContext="{Binding Main, Source={StaticResource Locator}}" >
    <Label Content="{Binding SubVM2.MyValue}" FontSize="40"/>
    <Button Command="{Binding SubVM1.OkCommand}" Width="100" Height="100" Content="GO"/>
</Grid>

主虚拟机

public class MainVM : VMBase
{
    public SubVM1 SubVM1 { get; }
    public SubVM2 SubVM2 { get; } = new SubVM2();

    public MainVM()
    {
        SubVM1 = new SubVM1(SubVM2);

        //Subvm2 = new SubVM2();
    }
}

SubVM1(使用 IsBusy 作为 canexecute 参数)

using GS = GalaSoft.MvvmLight.Command;
[...]
public class SubVM1 : VMBase
{
    private SubVM2 _subVM2 { get; set; }
    private bool _isBusy;
    public bool IsBusy
    {
        get => _isBusy;
        set
        { _isBusy = value; OnPropertyChanged("IsBusy"); }
    }

    public SubVM1() { }

    public SubVM1(SubVM2 SubVM2)
    {
        _subVM2 = SubVM2; ;
    }


    private GS.RelayCommand _okCommand;
    public GS.RelayCommand OkCommand
    {
        get
        {
            return _okCommand
                    ?? (_okCommand = new GS.RelayCommand(
                        async () =>
                        {
                            IsBusy = true;
                            Console.WriteLine(IsBusy);
                            await Task.Run(
                                () =>
                                {
                                    // This is a background operation!
                                    IsBusy = true;
                                    _subVM2.MyValue = 0;
                                    while (_subVM2.MyValue < 30)
                                    {
                                        _subVM2.MyValue++;
                                        // Sleep for a while
                                        Thread.Sleep(500);
                                    }
                                });
                            IsBusy = false;
                            Console.WriteLine(IsBusy);
                        },()=> { return !IsBusy; },false));
        }
    }
}

子VM2

public class SubVM2 : VMBase {
    private double _myValue;
    public double MyValue
    {
        get => _myValue;
        set
        { _myValue = value; OnPropertyChanged("MyValue"); }
    }

    public SubVM2() { } }

ViewModelLocator

public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            ////if (ViewModelBase.IsInDesignModeStatic)
            ////{
            ////    // Create design time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DesignDataService>();
            ////}
            ////else
            ////{
            ////    // Create run time view services and models
            ////    SimpleIoc.Default.Register<IDataService, DataService>();
            ////}

            SimpleIoc.Default.Register<MainVM>();
        }

        public MainVM Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainVM>();
            }
        }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }

我不知道这个解决方案有多漂亮,所以我会稍等片刻,然后再将其标记为答案。不过它对我有用! :)

【讨论】:

  • 永远不要在异步方法中使用Thread.Sleep() - 请改用await Task.Delay()
  • @Peregrine 为什么会这样?
  • 异步的全部意义在于避免阻塞线程。
猜你喜欢
  • 2017-11-01
  • 2019-04-21
  • 2019-06-18
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多