Logo
programming4us
programming4us
programming4us
programming4us
Home
programming4us
XP
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone
 
Windows Phone

Data Bindings : TextBox Binding Updates

- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019
3/22/2011 10:13:04 PM
The Text property of a TextBox can be a target of a data binding, but some potential problems are introduced. Once you allow the user to type anything into a TextBox, you need to deal with faulty input.

Suppose you want to write a program that solves quadratic equations, that is, solutions of the equation


To make the program most versatile, you’d probably supply three TextBox controls to allow the user to type in values of A, B, and C. You could then include a Button labeled “calculate” that obtains the two solutions from the standard equation:


You’d then display the solutions in a TextBlock.

With what you know about data bindings (and considering the example of the Adder binding server), a somewhat different approach comes to mind. This approach retains the three TextBox controls and uses a TextBlock to display results. These controls are all bound to properties of the binding server.

So where does the Button go? Well, perhaps the Button isn’t really needed.

To get started, here’s a class from Petzold.Phone.Silverlight named QuadraticEquationSolver. It implements the INotifyPropertyChanged interface, has three properties named A, B, and C, and get-only properties named Solution1 and Solution2. Two additional read-only properties are of type bool and named HasTwoSolutions and HasOneSolution.

Example 1. Solution: Petzold.Phone.Silverlight File: QuadaticEquationSolver.cs
using System;
using System.ComponentModel;

namespace Petzold.Phone.Silverlight
{
public class QuadraticEquationSolver : INotifyPropertyChanged
{
Complex solution1;
Complex solution2;
bool hasTwoSolutions;
double a, b, c;

public event PropertyChangedEventHandler PropertyChanged;

public double A
{
set
{
if (a != value)
{
a = value;
OnPropertyChanged(new PropertyChangedEventArgs("A"));
CalculateNewSolutions();
}
}
get
{
return a;
}
}

public double B
{
set
{
if (b != value)
{
b = value;
OnPropertyChanged(new PropertyChangedEventArgs("B"));
CalculateNewSolutions();
}
}
get
{
return b;
}
}

public double C
{
set
{
if (c != value)
{
c = value;
OnPropertyChanged(new PropertyChangedEventArgs("C"));
CalculateNewSolutions();
}
}
get
{
return c;
}
}

public Complex Solution1
{
protected set
{
if (!solution1.Equals(value))
{
solution1 = value;
OnPropertyChanged(new PropertyChangedEventArgs("Solution1"));
}
}

get
{
return solution1;
}
}

public Complex Solution2
{
protected set
{
if (!solution2.Equals(value))
{
solution2 = value;
OnPropertyChanged(new PropertyChangedEventArgs("Solution2"));
}
}

get
{
return solution2;
}
}

public bool HasTwoSolutions
{
protected set
{
if (hasTwoSolutions != value)
{
hasTwoSolutions = value;
OnPropertyChanged(new PropertyChangedEventArgs("HasTwoSolutions"));
OnPropertyChanged(new PropertyChangedEventArgs("HasOneSolution"));
}
}

get
{
return hasTwoSolutions;
}
}

public bool HasOneSolution
{
get
{
return !hasTwoSolutions;
}
}

void CalculateNewSolutions()
{
if (A == 0 && B == 0 && C == 0)
{
Solution1 = new Complex(0, 0);
HasTwoSolutions = false;
return;
}

if (A == 0)
{
Solution1 = new Complex(-C / B, 0);
HasTwoSolutions = false;
return;
}

double discriminant = B * B - 4 * A * C;
double denominator = 2 * A;
double real = -B / denominator;
double imaginary =
Math.Sqrt(Math.Abs(discriminant)) / denominator;

if (discriminant == 0)
{
Solution1 = new Complex(real, 0);
HasTwoSolutions = false;
return;
}

Solution1 = new Complex(real, imaginary);
Solution2 = new Complex(real, -imaginary);
HasTwoSolutions = true;
}

protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
PropertyChanged(this, args);
}
}
}


