【问题标题】:MvvmCross and navigating with MvxContentViewMvvmCross 和使用 MvxContentView 导航
【发布时间】:2019-06-05 15:55:24
【问题描述】:

我是 MvvmCross 的新手(我正在使用 Xamarin.Forms)并且在 MvxContentPages 之间导航很容易。

但我想在嵌入页面的 ContentView 之间导航,但找不到任何使用 MvxContentView 引用的文档。

考虑以下页面

<ContentPage.Content>

    <Grid>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <StackLayout Grid.Column="0">
            <Button Text="Content 1" Command="{Binding GotoContent1}"/>
            <Button Text="Content 2" Command="{Binding GotoContent2}"/>
            <Button Text="Content 3" Command="{Binding GotoContent3}"/>
            <Button Text="Content 4" Command="{Binding GotoContent4}"/>
        </StackLayout>

        <ContentView x:Name="ContentContainer" Grid.Column="1"
                     HorizontalOptions="Fill"
                     VerticalOptions="Fill"/>

    </Grid>

</ContentPage.Content>

当用户单击其中一个按钮时,我想在 ContentContainer 中显示一个 MvxContentView(或者如果有其他方法可以做到这一点,那很好,我只是不想制作 4 个页面基本相同,但在内容视图占位符中有不同的内容)。

【问题讨论】:

  • 如何使用导航? MvvmCross框架主要是支持viewmodels而不是views之间的导航。
  • 如果我们将“页面”定义为全屏视图,将“视图”定义为可视组件——两者都可以有 ViewModel——那么 MvvmCross 允许使用关联的 ViewModel 在页面之间轻松导航。但是,如果我想使用 ViewModel 导航并将关联的视图显示在页面中,那似乎是不可能的。
  • 是的,我的意思是“查看”为“页面”。我已经做到了,您只需创建一个 ContentView 和一个 Page,将此页面与一个 ViewModel 相关联,然后将该 ContentView 插入到 Page 中。通过这种方式,您可以确保您使用的是 ViewModel 导航而不是页面导航。如果你愿意,我可以写一个示例。
  • 对,我有一个与其 ViewModel 关联的 ContentPage,当我通过 _navigationService.Navigate() 导航时,我会转换到该页面。但是该页面内部是一个 ContentView,我想在用户单击不同按钮时对其进行更改。当然,每个 ContentView 都有自己的 ViewModel。但是使用 _navigationService.Navigate(),我似乎无法找到一种方法将与 ContentOneViewModel 关联的 ContentView 分配给 ContentPage 中的 ContentView。这有意义吗?
  • 我意识到我可以使用 MasterDetailPage 或 TabPage 来获得相同的行为,但我将嵌套更深。该项目专门针对具有足够屏幕空间的大型平板电脑。

标签: xamarin xamarin.forms mvvmcross


【解决方案1】:

内容视图只是下面的一个布局,因此只需使用 1 个内容视图并为所有 4 个使用相同的现有 ViewModel。并且只需根据单击的按钮从 ViewModel 更改内容视图中内容的值.

