mvvm - Prism正确处理ViewModel集合

标签 mvvm xamarin.forms prism

我正在为我的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); }
    }
}

模型集合页面ViewModel
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依赖于您知道要导航到/从哪个页面导航。因此,您无法按照自己的模式进行操作。
现在这里有几个注意事项。

您会注意到该示例实际上正在使用MvvmHelpers库中的一些帮助器。该库中的BaseViewModel类为您提供IsBusy/IsNotBusy属性以及Title属性和ObservableRangeCollection。

ObservableRangeCollection与Ob​​servableCollection

ObservableRangeCollection为您提供了更好的性能,尤其是在处理较大的数据集时。您可能已经注意到了选项1,在这里我们仅获取更新的数据集并替换整个数据集。在我看来,这就是ObservableRangeCollection真正发挥作用的地方,因为您可以确保拥有最新的数据集,同时将对UI的通知最小化,从而减少占用的CPU周期。

模型, View , View 模型

我的意思不是要有权威的答案,而是要至少提供思想上的帮助。从MVVM模式的高级概述中,您通常使用的是提供UX的View,提供了who/what/why/when/when/where/etc/etc的业务逻辑的ViewModel以及提供我们想要的数据的Model一起工作。在某些情况下,可能有必要引入DTO,该DTO进一步从我们要作为逻辑单元使用的模型中提取原始数据。

关于mvvm - Prism正确处理ViewModel集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43957912/

相关文章:

xamarin.forms - Prism Xamarin 形成可注入(inject)对象

c# - 将 ListView 与对象绑定(bind)

c# - 将未初始化的参数传递给方法时的 ref 关键字

xamarin - 如何在 Xamarin 主菜单中的项目之间绘制水平线?

c# - 如何摆脱 WPF MVVM View 模型中的重复属性

ios - Xamarin Prism 形式 : Application windows are expected to have a root view controller at the end of application launch

c# - 对项目源进行列表插入时,组合框项目未更新

c# - WPF MVVM 如何使用 ShowDialog() 上的绑定(bind)预填充文本框?

c# - 如何通过单击数据模板 ListView 中的项目来打开另一个xamarin表单页面?

xamarin - 如何更改 Xamarin.Forms 上的后退按钮文本