XAML 具有可绑定属性的IMarkupExtension

xfyts7mz  于 2023-09-28  发布在  其他
关注(0)|答案(3)|浏览(107)

我已经为ImageSource创建了一个IMarkupExtension,它从指定的字体中获取指定的符号,并以指定的颜色和指定的高度显示它。大多数情况下,图标名称是静态的,我直接写入XAML。但是有时候会有一些东西的列表,这些东西有一个属性来决定应该使用哪个图标。在这种情况下,图标名称必须是可绑定的。
以下是(或多或少)我的FontImageExtension的当前状态:

[ContentProperty(nameof(IconName))]
public class FontImageExtension : IMarkupExtension<ImageSource>
{
    private readonly IconFontService iconFontService;

    [TypeConverter(typeof(FontSizeConverter))]
    public double Size { get; set; } = 30d;

    public string IconName { get; set; }

    public Color Color { get; set; }

    public string FontFamily { get; set; }

    public FontImageExtension()
    {
        iconFontService = SomeKindOfContainer.Resolve<IconFontService>();
    }

    public ImageSource ProvideValue(IServiceProvider serviceProvider)
    {
        if (string.IsNullOrEmpty(IconName))
            return null;

        IconFont iconFont = iconFontService.GetIconFont();

        if (iconFont == null)
            return null;

        string glyphCode = iconFont.GetGlyphCode(IconName);

        if (string.IsNullOrEmpty(glyphCode))
            return null;

        FontImageSource fontImageSource = new FontImageSource()
        {
            FontFamily = iconFont.GetPlatformLocation(),
            Glyph = glyphCode,
            Color = this.Color,
            Size = this.Size,
        };

        return fontImageSource;
    }

    object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
    {
        return ProvideValue(serviceProvider);
    }
}

大多数时候,我在XAML中使用它(它已经完美地工作了):

<Image Source="{m:FontImage SomeIcon, Color=Black, Size=48}"/>

但是对于动态UI(例如列表或其他东西)我需要这样的:

<CollectionView ItemsSource={Binding SomeCollection}">
    <CollectionView.ItemTemplate>
        <StackLayout>
            <Image Source="{m:FontImage IconName={Binding ItemIcon}, Color=Black, Size=48}"/>
            <Label Text="{Binding ItemText}"/>
        </StackLayout>
    </CollectionView.ItemTemplate>
</CollectionView>

我该怎么做?

wbgh16ku

wbgh16ku1#

似乎你不能使用IMarkupExtension与bindableproperties.作为一个'绑定'只能设置在BindableObject的BindableProperty.问题是MarkupExtension类不派生BindableObject,这就是为什么它是不可能设置绑定在它的属性.虽然你让它实现BindableObject,它仍然无法工作.
解决方法是使用Value Converters
举例来说:

class ImageSourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var p = parameter.ToString().Split('|');
        string colorName = p[0];
        ColorTypeConverter colorTypeConverter = new ColorTypeConverter();
        Color color = (Color)colorTypeConverter.ConvertFromInvariantString(colorName);
        double fontSize = double.Parse(p[1]);

        //didn't test this here.
        IconFontService iconFontService = SomeKindOfContainer.Resolve<IconFontService();
        IconFont iconFont = iconFontService.GetIconFont();
        if (iconFont == null)
            return null;

        string glyphCode = iconFont.GetGlyphCode((string)value);
        if (string.IsNullOrEmpty(glyphCode))
            return null;

        FontImageSource fontImageSource = new FontImageSource()
        {
            FontFamily = iconFont.GetPlatformLocation(),
            Glyph = glyphCode,
            Color = color,
            Size = fontSize,
        };
        return fontImageSource;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

在你的xaml中使用:

<ContentPage.Resources>
    <ResourceDictionary>
        <local:ImageSourceConverter x:Key="imageConvert" />
    </ResourceDictionary>
</ContentPage.Resources>

<CollectionView ItemsSource={Binding SomeCollection}">
  <CollectionView.ItemTemplate>
    <StackLayout>
        <Image Source="{Binding Name,Converter={StaticResource imageConvert}, ConverterParameter=Color.Black|48}"/>
        <Label Text="{Binding ItemText}"/>
    </StackLayout>
  </CollectionView.ItemTemplate>
</CollectionView>

