MvvmCross和使用MvxContentView导航

时间:2019-06-05 15:55:24

标签: xamarin xamarin.forms mvvmcross

我是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个页面本质上相同,但内容视图占位符中的内容不同。

2 个答案:

答案 0 :(得分:0)

Content View只是其下面的一个Layout,因此仅使用1个内容视图,并对所有4个都使用相同的现有ViewModel。并且只需根据单击哪个按钮从ViewModel更改内容视图中内容的值即可。

答案 1 :(得分:0)

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