具有可绑定属性的IMarkupExtension



我为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>

我怎样才能做到这一点?

您似乎无法使用具有可绑定属性的IMarkupExtension。作为"Binding",只能在BindableObject的BindableProperty上设置。问题是MarkupExtension类不是从BindableObject派生的,这就是为什么不能在其属性上设置绑定的原因。尽管您让它实现了BindableObject,但它仍然无法工作。

解决方法是使用值转换器。

例如:

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和bindable属性的失败尝试:不起作用,针对不同情况的更雄心勃勃的方法可能是相关的:MarkupExtension用于绑定。

我通过创建一个转换器(就像@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}"/>

您实际上可以使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();
}
}

相关内容

  • 没有找到相关文章

最新更新