Как установить значение пользовательского свойства с типом, отличным от строки в XAML [Xamarin.Forms] - PullRequest
3 голосов
/ 10 января 2020

В Xamarin.Forms в XAML вы можете написать что-то вроде:

<Entry Keyboard="Plain" />

Я искал класс Entry, а свойство 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. До сих пор я выяснил, что это не имеет ничего общего ни с KeyboardProperty, ни с привязками, а с самим свойством Keyboard (если я прав). Я считаю, что это как-то связано с ValueConverters, что мне нужно определить некоторую форму преобразования строки в клавиатуру, когда парсер XAML доберется до этой части, просто не могу найти ответ, что мне нужно сделать.

Ответы [ 2 ]

3 голосов
/ 10 января 2020

Ответ на ваш вопрос хорошо объяснил Петцольд в своей прекрасной книге о Xamarin.Forms (которую вы можете скачать бесплатно здесь !).

В конце раздела Свойства и атрибуты в главе 7 вы можете прочитать

... вы можете включить пользовательские классы в XAML, и эти классы может иметь свойства пользовательских типов, или свойства могут быть стандартных типов, но допускают дополнительные значения. Все, что вам нужно, - пометить эти типы или свойства атрибутом C# TypeConverter и предоставить класс, производный от TypeConverter.

И некоторые подробности для любопытных

(просто цитата из вышеупомянутой книги)

В этой главе Петцольд иллюстрирует это, дав Пример с меткой:

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

, а затем продолжает объяснять, как эти свойства устанавливаются в XAML.

Краткий характер XAML является результатом краткости значений атрибутов. - например, использование слова «Large» вместо вызова метода Device.GetNamedSize. Эти сокращения не встроены в синтаксический анализатор XAML. Вместо этого синтаксическому анализатору XAML помогают различные классы преобразователей, определенные специально для этой цели.

Когда синтаксический анализатор XAML обнаруживает элемент Label, он может использовать отражение, чтобы определить, есть ли в Xamarin.Forms класс с именем Label, и, если это так, он может создать экземпляр этого класса. Теперь он готов инициализировать этот объект. Свойство Text имеет тип string, и этому атрибуту просто присваивается значение атрибута.

Свойства Label IsVisible и Opacity имеют тип bool и double соответственно, и они настолько просты, насколько вы можете ожидать , Анализатор XAML использует методы Boolean.Parse и Double.Parse для преобразования значений атрибута. Метод Boolean.Parse нечувствителен к регистру, но, как правило, логические значения пишутся с заглавной буквы как «True» и «False» в XAML. Методу Double.Parse передается аргумент CultureInfo.InvariantCulture, поэтому преобразование не зависит от локальной культуры программиста или пользователя.

Свойство HorizontalTextAlignment класса Label имеет тип TextAlignment, который является перечислением , Для любого свойства, являющегося типом перечисления, синтаксический анализатор XAML использует метод Enum.Parse для преобразования строки в значение.

Свойство VerticalOptions имеет тип LayoutOptions, структуру. Когда синтаксический анализатор XAML ссылается на структуру LayoutOptions с помощью отражения, он обнаруживает, что для структуры определен атрибут C#:

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

Атрибут TypeConverter поддерживается классом с именем TypeConverterAttribute. Этот конкретный атрибут TypeConverter в LayoutOptions ссылается на класс с именем LayoutOptionsConverter, который наследуется от абстрактного класса publi c с именем TypeConverter, который определяет методы с именами CanConvertFrom и ConvertFrom. Когда синтаксический анализатор XAML обнаруживает этот атрибут TypeConverter, он создает экземпляр LayoutOptionsConverter. Атрибуту VerticalOptions в XAML присваивается строка «Center», поэтому анализатор XAML передает эту строку «Center» в метод ConvertFrom объекта LayoutOptionsConverter и выводит значение LayoutOptions. Это присваивается свойству VerticalOptions объекта Label.

2 голосов
/ 10 января 2020

Ответить на вопрос полностью. Создайте этот класс:

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;
        }
    }
}

Затем добавьте атрибут TypeConverter к свойству Keyboard:

using System.Reflection;

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

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