【问题标题】:Picker within Custom Control自定义控件中的选取器
【发布时间】:2019-06-16 05:23:45
【问题描述】:

我正在构建一些表单(使用TableView),并注意到我对单元格的样式设置相同。我决定将这段重复的代码重构为一个通用控件。

我正在努力让绑定在选择器上正确工作。我的自定义控件如下所示:

我的控件是ViewCell,所以我可以在TableView 中显示它:

public partial class PickerCell : ViewCell
{
...
}

我已经能够设置选择器ItemSource,但我无法让SelectedItemDisplayItemBinding 属性工作。

我见过this post from 2015(现在很古老),但我试过了,这些方法被标记为过时,无论如何都没有用。

我也尝试过控制ctor:

ItemPicker.SetBinding(Picker.ItemsSourceProperty, "ItemSource");
ItemPicker.SetBinding(Picker.SelectedItemProperty, "SelectedItem", BindingMode.TwoWay);

但这也没有用。

我基本上只是想添加一种方法,以便我可以将选择器从我的 xaml 绑定到控件。我真的希望这是可能的,因为我在我的应用程序周围使用了这个精确的视图单元可能 9/10 次。我真的不想重复自己,我通常会在这种情况下创建控件。例如,我有一个类似样式的条目单元格,并且效果很好....

这是我用来设置项目来源的代码:

public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
    nameof(ItemsSource),
    typeof(IList),
    typeof(PickerCell)
    propertyChanged: (bindable, oldVal, newVal) => ((PickerCell) bindable).OnItemsSourceChanged((IList) newVal)
);

