1

我想扩展 Xamarin.Forms Picker,以便可以将集合绑定到它。快速搜索后,我找到了这个页面:Picker Example有两个很好的例子。拒绝只是复制和粘贴代码(出于学习目的),我继续根据这两个示例制作自己的代码。

它几乎相同,除了我的不起作用。

当我不向 ItemsSource 提供集合时,一切正常。每当我分配一个集合时,我都会收到以下错误:

Xamarin.Forms.Xaml.XamlParseException:位置 9:32。无法分配属性“ItemsSource”:“Xamarin.Forms.Binding”和“System.Collections.IEnumerable”之间的类型不匹配

选择器:

public class BindablePicker : Picker
{
    private static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(BindablePicker), null, propertyChanged: OnItemsSourceChanged);

    private static readonly BindableProperty SelectedItemProperty =
        BindableProperty.Create("SelectedItem", typeof(object), typeof(BindablePicker));

    public IEnumerable ItemsSource
    {
        get { return (IEnumerable)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }
    public object SelectedItem
    {
        get { return GetValue(SelectedItemProperty); }
        set { SetValue(SelectedItemProperty, value); }
    }
    public string DisplayMember { get; set; }


    private static void OnItemsSourceChanged(BindableObject bindable, Object oldValue, Object newValue)
    {
        var newval = newValue as IEnumerable; //Had to implement this because of the non-generic .Create() method expects Object as param.
        var picker = bindable as BindablePicker;

        if (picker != null)
        {
            picker.Items.Clear();
            if (newval == null) return;

            foreach (var item in newval)
            {
                if (string.IsNullOrEmpty(picker.DisplayMember))
                {
                    picker.Items.Add(item.ToString());
                }
                else
                {
                    var prop = item.GetType()
                        .GetRuntimeProperties()
                        .FirstOrDefault(p => string.Equals(p.Name, picker.DisplayMember, StringComparison.OrdinalIgnoreCase));

                    picker.Items.Add(prop.GetValue(item).ToString());
                }
            }
        }
    }

    private void OnSelectedIndexChanged(object sender, EventArgs args)
    {
        if (SelectedIndex < 0 || SelectedIndex > Items.Count - 1)
        {
            SelectedItem = null;
        }
        else
        {
            SelectedItem = ItemsSource.ItemOf(SelectedIndex); //ItemOf is an extension method I made for IEnumerable (has to be tested).
        }
    }
}

ViewModel(部分):

public class HomePageViewModel : ViewModelBase
{
    //In the app this is populated with a List<Person>.
    public IEnumerable<Person> People { get; set; }
}

XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:controls="clr-namespace:TestApp.Controls;assembly=TestApp"
         x:Class="TestApp.Views.HomePage">
  <ContentPage.Content>
     <StackLayout>

         <controls:BindablePicker ItemsSource="{Binding People}"
                                  HorizontalOptions="Center" />
     </StackLayout>
  </ContentPage.Content>
</ContentPage>

请注意,链接页面中的第一个示例选择器适用于提供的 VM/View 设置。

我还没有完成选择器,我仍然想为 SelectedItem 属性提供 TwoWay 绑定并支持 ObservableCollection。

你的,

4

1 回答 1

1

一开始我以为我疯了。

然后我觉得有什么东西严重坏了。

但最后我想通了...

private static readonly BindableProperty ItemsSourceProperty =
    BindableProperty.Create("ItemsSource", typeof(IEnumerable),  
    typeof(BindablePicker), null, propertyChanged: OnItemsSourceChanged);

为了让 Xaml 解析器看到 a BindableProperty,它必须是public(和static,但你得到了正确的部分)。

在您的情况下,Xaml 解析器看不到BindableProperty,因此它回退到属性,但它没有任何方法来设置Binding,并且由于类型不匹配,您会得到异常。

将您的代码更改为

public static readonly BindableProperty ItemsSourceProperty =
    BindableProperty.Create("ItemsSource", typeof(IEnumerable),  
    typeof(BindablePicker), null, propertyChanged: OnItemsSourceChanged);
于 2016-09-13T07:50:10.417 回答