如何将命令传递给模板,并使其在我的后端代码中执行并传递参数?

时间:2018-07-17 04:53:25

标签: xamarin xamarin.forms

我有这个模板:

<?xml version="1.0" encoding="utf-8"?>
<Grid Padding="20,0" xmlns="http://xamarin.com/schemas/2014/forms" 
      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
      xmlns:local="clr-namespace:Japanese;assembly=Japanese" 
      x:Class="Japanese.Templates.DataGridTemplate" 
      x:Name="this" HeightRequest="49" Margin="0">
    <Grid.GestureRecognizers>
         <TapGestureRecognizer 
             Command="{Binding TapCommand, Source={x:Reference this}}"
             CommandParameter="1"
             NumberOfTapsRequired="1" />
    </Grid.GestureRecognizers>
    <Label Grid.Column="0" Text="{Binding Test" />
</Grid>

在此之后,我有:

public partial class DataGridTemplate : Grid
{

    public DataGridTemplate()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty TapCommandProperty =
       BindableProperty.Create(
           "Command",
           typeof(ICommand),
           typeof(DataGridTemplate),
           null);

    public ICommand TapCommand
    {
        get { return (ICommand)GetValue(TapCommandProperty); }
        set { SetValue(TapCommandProperty, value); }
    }

}

我正在尝试在文件Settings.xaml.cs

中调用这样的模板
<template:DataGridTemplate TapCommand="openCFSPage" />

希望它将在文件Settings.cs

中调用我的方法
void openCFSPage(object sender, EventArgs e)
{
    Navigation.PushAsync(new CFSPage());
}

代码可以编译,但是当我单击网格时,它不会调用openCFSPage方法。

1)有人知道什么地方可能出问题吗?

2)还有一种方法可以向模板添加参数,然后在CS后端代码中将该参数传递给我的方法吗?

请注意,我尽可能避免添加视图模型。该应用程序很小,我只想在调用模板的页面的CS代码中包含所需的代码即可。

4 个答案:

答案 0 :(得分:1)

根据使用情况,您有2个选择:

仅供参考,无法直接从视图中调用其他方法(这样做是一种错误的设计模式)

  1. Using Event Aggregator

创建界面

public interface IEventAggregator
{
    TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
}

您要做的就是从您的 TapCommand

调用它
_eventAggregator.GetEvent<ItemSelectedEvent>().Publish(_selectedItem);

然后在您的 Settings.cs 中创建一个可以接收数据的方法

 this.DataContext = new ListViewModel(ApplicationService.Instance.EventAggregator);
  1. Inheritance and Polymorphism /使 openCFSPage 服务:

创建链接两个模型的界面/服务

public interface IOpenCFSPage
{
     Task OpenPage();
}

和方法:

public class OpenCFSPage : IOpenCFSPage
{
private INavigationService _navigationService;
public OpenCFSPage(INavigationService navigationService){
 _navigationService = navigationService;
}
        public async Task OpenPage()
        {
             await _navigationService.NavigateAsync(new CFSPage());
        }
}

答案 1 :(得分:1)

请注意,最简单的实现方法是通过MVVM(即视图模型),但是如果您想回避此选项(如您在问题中所述),则可以使用一个以下选项之一

选项1:将委托包装到命令对象中

如果从XAML解析器的角度来看它,则从技术上讲,您正在尝试将委托分配给类型为data = {d:data[d][0] for d in data} pd.DataFrame.from_dict(data) 的属性。避免类型不匹配的一种方法是将委托包装在页面代码背后的命令属性内。

隐藏代码 [Settings.xaml.cs]

ICommand

XAML [Settings.xaml]

ICommand _openCFSPageCmd;
public ICommand OpenCFSPageCommand {
    get {
        return _openCFSPageCmd ?? (_openCFSPageCmd = new Command(OpenCFSPage));
    }
}

void OpenCFSPage(object param)
{
    Console.WriteLine($"Control was tapped with parameter: {param}");
}

选项2:自定义标记扩展名

create a markup-extension的另一种选择(不太主流)是将委托包装到命令对象中。

<!-- assuming that you have added x:Name="_parent" in root tag -->
<local:DataGridView TapCommand="{Binding OpenCFSPageCommand, Source={x:Reference _parent}}" />

示例用法