The Solution1 and Solution2 properties are of type Complex, a structure that is also included in the Petzold.Phone.Silverlight project but which doesn’t implement any operations. The structure exists solely to provide ToString methods. (Silverlight 4 includes a Complex class in its System.Numerics namespace but this is not available in Silverlight for Windows Phone 7.)

Example 2. Silverlight Project: Petzold.Phone.Silverlight File: Complex.cs
using System;

namespace Petzold.Phone.Silverlight
{
public struct Complex : IFormattable
{
public double Real { get; set; }
public double Imaginary { get; set; }

public Complex(double real, double imaginary) : this()
{
Real = real;
Imaginary = imaginary;
}

public override string ToString()
{
if (Imaginary == 0)
return Real.ToString();

return String.Format("{0} {1} {2}i",
Real,
Math.Sign(Imaginary) >= 1 ? "+" : "-",
Math.Abs(Imaginary));
}
public string ToString(string format, IFormatProvider provider)
{
if (Imaginary == 0)
return Real.ToString(format, provider);

return String.Format(provider,
"{0} {1} {2}i",
Real.ToString(format, provider),
Math.Sign(Imaginary) >= 1 ? "+" : "-",
Math.Abs(Imaginary).ToString(format, provider));
}
}
}


Complex implements the IFormattable interface, which means it has an additional ToString method that includes a formatting string. This is necessary if you’re going to use numeric formatting specifications in String.Format to format these complex numbers, as StringFormatConverter does.

The QuadraticEquations1 project is a first attempt at providing a user interface for this class. The Resources collection of MainPage contains references to the QuadraticEquationSolver class and two converters that you’ve seen before:

Example 3. Silverlight Project: QuadraticEquations1 File: MainPage.xaml (excerpt)
<phone:PhoneApplicationPage.Resources>
<petzold:QuadraticEquationSolver x:Key="solver" />
<petzold:StringFormatConverter x:Key="stringFormat" />
<petzold:BooleanToVisibilityConverter x:Key="booleanToVisibility" />
</phone:PhoneApplicationPage.Resources>

The content area has two nested StackPanel elements. The horizontal StackPanel contains three TextBox controls of fixed width with two-way bindings for typing in values of A, B, and C. Notice that the InputScope is set to Number for a specifically numeric keyboard.

Example 4. Silverlight Project: QuadraticEquations1 File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel DataContext="{Binding Source={StaticResource solver}}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="12">

<TextBox Text="{Binding A, Mode=TwoWay}"
InputScope="Number"
Width="100" />

<TextBlock Text=" x" VerticalAlignment="Center" />
<TextBlock Text="2" VerticalAlignment="Center">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="0.7" ScaleY="0.7" />
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Text=" + " VerticalAlignment="Center" />

<TextBox Text="{Binding B, Mode=TwoWay}"
InputScope="Number"
Width="100" />

<TextBlock Text=" x + " VerticalAlignment="Center" />

<TextBox Text="{Binding C, Mode=TwoWay}"
InputScope="Number"
Width="100" />

<TextBlock Text=" = 0" VerticalAlignment="Center" />
</StackPanel>

<TextBlock Text="{Binding Solution1,
Converter={StaticResource stringFormat},
ConverterParameter='x = {0:F3}'}"
HorizontalAlignment="Center" />

<TextBlock Text="{Binding Solution2,
Converter={StaticResource stringFormat},
ConverterParameter='x = {0:F3}'}"
Visibility="{Binding HasTwoSolutions,
Converter={StaticResource
booleanToVisibility}}"
HorizontalAlignment="Center" />
</StackPanel>
</Grid>


The two TextBlock elements at the end display the two solutions; the second TextBlock has its Visibility property bound to the HasTwoSolutions property of QuadraticEquationSolver so it’s not visible if the equation has only one solution.



Probably the first thing you’ll notice is that typing a number into a TextBox has no effect on the solutions! At first it seems like the program is not working at all. Only when the TextBox you’ve been typing into loses input focus does the value get transferred to the A, B, or C property of the QuadraticEquationSolver class.

This behavior is by design. In the general case, controls could be bound to business objects over a network, and you probably don’t want an object bound to a TextBox being updated with every little keystroke. Users make a lot of mistakes and perform a lot of backspacing and in some cases waiting until the user has finished is really the proper time to “submit” the final value.

