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 :
- 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.
- 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.
- 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.
- Popup : This control holds the UI for the calculator. Calculator holds a number of buttons from 0 to 9 and few calculation buttons etc.
<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.
There are lots of things that we face which doesnt corresponds to what we think.
ReplyDeleteSo dont worry.. carry on with your work.