自定义控件中的选择器
Picker within Custom Control
我正在构建一些表单(使用 TableView
)并注意到我对单元格的样式相同。我决定将这段重复的代码重构为一个通用的控件。
我正在努力让绑定在选择器上正确工作。我的自定义控件如下所示:
我的控件是ViewCell
,所以我可以在TableView
:
内显示它
public partial class PickerCell : ViewCell
{
...
}
我已经能够设置选择器 ItemSource
,但我无法使 SelectedItem
或 DisplayItemBinding
属性起作用。
我见过 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 但无济于事。
有什么想法吗?
我的自定义选择器也有类似的情况,我想在所选项目更改时实现一个事件处理程序。以下是我的做法。
在您的自定义选择器的代码隐藏中,实现一个 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);
}
以上内容不可绑定,因此要实现它,您必须:
- 在您的主视图中设置处理程序:
<?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>
- 在视图的代码隐藏中实现处理程序以调用视图模型的处理程序:
private void Handle_PickerIndexChangedEvent(object sender, System.EventArgs e)
{
viewModel.HandlerIndexChanged(); // or whatever
}
可能有更好的方法来做到这一点,即实施 Command
和 CommandParameter
。然而,以上对我有用,即使我不得不稍微改变 MVVM 规则。
这里是自定义选择器单元格的实现。 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"/>
我正在构建一些表单(使用 TableView
)并注意到我对单元格的样式相同。我决定将这段重复的代码重构为一个通用的控件。
我正在努力让绑定在选择器上正确工作。我的自定义控件如下所示:
我的控件是ViewCell
,所以我可以在TableView
:
public partial class PickerCell : ViewCell
{
...
}
我已经能够设置选择器 ItemSource
,但我无法使 SelectedItem
或 DisplayItemBinding
属性起作用。
我见过 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 但无济于事。
有什么想法吗?
我的自定义选择器也有类似的情况,我想在所选项目更改时实现一个事件处理程序。以下是我的做法。
在您的自定义选择器的代码隐藏中,实现一个 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);
}
以上内容不可绑定,因此要实现它,您必须:
- 在您的主视图中设置处理程序:
<?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>
- 在视图的代码隐藏中实现处理程序以调用视图模型的处理程序:
private void Handle_PickerIndexChangedEvent(object sender, System.EventArgs e)
{
viewModel.HandlerIndexChanged(); // or whatever
}
可能有更好的方法来做到这一点,即实施 Command
和 CommandParameter
。然而,以上对我有用,即使我不得不稍微改变 MVVM 规则。
这里是自定义选择器单元格的实现。 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"/>