Comments 15
Вот бы ещё кто-то рассказал как в качестве значения параметра передать хотя-бы {StaticResource ...}, не говоря уже о {Binding ...}.
Вот ведь MarkupExtension's Binding и TemplateBinding это умеют делать (вот как, например, тот же параметр Converter):
Однако, похоже, эта возможность жёстко зашита в компилятор. Для проверки, я полность выдрал код реализации TemplateBindingExtension (он довольно простой), но такое присваивание компилятор не дал. :(
Вот ведь MarkupExtension's Binding и TemplateBinding это умеют делать (вот как, например, тот же параметр Converter):
{Binding Path=Date, Converter={StaticResource dateConverter}}
Однако, похоже, эта возможность жёстко зашита в компилятор. Для проверки, я полность выдрал код реализации TemplateBindingExtension (он довольно простой), но такое присваивание компилятор не дал. :(
Не знаю, что я делаю не так, но вот этот пример компилиться:
А вот этот нет:
Выдаёт ошибку при компиляции:
<Grid x:Name="grid">
<Grid.Resources>
<Button x:Key="xxx" />
<ControlTemplate x:Key="Test" >
<Button Content="{TemplateBinding Name, ConverterParameter={StaticResource xxx}}" />
</ControlTemplate>
</Grid.Resources>
<StackPanel DataContext="{Binding ElementName=grid}">
<Button x:Name="Button1" Content="1" Template="{StaticResource Test}" />
<Button x:Name="Button2" Content="2" Template="{StaticResource Test}" />
</StackPanel>
</Grid>
А вот этот нет:
[TypeConverter(typeof(TemplateBindingExtensionConverter))]
[MarkupExtensionReturnType(typeof(object))]
public class MyTemplateBinding : TemplateBindingExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return "1";
}
public MyTemplateBindingExtension(DependencyProperty path) : base(path) { }
}
<Grid x:Name="grid">
<Grid.Resources>
<Button x:Key="xxx" />
<ControlTemplate x:Key="Test" >
<Button Content="{local:MyTemplateBindingExtension Name, ConverterParameter={StaticResource xxx}}" />
</ControlTemplate>
</Grid.Resources>
<StackPanel DataContext="{Binding ElementName=grid}">
<Button x:Name="Button1" Content="1" Template="{StaticResource Test}" />
<Button x:Name="Button2" Content="2" Template="{StaticResource Test}" />
</StackPanel>
</Grid>
Выдаёт ошибку при компиляции:
Error 1 Unknown property 'ConverterParameter' for type 'MS.Internal.Markup.MarkupExtensionParser+UnknownMarkupExtension' encountered while parsing a Markup Extension. Line 10 Position 25. C:\Documents\Visual Studio 2010\Projects\WpfMarkupExtensionTest\WpfMarkupExtensionTest\MainWindow.xaml 10 25 WpfMarkupExtensionTest
знаю-знаю, конечно правильно писать «компилится» без мягкого знака :)
Думаю, что дело в том, что XAML-парсер понимает, что Name в "{TemplateBinding Name..." это параметр конструктора, по тому, что свойство Property помечено атрибутом [ConstructorArgument(«property»)], а в вашем классе параметр конструктора называется «path».
А какое имеет отношение ЭТОТ параметр конструктора к свойству ConverterParameter?
Когда я пишу ConverterParameter=1 вместо ConverterParameter={StaticResource xxx}}", то всё компилится нормально.
Когда я пишу ConverterParameter=1 вместо ConverterParameter={StaticResource xxx}}", то всё компилится нормально.
Если память мне не изменяет, статические расширения (типа StaticResource) можно присвоить любому свойству, а вот динамические (Binding, DynamicResource) работают только с DependencyProperty. Если не наследовать конвертер от MarkupExtension, то его можно наследовать от DependencyObject и насоздавать нужных свойств. Но в таком случае пропадает возможность его создания на месте и мы возвращаемся к ресурсам.
Можно поподробней, что значит фраза «Но в таком случае пропадает возможность его создания на месте и мы возвращаемся к ресурсам.» что-то я не уловил
На месте это как описано в статье:
А в ресурсах это как обычно создается конвертер.
<Label Content="{Binding Path=Date, Converter={StaticResource dateConverter}, ConverterParameter='Gregorian'}" />
А в ресурсах это как обычно создается конвертер.
К тому же создав в ресурсах конвертер можно выставить нужные значения свойств как обычно аттрибутами разметки, а не MarkupExtension'ами. Мне это кажется более простым решением. Если какие-то свойства ковертера сделать DependencyProperty, то и Binding без проблем сделать, думается.
Да, я так и знал, что сразу всплывет тема динамических параметров конвертера. Статические-то это премудрость не большая. Динамические — очень востребованная фича. Тут Microsoft постарался на славу, класс MarkupExtension не наследуется от DependencyObject, большая часть механизма DataBinding'а — internal… Еще проблема в том, что когда у конвертера меняется параметр, надо обновить BindingExpression'ы, которые используют конвертер, а это очень сильно усложняет систему, добавляется зависимость конвертера от Binding'а, всплывут утечки памяти в случаях статических конвертеров…
Поэтому тут особо некуда деваться от MultiBinding'а + IMultiValueConverter.
Поэтому тут особо некуда деваться от MultiBinding'а + IMultiValueConverter.
Собрал идеи, изложенные в двух статьях, немного дополнил и все это дело выложил на github.
Вообще, довольно странно, что в .NET Framework «из коробки» до сих пор нет например BooleanToVisibilityConverter.
Вообще, довольно странно, что в .NET Framework «из коробки» до сих пор нет например BooleanToVisibilityConverter.
Для начала немного изменим наш базовый класс:
После этих изменений обобщение T можно удалить
Sign up to leave a comment.
WPF: Несколько параметров для конвертера