c# - 使用静态 NavigateTo<T> 方法在 Xamarin.Forms 中导航时如何传递参数?

标签 c# mvvm xamarin.forms

NavigateTo<T>()函数位于App.cs

public static Task NavigateTo<T>()
    where T : BaseViewModel
{
    // do some very simple navigation/lookups
    // basically, just remove the "Model" part of the VM and that is the page
    // "converntion-based" :)
    var viewModelName = typeof(T).Name;
    var pageType = typeof(MainView);
    var pageNamespace = pageType.Namespace;
    var pageAssembly = pageType.GetTypeInfo().Assembly;
    var newPageName = viewModelName.Substring(0, viewModelName.Length - "Model".Length);
    var newPageType = pageAssembly.GetType($"{pageNamespace}.{newPageName}");

    var newPage = Activator.CreateInstance(newPageType) as Page;
    var currentPage = ((NavigationPage)Current.MainPage).CurrentPage;
    return currentPage.Navigation.PushAsync(newPage);
}

ItemListViewModel.cs

public ItemListViewModel()
{
    NextPageCommand = new Command(() => App.NavigateTo<DataCaptureViewModel>());
}

public ObservableCollection<Item> LocalItems
{
    get { return localItems; }
    set
    {
        localItems = value;
        Refresh(nameof(LocalItems));
    }
}

public Command NextPageCommand { get; private set; }

DataCaptureViewModel.cs

public class DataCaptureViewModel : BaseViewModel
{
    public DataCaptureViewModel()
    {
        //will probably have to set passed in param to SelectedItems
        //once passing in params gets figured out
    }
    public ObservableCollection<Item> SelectedItems { get; set; }
}

BaseViewModel.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Application
{
    public abstract class BaseViewModel : INotifyPropertyChanged
    {
        protected void Refresh([CallerMemberName]string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        protected bool Set<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
        {
            if (!object.Equals(field, value))
            {
                field = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                return true;
            }
            return false;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnDisappearing()
        {
        }

        public virtual void OnAppearing()
        {
        }
    }
}

我一直在使用 MVVM 模式开发 Xamarin.Forms 应用程序并调用 App.NavigateTo<T>()当需要导航到另一个页面时。现在我想通过 ObservableCollection<Item>进入我正在导航到的页面。我一直无法弄清楚如何使用NavigateTo<T>()来做到这一点方法。我在 WPF 中询问过和C#聊了很多圈子,但还没有找到解决方案。有什么想法吗?

如果需要显示更多代码,请告诉我。

最佳答案

选项-1。使用附加属性

工作原理:为了传递一些导航参数 - 您可以使用附加属性存储 View (和 View 模型)的状态。我们将绑定(bind)模式保留为“OneWayToSource” - 这样一旦绑定(bind) - 它只会更新 ViewModel,而不是 View 本身的附加属性;

这样,一旦 NavigateTo 方法显式设置附加属性的值,我们就可以在 View 上设置绑定(bind);一旦将 viewmodel 分配给 View 的 BindingContext - viewmodel 就会因绑定(bind)而使用附加值进行更新。

步骤:

创建附加的可绑定(bind)属性。

public class NavigationContext
{
    public static readonly BindableProperty ParamProperty =
        BindableProperty.CreateAttached("Param", typeof(object), typeof(NavigationContext), null, 
                                        defaultBindingMode: BindingMode.OneWayToSource);

    public static object GetParam(BindableObject view)
    {
        return view.GetValue(ParamProperty);
    }

    public static void SetParam(BindableObject view, object value)
    {
        view.SetValue(ParamProperty, value);
    }

    // add more properties if more parameters to be passed..

}

重构 NavigateTo 以接受参数并分配属性值。

public static Task NavigateTo<T>(object param = null)
    where T : BaseViewModel
{
    // do some very simple navigation/lookups
    // basically, just remove the "Model" part of the VM and that is the page
    // "converntion-based" :)
    var viewModelName = typeof(T).Name;
    var pageType = typeof(MainView);
    var pageNamespace = pageType.Namespace;
    var pageAssembly = pageType.GetTypeInfo().Assembly;
    var newPageName = viewModelName.Substring(0, viewModelName.Length - "Model".Length);
    var newPageType = pageAssembly.GetType($"{pageNamespace}.{newPageName}");

    var newPage = Activator.CreateInstance(newPageType) as Page;
    if (param != null)
        NavigationContext.SetParam(newPage, param);

    var currentPage = ((NavigationPage)App.Current.MainPage).CurrentPage;
    return currentPage.Navigation.PushAsync(newPage);
}

确保在 (DataCapture) View XAML 中创建与属性的绑定(bind)。例如:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:local="clr-namespace:SampleApp" 
    x:Class="SampleApp.DataCaptureView"
    local:NavigationContext.Param="{Binding SelectedItems}">
    <ContentPage.Content>

或者,选项 2。在 BaseViewModel

中使用虚拟方法

重构 NavigateTo 以接受参数并更新 View 模型。

public static Task NavigateTo<T>(object navigationContext = null)
    where T : BaseViewModel
{
    // do some very simple navigation/lookups
    // basically, just remove the "Model" part of the VM and that is the page
    // "converntion-based" :)
    var viewModelName = typeof(T).Name;
    var pageType = typeof(MainView);
    var pageNamespace = pageType.Namespace;
    var pageAssembly = pageType.GetTypeInfo().Assembly;
    var newPageName = viewModelName.Substring(0, viewModelName.Length - "Model".Length);
    var newPageType = pageAssembly.GetType($"{pageNamespace}.{newPageName}");
    var newPage = Activator.CreateInstance(newPageType) as Page;

    /* newPage.BindingContext ?? */
    var viewModel = Activator.CreateInstance<T>();
    if (param != null)
        viewModel.SetNavigationContext(navigationContext);
    newPage.BindingContext = viewModel;

    var currentPage = ((NavigationPage)Current.MainPage).CurrentPage;
    return currentPage.Navigation.PushAsync(newPage);
}

重用OnAppearing,或者在BaseViewModel中创建一个类似SetNavigationContext的方法

public abstract class BaseViewModel : INotifyPropertyChanged
{
    .....
    public virtual void SetNavigationContext(object context)
    {
    }

DataCaptureViewModel中重写此方法

public class DataCaptureViewModel : BaseViewModel
{
...
     public ObservableCollection<Item> SelectedItems { get; set; }
     public override void SetNavigationContext(object context)
     {
          var selectedItems = context as ObservableCollection<Item>;
          SelectedItems = selectedItems;
     }
}

关于c# - 使用静态 NavigateTo<T> 方法在 Xamarin.Forms 中导航时如何传递参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45765081/

相关文章:

c# - Xamarin Forms 最佳行为和验证

xaml - 图像不会显示在 Xamarin Forms 的 <Image> 中

xaml - 如何在选项卡式页面之前添加内容页面或查看

c# - 使用 LINQ,如何将 IList<IList<object>> 转换为 IList<object>?

c# - 将 SQL 存储过程和参数存储在数据库表中是否是一种不好的做法

c# - Windows 7 计算器破坏 WPF 多点触控?

c# - C# new 语句后的大括号有什么作用?

c# - 类库不识别 CommandManager 类

wpf - 实心画笔属性不绑定(bind)

c# - 如何在viewmodel(MVVM模型)wpf应用程序中使用命令行参数