Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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", 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.

like image 643
Jay Avatar asked May 27 '10 01:05

Jay


People also ask

How does binding work in WPF?

Data binding is a mechanism in WPF applications that provides a simple and easy way for Windows Runtime apps to display and interact with data. In this mechanism, the management of data is entirely separated from the way data. Data binding allows the flow of data between UI elements and data object on user interface.

What are the different binding mechanism in XAML?

Data binding is a mechanism in XAML applications that provides a simple and easy way for Windows Runtime Apps using partial classes to display and interact with data. The management of data is entirely separated from the way the data is displayed in this mechanism.

What is path binding WPF?

Binding path syntax. Use the Path property to specify the source value you want to bind to: In the simplest case, the Path property value is the name of the property of the source object to use for the binding, such as Path=PropertyName . Subproperties of a property can be specified by a similar syntax as in C#.

What is DataContext in WPF?

The DataContext property is the default source of your bindings, unless you specifically declare another source, like we did in the previous chapter with the ElementName property. It's defined on the FrameworkElement class, which most UI controls, including the WPF Window, inherits from.


2 Answers

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>
like image 136
Henrik Avatar answered Sep 20 '22 14:09

Henrik


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)
like image 24
bitbonk Avatar answered Sep 18 '22 14:09

bitbonk