Pull to refresh

Comments 15

Вот бы ещё кто-то рассказал как в качестве значения параметра передать хотя-бы {StaticResource ...}, не говоря уже о {Binding ...}.
Вот ведь MarkupExtension's Binding и TemplateBinding это умеют делать (вот как, например, тот же параметр Converter):
{Binding Path=Date, Converter={StaticResource dateConverter}}


Однако, похоже, эта возможность жёстко зашита в компилятор. Для проверки, я полность выдрал код реализации TemplateBindingExtension (он довольно простой), но такое присваивание компилятор не дал. :(
UFO landed and left these words here
Не знаю, что я делаю не так, но вот этот пример компилиться:
    <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}}", то всё компилится нормально.
Да, очевидно это недоработка в классе MarkupExtensionParser. Лень было копаться, но по виду там используется логика известных классов расширений разметки, видать хотели так соптимизовать частые случаи.
Если память мне не изменяет, статические расширения (типа 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.
Собрал идеи, изложенные в двух статьях, немного дополнил и все это дело выложил на github.

Вообще, довольно странно, что в .NET Framework «из коробки» до сих пор нет например BooleanToVisibilityConverter.
Sign up to leave a comment.

Articles