7

I have what I think is a very simple databinding question (I'm still new to WPF). I have a class (simplified for this question)

public class ConfigurationData
{
    public int BaudRate { get; set; } 
}

In MainWindow.Xaml.cs I have a private member variable:

private ConfigurationData m_data;

and a method

void DoStuff()
{
   // do a bunch of stuff (read serial port?) which may result in calling...
   m_data.BaudRate = 300; // or some other value depending on logic
}

In my MainWindow gui, I'd like to have a TextBox that displays m_data.BaudRate AND allows for two way binding. The user should be able to enter a value in the textbox, and the textbox should display new values that we're caused by "DoStuff()" method. I've seen tons of examples on binding to another property of a control on MainWindow, and binding to a datacollection, but no examples of binding to a property of another object. I would think that my example is about as simple as it get, with the nagging annoyance that I'm binding to an integer, not a string, and if possible, I would like the user to only be able to enter integers.
BTW I considered using a numeric up/down, but decided against it as there did not seem to be a lot of support/examples of non-commercial numeric up/down controls. Plus, it could be an awfully big range of numbers to spin through.

I think a pointer to one good example would get me on my way. Many thanks in advance, Dave

4
  • 1
    I should have said that I think a pointer to one good example would set me on my way. Commented Oct 7, 2010 at 22:04
  • This does not work (syntac error) <TextBox Text="{Binding Source={m_data}, Path=BaudRate}" Height="23" Margin="137,70,21,0" Name="textBox1" VerticalAlignment="Top" /> I did see that one can setup a "resource" in the Xaml. This would seem to be overkill for me. Can't I just do it inline somehow? It's only one time I'm going to bind to m_data. Aside: Is it possible to format code or use the return key in comments :) ? Commented Oct 7, 2010 at 22:14
  • Just wanted to note that a numeric up-down (or spinner) control also helps with preventing non-numerical keyboard input and min/max validations, not just the actual spinning of numbers. I think there's one in Bag of Tricks that does the job decently (you only need to style it properly). Commented Oct 7, 2010 at 23:02
  • Thanks for the comment Alex. Unfortunately, I saw your comment at work where we use Visual Studio 2008 (sigh). The "Bag of Tricks" (github.com/thinkpixellab/bot is what I saw) seems to only have the Visual Studio 2010 code. I'll look at it at home. Commented Oct 8, 2010 at 14:46

2 Answers 2

20

Although this question is old, it's a good one, and is rarely answered succinctly. Let me offer a simplified solution for others who come upon this page.

In order to support two-way binding, your initial ConfigurationData class must be expanded to support property changes. Otherwise the changes in DoStuff() will not be reflected in the UI textbox. Here is a typical way of doing that:

using System.ComponentModel;
public class ConfigurationData : INotifyPropertyChanged
{
    private int _BaudRate;
    public int BaudRate
    {
        get { return _BaudRate; }
        set { _BaudRate = value; OnPropertyChanged("BaudRate"); }
    }

    //below is the boilerplate code supporting PropertyChanged events:
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

I choose to put the textbox binding right in the XAML (I've also added a button to DoStuff), like so:

<Canvas>
    <TextBox Width="96" Name="textBox1" Text="{Binding BaudRate}" />
    <Button  Width="96" Canvas.Top="25" Content="ChangeNumber" Click="DoStuff"/>
</Canvas>

The tricky part is gluing this all together. For that you'll need to define your DataContext. I prefer to do this in the constructor of my main window. Here's the code:

public partial class MainWindow : Window
{
    private ConfigurationData m_data;
    public MainWindow()
    {
        InitializeComponent();
        m_data = new ConfigurationData();
        this.DataContext = m_data;  // This is the glue that connects the
                                    // textbox to the object instance
    }

    private void DoStuff(object sender, RoutedEventArgs e)
    {
        m_data.BaudRate += 300;
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Hi, Is there an easier way without defining such a public class and it's own methods? Why I am asking is because when I was using the textbox together with a slidebar, actually I can bind the name of slide bar as an element name together with my textbox. <TextBox Text="{Binding ElementName=slValue, Path=Value, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" TextAlignment="Right" Width="40" /> <Slider Maximum="255" TickPlacement="BottomRight" TickFrequency="5" IsSnapToTickEnabled="True" Name="slValue" /> So there must be something existing, used in slidebar,
0

I'm sure there's a better way (please tell me!), but here's one way I cobbled together that works. It seems that there should be an easier way. For the property BaudRate use:

public int BaudRate
    {
        get
        { 
            return m_baudRate;
        }
        set 
        {
            if (value != m_baudRate)
            {
                m_baudRate = value;
                OnPropertyChanged("BaudRate");//OnPropertyChanged definition left out here, but it's pretty standard
            }
        }
    }

For the XAML, I have no significant markup:

<TextBox  Height="23" Margin="137,70,21,0" Name="textBox1" VerticalAlignment="Top"  />

Now this is the messy part... Create class for validation:

public class IntRangeRule : ValidationRule
{
    // See ValidationRule Class
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        try
        {
            if (value is int) // replace with your own logic or more robust handling...
            {
                return new ValidationResult(true, "Fine");
            }
            else
            {
                return new ValidationResult(false, "Illegal characters or ");
            }
        }

        catch (Exception e)
        {
            return new ValidationResult(false, "Illegal characters or " + e.Message);
        }


    }
}

And then in the constructor of Window1 (MainWindow), have:

Binding myBinding = new Binding("BaudRate");
        myBinding.NotifyOnValidationError = true;
        myBinding.Mode = BindingMode.TwoWay;
        ValidationRule rule = new IntRangeRule();
                    myBinding.ValidationRules.Add(rule);
        myBinding.Source = m_data; // where m_data is the member variable of type ConfigurationData
        textBox1.SetBinding(TextBox.TextProperty, myBinding);

All my attempts to do everything in markup failed. Better ways?

Dave

Comments

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.