1

I have a simple WPF app with 3 textboxes, 2 of the text boxes input numbers and the third textbox shows the sum of inputs when another button is clicked.

I come from WinForms and MFC background and for me, the intuitive thing to do is to right click the textBoxes, open their properties and specify local variables to read the data from the boxes. For example, MFC has the DDX mechanism for this.

However, in WPF, the only way to specify a binding seems to add XAML code directly to App.XAML, as shown here on MSDN. Is there a way to create a binding without coding it manually into XAML? XAML coding seems a little daunting to me, since I am new to it.

My WPF form is as follows :

<Window x:Class="SimpleAdd.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="174,43,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value1}" VerticalAlignment="Top" Width="120"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="174,84,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value2}" VerticalAlignment="Top" Width="120"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="174,127,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value3}" VerticalAlignment="Top" Width="120"/>
        <Button Content="Add" HorizontalAlignment="Left" Margin="393,84,0,0" VerticalAlignment="Top" Width="75" Click="OnAdd"/>
    </Grid>
</Window>

My C# file is as follows :

namespace SimpleAdd
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnAdd(object sender, RoutedEventArgs e)
        {
            dataModel m1 = new dataModel();
            m1.Value3 = m1.Value1 + m1.Value2; // BUG : All Properties are 0 even after updating the boxes.
        }
    }

    public class dataModel
    {
        private int val1, val2, val3;

        public int Value1
        {
            get {return val1;}
            set { val1 = value; }
        }
        public int Value2
        {
            get { return val2; }
            set { val2 = value; }
        }
        public int Value3
        {
            get { return val3; }
            set { val3 = value; }
        }
    }

}

EDIT : Adding implementation for INotifyPropertyChanged

namespace SimpleAdd
{
    public abstract class ObservableObject : INotifyPropertyChanged
    {
        #region Debugging Aides

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This 
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public virtual void VerifyPropertyName(string propertyName)
        {
            // Verify that the property name matches a real,  
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);
                else
                    Debug.Fail(msg);
            }
        }

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used
        /// when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might 
        /// override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        #endregion // Debugging Aides

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Raises the PropertyChange event for the property specified
        /// </summary>
        /// <param name="propertyName">Property name to update. Is case-sensitive.</param>
        public virtual void RaisePropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);
            OnPropertyChanged(propertyName);
        }

        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion // INotifyPropertyChanged Members
    }

}
user2654449
  • 119
  • 7
  • 1
    The correct WPF way of doing it would be to either bind all 3 textboxes to properties on the data model, and in the datamodel make sure `Value3 = Value1 + Value2`, or create an `IMultiValueConverter`, pass it the values from TextBox1 and TextBox2 to it, and have it combine them and return the value for TextBox3. XAML would typically be used for both of those scenarios. If you're not trying to do things the proper WPF way, you're welcome to use code behind to just do `TextBox3.Text = int.Parse(TextBox1.Text) + int.Parse(TextBox2.Text);` or however you want it :) – Rachel Apr 28 '15 at 20:57
  • @Rachel Thanks for showing me the ropes. I'm not quite there yet however. I would like to learn to do it the correct WPF way, but based on the new implementation, I don't think my bindings are working, since all my properties only have 0 values. – user2654449 Apr 28 '15 at 23:53
  • Honestly I still find WinForms a bit easier on my brain as far as the way UIs are constructed. However, I would recommend delving into MVVM if you haven't tried it- you will definitely get the most out of your XAML! – Craig Apr 29 '15 at 00:16
  • @Rintintin There is definitely a change in mindset when making the switch. You may find [this answer about transitioning from WPF to Winforms](http://stackoverflow.com/a/15684569/302677) useful :) – Rachel Apr 29 '15 at 13:33
  • @Rachel Thanks for sharing the links to your blog, I didn't know about Data Context. Now will read about it. – user2654449 Apr 29 '15 at 15:25

3 Answers3

1

Technically, that's not in App.xaml (which is a special file in WPF).

That said, yes you can do it. You can set up the binding in code like so:

textBox1.Text = new Binding("SomeProperty");
...

Okay, that gets really annoying so we just do it in XAML:

<TextBox Text="{Binding SomeProperty}"/>

Both pieces of code do the same thing, but when you get into more advanced bindings, the XAML syntax is a lot easier to use. Plus, its more obvious where you text is coming from rather than having to open two files.

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • I tried the binding based on your suggestion, but I think I have done it wrong, because the binding does not work. Could you please identify the mistake in the above and show me the correct implementation? – user2654449 Apr 28 '15 at 23:56
  • 1
    @user2654449 For starters you never set the `DataContext` of your window to your data class. Also, if and when the properties change from code, you need to use `INotifyPropertyChanged` to let the UI know. – BradleyDotNET Apr 29 '15 at 00:03
  • thanks Bradley. I read up on `INotifyPropertyChanged`. I am adding code for `INotifyPropertyChanged` but the `textBox3` still does not refresh its contents when the associated `value3` is changed – user2654449 Apr 30 '15 at 00:12
  • @user2654449 Are you notifying (raising the event) in the `value3` setter? – BradleyDotNET Apr 30 '15 at 00:14
  • I don't think so. There is no code after `_data.Value3 = _data.Value1 + _data.Value2;` to notify `textBox3` that its value is to be updated. Do I need to? I thought the binding automatically causes a notification to be generated. The code I have now added to the post is current. Also the setter is simply implemneted as `set { val3 = value; }` – user2654449 Apr 30 '15 at 00:17
  • 1
    @user2654449 Its not magic, you acutally have to raise the event. Try adding `RaisePropertyChanged("Value3")` to your `Value3` setter at the end. – BradleyDotNET Apr 30 '15 at 00:19
  • I was reading Rachel's [tutorial](https://rachel53461.wordpress.com/2011/05/08/simplemvvmexample/) She doesn't use the `RaisePropertyChanged()` function, but her boxes update the underlying model Property changes. Could you please tell me how so? – user2654449 Apr 30 '15 at 00:25
  • 1
    @user2654449 Sure she does: `OnPropertyChanged("ProductId");` Yours was protected, and I wasn't sure of your inheritance hierarchy, so I suggested the other method. The *usual* implementation can be found here (you don't even need the string!) https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged%28v=vs.110%29.aspx – BradleyDotNET Apr 30 '15 at 00:28
