具有可绑定属性的 IMarkupExtension
IMarkupExtension with bindable properties
我为 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.The 的 BindableProperty 上设置,问题在于 MarkupExtension class不是从 BindableObject 派生的,这就是为什么不能对其设置绑定的原因 properties.Though 你让它实现 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 的失败尝试: and a more ambitious approach to a somewhat different situation - might be relevant: 。
我通过创建一个转换器解决了这个问题(就像@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}"/>
我为 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.The 的 BindableProperty 上设置,问题在于 MarkupExtension class不是从 BindableObject 派生的,这就是为什么不能对其设置绑定的原因 properties.Though 你让它实现 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 的失败尝试:
我通过创建一个转换器解决了这个问题(就像@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}"/>