3

I'm new to WPF, and I'm trying to do what I thought would be a simple task - display the value of a field in my business object as it changes during my program. I know how to "force" it to change by manually changing the TextBox.Text property in C#, but as I'm learning WPF I want to do it the "right" way, and that means databinding.

Question #1: As far as I understand it, my choice is to either use a DependencyProperty or implement INotifyPropertyChanged in my business object, right?

Question #2: Here is a generic version of my code in which I attempted to go the DependencyProperty route.

Markup:

Button x:Name="nextButton" Content="Click"  Grid.Row="2" Click="nextButton_Click" />
TextBox x:Name="myTextBox" Grid.Row="1" Text="{Binding Source=myTest, Path=Name, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>

Code-Behind:

namespace DependencyTest2
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        private int i;

        private TestSphere myTest;

        public MainWindow()
        {
            InitializeComponent();

            i = 0;

            myTest = new TestSphere();
        }

        private void nextButton_Click(object sender, RoutedEventArgs e)
        {
            switch (i)
            {
                case 0:
                    myTest.Name = "string1";
                    break;
                case 1:
                    myTest.Name = "string2";
                    break;
                case 2:
                    myTest.Name = "string3";
                    break;
            }

            i++;
        }
    }

    class TestSphere : DependencyObject
    {

        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }

        public static readonly DependencyProperty NameProperty =
            DependencyProperty.Register("Name", typeof(string), typeof(TestSphere));


        public TestSphere()
        {
            Name = "default";
        }
    }
}

When I run the program, nothing appears in text box, even though the bound property has a value - is there something else I need to do to alert the binding that the source value has changed? I thought that using a DependencyProperty as the source would take care of that, but then again, I'm a WPF rookie. Thanks!

  • Steve

Ok, I tried to implement INotifyPropertyChanged using a wrapper class I found on codeproject as follows:


    class TestSphere : NotifyProperyChangedBase
    {
        private string _Name;

        public string Name
        {
            get { return _Name; }
            set 
            {
                this.CheckPropertyChanged("Name", ref _Name, ref value);
            }
        }

        public TestSphere()
        {
            Name = "default";
        }
    }

    public abstract class NotifyProperyChangedBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        #region methods
        protected bool CheckPropertyChanged(string propertyName, ref T oldValue, ref T newValue)
        {
            if (oldValue == null && newValue == null)
            {
                return false;
            }

            if ((oldValue == null && newValue != null) || !oldValue.Equals((T)newValue))
            {
                oldValue = newValue;
                FirePropertyChanged(propertyName);
                return true;
            }

            return false;
        }

        protected void FirePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

Here is my new Markup, too:


    Grid Name="myGrid">
        Grid.RowDefinitions>
            RowDefinition Height="30"/>
            RowDefinition Height="30"/>
            RowDefinition Height="30"/>
            RowDefinition Height="*"/>
        /Grid.RowDefinitions>
        Label x:Name="myLabel" Grid.Row="0" Foreground="Black"  />
        Button x:Name="nextButton" Content="Click"  Grid.Row="2" Click="nextButton_Click" />
        TextBox x:Name="myTextBox" Grid.Row="1" Text="{Binding Path=myTest.Name}"/>
    /Grid>

I also added the line myGrid.DataContext = myTest; to 'public MainWindow()' immediately after I instantiate myTest. When I step through the resulting program, the value of this.PropertyChanged always evaluates to null, so that the PropertyChanged even never fires. Sorry in advance for what must be a really noob question.

  • Steve
4
  • You probably won't see any performance gain by comparing old/new values in NotifyPropertyChangedBase, but as an aside, it looks like you're calling FirePropertyChanged() twice on each update, too. Commented Feb 18, 2011 at 15:39
  • And second, can you show us what your {Binding} now looks like? If the parent container of the TextBox is <Grid Name="myGrid">, and myGrid.DataContext = myTest, then the binding should now be {Binding Path=Name} Commented Feb 18, 2011 at 15:40
  • Thanks for catching that! I forgot to change it back to the original - see the edit above. Commented Feb 18, 2011 at 15:54
  • Changing to Binding Path=Name rather than Binding Path=myTest.Name did the trick! Thanks so much for your help! Commented Feb 18, 2011 at 16:04

1 Answer 1

1

You should only need to implement INotifyPropertyChanged on the TestSphere class, not DependencyObject. As you update the value, call PropertyChanged(this, new PropertyChangedEventArgs("Name")).

Second, you need to set the DataContext for the window in your code-behind. Lets say you used this in your XAML for the root grid element:

<Grid Name="MainForm">

Then in your code-behind, you'd do this:

MainForm.DataContext = this;

Finally, change the myTest property to public, and the binding in your XAML should then only need to be

Text="{Binding Path=myTest.Name}"
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the quick response - I edited my original post to add the new question that resulted from your suggestions. Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.