public IList ItemsSource
{
    get { return (IList)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

private void OnItemsSourceChanged(IList list)
{
    ItemPicker.ItemsSource = list;
}

我尝试为选择器from Xamarin 实现一些代码,但无济于事。

有什么想法吗?

【问题讨论】:

    标签: c# xamarin xamarin.forms picker


    【解决方案1】:

    我的自定义选择器遇到了类似的情况,我想在所选项目更改时实现事件处理程序。以下是我的做法。

    在自定义选择器的代码隐藏中,实现 EventHandler 属性和私有变量:

    private EventHandler onIndexChanged = null;
    ...
    public event EventHandler OnIndexChangedEvent
    {
        add
        {
            onIndexChanged = null;
            onIndexChanged = value;
        }
        remove
        {
            onIndexChanged = null;
        }
    }
    

    在您的自定义选择器的 XAML 中,将处理程序添加到 SelectedIndexChanged 属性:

    <Picker
        x:Name="MyPicker"
        SelectedIndexChanged="Handle_SelectedIndexChanged"/>
    

    然后回到你的代码隐藏,实现这个处理程序:

    void Handle_SelectedIndexChanged(object sender, System.EventArgs e)
    {
        // Trigger the implemented event, if not null.
        onIndexChanged?.Invoke(sender, e);
    }
    

    以上是不可绑定的,所以要实现它,你必须:

    1. 在主视图中设置处理程序:
    <?xml version="1.0" encoding="UTF-8"?>
    <ContentPage 
        xmlns="http://xamarin.com/schemas/2014/forms" 
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        x:Class="MyApp.MyPage"
        xmlns:controls="clr-namespace:MyApp.Controls">
        <ContentPage.Content>
            <StackLayout
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand">
                 <controls:CustomPicker
                    ItemSource="{Binding SelectionList}"
                    OnIndexChangedEvent="Handle_PickerIndexChangedEvent"/>
            </StackLayout>
        </ContentPage.Content>
    </ContentPage>
    
    1. 在视图的代码隐藏中实现处理程序以调用视图模型的处理程序:
    private void Handle_PickerIndexChangedEvent(object sender, System.EventArgs e)
    {
        viewModel.HandlerIndexChanged(); // or whatever
    }
    

    可能有更好的方法来做到这一点,即实现CommandCommandParameter。但是,即使我不得不稍微改变一下 MVVM 规则,上述方法对我也有用。

    【讨论】:

    • 选择器的选定项属性是可绑定的,并将触发您正在寻找的事件。我的情况是将选择器的主要功能(项目源、显示绑定和选定项目)更多地拼接到单独的控件(视图/单元格/等)。我也不打算弯曲 MVVM,因为我将代码重构为这种格式的原因是为了更好地遵守 MVVM!
    • 我忘了实际展示 XAML 实现。现在添加它。您不是在寻找将外部处理程序附加到 Picker 的索引更改事件的方法吗?
    • 不,我一直在寻找一种将选择器嵌入到单独控件中的方法。我的场景是我有一个表单,上面有很多选择器,UI 是一样的,所以我制作了一个自定义的 ViewCell,称为 PickerCell。这个控件可以绑定标题、占位符和到选择器。我目前正处于进入砖墙的阶段,我已经正确绑定了ItemSourceSelectedItem。我刚刚得到了ItemDisplayBinding
    【解决方案2】:

    这是自定义选择器单元格的实现。 vanilla xamarin 的唯一变化是绑定显示必须输入为string 而不是binding: property

    这里是所有重要的代码:

    可绑定属性声明

    /// <summary>
    /// The placeholder property.
    /// </summary>
    public static readonly BindableProperty PlaceholderProperty = BindableProperty.Create(
        nameof(Placeholder),
        typeof(string),
        typeof(CustomPickerCell),
        propertyChanged: (bindable, oldVal, newVal) => ((CustomPickerCell)bindable).OnPlaceholderChanged((string)newVal)
    );
    
    /// <summary>
    /// The items source property.
    /// </summary>
    public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(
        nameof(ItemsSource),
        typeof(IList),
        typeof(CustomPickerCell),
        propertyChanged: (bindable, oldVal, newVal) => ((CustomPickerCell)bindable).OnItemsSourceChanged((IList)newVal)
    );
    
    /// <summary>
    /// The selected item property.
    /// </summary>
    public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(
        nameof(SelectedItem),
        typeof(object),
        typeof(CustomPickerCell),
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanged: (bindable, oldVal, newVal) => ((CustomPickerCell)bindable).OnSelectedItemChanged((object)newVal)
    );
    
    /// <summary>
    /// The item display binding property
    /// NOTE: Use the name of the property, you do not need to bind to this property.
    /// </summary>
    public static readonly BindableProperty ItemDisplayBindingProperty = BindableProperty.Create(
        nameof(ItemDisplayBinding),
        typeof(string),
        typeof(CustomPickerCell),
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanged: (bindable, oldVal, newVal) => ((CustomPickerCell)bindable).OnItemDisplayBindingChanged((string)newVal)
    );
    

    可绑定属性

    /// <summary>
    /// The cell's placeholder (select a ....).
    /// </summary>
    /// <value>The placeholder.</value>
    public string Placeholder
    {
        get { return (string)GetValue(PlaceholderProperty); }
        set { SetValue(PlaceholderProperty, value); }
    }
    
    /// <summary>
    /// The cell's picker item source.
    /// </summary>
    /// <value>The items source.</value>
    public IList ItemsSource
    {
        get { return (IList)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }
    
    /// <summary>
    /// Gets or sets the selected item.
    /// </summary>
    /// <value>The selected item.</value>
    public object SelectedItem
    {
        get { return GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }
    
    /// <summary>
    /// Gets or sets the item display binding.
    /// </summary>
    /// <value>The item display binding.</value>
    public string ItemDisplayBinding
    {
        get { return (string)GetValue(ItemDisplayBindingProperty); }
        set { SetValue(ItemDisplayBindingProperty, value); }
    }
    

    属性更改方法

    /// <summary>
    /// Called when PlaceholderProperty changes.
    /// </summary>
    /// <param name="newVal">New value.</param>
    private void OnPlaceholderChanged(string newVal)
    {
        ItemPicker.Title = newVal;
    }
    
    /// <summary>
    /// Called when ItemSourceProperty changes.
    /// </summary>
    private void OnItemsSourceChanged(IList list)
    {
        ItemPicker.ItemsSource = list;
    }
    
    /// <summary>
    /// Called when SelectedItemProperty changes.
    /// </summary>
    /// <param name="obj">Object.</param>
    private void OnSelectedItemChanged(object obj)
    {
        ItemPicker.SelectedItem = obj;
    }
    
    /// <summary>
    /// Called when ItemDisplayBindingProperty changes.
    /// </summary>
    /// <param name="newvalue">Newvalue.</param>
    private void OnItemDisplayBindingChanged(string newvalue)
    {
        ItemPicker.ItemDisplayBinding = new Binding(newvalue);
    }
    

    初始化

    public CustomPickerCell()
    {
        InitializeComponent();
    
        ItemPicker.ItemsSource = this.ItemsSource;
        ItemPicker.SelectedItem = this.SelectedItem;
    
        ItemPicker.SelectedIndexChanged += OnSelectedIndexChanged;
    }
    
    /// <summary>
    /// Calle when ItemPicker's SelectedIndexChanged event fires.
    /// </summary>
    /// <param name="sender">Sender.</param>
    /// <param name="e">E.</param>
    void OnSelectedIndexChanged(object sender, EventArgs e)
    {
        this.SelectedItem = (ItemPicker.SelectedIndex < 0 || ItemPicker.SelectedIndex > ItemPicker.Items.Count - 1) ? null : ItemsSource[ItemPicker.SelectedIndex];
    }
    

    现在您需要做的就是将此单元格添加到您的 xaml 中,一切顺利!

    用法

    <!-- Where controls is a namespace defined in your xaml -->
    <!-- Not not to bind ItemDisplayBinding, you only need the property name as a string-->
    <controls:CustomPickerCell Title="Type" Placeholder="Select an ice cream" 
                            ItemsSource="{Binding IceCreamList}" 
                            SelectedItem="{Binding SelectedIceCream, Mode=TwoWay}" 
                            ItemDisplayBinding="Name"/>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-05-09
      • 1970-01-01
      • 1970-01-01
      • 2020-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多