I fell in love with WPF the first time I saw it in action! WPF is a thing of pure beauty! Anyway, from the second I saw how the data binding worked I knew that there was going to be a lot to figure out there! It also seems as though there is no real data binding expert -- or the experts don't blog too much about it. I created my XAML Cheat Sheet shortly after feeling like I had some kind of grasp on the concept. It is still a great reference for basic bindings, but when you are real serious about binding you'll have to do better than that.
So I have been working on a project in my spare time called SmartForms 2.0 (more on that later) and I have had to do some very difficult things using bindings. In my previous experience with WPF I would either create a cleaver type converter and/or just give up and set the value/state of the controls in code (eww!). This time is different and I have to make data binding work.
Example #1
Lets say that you need to bind a bool? (nullable boolean) type to a checkbox.
<CheckBox IsChecked="{Binding SomeBooleanField}">Checkbox</CheckBox>
Produces:
You will notice that the checkbox is in a neither checked nor unchecked state. Setting IsThreeState="False" doesn't help you as it is false by default. I want the checkebox to be unchecked by default but have no control over the type or the initialization of the type. Check this out:
<CheckBox IsChecked="{Binding SomeBooleanField,
TargetNullValue=false}">
Checkbox
</CheckBox>
And now we have normal looking checkboxes! The best part is that this method doesn't mutate our property. This was exactly what I was looking for!
Example #2
The following XAML is pretty straight forward. It is simply a text box that we expect to put a city into.
<StackPanel Width="130" Height="40" HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>City</TextBlock>
<TextBox Text="{Binding SomeTextField}" />
</StackPanel>
Produces:
What if the item we are attempting to data bind to is not available. It's not going to be null, the binding will just fail. This should work well:
<StackPanel Width="130" Height="40" HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>City</TextBlock>
<TextBox Text="{Binding SomeTextField,
FallbackValue=UNBOUND}" />
</StackPanel>
Produces:
Example #3
This is a much more common occurrence. You have a number that you need to format in a textbox. This example shows the decimal value with no real format at all.
<StackPanel Width="130" Height="40" HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>Cost</TextBlock>
<TextBox Text="{Binding SomeDecimalField}" />
</StackPanel>
Produces:
There are a few ways to get the format we desire. The first example uses a static resource:
xmlns:clr="clr-namespace:System;assembly=mscorlib"
<StackPanel Width="130" Height="40" HorizontalAlignment="Left"
VerticalAlignment="Top">
<StackPanel.Resources>
<clr:String x:Key="formatStr">{0:C}</clr:String>
</StackPanel.Resources>
<TextBlock>Cost</TextBlock>
<TextBox Text="{Binding SomeDecimalField,
StringFormat={StaticResource formatStr}}" />
</StackPanel>
You can put the string inline but it looks really funny and for some reason the there has to be a non-whitespace character before the formatting expression which you may not want:
<TextBox Text="{Binding SomeDecimalField,
StringFormat=Cost: {0:C}}" />
This formats the string to look like "Cost: $1,2398.00". You can see how that could cause lots of confusion and would be undesirable. There is a way we can get the binding to work a little better:
<TextBox Text="{Binding SomeDecimalField,
StringFormat={}{0:C}}" />
OR
<TextBox>
<TextBox.Text>
<Binding Path="SomeDecimalField"
StringFormat="{}{0:C}" />
</TextBox.Text>
</TextBox>
The result is a nicely formatted output:
Notice how the binding was applied? This is how you apply MultiBindings which is the next topic.
Example #4
What if you want the value of one or more fields to exist in a single binding? That is where MultiBindings shine! Check out this very easy example:
<StackPanel Width="130" Height="40" HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>Name</TextBlock>
<TextBox>
<TextBox.Text>
<MultiBinding StringFormat="{}{0}, {1}">
<Binding Path="LastName" />
<Binding Path="FirstName" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
Results:
Example #5
Validation can happen at the point of binding. This can be handy if the objects you are binding to have logic built in. For example, lets say you have a property for birth date. Obviously the person can not be born in the future! Here is how we avoid such rotten input!
public DateTime birthdate;
public DateTime BrithDate {
get { return birthdate; }
set {
if (value > DateTime.Today)
throw new ArgumentException(
"Person can not be born in the future!");
birthdate = value;
}
}
And the XAML:
<StackPanel Margin="5" Width="130" Height="40"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>Birthdate</TextBlock>
<TextBox Text="{Binding BrithDate,
Mode=TwoWay,
StringFormat=d,
NotifyOnValidationError=true,
ValidatesOnExceptions=true}" />
</StackPanel>
Result:
The error can also be styled if required. Shown is the default style (nothing custom).
Example #6
The last example is for something called PriorityBinding. This is poorly named in my opinion as it would be better called CascadingBinding. The point to PriorityBinding is to name multiple data bindings in order of most desirable to least desirable. This way if the first binding fails, is empty and/or default, another binding can take it's place. You may have to be a little imaginative to think of such a scenario, but this kind of thing could be useful so I'm glad it's there. Just for fun, I am using the IsAsync field as well. This tells WPF to use a thread other than the GUI thread to retrieve this property. This is important for properties that take a long time to return as it will leave your app unresponsive until the value is bound. In the case of this example without that attribute the window will not show until the 5 seconds had elapsed.
private string fname;
public string FirstName {
get {
Thread.Sleep(5000);
return fname;
}
set { fname = value; }
}
Xaml:
<StackPanel Margin="5" Width="130" Height="40"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>Name</TextBlock>
<TextBox>
<TextBox.Text>
<PriorityBinding>
<Binding Path="LastNameNonExistant"
IsAsync="True" />
<Binding Path="FirstName"
IsAsync="True" />
</PriorityBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
Result:
Another neat little thing to know is that the Hierarchy for Binding is Binding -> BindingBase -> MarkupExtension -> Object. Unlike much of the rest of the .NET Framework you can actually inherit from any of these classes and provide support for your customized binding needs. That is exactly what I am doing for SmartForms 2.0! I created a binding class called DataBinding and it looks a little like this:
xmlns:db="clr-namespace:SmartForms2.Binding;assembly=SmartForms2"
<TextBox Name="firstName" Width="175"
Text="{db:DataBinding Data, DataSource=DB1}" />
My DataBinding class inherits directly from MarkupExtension so all I had to do is implement the abstract method ProvideValue.
Links:
This post has been sitting in my drafts for months! Because my friend Phil just posted his post on Lambda Expressions I'll simply link to his post and present the rest of this as supplemental examples.
// Delegates
// My Blog Engine is having trouble showing these so there may be some extra spaces
public delegate IntMathExpression (int x, int y);
public delegate IntMathExpressionSingle (int x);
public delegate FloatMathExpression (float x, float y);
// Basic Lamda Expression
IntMathExpression a = (int x, int y) => x * y;
IntMathExpression b = (int alpha, int brovo) => alpha * brovo;
IntMathExpressionSingle c = (int x) => x * x;
// "such that" =>
// Parameters => function body
// (param1, param2) => param1 + param2
// Lamda Expression using Type Inference
FloatMathExpression d = (x, y) => x + 3.14f * y;
// There is usually no need for custom delegates
Func<float, float, float> e = (x, y) => x * 100.0f + y;
// Example using a lamda expression
List<int> primes = new List<int>() { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101 };
var p = primes.Where( prime => prime.ToString().EndsWith( "3" ) );
// Deferred Execution
foreach (int i in p) {
MessageBox.Show(i.ToString());
}
// Query Expression Format
var p2 = from prime in primes
where prime.ToString().EndsWith("3")
select prime;
List<int> results = p2.ToList();
// Lamda Expressions mixed with Extension Methods
List<string> str = new List<string>() { "The Lord of the Rings", "Star Wars, Eposode III", "Ratatouille" };
var p3 = from movie in str
where movie.CountVowels() > 5
select movie;
List<string> movieResults = p3.ToList();
There are things that people do that are in poor taste. Some things that come to mind is scavenging at the dump (yeah, I saw this guy do that one time), yelling at the waitress for onions on your burger, or leaving the lavatory without property sanitizing your hands. If you do any of those things you are likely guilty of poor taste.
Now, I don't really like Bush nor do I like Obama but I do show proper respect to both of these individuals. Being a Republican without a party I tend to starkly disagree with much of President Obama's platform. I understand that many people have a lot of pent up emotion and anger toward the decisions the previous administration has made. Personally I have much of it toward both former President Bush and President Obama, but there is absolutely no way I would ever boo either of them at the inaugural ceremony! Apparently many of those in attendance choose to boo anytime President Bush and Vice President Cheney where shown on the screen. Having recently undergone back surgery, Vice President Cheney was in a wheel chair for heavens sakes!
I have personally never been to an inauguration, but I should think that the kind of patriotic atmosphere that exists would prohibit such disgustingly undignified acts. To those who choose to boo, you have none of my respect! I place you below the people who use profane language, below people who put gum under restaurant tables, below people who throw lit cigarettes out the window, and even below people spit on the ground while indoors. You people have no sense of taste and could never earn respect from anyone who does.
There have been a lot of buzz around Windows 7 lately. Most people plan to migrate from XP straight to Windows 7, skipping Vista altogether! Personally I have really liked Vista. The only real issue I had is it wouldn't run a really old game that I used to play on Windows XP. Considering the game was developed for Windows 98 I am pretty impressed it ran on XP! I also really like the idea that most of my programs are not running with Administrator permissions. I also must admit that I purchased hardware specifically geared toward Vista compatibility. I also switched to 64 bit because 3GB of RAM just isn't enough for me, but that would have been independent of XP or Vista.
I've installed a copy of Windows 7 64-bit on a virtual machine. To me it looks and feels just like Vista. I was impressed that the memory foot-print was much smaller but a recent fresh install of Vista wasn't too much larger. It also featured an improved installer and some new features. If someone can tell me why it wants to make a 100MB partition and what that is used for please leave a comment here.
Microsoft has decided that it's better to have more eyes on something than less so they have made Windows 7 Beta 1 an open invitation. The caveat is that the download link is only valid until (or so I am told) the 24th of Jan 09. So if you think you may want to tinker you better get downloading! Especially since the download servers have been saturated since the beta was made public.
Download Windows 7 Beta 1