[ContentProperty("Handler")]
public class ToCommandExtension : IMarkupExtension
{
    public string Handler { get; set; }
    public object Source { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        if (serviceProvider == null)
            throw new ArgumentNullException(nameof(serviceProvider));
        var lineInfo = (serviceProvider?.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo ?? new XmlLineInfo();


        object rootObj = Source;
        if (rootObj == null)
        {
            var rootProvider = serviceProvider.GetService<IRootObjectProvider>();
            if (rootProvider != null)
                rootObj = rootProvider.RootObject;
        }

        if(rootObj == null)
        {
            var valueProvider = serviceProvider.GetService<IProvideValueTarget>();
            if (valueProvider == null)
                throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");

            //we assume valueProvider also implements IProvideParentValues
            var propInfo = valueProvider.GetType()
                                        .GetProperty("Xamarin.Forms.Xaml.IProvideParentValues.ParentObjects", 
                                                     BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            if(propInfo == null)
                throw new ArgumentException("valueProvider does not provide an ParentObjects");

            var parentObjects = propInfo.GetValue(valueProvider) as IEnumerable<object>;
            rootObj = parentObjects?.LastOrDefault();
        }

        if(rootObj != null)
        {
            var delegateInfo = rootObj.GetType().GetMethod(Handler,
                                                           BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            if(delegateInfo != null)
            {
                var handler = Delegate.CreateDelegate(typeof(Action<object>), rootObj, delegateInfo) as Action<object>;
                return new Command((param) => handler(param));
            }
        }

        throw new XamlParseException($"Can not find the delegate referenced by `{Handler}` on `{Source?.GetType()}`", lineInfo);        
    }
}

答案 2 :(得分:1)

Settings.xaml:

<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" />

<!-- Uncomment below and corresponding parameter property code in DataGridTemplate.xaml.cs to pass parameter from Settings.xaml -->
<!--<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" CommandParameter="A" />-->

Settings.xaml.cs:

public Settings()
{
    InitializeComponent();

    OpenCFSPage = new Command(p => OpenCFSPageExecute(p));

    BindingContext = this;
}

public ICommand OpenCFSPage { get; private set; }

void OpenCFSPageExecute(object p)
{
    var s = p as string;
    Debug.WriteLine($"OpenCFSPage:{s}:");
}

DataGridTemplate.xaml:

<?xml version="1.0" encoding="UTF-8"?>
    <Grid xmlns="http://xamarin.com/schemas/2014/forms" 
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:Japanese;assembly=Japanese" 
         Padding="0,20" 
         HeightRequest="49" Margin="0"
         x:Class="Japanese.DataGridTemplate">
    <Grid.GestureRecognizers>
        <TapGestureRecognizer 
             Command="{Binding TapCommand}"
             CommandParameter="1"
             NumberOfTapsRequired="1" />
    </Grid.GestureRecognizers>
    <Label Grid.Column="0" Text="Test" />
</Grid>

DataGridTemplate.xaml.cs:

public partial class DataGridTemplate : Grid
{
    public DataGridTemplate()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty TapCommandProperty = 
        BindableProperty.Create(
            nameof(TapCommand), typeof(ICommand), typeof(DataGridTemplate), null,
            propertyChanged: OnCommandPropertyChanged);

    public ICommand TapCommand
    {
        get { return (ICommand)GetValue(TapCommandProperty); }
        set { SetValue(TapCommandProperty, value); }
    }

    //public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
    //    nameof(CommandParameter), typeof(string), typeof(DataGridTemplate), null);

    //public string CommandParameter
    //{
    //    get { return (string)GetValue(CommandParameterProperty); }
    //    set { SetValue(CommandParameterProperty, value); }
    //}

    static TapGestureRecognizer GetTapGestureRecognizer(DataGridTemplate view)
    {
        var enumerator = view.GestureRecognizers.GetEnumerator();
        while (enumerator.MoveNext())
        {
            var item = enumerator.Current;
            if (item is TapGestureRecognizer) return item as TapGestureRecognizer;
        }
        return null;
    }

    static void OnCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is DataGridTemplate view)
        {
            var tapGestureRecognizer = GetTapGestureRecognizer(view);
            if (tapGestureRecognizer != null)
            {
                tapGestureRecognizer.Command = (ICommand)view.GetValue(TapCommandProperty);
                //tapGestureRecognizer.CommandParameter = (string)view.GetValue(CommandParameterProperty);
            }
        }
    }
}

答案 3 :(得分:-1)

检查此代码可为您提供帮助。在这里,您必须传递列表视图的引用,并且还需要将命令与BindingContext绑定。

export PATH=$PATH:/Users/theodosiostziomakas/apache-maven-3.5.4/bin