1

Your TextBox are not being updated because you haven't set the data source (DataContext typically) behind the bindings.

When you write

<TextBox Text="{Binding dataModel.Value1}" />

What you are really saying "pull the value for this field from TextBox.DataContext.dataModel.Value1". If TextBox.DataContext is null, then nothing will be displayed.

The DataContext is inherited automatically, so the following code would work :

public partial class MainWindow : Window
{
    public dataModel _data { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        _data = new dataModel();
        this.DataContext = _data;
    }

    private void OnAdd(object sender, RoutedEventArgs e)
    {
        _data.Value3 = _data.Value1 + _data.Value2;
    }
}

assuming you also change your TextBox bindings to remove the dataModel. from them

<TextBox Text="{Binding Value1}" />

This sets the DataContext of the entire form to the _data object, and in your OnAdd method we can update the _data object properties in order to have the UI update.

I like to blog a bit about beginner WPF stuff, and you may be interested in checking out a couple of the posts there which explain these concepts :

Rachel
  • 130,264
  • 66
  • 304
  • 490
0

The FrameworkElement class and the FrameworkContentElement class both expose a SetBinding method. If you are binding an element that inherits either of these classes, you can call the SetBinding method directly. The following example creates a class named, MyData, which contains a property named MyDataProperty.

public class MyData : INotifyPropertyChanged
{
private string myDataProperty;

public MyData() { }

public MyData(DateTime dateTime)
{
    myDataProperty = "Last bound time was " + dateTime.ToLongTimeString();
}

public String MyDataProperty
{
    get { return myDataProperty; }
    set
    {
        myDataProperty = value;
        OnPropertyChanged("MyDataProperty");
    }
}

public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(string info)
{
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(info));
    }
}

}

The following example shows how to create a binding object to set the source of the binding. The example uses SetBinding to bind the Text property of myText, which is a TextBlock control, to MyDataProperty.

MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");    
myBinding.Source = myDataObject;
myText.SetBinding(TextBlock.TextProperty, myBinding);
BSG
  • 2,084
  • 14
  • 19