WPF Binding работает только при использовании конвертера - PullRequest
0 голосов
/ 24 февраля 2020

Я борюсь с многоразовым и относительно простым UserControl.

Мои данные хранятся в классах, которые реализуют простой интерфейс IHasValue:

public interface IHasValue<T>
{
    T Value { get; }
    ValueType Type { get; }
}

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

<UserControl x:Class="DotsCompanion.Controls.HasValueFloatSelectorControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DotsCompanion.Controls"
             xmlns:conv="clr-namespace:DotsCompanion.Converters"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">


    <UserControl.Resources>
        <conv:NullToTrueFalseConverter x:Key="NullToTrueFalseConverter" />
        <conv:MyDebugConverter x:Key="MyDebugConverter" />

        <DataTemplate x:Key="EmptyValueTemplate" DataType="{x:Type ContentControl}">
            <Button Click="NewValueClick">
                <TextBlock Text="+"></TextBlock>
            </Button>
        </DataTemplate>
        <DataTemplate x:Key="HasValueTemplate" DataType="{x:Type ContentControl}">
            <StackPanel DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}">
                <!-- THIS IS THE PROBLEMATIC BINDING -->
                <TextBlock Text="{Binding Path=SelectedValue, Converter={StaticResource MyDebugConverter}}"></TextBlock>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>


    <Grid Name="LayoutRoot">
        <ContentControl>
            <ContentControl.Style>
                <Style TargetType="ContentControl">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding SelectedValue, Converter={StaticResource NullToTrueFalseConverter}}" Value="true">
                            <Setter Property="ContentTemplate" Value="{DynamicResource HasValueTemplate}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding SelectedValue, Converter={StaticResource NullToTrueFalseConverter}}" Value="false">
                            <Setter Property="ContentTemplate" Value="{DynamicResource EmptyValueTemplate}"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </Grid>
</UserControl>

Это чрезвычайно простой код для элемента управления:

public partial class HasValueFloatSelectorControl : UserControl
{
    public IHasValue<float> SelectedValue
    {
        get { return (IHasValue<float>)GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }

    public static readonly DependencyProperty SelectedValueProperty =
        DependencyProperty.Register("SelectedValue", typeof(IHasValue<float>), typeof(HasValueFloatSelectorControl), new PropertyMetadata(default(IHasValue<float>)));


    public HasValueFloatSelectorControl()
    {
        InitializeComponent();
        LayoutRoot.DataContext = this;
    }

    private void NewValueClick(object sender, EventArgs e)
    {
        SelectedValue = new FloatPrimitive(2.0f);
    }
}

FloatPrimitive - это класс, в котором фактически хранятся данные (с плавающей точкой), он реализует IHasValue, а также переопределяет ToString (), поэтому что float отображается в виде строки.

Проблема в том, что привязка Text к SelectedValue, похоже, работает только при использовании конвертера. В приведенном выше коде MyDebugConverter просто возвращает значение как есть:

// This should be literally useless AFAIK
public class MyDebugConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }

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

Если я удаляю конвертер, привязка просто ничего не показывает. Согласно трассировке выходного окна, DataItem имеет значение null, но с помощью проводника динамических свойств я вижу, что DataContext правильно установлен, а SelectedValue действительно является FloatPrimitive.

Я пытался найти решение, но до сих пор Я нашел только противоположные случаи (привязка не работает с конвертером). Я довольно новичок в WPF, поэтому я не уверен, с чего начать отладку, так как это кажется довольно необычной ситуацией, и онлайн-ресурсы на данный момент не помогают.

Что я пробовал:

  • привязка к SelectedValue за пределами DataTemplate путем добавления простого TextBlock внутри сетки Root, но я получил тот же результат (необходим конвертер);
  • setting SelectedValue путем привязки к нему извне элемента управления и вызова метода NewValueClick () внутри элемента управления;
  • добавление вызова Debugger.Break () внутри MyDebugConverter, который позволяет мне проверить, что значение не равно нулю, и корректно устанавливается, но с другой стороны, использование конвертера - это то, что позволяет ему правильно отображаться в первую очередь;
  • много возни с проводником текущих свойств

Может это связано с тем, что я использую интерфейс? У меня есть очень похожая настройка для другого пользовательского элемента управления в проекте, который прекрасно работает, но он использует абстрактный класс + наследование в отличие от интерфейса + реализация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...