Search code examples
silverlight-4.0textboxtextchanged

Silverlight 4 Overloaded Textbox's textchanged event is fired by parent textbox


I have an custom textbox that I got from http://blog.roboblob.com/2010/07/16/custom-silverlight-textbox-control-that-immediately-updates-databound-text-property-in-twoway-binding/comment-page-1/:

public class ImmediateTextBox : TextBox
{
    public ImmediateTextBox()
    {
        this.Loaded += ImmediateTextBox_Loaded;
    }

    void ImmediateTextBox_Loaded(object sender, RoutedEventArgs e)
    {            
        this.GotFocus +=  ImmediateTextBox_GotFocus;
        this.TextChanged += new TextChangedEventHandler(ImmediateTextBox_TextChanged);
    }

    void ImmediateTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TextBox txt = sender as TextBox;

        if (txt != null)
        {
            BindingExpression bindExp = txt.GetBindingExpression(TextBox.TextProperty);
            if (bindExp != null)
            {
                bindExp.UpdateSource();
            }//if

        }//if

    }

    void ImmediateTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        this.SelectAll();
    }
 }

In my xaml I am using this, and it works fine, except when I have it nested and the parent container has a ImmediateTextBox:

 <ItemsControl Grid.Column="1" ItemsSource="{Binding Path=LstForecast}">
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
                    <customControls:ImmediateTextBox Padding="8" Height="36" Text="{Binding Path=DForecastQuantityShippedTotal,
                                                                                StringFormat=\{0:n0\},
                                                                                Mode=TwoWay,
                                                                                Converter={StaticResource StringToNullableDoubleConverter}}"
                                                                                 Width="70" IsEnabled="{Binding Path=IsForecastUserEditable}"/>
            <!--Weeks-->
            <ItemsControl ItemsSource="{Binding Path=LstWeeks}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                                    <customControls:ImmediateTextBox Padding="8" Width="70" Height="36"
                                                                                    Text="{Binding Path=DForecastQuantityShippedTotal, StringFormat=\{0:n0\}, Mode=TwoWay, Converter={StaticResource StringToNullableDoubleConverter}}"
                                                                                    IsEnabled="{Binding Path=IsForecastUserEditable}"/>
                            <!--days data-->
                            <ItemsControl ItemsSource="{Binding Path=LstDays}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel Orientation="Horizontal" />
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate>
                                                <customControls:ImmediateTextBox Padding="8" Width="70" Height="36"
                                                                                                             Text="{Binding Path=DForecastAutoManual,
                                                                                                                            StringFormat=\{0:n0\},
                                                                                                                            Mode=TwoWay,
                                                                                                              Converter={StaticResource StringToNullableDoubleConverter}}"
                                                                                                             IsEnabled="{Binding Path=IsForecastUserEditable}"/>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ItemsControl>
                            <!--end days data-->
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <!--end weeks-->
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>

At the lowest level it gets really funky. modifying the previous and sometimes the following generated textboxes. If I use an autocompletebox, of course it works. The problem is that I am wanting to add behaviors to the textbox for copy/paste/selectall, etc that I can't do with the autocompletebox, since there is no selectedtext or a selectall() method. Also I noticed in the RoutedEventArgs, the originalsource is null. I figured using that would cause it to work, but no. Any help? thanks in advance.


Solution

  • Okay so I figured it out. I have each text-box bound to properties. When any one of these text-boxes are modified then the bound properties of the ViewModel update each other. When that happens they update the text-box where it occurs and not just the property, because it is a 2-way binding. So when there is an update that is caused by the user's input it updates. It shouldn't when the user doesn't type anything in that text-box (when the text-box is being updated from the algorithm.

    My solution is creating a boolean property "isUser" in ImmediateTextBox. When my overridden OnKeyDown or OnMouseLeftButtonPressed are called I set isUser to true. When the text changes from the user typing in or pasting in something, then i update the binding and set isUser to false.