Асинхронно обновить свойство c# MVVM - PullRequest
0 голосов
/ 18 июня 2020

XAML привязан к ViewModel MainVM, который загружает 2 других под-ViewModel SubVM1 и SubVM2. Я использую асинхронную команду , представленную Джоном Тириетом . Мое намерение: использовать команду, определенную в 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>

MainVM

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;
    }

}

SubVM1

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

SubVM2

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

    public SubVM2() { }
}

Я пробовал использовать в этой статье от Стивена Клири . Это было действительно поучительно, но я только понял, что делаю это неправильно. Кажется, я не могу понять, как использовать предоставленный им код. Кроме того, я не мог найти исходный код, который обычно действительно помогает мне понять. Я также читал о диспетчерах , но то же самое, без исходного кода. Было это решение , которое нацелено на изменение OnPropertyChanged, однако это было из приложения win8, и я не мог экстраполировать оттуда.

1 Ответ

0 голосов
/ 19 июня 2020

Подробное решение и ссылка для получения файла, относящегося к статье

В итоге я использовал 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>

MainVM

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));
        }
    }
}

SubVM2

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
        }
    }

Я не знаю, насколько красиво это решение, поэтому я немного подожду, прежде чем отмечать его как ответ. Но он выполняет свою работу за меня! :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...