开发者

WPF binding behaviour different when bound property is declared as interface vs class type?

开发者 https://www.devze.com 2023-01-01 08:51 出处:网络
This started with weird behaviour that I thought was tied to my implementation of ToString(), and I asked this question: Why won't WPF databindings show text when ToString() has a collaborating ob

This started with weird behaviour that I thought was tied to my implementation of ToString(), and I asked this question: Why won't WPF databindings show text when ToString() has a collaborating object?

It turns out to have nothing to do with collaborators and is reproducible.

When I bind Label.Content to a property of the DataContext that is declared as an interface type, ToString() is called on the runtime object and the label displays the result.

When I bind TextBlock.Text to the same property, ToString() is never called and nothing is displayed. But, if I change the declared property to a concrete implementation of the interface, it works as expected.

Is this somehow by design? If so, any idea why?

To reproduce:

  • Create a new WPF Application (.NET 3.5 SP1)
  • Add the following classes:
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开发者_JAVA技巧", foo_part2 = "second"}; } 
    }
}
  • Set the XAML of Window1 to:

    <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}"/>
        </StackPanel>
    </Window>
    
  • in Window1.xaml.cs:

public partial class Window1 : Window  
{  
    public Window1()  
    {  
        InitializeComponent();  
        DataContext = new Bar();  
    }  
}

When you run this application, you'll see the text only once (at the top, in the label). If you change the type of foo property on Bar class to Foo (instead of IFoo) and run the application again, you'll see the text in both controls.


I know this thread is old but I found a workaround for this problem. Use the StringFormat property on the binding:

<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>


Yes you are right. Obviously the ContentControl.Content property is implemented differently from the TextBlock.Text property. One obvious difference of course is that the ContentControl will actually generate a TextBlock instance for a content object that is not a Visual and doesn't have a DataTemplate. The TextBlock does not. It will render the text by itself. In both cases the string is determined by

  1. the IValueConverter (if present in the binding)
  2. the TypeConverter (if declared)
  3. object.ToString()

It seems that this algorithm differs only in step 3 between TextBlock and ContentControl as you have shown. While the ContentControl actually resolves the object behind the interface, the TextBlock does not. Interesting.

I guess this is something that you have to live with. You have several options now:

  • Expose a string property on your interface and bind to that
  • Expose the data object as a concrete class instead as an interface
  • Use a ContentControl instead of a TextBlock
  • provide a IValueConverter and use it in the binding
  • provide a TypeConverter for your interface
  • do something else (there's probably more)
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号