views:

236

answers:

3

Hi, I'm new to WPF and trying a simple example of databinding, but it's not working. My window has a TextBlock, which I bound to a property of the window object. I declared the property in code.

When running this, I see the correct value appearing in the TextBlock. There's also a button, which when clicked updates the property, except I don't see this affecting the TextBlock.

I implemented the INotifyPropertyChanged correctly, as far as I'm able to determine. I also see, when debugging, that something has subscribed to the PropertyChanged event, except it doesn't seem to do anything.

I have 2 questions:

1) Why isn't this working as expected?

2) Is there any easy way to debug during run-time what's causing this, without resorting to third-party tools? From my cursory knowledge, it seems to me the debugging support in WPF is sorely lacking.

The XAML is (not including the "standard" XAML window element):

<TextBlock Height="28" Name="label1" VerticalAlignment="Top"
       Text="{Binding Path=TheName}"
       Grid.Row="0"
       ></TextBlock>
<Button Height="23" Name="button1" VerticalAlignment="Stretch" Grid.Row="1"
Click="button1_Click">
    Button
</Button>

The code in the window class is:

    public partial class Window1 : Window
    {
        protected MyDataSource TheSource { get; set; }

        public Window1()
        {
            InitializeComponent();
            TheSource = new MyDataSource();
            TheSource.TheName = "Original";  // This works
            this.label1.DataContext = this.TheSource;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            TheSource.TheName = "Changed";  // This doesn't work
        }
    }
    public class MyDataSource : INotifyPropertyChanged
    {
        string thename;
        public string TheName 
        {
            get { return thename; }
            set { thename = value; OnPropertyChanged(thename); }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}
+7  A: 

The problem is in you "TheName" property setter. The OnPropertyChanged method call is passing the value of "thename", not "the name" of "thename". (Sorry if that doesn't make sense - the variable names used for the example conspired against us!)

The correct code would be:

string thename;
public string TheName 
{
  get { return thename; }
  set { thename = value; OnPropertyChanged("TheName"); }
}

Here is an example from MSDN.

Hope this helps!

Brad Leach
A: 

Okay, thanks, I didn't pay enough attention when reading about the PropertyChangedEventArgs arguments. It seems like brittle design to rely on a string for the property name, but apparently other people think so too (from the link at the bottom of the user comments on that MSDN page you linked to).

However, my second question is still unanswered: how could I debug this and find out what I did wrong?

For example, doesn't WPF log its actions in some form? It should notice that I do not have a property called "Changed" on this object, why doesn't it raise an exception or at least write something to the debug console?

I currently use an extension method on the PropertyChanged event to raise a "strongly-typed" property changed event. This method is discussed as "Option 3" in the following link: http://www.orkpad.com/Blog/post/2009/04/12/Super-Strongly-Typed-Property-Changed-Notifications-with-Extension-Methods.aspxNote that there may be some impact at run-time. Measure if it is important to you.
Brad Leach
+3  A: 

From Bea Stollnitz blog, here are a few ways to debug a WPF binding.

  • Check the output window, as WPF logs alot of errors there

  • You can add a Trace source to your app.config. Details on Mike Hillberg’s blog

  • Add a trace level to the binding (this is my favourite). Add the namespace xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
    and in your binding add the tracelevel
    <TextBlock Text="{Binding Path=TheName, diagnostics:PresentationTraceSources.TraceLevel=High}" />

  • Add a converter to your binding, and then put a breakpoint in the converter.

Cameron MacFarland