如何使ViewCell的Tapped事件向通用函数发送一个参数,然后为该ViewCell元素打开一个选择器?

时间:2017-07-28 11:19:24

标签: xamarin xamarin.forms

更新:提醒一下,如果有人可以在不使用手势的情况下向我展示如何实施此功能>

我使用ViewCell和手势识别器打开一个带有以下代码的选择器。 ViewCell左侧有一个标签,右侧有一个标签区域,最初在应用程序启动时填充,稍后在单击ViewCell时填充选择器。

XAML

<ViewCell x:Name="ati" Tapped="OpenPickerCommand">
   <Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
      <Grid.GestureRecognizers>
         <TapGestureRecognizer 
             Command="{Binding OpenPickerCommand}" 
             CommandParameter="{x:Reference atiPicker}" NumberOfTapsRequired="1" />
      </Grid.GestureRecognizers>
      <local:LabelBodyRendererClass Text="Answer Time Interval" HorizontalOptions="StartAndExpand" />
      <Picker x:Name="atiPicker" IsVisible="false" HorizontalOptions="End" SelectedIndexChanged="atiPickerSelectedIndexChanged" ItemsSource="{Binding Times}"></Picker>
      <local:LabelBodyRendererClass x:Name="atiLabel" HorizontalOptions="End"/>
   </Grid>
</ViewCell>

<ViewCell x:Name="pti" Tapped="OpenPickerCommand">
   <Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
      <Grid.GestureRecognizers>
         <TapGestureRecognizer 
             Command="{Binding OpenPickerCommand}" 
             CommandParameter="{x:Reference ptiPicker}" NumberOfTapsRequired="1" />
      </Grid.GestureRecognizers>
      <local:LabelBodyRendererClass Text="Phrase Time Interval" HorizontalOptions="StartAndExpand" />
      <Picker x:Name="ptiPicker" IsVisible="false" HorizontalOptions="End" SelectedIndexChanged="ptiPickerSelectedIndexChanged" ItemsSource="{Binding Times}"></Picker>
      <local:LabelBodyRendererClass x:Name="ptiLabel" HorizontalOptions="End"/>
   </Grid>
</ViewCell>

C#这适用于使用CommandParameter的不同选择器(ati,bti,pti等)

public SettingsPage()
{
   InitializeComponent();
   BindingContext = new CommandViewModel();
}

void atiPickerSelectedIndexChanged(object sender, EventArgs e)
    {
        var picker = (Picker)sender;
        int selectedIndex = picker.SelectedIndex;
        if (selectedIndex != -1)
        {
            App.DB.UpdateIntSetting(Settings.Ati, selectedIndex);
            atiLabel.Text = AS.ati.Text();
        }
    }

void ptiPickerSelectedIndexChanged(object sender, EventArgs e)
    {
        var picker = (Picker)sender;
        int selectedIndex = picker.SelectedIndex;
        if (selectedIndex != -1)
        {
            App.DB.UpdateIntSetting(Settings.Pti, selectedIndex);
            ptiLabel.Text = AS.pti.Text();
        }
    }


public class CommandViewModel: ObservableProperty
{
    public ICommand openPickerCommand;

    public CommandViewModel()
    {
        openPickerCommand = new Command<Picker>(PickerFocus);
        //openPickerCommand = new Command(tapped);
    }

    public ICommand OpenPickerCommand
    {
        get { return openPickerCommand; }
    }

    void PickerFocus(Picker param)
    {
        param.Focus();
    }
}

我想删除TapGestureRecognizers的使用,但我仍然希望保留功能和布局。

有人向我建议,如果我像这样使用ViewCell的Tapped事件会更好:

 Tapped="OnTapped"

有人可以详细解释我如何用C#连接它。我是否最好在CommandViewModel以及C#支持代码中编写代码。视图模型还有一个方法可以使用参数,因此它可以用来打开不同的选择器吗?

我非常感谢能够做到这一点的一个例子。请注意,如果有一种方法可以通过.cs支持代码进行编码,我并不特别需要使用CommandViewModel。

3 个答案:

答案 0 :(得分:2)

(抱歉英语不好)

尽管不是最佳实践,但我猜你可以做这样的事情,解雇视图模型:

<强> XAML:

<ViewCell x:Name="ati" Tapped="OpenPickerCommand">
   <Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
      <local:LabelBodyRendererClass Text="Answer Time Interval" HorizontalOptions="StartAndExpand" />
      <Picker x:Name="atiPicker" 
              IsVisible="false" 
              HorizontalOptions="End" 
              SelectedIndexChanged="atiPickerSelectedIndexChanged" 
              ItemsSource="{Binding Times}">
      </Picker>
      <local:LabelBodyRendererClass x:Name="atiLabel" HorizontalOptions="End"/>
   </Grid>
</ViewCell>

<ViewCell x:Name="pti" Tapped="OpenPickerCommand">
   <Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
      <local:LabelBodyRendererClass Text="Phrase Time Interval" HorizontalOptions="StartAndExpand" />
      <Picker x:Name="ptiPicker" IsVisible="false" HorizontalOptions="End" SelectedIndexChanged="ptiPickerSelectedIndexChanged" ItemsSource="{Binding Times}"></Picker>
      <local:LabelBodyRendererClass x:Name="ptiLabel" HorizontalOptions="End"/>
   </Grid>
