Поведение связывания WPF отличается, когда связанное свойство объявлено как интерфейс против типа класса? - PullRequest
8 голосов
/ 27 мая 2010

Это началось со странного поведения, которое, как мне показалось, было связано с моей реализацией ToString(), и я задал этот вопрос: Почему привязки данных WPF не показывают текст, когда ToString () имеет взаимодействующий объект?

Оказывается, не имеет ничего общего с соавторами и воспроизводимо.

Когда я связываю Label.Content со свойством DataContext, которое объявлено как тип интерфейса, ToString() вызывается для объекта времени выполнения, и метка отображает результат.

Когда я связываю TextBlock.Text с тем же свойством, ToString() никогда не вызывается и ничего не отображается. Но , если я изменю объявленное свойство на конкретную реализацию интерфейса, оно будет работать как положено.

Это как-то задумано? Если это так, есть идеи, почему?

Воспроизвести:

  • Создание нового приложения WPF (.NET 3.5 SP1)
  • Добавьте следующие классы:
public interface IFoo
{
    string foo_part1 { get; set; }
    string foo_part2 { get; set; }
}

public class Foo : IFoo
{
    public string foo_part1 { get; set; }

    public string foo_part2 { get; set; }

    public override string ToString() 
    { 
        return foo_part1 + " - " + foo_part2; 
    }
}
public class Bar
{
    public IFoo foo 
    { 
        get { return new Foo {foo_part1 = "first", foo_part2 = "second"}; } 
    }
}
public partial class Window1 : Window  
{  
    public Window1()  
    {  
        InitializeComponent();  
        DataContext = new Bar();  
    }  
}

Когда вы запустите это приложение, вы увидите текст только один раз (вверху, на ярлыке). Если вы измените тип свойства foo в классе Bar на Foo (вместо IFoo) и снова запустите приложение, вы увидите текст в обоих элементах управления.

Ответы [ 2 ]

8 голосов
/ 21 декабря 2012

Я знаю, что эта ветка старая, но я нашел решение этой проблемы. Используйте свойство StringFormat для привязки:

<Window x:Class="WpfApplication1.Window1"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="Window1" Height="300" Width="300">
     <StackPanel>
        <Label Content="{Binding foo, Mode=Default}"/>
        <TextBlock Text="{Binding foo, Mode=Default, StringFormat={}{0}}"/>
    </StackPanel>
</Window>
3 голосов
/ 27 мая 2010

Да, вы правы. Очевидно, свойство ContentControl.Content реализовано не так, как свойство TextBlock.Text. Разумеется, одно очевидное отличие состоит в том, что ContentControl фактически создаст экземпляр TextBlock для объекта содержимого, который не является Visual и не имеет DataTemplate. TextBlock нет. Это сделает текст самостоятельно. В обоих случаях строка определяется как

  1. IValueConverter (если присутствует в переплете)
  2. TypeConverter (если объявлено)
  3. object.ToString()

Кажется, что этот алгоритм отличается только на шаге 3 между TextBlock и ContentControl, как вы показали. В то время как ContentControl фактически разрешает объект за интерфейсом, TextBlock не делает. Интересно.

Я полагаю, это то, с чем вам придется жить. У вас есть несколько вариантов:

  • Предоставьте строковое свойство в вашем интерфейсе и привяжите к нему
  • Представьте объект данных как конкретный класс вместо интерфейса
  • Используйте ContentControl вместо TextBlock
  • укажите IValueConverter и используйте его в привязке
  • укажите TypeConverter для вашего интерфейса
  • сделать что-то еще (возможно, еще)
...