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 :
- 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.
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.