Saturday, May 29, 2010

Strange UserControl Issue (Struck with the easiest)

Hi Guys,

Today I am going to discuss about one of the most silly issue that I faced very recently. Its all about building a Composite UserControl using WPF. Let me discuss how I build the control.

Introduction

TextCalc is a control that takes numeric input from the user. Traditionally, we use TextBox to do this, but here I have implemented one control that allows you to choose numeric digits to input into the control. I will not discuss on the actual implementation of the control; which is fairly simple;  rather I emphasis here only on the problem I have faced while creating the control.

The Implementation

The TxtCalc control has few components :

  1. Border : This element wraps around all the controls inside it. I have created this just to ensure that the control has a firm border around the controls inside it. 
  2. TextBox : This is the main textbox, where the contents will be put and the data bound to this control will primarily be displayed over this TextBox. 
  3. ToggleButton : The ToggleButton is used to display the calculator. Just like what ComboBox has, it opens up a Popup below it and displays the Calculator UI.
  4. Popup : This control holds the UI for the calculator. Calculator holds a number of buttons from 0 to 9 and few calculation buttons etc.
Let us see how the code looks like (I intentionally removed the code of all the buttons, you will find in the demo application )  :

<usercontrol loaded="UserControl_Loaded" 
x:class="TextCalc.txtCalc" x:name="txtCalculator" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <border horizontalalignment="Stretch" x:name="brdCalculator">
        <grid>
            <grid.columndefinitions>
                <columndefinition width="*">
                <columndefinition width="Auto">
            </columndefinition>
            <textbox borderthickness="0" 
horizontalalignment="Stretch" 
text="{Binding ElementName=txtCalculator, Path=Message, Mode=TwoWay}" textalignment="Right" x:name="txtObj">
            <togglebutton click="ToggleButton_Click" 
grid.column="1" 
ischecked="{Binding ElementName=pupCalculator,Path=IsOpen}" name="btnDropdown" template="{Binding ElementName=txtCalculator, Path=CalculatorComboButton}"></togglebutton>
            <popup allowstransparency="True" horizontaloffset="-5" placement="Bottom" 
PlacementTarget="{Binding ElementName=txtObj}" staysopen="False" verticaloffset="10" x:name="pupCalculator">
                <border>
                    <grid background="Transparent" cliptobounds="True" margin="1">
</grid>
                </border>
            </popup>
        </textbox>
    </columndefinition>
</grid.columndefinitions>
</grid></border></usercontrol>

From the above code you can see I have created a textbox, which is bound to a property Message. There is a popup which displays the calendar, which is opened whenever the Toggle Button is opened.  All these is wrapped around using a Border.

In the code Behind I have created a Dependancy property MessageProperty and associated with my control txtCalc. A wrapper property is also created which allows you to call the property whenever Source value is modified. The code page looks like :

public readonly static DependencyProperty MessageProperty = 
DependencyProperty.Register("Message", typeof(string), typeof(txtCalc), new UIPropertyMetadata("0"));
  public string Message
        {
            get { return this.GetValue(MessageProperty) as string; }
            set
            {
                string oldValue = this.Message;
                this.SetValue(MessageProperty, value);
                this.OnPropertyChanged(new 
DependencyPropertyChangedEventArgs(MessageProperty, oldValue, value));
            }
        }
private static void PropertyChangedCallback(DependencyObject obj, 
DependencyPropertyChangedEventArgs args)
        {
            txtCalc tcalc = obj as txtCalc;
            tcalc.txtObj.Text = tcalc.Message;
            if (tcalc != null)
                tcalc.OnPropertyChanged("Message");
        }


Conclusion

Everything looks fairly simple but as I said, it was not working for me. The problem here is Binding generally defaults to TwoWay for any control. But in case of UserControl when I use the my custom binding statements, it is essential to explicitely define the Binding Mode to TwoWay, otherwise, it defaults to OneTime.

So the Culprit here with my implementation is :
Text="{Binding ElementName=txtCalculator, Path=Message, Mode=TwoWay}"

Here if you forget to mention the Mode=TwoWay, the binding will not be proper. And hence any changes will not be notified to the Observer when used with collection.

So I have solved the issue just by mentioning {Binding Mode=TwoWay}. Pity on me. :)

You can try out the sample application from :
TextCalc.zip (80 KB)

Thank you for reading.

1 comment:

  1. There are lots of things that we face which doesnt corresponds to what we think.

    So dont worry.. carry on with your work.

    ReplyDelete

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.

Author's new book

Abhishek authored one of the best selling book of .NET. It covers ASP.NET, WPF, Windows 8, Threading, Memory Management, Internals, Visual Studio, HTML5, JQuery and many more...
Grab it now !!!