【问题标题】:IMarkupExtension with bindable properties具有可绑定属性的 IMarkupExtension
【发布时间】:2021-01-03 09:56:41
【问题描述】:

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

我怎样才能做到这一点?

【问题讨论】:

    标签: c# xaml xamarin binding markup-extensions


    【解决方案1】:

    看来您不能将IMarkupExtension 与可绑定属性一起使用。因为“绑定”只能在 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 for binding

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 这是一个好主意,让 FontIconConverter 直接实现 IMarkupExtension。
      • 如果我的回答对您有帮助,您能否将其标记为接受答案,或者您可以标记自己。它可能会帮助其他人。
      猜你喜欢
      • 2017-07-27
      • 2019-01-07
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 2017-07-06
      • 2011-02-19
      相关资源
      最近更新 更多