In this particular program, that behavior is probably not what you want. To change it, you’ll want to set the UpdateSourceTrigger property of the Binding in each of the TextBox controls to Explicit:

<TextBox Text="{Binding A, Mode=TwoWay,
UpdateSourceTrigger=Explicit}"
InputScope="Number"
Width="100" />

The UpdateSourceTrigger property governs how the source (in this case, the A, B, or C property in QuadraticEquationSolver) is updated from the target (the TextBox) in a two-way binding. The property is set to a member of the UpdateSourceTrigger enumeration. In the WPF version of UpdateSourceTrigger, members named LostFocus and PropertyChanged are available, but in Silverlight the only two options are Default and Explicit.

Default means “the default behavior for the target control” which for a TextBox target means that the source is updated when the TextBox loses focus. When you specify Explicit, you need to provide some code that triggers the transfer of data from the target to the source. This could be the role of a Button labeled “calculate.”

If you’d rather avoid that Button, you can trigger the transfer when the text changes in the TextBox, so in addition to setting the UpdateSourceTrigger property of Binding, you need to provide a handler for the TextChanged event of the TextBox:

<TextBox Text="{Binding A, Mode=TwoWay,
UpdateSourceTrigger=Explicit}"
InputScope="Number"
Width="100"
TextChanged="OnTextBoxTextChanged" />

In the TextChanged event handler, you need to “manually” update the source by calling the UpdateSource method defined by BindingExpression.

Earlier in this artilce, I showed you how to call the SetBinding method defined by FrameworkElement or the static BindingOperations.SetBinding method to set a binding on a property in code. (The SetBinding method defined by FrameworkElement is just a shortcut for BindingOperations.SetBinding.) Both methods return an object of type BindingExpression.

If you haven’t called these methods in code, you’ll be pleased to learn that FrameworkElement stores the BindingExpression object so it can be retrieved with the public GetBindingExpression method. This method requires the particular property that is the target of the data binding, which is always, of course, a dependency property.

Here’s the code for updating the source when the TextBox text changes:

void OnTextBoxTextChanged(object sender, TextChangedEventArgs args)
{
TextBox txtbox = sender as TextBox;
BindingExpression bindingExpression = txtbox.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}


Another problem with the TextBox is that the user can enter a character string that cannot be resolved to a number. Although you can’t see it, a converter is at work converting the string object from the TextBox to a double to set to the A, B, or C property of QuadraticEquationSolver. This hidden converter probably uses the Double.Parse or Double.TryParse method.

If you’d like to catch exceptions raised by this converter, you can. You’ll probably want to set two more properties of the Binding class to true, as shown here:

<TextBox Text="{Binding A, Mode=TwoWay,
UpdateSourceTrigger=Explicit,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}"
InputScope="Number"
Width="100"
TextChanged="OnTextBoxTextChanged" />

This causes a BindingValidationError event to be fired. This is a routed event, so it can be handled anywhere in the visual tree above the TextBox. Most conveniently in a small program, a handler for the event can be set right in the MainPage constructor:

readonly Brush okBrush;
static readonly Brush errorBrush = new SolidColorBrush(Colors.Red);

public MainPage()
{
InitializeComponent();
okBrush = new TextBox().Foreground;
BindingValidationError += OnBindingValidationError;
}

Notice that the normal Foreground brush of the TextBox is saved as a field. Here’s a simple event handler that colors the text in the TextBox red if it’s invalid:

void OnBindingValidationError(object sender, ValidationErrorEventArgs args)
{
TextBox txtbox = args.OriginalSource as TextBox;
txtbox.Foreground = errorBrush;
}

Of course, as soon the TextBox text changes again, you’ll want to restore that color, which you can do in the OnTextBoxTextChanged method:

void OnTextBoxTextChanged(object sender, TextChangedEventArgs args)
{
TextBox txtbox = sender as TextBox;
txtbox.Foreground = okBrush;
. . .
}

These two techniques—updating with each keystroke and giving a visual indication of invalid input—are combined in the QuadraticEquations2 project. Here’s the XAML file:

