Powered by MSDN

US - English
NEW! Silverlight 5 is available Learn More

  • nelson5000

    nelson5000

    0 Points

    4 Posts

    DataBinding Bug - TextBox change does not update binding

    Oct 09, 2008 07:04 PM | LINK

    I have found that in a very specific situation, changing text in a TextBox does not update the underlying datasource when using data binding.  If you set a property to either null or String.Empty twice in a row, then you enter a 1 character value in the text box, the binding  does not fire.  Below is the code to reproduce.

    In this code if have a "Person" class which stores a first and last name.  I have implemented the INotifyPropertyChanged interface and made the FirstName and LastName properties bindable.  I have put FirstName and LastName textboxes on my Silverlight form and bound those textboxes to an instance of the class.  On this form I have a button which sets the two properties to null and another that sets the two properties to String.Empty.  There is also a multi-line textbox which displays a message any time the FirstName property is set.  This fires normally if you change the first name (and tab) or click one of the two buttons. 

    Here is how to reproduce the issue.  Run the app, then click the "Set Null" button one time.  Change the text of the First Name text box to a single character and press tab.  You'll see that the change message properly fires.  Now click the "Set Null" button twice in a row, then change the First Name text box to a single character and press tab.  This time the change message won't fire.

    It will fire properly if you enter text that is more than one character long.  You can also get the error to reproduce if you set the property to String.Empty twice in a row (or null then empty or empty then null).  This issue appeared after upgrading to RC0.

    This will be a real issue for my application.  I am writing a data entry application where the user can scroll through records and update data.  For example, if the user is scrolling through names and two people in a row don't have middle initials, then on the third name the user enters a one character initial, then the data won't be saved properly.

    XAML:
     

    <UserControl x:Class="BindIssue.Page"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="400" Height="300">

    <Canvas x:Name="LayoutRoot" Background="BlanchedAlmond">

    <TextBlock x:Name="lblFirst" Canvas.Left="0" Canvas.Top="0" Text="First"></TextBlock>

    <TextBlock x:Name="lblLast" Canvas.Left="150" Canvas.Top="0" Text="Last"></TextBlock>

    <TextBox x:Name="tbFirst" Canvas.Left="0" Canvas.Top="20" Width="140"></TextBox>

    <TextBox x:Name="tbLast" Canvas.Left="150" Canvas.Top="20" Width="140"></TextBox>

    <Button x:Name="btnSetNull" Canvas.Left="0" Canvas.Top="50" Width="100" Height="30" Content="Set Null" Click="btnSetNull_Click"></Button>

    <Button x:Name="btnSetEmpty" Canvas.Left="150" Canvas.Top="50" Width="100" Height="30" Content="Set Empty" Click="btnSetEmpty_Click"></Button>

    <TextBlock x:Name="lblMessage" Canvas.Left="0" Canvas.Top="90" Text="Messages"></TextBlock>

    <TextBox x:Name="tbMessage" Canvas.Left="0" Canvas.Top="110" Width="400" Height="160"></TextBox>

    </Canvas>

    </UserControl>

    Code:

     

    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace BindIssue
    {
        public partial class Page : UserControl
        {
            public Page()
            {
                InitializeComponent();
                this.Loaded += new RoutedEventHandler(Page_Loaded);
            }
    
            private Person pBind;
            private void Page_Loaded(object sender, RoutedEventArgs e)
            {
                pBind = new Person(tbMessage);
                pBind.First = "Bobby";
                pBind.Last = "Slobby";
    
                Bind(tbFirst, TextBox.TextProperty, "First");
                Bind(tbLast, TextBox.TextProperty, "Last");
            }
    
            public void Bind(FrameworkElement Element, DependencyProperty Property, string PersonProp)
            {
                System.Windows.Data.Binding bind = new System.Windows.Data.Binding(PersonProp);
                bind.Mode = System.Windows.Data.BindingMode.TwoWay;
                Element.SetBinding(Property, bind);
                Element.DataContext = pBind;
            }
    
            private void btnSetNull_Click(object sender, RoutedEventArgs e)
            {
                pBind.First = null;
                pBind.Last = null;
            }
    
            private void btnSetEmpty_Click(object sender, RoutedEventArgs e)
            {
                pBind.First = string.Empty;
                pBind.Last = string.Empty;
            }
        }
    
        public class Person : System.ComponentModel.INotifyPropertyChanged
        {
            private TextBox tbMessage;
            public Person(TextBox tbMessage)
            {
                this.tbMessage = tbMessage;
            }
    
            private object fFirst = null;
            public object First
            {
                get { return fFirst; }
                set
                {
                    fFirst = value;
                    NotifyPropertyChanged("First");
                    string sValue = (value == null) ? "Null" : (value.ToString() == string.Empty ? "{Empty}" : value.ToString());
                    tbMessage.Text = DateTime.Now.ToLongTimeString() + " " + sValue + "\r\n" + tbMessage.Text;
                }
            }
    
            private object fLast = null;
            public object Last
            {
                get { return fLast; }
                set
                {
                    fLast = value;
                    NotifyPropertyChanged("Last");
                }
            }
    
            public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
            public void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this,
                    new System.ComponentModel.PropertyChangedEventArgs(propertyName));
                }
            }
    
        }
    }