</ViewCell>

<强> C#:

private void OpenPickerCommand(object sender, System.EventArgs e)
{
    if (sender != null)
    {
        Picker pkr = sender == ati ? atiPicker : ptiPicker;
        pkr.Focus();
    }
}

回答你的问题&#34;视图模型是否有一种方法可以参与?&#34;,这正是你使用&#39; OpenPickerCommand&#39;方法。问题是,使用ViewCell的公共活动&#39; Tapped&#39;,您无法为委托处理程序设置参数。

请告诉我它是否适合您,或者您是否需要更多信息。

我希望它有所帮助。

答案 1 :(得分:1)

  • 第一个问题是你要混合代码隐藏和MVVM 接近相同的代码。这很令人困惑,当然不是 正确的方式来编码你想要实现的目标。所以,所有的指挥都必须 在附加到View的ViewModel中,没有代码隐藏的一些 代码仅用于UI效果。
    • 无需为所有视觉项目定义手势识别器,因为您只想检测视单元的所有表面上的水龙头。要实现此目的,您必须使用InputTransparent = true定义ViewCell的所有子项。因此,将无法检测到水龙头,并且将由父ViewCell(您 必须指明InpuTransparent,因为没有tap事件 冒泡在X.Forms)。
    • 显示和隐藏选择器是View问题,而不是ViewModel问题。所以在这里你可以使用一些代码隐藏为ViewCell Tapped事件创建一个事件处理程序。这个处理程序只会在选择器上设置visible = true。
    • 选择器选择事件必须连接到ViewModel中的相应命令。因此,每次显示选择器并选择一个值时,您的viewmodel将知道该操作。这是viewmodel中唯一需要的命令。根据XForms版本,选择器没有可绑定命令,因此您可以使用Web上可以找到的众多“bindablepicker”实现之一,或者也可以使用XAML EventToCommand Behavior。

所以有两个不同的问题:显示/隐藏选择器,这可以直接在XAML中实现,或者借助一些代码隐藏;以及必须使用viewmodel中的Command管理的选择器项目选择。

希望这会对你有所帮助

答案 2 :(得分:1)

您可以使用附加属性解决此问题。只需为ViewCell定义一个“ 行为”类即可添加Command / Parameter属性。

public static class TappedCommandViewCell
{
    private const string TappedCommand = "TappedCommand";
    private const string TappedCommandParameter = "TappedCommandParameter";

    public static readonly BindableProperty TappedCommandProperty =
        BindableProperty.CreateAttached(
            TappedCommand,
            typeof(ICommand),
            typeof(TappedCommandViewCell),
            default(ICommand),
            BindingMode.OneWay,
            null,
            PropertyChanged);

    public static readonly BindableProperty TappedCommandParameterProperty =
        BindableProperty.CreateAttached(
            TappedCommandParameter,
            typeof(object),
            typeof(TappedCommandViewCell),
            default(object),
            BindingMode.OneWay,
            null);

    private static void PropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is ViewCell cell)
        {
            cell.Tapped -= ViewCellOnTapped;
            cell.Tapped += ViewCellOnTapped;
        }
    }

    private static void ViewCellOnTapped(object sender, EventArgs e)
    {
        if (sender is ViewCell cell && cell.IsEnabled)
        {
            var command = GetTappedCommand(cell);
            var parameter = GetTappedCommandParameter(cell);

            if (command != null && command.CanExecute(parameter))
            {
                command.Execute(parameter);
            }
        }
    }

    public static ICommand GetTappedCommand(BindableObject bindableObject) =>
        (ICommand)bindableObject.GetValue(TappedCommandProperty);

    public static void SetTappedCommand(BindableObject bindableObject, object value) =>
        bindableObject.SetValue(TappedCommandProperty, value);

    public static object GetTappedCommandParameter(BindableObject bindableObject) =>
        bindableObject.GetValue(TappedCommandParameterProperty);

    public static void SetTappedCommandParameter(BindableObject bindableObject, object value) =>
        bindableObject.SetValue(TappedCommandParameterProperty, value);
}

之后,请在XAML中引用您的行为名称空间,并使用完全限定的名称指定属性值:

<ViewCell StyleId="disclosure-indicator"
          behaviors:TappedCommandViewCell.TappedCommand="{Binding BrowseCommand}"
          behaviors:TappedCommandViewCell.TappedCommandParameter="https://www.google.com">
    <StackLayout Orientation="Horizontal">
        <Label Text="Recipient"
               VerticalOptions="Center"
               Margin="20,0"/>
        <Label Text="{Binding LedgerRecord.Recipient}"
               HorizontalOptions="EndAndExpand"
               VerticalOptions="Center"
               Margin="0,0,20,0"/>
        </Label>
    </StackLayout>
</ViewCell>

以上内容使您可以使用MVVM,而无需使用Tap Gesture Recognizer。