WPF - конвертер как DependencyObject - PullRequest
0 голосов
/ 12 июня 2019

Невозможно привязать значение к ConverterParametr из Binding. Binding можно установить только для DependencyProperty из DependencyObject.

Мне любопытно реализовать конвертер IValueConverter как DependencyObject.

public class AddConverter : DependencyObject, IValueConverter
{
    public static readonly DependencyProperty AddIntegerProperty =
        DependencyProperty.Register(nameof(AddInteger),
            typeof(int),
            typeof(AddConverter),
            new PropertyMetadata(0));

    public int AddInteger
    {
        get => (int)GetValue(AddIntegerProperty);
        set => SetValue(AddIntegerProperty, value);
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is int intValue)) return 0;
        return intValue + AddInteger;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int intValue;
        try
        {
            intValue = System.Convert.ToInt32(value);
        }
        catch (Exception)
        {
            return 0;
        }

        return intValue - AddInteger;
    }
}

Давайте рассмотрим пример.

<TextBox>
    <TextBox.Text>
        <Binding Path="MyIntegerProperty">
            <Binding.Converter>
                <local:AddConverter AddInteger="{Binding MyAddIntegerProperty, Mode=OneWay}" />
            </Binding.Converter>
        </Binding>
    </TextBox.Text>
</TextBox>

В результате AddInteger по-прежнему возвращает значение по умолчанию. По какой причине не изменяется AddInteger Свойство зависимости через предоставленное Binding?


Сноска: MultiBinding мне не помогает, поскольку метод ConvertBack состоит только из значения, предоставленного элементом управления. Этот материал также должен быть решен во ViewModel, но мне любопытно найти решение с конвертером.

1 Ответ

1 голос
/ 12 июня 2019

Во-первых, проблема в том, что конвертер не может наследовать DataContext там, где он находится, поэтому привязка не может работать: вы увидите «Ментор фреймворка не найден» в выходных данных VS, если вы поместите трассировку в привязка у вас есть (см. приложение A). Вот почему вы не можете просто извлечь из FrameworkElement и использовать RelativeSource={RelativeSource AncestorType=Whatever}: вы находитесь вне визуального дерева. Там нет предков. Кроме того, даже если бы существовал наставник фреймворка, DependencyObject не может предоставить источник для привязки. Источник должен быть явным. Только классы, которые наследуются от FrameworkElement, могут наследовать DataContext.

Итак, я украл класс BindingProxy ( из этого ответа ) и использовал его для предоставления источника для привязки. Это немного неуклюже, но другой альтернативой, о которой я подумал, было наследование конвертера от Freezable, по сути, присвоение ему свойств BindingProxy и создание конвертера в ресурсах. Это сработало, но я предпочитаю, как это организовано.

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    #region Data Property
    public Object Data
    {
        get { return (Object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register(nameof(Data), typeof(Object), typeof(BindingProxy),
            new PropertyMetadata(null));
    #endregion Data Property
}

1009 * XAML *

<StackPanel.Resources>
    <local:BindingProxy
        x:Key="VMProxy"
        Data="{Binding}"
        />
</StackPanel.Resources>
<TextBlock>
    <TextBlock.Text>
        <Binding Path="MyIntegerProperty">
            <Binding.Converter>
                <local:AddConverter
                    AddInteger="{Binding Data.MyAddIntegerProperty, Source={StaticResource VMProxy}}" 
                    />
            </Binding.Converter>
        </Binding>
    </TextBlock.Text>
</TextBlock>
<Ч />

Приложение A

AddInteger="{Binding MyAddIntegerProperty, Mode=OneWay, 
    PresentationTraceSources.TraceLevel=High}"
System.Windows.Data Warning: 56 : Created BindingExpression (hash=14964341) for Binding (hash=21653700)
System.Windows.Data Warning: 58 :   Path: 'MyAddIntegerProperty'
System.Windows.Data Warning: 60 : BindingExpression (hash=14964341): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=14964341): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=14964341): Attach to WpfApp2.AddConverter2.AddInteger (hash=57434139)
System.Windows.Data Warning: 64 : BindingExpression (hash=14964341): Use Framework mentor <null>
System.Windows.Data Warning: 67 : BindingExpression (hash=14964341): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14964341): Framework mentor not found
System.Windows.Data Warning: 65 : BindingExpression (hash=14964341): Resolve source deferred
System.Windows.Data Warning: 67 : BindingExpression (hash=14964341): Resolving source 
System.Windows.Data Warning: 69 : BindingExpression (hash=14964341): Framework mentor not found
...