WPF ValueConverter против MarkupExtension для привязки DrawingImage
У меня есть набор значков, хранящихся в ResourceDictionary как Geometry
объекты. Я намерен использовать их для создания значков для элементов пользовательского интерфейса, таких как кнопки. Мне нужно иметь возможность настроить эти значки различными способами:
- Control Brush используется для заполнения геометрии
- Контроль структуры рендеринга
- Уметь вращать / переворачивать значок
- и т.п.
Большинство элементов пользовательского интерфейса, которые могут представлять такие значки, ожидают ImageSource
как тип цели. Поэтому мне нужен эффективный и простой в использовании механизм для преобразования моего Geometry
ресурсы в фактические ImageSource
s. В псевдокоде: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>
Итак, теперь есть несколько вопросов:
- Как лучше с точки зрения производительности? Это упрощенный пример, и в реальной жизни будет больше свойств, чем просто
Fill
, Кроме того, в одном окне может быть несколько сотен иконок такого рода. Однако сами значки не будут меняться во время выполнения (включая заливку и цвета), так как лучше всего свести к минимуму создание новых объектов и по возможности использовать существующие DrawingImages? - Есть ли другие отличия, о которых нужно знать? Я предпочитаю синтаксис расширения разметки, потому что он кажется чище.
- Как насчет этого странного
Fill
свойство не найдено ошибки в VS на ValueConverter? Из-за этого он не скомпилируется, но, как ни странно, у него нет проблем с отображением правильной кисти (Fill) в конструкторе. - Я знаю, что преобразователи значений можно превратить в статические ресурсы всего приложения, а не в расширения разметки. Итак, буду ли я использовать Value Converter в качестве статического ресурса быстрее, чем Markup Extension (который я не могу превратить в ресурс)?
- Любые другие соображения о том, когда использовать Value Converter против Markup Extension для такого рода привязки (т. Е. Где задействована небольшая часть логики преобразования / преобразования)?