WPF ValueConverter против MarkupExtension для привязки DrawingImage

У меня есть набор значков, хранящихся в ResourceDictionary как Geometry объекты. Я намерен использовать их для создания значков для элементов пользовательского интерфейса, таких как кнопки. Мне нужно иметь возможность настроить эти значки различными способами:

  • Control Brush используется для заполнения геометрии
  • Контроль структуры рендеринга
  • Уметь вращать / переворачивать значок
  • и т.п.

Большинство элементов пользовательского интерфейса, которые могут представлять такие значки, ожидают ImageSource как тип цели. Поэтому мне нужен эффективный и простой в использовании механизм для преобразования моего Geometry ресурсы в фактические ImageSources. В псевдокоде:ImageSource Convert(Geometry resource, RenderOptions options) где options представлять все виды настроек, которые мне могут понадобиться, например, кисть, цвет и т. д.

Насколько я понимаю, есть два основных подхода: расширения разметки и преобразователи значений. Я создал 2 вспомогательных класса, основанных на общей базе. Базовый класс фактически определяет логику преобразования, в то время как 2 помощника предоставляют специфичные для Markup Extension и Value Converter функциональные возможности:

namespace MyWPFHelpers {

/// <summary>
/// Provides functionality for actual conversion. Also defines various options. In this example just Fill.
/// </summary>
public abstract class GeometryImageBase : MarkupExtension
{

    public Brush Fill { get; set; } = Brushes.Black;

    public DrawingImage CreateImage(Geometry g)
    {
        var d = new GeometryDrawing(Fill, null, g).FreezeAndReturn();
        var di = new DrawingImage(d).FreezeAndReturn();
        return di;
    }

}

[MarkupExtensionReturnType(typeof(DrawingImage))]
public class GeometryImageExetnsion : GeometryImageBase
{

    public Geometry Value { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return CreateImage(Value);
    }

}

[ValueConversion(typeof(Geometry), typeof(DrawingImage))]
public class GeometryImageConverter : GeometryImageBase, IValueConverter
{
    /// <summary>
    /// Unfortunately we need to provide this since we inherit from GeometryImageBase that provides conversion functionality (which itself inherits from MarkupExtension)
    /// </summary>
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return CreateImage((Geometry)value);
    }

    public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
    {
        return null;
    }

}


}

FreezeAndReturn() это простой вспомогательный метод, чтобы заморозить объект и вернуть его после того, как он был заморожен.

Теперь в разметке XAML я могу использовать эти классы следующим образом:

<Window x:Class="MyProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:o="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:wpfx="clr-namespace:MyWPFHelpers"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <Geometry x:Key="myicongeometry" o:Freeze="True">M16,56L16,26 32,8 48,26 48,56 16,56z</Geometry>
    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" x:Key="mybrush" o:Freeze="True">
        <GradientStop Color="Black" Offset="0"/>
        <GradientStop Color="Red" Offset="1"/>
    </LinearGradientBrush>
</Window.Resources>
   <Grid>
        <!-- Using Value Converter -->
        <Image Height="94" Width="95" Margin="207,98,215,127" 
            Source="{Binding Source={StaticResource myicongeometry},
            Converter={wpfx:GeometryImageConverter Fill={StaticResource mybrush}}}" />
        <!--
        Incidentally, for some reason the above line "Fill={StaticResource mybrush}"
        produces this error in VS 2015 and VS 2017:
        "Unknown property 'Fill' for type 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' encountered while parsing a Markup Extension."
        However, if I change that line to say: "Fill=Red"
        everything works fine. However in both cases,
        the content is rendered correctly.
        Anyone knows why?
        -->


        <!-- Using Markup Extension -->
        <Image Source="{wpfx:GeometryImageExetnsion
        Fill={StaticResource mybrush},
        Value={StaticResource myicongeometry}}"
        Margin="352,107,70,118"/>


    </Grid>
</Window>

Итак, теперь есть несколько вопросов:

  1. Как лучше с точки зрения производительности? Это упрощенный пример, и в реальной жизни будет больше свойств, чем просто Fill, Кроме того, в одном окне может быть несколько сотен иконок такого рода. Однако сами значки не будут меняться во время выполнения (включая заливку и цвета), так как лучше всего свести к минимуму создание новых объектов и по возможности использовать существующие DrawingImages?
  2. Есть ли другие отличия, о которых нужно знать? Я предпочитаю синтаксис расширения разметки, потому что он кажется чище.
  3. Как насчет этого странного Fill свойство не найдено ошибки в VS на ValueConverter? Из-за этого он не скомпилируется, но, как ни странно, у него нет проблем с отображением правильной кисти (Fill) в конструкторе.
  4. Я знаю, что преобразователи значений можно превратить в статические ресурсы всего приложения, а не в расширения разметки. Итак, буду ли я использовать Value Converter в качестве статического ресурса быстрее, чем Markup Extension (который я не могу превратить в ресурс)?
  5. Любые другие соображения о том, когда использовать Value Converter против Markup Extension для такого рода привязки (т. Е. Где задействована небольшая часть логики преобразования / преобразования)?

0 ответов

Другие вопросы по тегам