另请参见失败的尝试声明BindableProperty:IMarkupExtension with bindable property does not work和一个更雄心勃勃的方法来处理稍微不同的情况-可能是相关的:用于绑定的MarkupExtension。

ev7lccsx

ev7lccsx2#

我通过创建一个转换器解决了这个问题(就像@Leo Zhu建议的那样),但除了IMarkupExtension之外。因此,我的扩展保持原样(添加了一个在转换器中使用的常量值),转换器的代码如下:

public class FontIconConverter : IValueConverter, IMarkupExtension
{
    private IServiceProvider serviceProvider;

    public Color Color { get; set; }

    [TypeConverter(typeof(FontSizeConverter))]
    public double Size { get; set; } = FontIconExtension.DefaultFontSize;

    public string FontFamily { get; set; }

    public FontIconConverter()
    {
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is string iconName))
            return null;

        var fontIcon = new FontIconExtension()
        {
            IconName = iconName,
            Color = Color,
            Size = Size,
            FontFamily = FontFamily,
        };

        return fontIcon.ProvideValue(serviceProvider);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
        return this;
    }
}

然后可以像这样使用它:

<Image Source="{Binding IconNameProperty, Converter={c:FontIconConverter Color=Black, Size=48}}"/>

对于静态值,它保持如下:

<Image Source="{m:FontImage SomeIconsName, Color=Black, Size=48}"/>
2ul0zpep

2ul0zpep3#

实际上,您可以让FontImageExtension标记扩展同时实现(1)BindableObject,(2)IMarkupExtensionIMultiValueConverter

[ContentProperty(nameof(IconName))]
public class FontImageExtension : BindableObject, IMarkupExtension<BindingBase>, IMultiValueConverter
{
    private readonly IconFontService iconFontService;

    public static readonly BindableProperty SizeProperty
        = BindableProperty.Create(nameof(Size), typeof(double), typeof(FontImageExtension));
    public static readonly BindableProperty IconNameProperty
        = BindableProperty.Create(nameof(IconName), typeof(string), typeof(FontImageExtension));
    public static readonly BindableProperty ColorProperty
        = BindableProperty.Create(nameof(Color), typeof(Color), typeof(FontImageExtension));
    public static readonly BindableProperty FontFamilyProperty
        = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(FontImageExtension));

    [TypeConverter(typeof(FontSizeConverter))]
    public double Size
    {
        get { return (double)GetValue(SizeProperty); }
        set { SetValue(SizeProperty, value); }
    }

    public string IconName
    {
        get { return (string)GetValue(IconNameProperty); }
        set { SetValue(IconNameProperty, value); }
    }

    public Color Color
    {
        get { return (Color)GetValue(ColorProperty); }
        set { SetValue(ColorProperty, value); }
    }

    public string FontFamily
    {
        get { return (string)GetValue(IconNameProperty); }
        set { SetValue(FontFamilyProperty, value); }
    }

    public FontImageExtension()
    {
        iconFontService = SomeKindOfContainer.Resolve<IconFontService>();
    }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        return (this as IMarkupExtension<BindingBase>).ProvideValue(serviceProvider);
    }

    BindingBase IMarkupExtension<BindingBase>.ProvideValue(IServiceProvider serviceProvider)
    {
        return new MultiBinding()
        {
            Converter = this,
            Mode = BindingMode.OneWay,
            Bindings = new Collection<BindingBase>
            {
                new Binding(nameof(Size), BindingMode.OneWay, null, null, null, this),
                new Binding(nameof(IconName), BindingMode.OneWay, null, null, null, this),
                new Binding(nameof(Color), BindingMode.OneWay, null, null, null, this),
                new Binding(nameof(FontFamily), BindingMode.OneWay, null, null, null, this)
            }
        };
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        double Size = (double)values[0];
        string IconName = (string)values[1];
        Color Color = (Color)values[2];
        string FontFamily = (string)values[3];

        if (string.IsNullOrEmpty(IconName))
            return null;

        IconFont iconFont = iconFontService.GetIconFont();
        if (iconFont == null)
            return null;

        string glyphCode = iconFont.GetGlyphCode(IconName);
        if (string.IsNullOrEmpty(glyphCode))
            return null;

        FontImageSource fontImageSource = new FontImageSource()
        {
            FontFamily = iconFont.GetPlatformLocation(),
            Glyph = glyphCode,
            Color = this.Color,
            Size = this.Size,
        };

        return fontImageSource;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

相关问题