如何使用 XAML [Xamarin.Forms] 中除字符串以外的类型设置自定义 属性 值

How to set custom property value with Type other than String in XAML [Xamarin.Forms]

在Xamarin.Forms中XAML,你可以这样写:

<Entry Keyboard="Plain" />

我搜索了 Entry class 并且 Keyboard 属性 是 Xamarin.Forms.Keyboard 类型。但是,如果我创建自己的自定义 ContentView 并在里面写这样的内容:

    public static readonly BindableProperty KeyboardProperty = BindableProperty.Create
    (
        propertyName: "Keyboard",
        returnType: typeof(Keyboard),
        declaringType: typeof(MyCustomContentView),
        defaultValue: Keyboard.Default,
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanged: (bindable, oldValue, newValue) =>
        {
            // some unrelated stuff here
        }
    );
    public Keyboard Keyboard
    {
        get => (Keyboard)GetValue(KeyboardProperty);
        set => SetValue(KeyboardProperty, value);
    }

我不能对我自己的内容视图使用相同的 XAML 格式。显然它是一个简单的字符串,它期望 Xamarin.Forms.Keyboard class 的实例。到目前为止,我发现它与 KeyboardProperty 和绑定无关,而是 Keyboard 属性 本身(如果我是正确的话)。我相信它与 ValueConverters 有关,当 XAML 解析器到达这部分时,我必须定义某种形式的字符串到键盘转换,只是似乎无法找到我想要的答案需要做的。

Petzold 在他关于 Xamarin.Forms 的精美书中很好地解释了您问题的答案(您可以免费下载 here!)。

第 7 章特性和特性部分的末尾,您可以阅读

... you can include custom classes in XAML, and these classes can have properties of custom types, or the properties can be of standard types but allow additional values. All you need is to flag these types or properties with a C# TypeConverter attribute and provide a class that derives from TypeConverter.

还有一些好奇的细节

(简单引用上述书籍)

在那一章中,Petzold 通过给出一个带有标签的例子来说明这一点:

<Label Text="Hello from XAML!"        
       IsVisible="True"
       Opacity="0.75" 
       HorizontalTextAlignment="Center" 
       VerticalOptions="CenterAndExpand" 
       TextColor="Blue" 
       BackgroundColor="#FF8080" 
       FontSize="Large" 
       FontAttributes="Bold,Italic" /> 

然后继续解释 XAML 如何设置这些属性。

The concision of the XAML results mostly from the brevity of the attribute values—for example, the use of the word "Large" rather than a call to the Device.GetNamedSize method. These abbreviations are not built into the XAML parser. The XAML parser is instead assisted by various converter classes defined specifically for this purpose.

When the XAML parser encounters the Label element, it can use reflection to determine whether Xamarin.Forms has a class named Label, and if so, it can instantiate that class. Now it is ready to initialize that object. The Text property is of type string, and the attribute value is simply assigned to that property.

The IsVisible and Opacity properties of Label are of type bool and double, respectively, and these are as simple as you might expect. The XAML parser uses the Boolean.Parse and Double.Parse methods to convert the attribute values. The Boolean.Parse method is case insensitive, but generally Boolean values are capitalized as “True” and “False” in XAML. The Double.Parse method is passed a CultureInfo.InvariantCulture argument, so the conversion doesn’t depend on the local culture of the programmer or user.

The HorizontalTextAlignment property of Label is of type TextAlignment, which is an enumeration. For any property that is an enumeration type, the XAML parser uses the Enum.Parse method to convert from the string to the value.

The VerticalOptions property is of type LayoutOptions, a structure. When the XAML parser references the LayoutOptions structure using reflection, it discovers that the structure has a C# attribute defined:

[TypeConverter (typeof(LayoutOptionsConverter))] 
public struct LayoutOptions 
{ 
    … 
}

The TypeConverter attribute is supported by a class named TypeConverterAttribute. This particular TypeConverter attribute on LayoutOptions references a class named LayoutOptionsConverter, which derives from a public abstract class named TypeConverter that defines methods named CanConvertFrom and ConvertFrom. When the XAML parser encounters this TypeConverter attribute, it instantiates the LayoutOptionsConverter. The VerticalOptions attribute in the XAML is assigned the string “Center”, so the XAML parser passes that “Center” string to the ConvertFrom method of LayoutOptionsConverter, and out pops a LayoutOptions value. This is assigned to the VerticalOptions property of the Label object.

完整回答问题。创建这个 class:

using System;
using Xamarin.Forms;

public class KeyboardTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object ConvertFromInvariantString(string value)
    {
        switch (value)
        {
            case "Chat": return Keyboard.Chat;
            case "Email": return Keyboard.Email;
            case "Numeric": return Keyboard.Numeric;
            case "Plain": return Keyboard.Plain;
            case "Telephone": case "Phone": return Keyboard.Telephone;
            case "Text": return Keyboard.Text;
            case "Url": return Keyboard.Url;
            default: return Keyboard.Default;
        }
    }
}

然后给Keyboard添加一个TypeConverter属性 属性:

using System.Reflection;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[TypeConverter(typeof(KeyboardTypeConverter))]
public Keyboard Keyboard
{
    get => (Keyboard)GetValue(KeyboardProperty);
    set => SetValue(KeyboardProperty, value);
}