Prism 正确处理视图模型集合

Prism proper handling of viewmodel collections

我正在为我的 Xamarin.Forms 应用程序使用 prism 框架。

这种情况很常见,但让我很头疼。

MainPage
 - MainPageViewModel
  - ObserveableCollection<SomePageViewModel>

public class MainPageViewModel : BaseViewModel
{
    private ObservableCollection<SomePageViewModel> viewModels;

    public MainPageViewModel(INavigationService navigationService) : base(navigationService)
    {
        SomePageSelectedCommand = DelegateCommand.FromAsyncHandler(NavigateToSomePage);
    }

    public ICommand SomePageSelectedCommand { get; private set; }

    public ObservableCollection<SomePageViewModel> ViewModels
    {
        get { return viewModels; }
        set { SetProperty(ref viewModels, value); }
    }   

    private async Task NavigateToSomePage(SomePageViewModel viewModel)
    {
        var navParams = new NavigationParameters
        {
               {viewModel.typeof(SomePageViewModel).Name, viewModel}
        };
        await Navigation.NavigateAsync(NavigationConstants.SomePageUri, navParams, false);
    }
}

public class SomePageViewModel : BaseViewModel
{
    protected SomeModel someModel;

    public SomePageViewModel(INavigationService navigationService) : base(navigationService)
    {
        someModel = new SomeModel();
        EditCommand = DelegateCommand.FromAsyncHandler(Edit);
    }

    public ICommand EditCommand { get; private set; }

    public string Name
    {
        get { return SomeModel.Name; }
        set { SetProperty(ref SomeModel.Name, value); }
    }

    public string Description
    {
        get { return SomeModel.Description; }
        set { SetProperty(ref SomeModel.Description, value); }
    }   

    public override void OnNavigatedTo(NavigationParameters parameters)
    {
        if (parameters.ContainsKey(typeof(SomePageViewModel).Name))
        {
            var viewModel = (SomePageViewModel)parameters[typeof(SomePageViewModel).Name];
            Name = viewModel.Name;
            Description = viewModel.Name;
        }       
    }

    private async Task Edit()
    {
        var navParams = new NavigationParameters
        {
            {viewModel.typeof(SomePageViewModel).Name, this}
        };
        await Navigation.NavigateAsync(NavigationConstants.SomePageEditUri, navParams, false);
    }   
}

public class SomePageEditViewModel : BaseViewModel
{
    public SomePageEditViewModel(INavigationService navigationService) : base(navigationService)
    {
        SaveCommand = DelegateCommand.FromAsyncHandler(Save);
    }

    public ICommand SaveCommand { get; private set; }

    private async Task Save()
    {
        App.ContentService.Save(someModel);
        await Navigation.GoBackAsync();
    }
}

所以让我们从 MainPage 导航到 SomePage。我们想要编辑它,所以我们之后导航到 SomePageEdit 并最后保存。

根据 mvvm/prsim 使更改对 SomePage 和 MainPage 可见的正确方法是什么?对于第一个,我可以将更改作为 NavigationParameter 传递到 GoBackAsync。但是 MainPage 呢?

看来您的设计有点问题。要正确构建您的应用程序,您需要更接近于:

型号

public class TodoItem : ObservableObject
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set { SetProperty(ref _name, value); }
    }

    private bool _done;
    public bool Done
    {
        get { return _done; }
        set { SetProperty(ref _done, value); }
    }
}

模型Collection页面视图模型

public class TodoItemListPageViewModel : BaseViewModel, INavigationAware
{
    private INavigationService _navigationService { get; }
    public TodoItemListViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;
        TodoItems = new ObservableRangeCollection<TodoItem>();
        AddTodoItemCommand = new DelegateCommand(OnAddTodoItemCommandExecuted);
        EditTodoItemCommand = new DelegateCommand<TodoItem>(OnEditTodoItemCommandExecuted);
    }

    public ObservableRangeCollection<TodoItem> TodoItems { get; }

    public DelegateCommand AddTodoItemCommand { get; }

    public DelegateCommand<TodoItem> EditTodoItemCommand { get; }

    public void OnNavigatingTo(NavigationParameters parameters)
    {
        // Initialize your collection
    }

    public void OnNavigatedTo(NavigationParameters parameters)
    {
        if(parameters.GetValue<NavigationMode>(KnownNavigationParameters.NavigationMode) == NavigationMode.Back)
        {
            // Option 1
            // Fetch an updated list of TodoItems from your data source
            TodoItems.ReplaceRange(updatedTodoItems);

            // Option 2
            // Replace the updated item or add a new item
        }
    }

编辑模型页面 ViewModel

    public void OnNavigatedFrom(NavigationParameters parameters)
    {

    }

    private async void OnAddTodoItemCommandExecuted() =>
        await _navigationService.NavigateAsync("AddTodoItemPage");

    private async void OnEditTodoItemCommandExecuted(TodoItem item) =>
        await _navigationService.NavigateAsync("EditTodoItemPage", new NavigationParameters { { "item", item } });
}

public class EditTodoItemPageViewModel : BaseViewModel
{
    private INavigationService _navigationService { get; }
    public EditTodoItemPageViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;
        SaveCommand = new DelegateCommand(OnSaveCommandExecuted, () => IsNotBusy)
                            .ObservesProperty(() => IsBusy);
    }

    private TodoItem _model;
    public TodoItem Model
    {
        get { return _model; }
        set { SetProperty(ref _model, value); }
    }

    public DelegateCommand SaveCommand { get; }

    public void OnNavigatingTo(NavigationParameters parameters)
    {
        Model = parameters.GetValue<TodoItem>("item");
    }

    private async void OnSaveCommandExecuted()
    {
        IsBusy = true;
        // Persist any changes

        // Option 1
        await _navigationService.GoBackAsync();

        // Option 2
        await _navigationService.GoBackAsync(new NavigationParameters { { "updatedItem", Model } });
        IsBusy = false;
    }
}

为什么...

您的 ObservableCollection 应该是 where T : TModel 而不是 where T : TViewModel。您会立即遇到的另一个问题是 INavigationService 取决于您是否知道您正在浏览的页面 to/from。所以你不能遵循你在那里做的模式。 现在这里有几个注释。

您会注意到此示例实际上使用了 MvvmHelpers 库中的一些助手。该库中的 BaseViewModel class 为您提供 IsBusy/IsNotBusy 属性 以及标题 属性 和 ObservableRangeCollection。

ObservableRangeCollection 对比 ObservableCollection

ObservableRangeCollection 为您提供更好的性能,尤其是在处理较大的数据集时。您可能已经注意到选项 1,我们只是获取更新后的数据集并替换整个数据集。在我看来,这就是 ObservableRangeCollection 真正闪耀的地方,因为您能够确保拥有最新的数据集,同时最大限度地减少对 UI 的通知,从而减少 CPU 周期向上。

模型、视图、视图模型

我并不是要给这个权威的答案,但至少要提供一些思考。从 MVVM 模式的高级概述来看,您通常使用提供 UX 的视图、为 who/what/why/when/where/etc 提供业务逻辑的 ViewModel,以及我们要使用的数据模型。在某些情况下,可能有必要引入一个 DTO,它进一步从我们要作为逻辑单元使用的模型中抽象出我们的原始数据。