【讨论】:

    【解决方案2】:

    我在 ContentPage 和 ContentView 上编写了一个包装器,以启用 ViewModel 首次导航。 这很简单,你把一个 MvxNestableContentView 放在一个 MvxNestableContentPage 里面并给它一个名字。 然后在您的 ViewModel(派生自 MvxNestableViewModel)中,您只需调用 SetContent(typeof(viewmodel));

    public interface INestedViewModelEventArgs
    {
        string ContainerName { get; set; }
        IMvxNestableViewModel ViewModel { get; set; }
        bool SuppressAppearing { get; set; }
    }
    
    public interface IMvxNestableViewModel : IMvxViewModel
    {
        event EventHandler SetNestedViewModel;
    
        IMvxNestableViewModel SetContent(Type tViewModel, string containerName, bool suppressAppearing = false);
    }
    
    public interface IMvxNestableContentView
    {
        void OnAppearing();
        void OnDisappearing();
    }
    
    public interface IMvxNestableContentPage
    {
    }
    
    public class MvxNestableContentView : MvxContentView, IMvxNestableContentView
    {
        public MvxNestableContentView()
        {
        }
    
        protected override void OnViewModelSet()
        {
            if (ViewModel is IMvxNestableViewModel vm)
            {
                vm.SetNestedViewModel += VmOnSetNestedViewModel;
            }
            ViewModel?.ViewCreated();
        }
    
        public virtual void OnAppearing()
        {
            ViewModel?.ViewAppearing();
            ViewModel?.ViewAppeared();
            foreach (var v in _nestedContentViews)
            {
                v.OnAppearing();
            }
        }
    
        public virtual void OnDisappearing()
        {
            if (ViewModel is IMvxNestableViewModel vm)
            {
                vm.SetNestedViewModel -= VmOnSetNestedViewModel;
            }
            foreach (var v in _nestedContentViews)
            {
                v.OnDisappearing();
            }
            ViewModel?.ViewDisappearing();
            ViewModel?.ViewDisappeared();
            ViewModel?.ViewDestroy();
        }
    
        private readonly List<MvxNestableContentView> _nestedContentViews = new List<MvxNestableContentView>();
    
        private void VmOnSetNestedViewModel(object sender, EventArgs e)
        {
            if (!(e is NestedViewModelEventArgs args)) return;
            if (string.IsNullOrWhiteSpace(args.ContainerName) || args.ViewModel == null) return;
            var contentView = this.FindByName<MvxNestableContentView>(args.ContainerName);
            if (contentView == null)
            {
                throw new Exception("MvxNestableContentView : MvxNestableContentView named " + args.ContainerName + " not found");
            }
            var viewLookup = Mvx.IoCProvider.Resolve<IMvxViewsContainer>();
            var viewType = viewLookup.GetViewType(args.ViewModel.GetType());
            var viewObject = Mvx.IoCProvider.IoCConstruct(viewType);
            if (!(viewObject is MvxNestableContentView view))
            {
                throw new Exception("MvxNestableContentView : view is not MvxNestableContentView");
            }
            view.ViewModel = args.ViewModel;
            var existingContent = contentView.Content as MvxNestableContentView;
            if (!args.SuppressAppearing) view.OnAppearing();
            _nestedContentViews.Add(view);
            contentView.Content = view;
            if (existingContent == null) return;
            existingContent.OnDisappearing();
            _nestedContentViews.Remove(existingContent);
        }
    }
    
    public class MvxNestableContentView<TViewModel> : MvxNestableContentView, IMvxElement<TViewModel>
        where TViewModel : class, IMvxViewModel
    {
        public new TViewModel ViewModel
        {
            get => (TViewModel)base.ViewModel;
            set => base.ViewModel = value;
        }
    }
    
    public class MvxNestableContentPage : MvxContentPage, IMvxNestableContentPage
    {
        public MvxNestableContentPage()
        {
        }
    
        protected override void OnViewModelSet()
        {
            if (ViewModel is IMvxNestableViewModel vm)
            {
                vm.SetNestedViewModel += VmOnSetNestedViewModel;
            }
            base.OnViewModelSet();
        }
    
        protected override void OnAppearing()
        {
            base.OnAppearing();
            foreach (var v in _nestedContentViews)
            {
                v.OnAppearing();
            }
        }
    
        protected override void OnDisappearing()
        {
            if (ViewModel is IMvxNestableViewModel vm)
            {
                vm.SetNestedViewModel -= VmOnSetNestedViewModel;
            }
            foreach (var v in _nestedContentViews)
            {
                v.OnDisappearing();
            }
            base.OnDisappearing();
        }
    
        private readonly List<MvxNestableContentView> _nestedContentViews = new List<MvxNestableContentView>();
    
        private void VmOnSetNestedViewModel(object sender, EventArgs e)
        {
            if (!(e is NestedViewModelEventArgs args)) return;
            if (string.IsNullOrWhiteSpace(args.ContainerName) || args.ViewModel == null) return;
            var contentView = this.FindByName<MvxNestableContentView>(args.ContainerName);
            if (contentView == null)
            {
                throw new Exception("MvxNestableContentPage : MvxNestableContentView named " + args.ContainerName + " not found");
            }
            var viewLookup = Mvx.IoCProvider.Resolve<IMvxViewsContainer>();
            var viewType = viewLookup.GetViewType(args.ViewModel.GetType());
            var viewObject = Mvx.IoCProvider.IoCConstruct(viewType);
            if (!(viewObject is MvxNestableContentView view))
            {
                throw new Exception("MvxNestableContentPage : view is not MvxNestableContentView");
            }
            view.ViewModel = args.ViewModel;
            var existingContent = contentView.Content as MvxNestableContentView;
            if (!args.SuppressAppearing) view.OnAppearing();
            _nestedContentViews.Add(view);
            contentView.Content = view;
            if (existingContent == null) return;
            existingContent.OnDisappearing();
            _nestedContentViews.Remove(existingContent);
        }
    }
    
    public class MvxNestableContentPage<TViewModel> : MvxNestableContentPage, IMvxPage<TViewModel>
        where TViewModel : class, IMvxViewModel
    {
        public new TViewModel ViewModel
        {
            get => (TViewModel)base.ViewModel;
            set => base.ViewModel = value;
        }
    }
    
    public abstract class MvxNestableViewModel : MvxViewModel, IMvxNestableViewModel
    {
        public event EventHandler SetNestedViewModel;
    
        public virtual IMvxNestableViewModel SetContent(Type viewModelType, string containerName, bool suppressAppearing = false)
        {
            if (!(Mvx.IoCProvider.IoCConstruct(viewModelType) is IMvxNestableViewModel viewModel))
            {
                return null;
            }
            viewModel.Start();
            viewModel.Prepare();
            viewModel.Initialize();
            SetNestedViewModel?.Invoke(this, new NestedViewModelEventArgs
            {
                ContainerName = containerName,
                ViewModel = viewModel,
                SuppressAppearing = suppressAppearing
            });
            return viewModel;
        }
    }
    
    public abstract class MvxNestableViewModel<TParameter> : MvxNestableViewModel, IMvxViewModel<TParameter>, IMvxNestableViewModel
    {
        public abstract void Prepare(TParameter parameter);
    }
    
    public abstract class MvxNestableViewModelResult<TResult> : MvxNestableViewModel, IMvxViewModelResult<TResult>, IMvxNestableViewModel
    {
        public TaskCompletionSource<object> CloseCompletionSource { get; set; }
    
        public override void ViewDestroy(bool viewFinishing = true)
        {
            if (viewFinishing && CloseCompletionSource != null && !CloseCompletionSource.Task.IsCompleted && !CloseCompletionSource.Task.IsFaulted)
                CloseCompletionSource?.TrySetCanceled();
            base.ViewDestroy(viewFinishing);
        }
    }
    
    public abstract class MvxNestableViewModel<TParameter, TResult> : MvxNestableViewModelResult<TResult>, IMvxViewModel<TParameter, TResult>, IMvxNestableViewModel
    {
        public abstract void Prepare(TParameter parameter);
    }
    
    public class NestedViewModelEventArgs : EventArgs, INestedViewModelEventArgs
    {
        public string ContainerName { get; set; }
        public IMvxNestableViewModel ViewModel { get; set; }
        public bool SuppressAppearing { get; set; }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-13
      • 2019-01-27
      • 1970-01-01
      相关资源
      最近更新 更多