Example 5. Silverlight Project: QuadraticEquations2 File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel DataContext="{Binding Source={StaticResource solver}}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="12">

<TextBox Text="{Binding A, Mode=TwoWay,
UpdateSourceTrigger=Explicit,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}"
InputScope="Number"
Width="100"
TextChanged="OnTextBoxTextChanged" />

<TextBlock Text=" x" VerticalAlignment="Center" />
<TextBlock Text="2" VerticalAlignment="Center">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="0.7" ScaleY="0.7" />
</TextBlock.RenderTransform>
</TextBlock>
<TextBlock Text=" + " VerticalAlignment="Center" />

<TextBox Text="{Binding B, Mode=TwoWay,
UpdateSourceTrigger=Explicit,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}"
InputScope="Number"
Width="100"
TextChanged="OnTextBoxTextChanged" />

<TextBlock Text=" x + " VerticalAlignment="Center" />

<TextBox Text="{Binding C, Mode=TwoWay,
UpdateSourceTrigger=Explicit,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}"
InputScope="Number"
Width="100"
TextChanged="OnTextBoxTextChanged" />

<TextBlock Text=" = 0" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Name="result"
Orientation="Horizontal"
HorizontalAlignment="Center">

<TextBlock Text="{Binding Solution1.Real,
Converter={StaticResource stringFormat},
ConverterParameter='x = {0:F3} '}" />
<TextBlock Text="+"
Visibility="{Binding HasOneSolution,
Converter={StaticResource
booleanToVisibility}}" />

<TextBlock Text="&#x00B1;"
Visibility="{Binding HasTwoSolutions,
Converter={StaticResource
booleanToVisibility}}" />

<TextBlock Text="{Binding Solution1.Imaginary,
Converter={StaticResource stringFormat},
ConverterParameter=' {0:F3}i'}" />
</StackPanel>
</StackPanel>
</Grid>


You might also notice that I completely revamped the display of the solutions. Rather than two TextBlock elements to display two solutions, I use four TextBlock elements to display a single solution that might contain a ± sign (Unicode 0x00B1).

The code-behind file implements the updating and error handling:

Example 6. Silverlight Project: QuadraticEquationSolver2 File: MainPage.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using Microsoft.Phone.Controls;

namespace QuadraticEquationSolver2
{
public partial class MainPage : PhoneApplicationPage
{
readonly Brush okBrush;
static readonly Brush errorBrush = new SolidColorBrush(Colors.Red);

public MainPage()
{
InitializeComponent();
okBrush = new TextBox().Foreground;
BindingValidationError += OnBindingValidationError;
}

void OnTextBoxTextChanged(object sender, TextChangedEventArgs args)
{
TextBox txtbox = sender as TextBox;
txtbox.Foreground = okBrush;

BindingExpression bindingExpression =
txtbox.GetBindingExpression(TextBox.TextProperty);
bindingExpression.UpdateSource();
}

void OnBindingValidationError(object sender, ValidationErrorEventArgs args)
{
TextBox txtbox = args.OriginalSource as TextBox;
txtbox.Foreground = errorBrush;
}
}
}


Here’s a TextBox indicating that an entry is incorrect:



If you had written a quadratic equation solver for Windows Phone 7 prior to this article, the screen might looked pretty much the same, but I suspect the program would have been structured quite differently. I know that’s the case if you wrote such a program for a code-only environment such as Windows Forms.

Notice how converting the program to a mostly XAML solution causes us to rethink the whole architecture of the program. It’s always an interesting process to me how our tools seems to govern how we solve problems. But in some ways this is a good thing, and if you find yourself writing code specifically to use in XAML (such as binding services and data converters), I think you’re on the right track.

Other -----------------
- Data Bindings : Give and Take
- Data Bindings : Converters with Properties
- Data Bindings : Simple Decision Making
- Data Bindings : Setting the DataContext
- Data Bindings : A Simple Binding Server
- Data Bindings : Notification Mechanisms
- Data Bindings - The “this” Source
- Data Bindings - Relative Source
- Data Bindings - Binding Converters
- Data Bindings : Target and Mode
 
 
Top 10
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
 
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server