.Net MAUI实现MultiPicker
基本原理
主要基于官方提供的 Entry 控件和第三方提供的 Popup 控件,通过继承 ContentView 类,来实现自定义控件;其中多项选择功能通过内置的官方 CollectionView 控件来实现。
属性说明
| 可绑定属性 | 属性类型 | 描述 |
|---|---|---|
| ItemsSource | IEnumerable |
绑定数据源 |
| SelectedItems | IList<object> |
预选择项内容 |
| Title | string |
标题 |
| DisplayPath | string |
显示内容绑定属性名称 |
代码实现
using System.Collections;
using Syncfusion.Maui.Toolkit.Popup;
namespace Company.User.Controls
{
public class MultiPicker : ContentView
{
public static readonly BindableProperty TitleProperty = BindableProperty.Create(
nameof(Title), typeof(string), typeof(MultiPicker), defaultValue: null);
public static readonly BindableProperty DisplayPathProperty = BindableProperty.Create(
nameof(DisplayPath), typeof(string), typeof(MultiPicker), defaultValue: null, propertyChanged: OnDisplayPathChanged);
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(MultiPicker), null);
public static readonly BindableProperty SelectedItemsProperty =
BindableProperty.Create(nameof(SelectedItems), typeof(IList<object>), typeof(MultiPicker), null);
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public IList<object> SelectedItems
{
get => (IList<object>)GetValue(SelectedItemsProperty);
set => SetValue(SelectedItemsProperty, value);
}
public string Title
{
get => (string)GetValue(TitleProperty);
set => SetValue(TitleProperty, value);
}
public string DisplayPath
{
get => (string)GetValue(DisplayPathProperty);
set => SetValue(DisplayPathProperty, value);
}
public MultiPicker()
{
var stack = new VerticalStackLayout()
{
Spacing = 10,
Padding = new Thickness(10, 0),
VerticalOptions = LayoutOptions.Center
};
_itemsEntry = new Entry()
{
IsReadOnly = true,
VerticalOptions = LayoutOptions.Center
};
_itemsEntry.SetBinding(Entry.PlaceholderProperty, new Binding(nameof(Title), source: this));
var tap = new TapGestureRecognizer();
tap.Tapped += Entry_Tapped;
_itemsEntry.GestureRecognizers.Add(tap);
stack.Add(_itemsEntry);
_popup = new SfPopup()
{
BackgroundColor = popBackColor,
AppearanceMode = PopupButtonAppearanceMode.TwoButton,
ShowFooter = true,
AcceptButtonText = "确定",
DeclineButtonText = "取消",
AutoSizeMode = PopupAutoSizeMode.Height,
ContentTemplate = new DataTemplate(() => CreatePopupContentTemplate())
};
_popup.SetBinding(SfPopup.HeaderTitleProperty, new Binding(nameof(Title), source: this));
stack.Add(_popup);
Content = stack;
}
private void Entry_Tapped(object? sender, TappedEventArgs e)
{
_popup.IsOpen = true;
}
private Border CreatePopupContentTemplate()
{
_collection = new CollectionView()
{
ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical),
SelectionMode = SelectionMode.Multiple,
ItemTemplate = new DataTemplate(() => CreateItemTemplate())
};
_collection.SetBinding(CollectionView.ItemsSourceProperty, new Binding(nameof(ItemsSource), source: this));
_collection.SetBinding(CollectionView.SelectedItemsProperty, new Binding(nameof(SelectedItems), source: this));
_collection.SelectionChanged += _collection_SelectionChanged;
var border = new Border()
{
Stroke = Colors.Black,
Content=_collection
}; ;
return border;
}
private void _collection_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
string? items = null;
var count = e.CurrentSelection.Count;
if (count > 0)
{
var first = e.CurrentSelection[0];
if (string.IsNullOrEmpty(DisplayPath))
{
items = first.ToString();
}
else
{
var property = first.GetType().GetProperty(DisplayPath);
var firstValue = property?.GetValue(first);
items = firstValue?.ToString();
}
if (count > 1)
{
items += $" +{count - 1}";
}
}
_itemsEntry.Text = items;
}
private Label CreateItemTemplate()
{
var label = new Label();
label.Margin = new Thickness(6);
label.SetBinding(Label.TextProperty, string.IsNullOrEmpty(DisplayPath) ? new Binding(".") : new Binding(DisplayPath));
return label;
}
private static void OnDisplayPathChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = (MultiPicker)bindable;
view.UpdateDisplayPath();
}
private void UpdateDisplayPath()
{
_collection.ItemTemplate = new DataTemplate(() => CreateItemTemplate());
}
private Entry _itemsEntry = new();
private SfPopup _popup = new();
private CollectionView _collection = new();
}
}
注意事项:
- 引用Nuget包
Syncfusion.Maui.Toolkit 1.0.7; DisplayPathProperty默认值为空,表示绑定整个对象;- 数据模板使用格式:
new DataTemplate(() => CreateItemTemplate());
示例
Test.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Company.User.Views.TestPage"
xmlns:user="clr-namespace:Company.User.Controls"
Title="测试多项选择">
<user:MultiPicker Title="选择项目"
ItemsSource="{Binding PopupItemsSource}"
SelectedItems="{Binding SelectItems}"
DisplayPath="Text"
VerticalOptions="Center" />
</ContentPage>
Test.xaml.cs
using System.Collections.ObjectModel;
namespace Company.User.Views;
public partial class TestPage : ContentPage
{
public TestPage()
{
InitializeComponent();
SetupPopup();
this.BindingContext = this;
}
private ObservableCollection<PopupModelCollection> _popupItemsSource = new();
public ObservableCollection<PopupModelCollection> PopupItemsSource
{
get => _popupItemsSource;
set
{
_popupItemsSource = value;
OnPropertyChanged(nameof(PopupItemsSource));
}
}
private List<object> _selectItems = new();
public List<object> SelectItems
{
get => _selectItems;
set
{
_selectItems = value;
OnPropertyChanged(nameof(SelectItems));
}
}
void SetupPopup()
{
var collection = new List<PopupModelCollection>();
for (int i = 0; i < 15; i++)
{
PopupModelCollection model = new PopupModelCollection
{
Id = i,
Text = $"Item{i}"
};
collection.Add(model);
}
PopupItemsSource = new(collection);
SelectItems = new(collection.Take(3));
}
public class PopupModelCollection
{
public int Id { get; set; }
public string? Text { get; set; }
}
}
截图(基于安卓平台)
- 主页面截图

- 弹窗截图

浙公网安备 33010602011771号