Beth's Chinese blog
And speaking of RAD with WPF....
I just posted a new Channel 9 video with Milind Lele, Program Manager on the Visual Studio Pro Tools team. In this interview Milind shows one of the new RAD improvements I was talking about that is coming to WPF in Visual Studio 2010 -- Drag-Drop Data Binding.
See it in action for yourself on Channel 9.
Enjoy!
Today I was looking at comments on one of my WPF videos on how to create a Master-Detail form in WPF and I had to agree:
"I was looking into learning WPF - but jeez, what happened to RAD? It takes all this to create a simple Master/Detail form?"
(BTW, Sorry I couldn't respond on the site itself, I've been having problems with my site login.)
Designing WPF forms that work with data in Visual Studio 2008 is tedious (to say the least) -- you have to hook up all the data binding in XAML and code. It's not hard, it's just time consuming. (BTW, here's the link to all the WPF videos I've done on this topic so far.)
Well luckily Visual Studio 2010 will enable drag-drop data binding in WPF! Milind Lele, the Program Manager on this feature, blogged about it here and here. I'm heading to DevTeach on Monday and I just picked up another session, one on WPF data binding. I plan on showing this feature of VS 2010 at the end and I think it will be a big hit, especially to those who are building business applications in WPF today.
If you want to try it out (and have a few hours to spend downloading ;-)) then you can pick up a copy of the Visual Studio 2010 Community Tech Preview (CTP) which is distributed as a VPC. Otherwise come back here for more info on this, I'll be playing with it myself... ;-)
Everything you need to know about building Office solutions in Visual Studio is on the new Office Development with Visual Studio Developer Center! (update your bookmarks: http://msdn.com/vsto)
- Right on the home page meet MVPs and members of the product team who share tips, tricks, presentations, and trip reports about meeting developers like you.
- Download code samples and tools that showcase Office and Visual Studio features or access shared source projects on Codeplex that are using Visual Studio to build Office solutions.
- Watch educational and entertaining videos about Office development, watch team interviews and demonstrations.
- Learn new skills by browsing the learning resources by topic or learning type as well as get easy access to the MSDN library resources you need.
- Meet your forum moderators and access other developer community resources.
- Get quick access to news and featured resources and take a peek at what’s coming in Visual Studio 2010!
Make it your new home today. :-)
I'll be speaking at DevTeach in Montreal December 1st - 5th this year.... brrrrr!!!! I guess I need a winter coat! Seriously though, this Canadian .NET/SQL conference is always jam-packed with high caliber speakers, take a look. I'll be speaking on Visual Basic 6 to .NET Migration (similar to this webcast I did with Rob) and I'm doing a fun session on manipulating Office Documents using the Open XML SDK and LINQ. Check out all the sessions here.
Plus, they have some awesome goods to give away this year -- every attendee will get Visual Studio 2008 Pro, Expression Web 2 and the TechEd DVD set in their bag!
Register today!
Hope to see you there.
Alessandro Del Sole, Visual Basic MVP, has been busy on CodePlex writing a neat WPF learning application that aggregates RSS feeds. In his words:
It's developed in Visual Basic 2008 and uses LINQ-to-Xml and XML Literals to read feeds and persist XML data. You'll learn a lot of things like:
I just started playing with it myself and it's pretty cool. I wrote a mini feed browser to search Sara's Visual Studio Tip of the Day posts using XML Literals that can cache the posts as well. I'll probably try to update it in WPF as well based on this. Thanks Alessandro!
When designing WPF Windows with data (or as I usually refer to them WPF "Forms") we have many options on how we want to load the data and bind our controls. Depending on where the data is coming from and how it's being used there are a lot of possibilities.
DataContext "Direct"
Suppose we have the following simple window defined and we've set up data binding on our text boxes to the corresponding properties on a Customer object.
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="152" Width="300" Name="Window1"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="95*" /> <ColumnDefinition Width="183*" /> </Grid.ColumnDefinitions> <StackPanel Name="StackPanel1" Grid.Column="0"> <Label Height="28" Name="Label1" Width="Auto">Customer ID</Label> <Label Height="28" Name="Label2" Width="Auto">Contact Name</Label> <Label Height="28" Name="Label3" Width="Auto">Company Name</Label> </StackPanel> <StackPanel Name="StackPanel2" Grid.Column="1"> <TextBox Height="28" Name="TextBox1" Width="Auto" Text="{Binding Path=CustomerID}"/> <TextBox Height="28" Name="TextBox2" Width="Auto" Text="{Binding Path=ContactName}"/> <TextBox Height="28" Name="TextBox3" Width="Auto" Text="{Binding Path=CompanyName}"/> </StackPanel> </Grid> </Window>
One technique to load the data onto the form is to directly set the DataContext property of a Window or container at runtime and all the contained controls will inherit the same DataContext:
Class Window1 Private Sub Window1_Loaded() Handles MyBase.Loaded 'Returns List(Of Customer) and sets the Window.DataContext Me.DataContext = CustomerFactory.GetCustomers() End Sub End Class
In our simple example above this means that when we run the form any controls on the window will bind to the Customer object. If we set the StackPanel2.DataContext property, then only the controls in the StackPanel will bind to the Customer object. This is a handy way of setting up binding on containers of controls.
Using ObjectDataProvider
Another way to set up binding is to use an ObjectDataProvider in the Window.References section of our XAML which specifies a method to call on a specific type in our project and uses the results as the source of data. Using this technique loads the data at design time as well.
To do this we remove our code in the Loaded event above and instead just specify the XAML to set up the ObjectDataProvider. First we need to add a namespace for our local project and then we can set up the ObjectDataProvider in the Window's resources section. Finally we specify the binding to the ObjectDataProvider by setting the DataContext of the grid container control. All the controls on the form are contained in this grid:
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFCollectionViewSource" Title="Window1" Height="152" Width="300" Name="Window1"> <Window.Resources> <ObjectDataProvider x:Key="CustomerData" MethodName="GetCustomers" ObjectType="{x:Type local:CustomerFactory}"/> </Window.Resources> <Grid DataContext="{Binding Source={StaticResource CustomerData}}" >...
This has the immediate effect of loading the data onto our form in the designer.
Loading the data into the designer can be a good thing if the data source is a local class contained in the project because it gives visual queues as to what the form will actually look like when it runs. However, if you are accessing data in a database, especially if it's not local to your machine, then this can cause potential performance problems at design time. In this case I recommend loading the data at run time only.
Master-Detail Binding
In this simple example we can set the DataContext directly in the Loaded event like we did initially. But things start to get tricky if you have a more complex form. Consider this master/details form that contains a ListView set up in GridView mode specifying DataTemplates for how to bind the columns (similar to how I showed in this video). One of the columns in this GridView is a Lookup list to the employee table (a foreign key relationship to reference data).
<ListView Name="lstDetails" Grid.Row="1" Grid.ColumnSpan="2" IsSynchronizedWithCurrentItem="True"> <ListView.View> <GridView> <GridViewColumn Header="Order Date" Width="100"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBox Name="txtOrderDate" Text="{Binding Path=OrderDate}" /> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Ship Date" Width="100" > <GridViewColumn.CellTemplate> <DataTemplate> <TextBox Name="txtShipDate" Text="{Binding Path=ShippedDate}" /> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Employee" Width="115" > <GridViewColumn.CellTemplate> <DataTemplate> <ComboBox IsEditable="False" Name="cboEmployee" IsSynchronizedWithCurrentItem="False" SelectedValue="{Binding Path=EmployeeID}" DisplayMemberPath="LastName" SelectedValuePath="EmployeeID" /> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </ListView.View> </ListView>
For a ListView and a Combobox we need to specify the data source of the lists by setting the ItemsSource property. For the ListView it's easy to specify this in the code behind because we have a reference to the lstDetails control. We can also just simply set it here in XAML to the "Orders" collection on the Customer (master). We can set it up this way because my customer objects contain a collection called "Orders" that contain the orders for that customer.
<ListView Name="lstDetails" Grid.Row="1" Grid.ColumnSpan="2" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Orders}">
Note that if we're still using the ObjectDataProvider technique then when specifying this ItemsSource on the lstDetails the designer will populate the GridView with data, which is something we probably don't want to do if the data is stored in a remote database. So we could just set the DataContext in the code behind like in the first example, however, we still need to set up the embedded Combobox in that GridView. Unfortunately it's not exposed as a field of the Window class in our code behind because it's a DataTemplate. There are ways to get at this DataTemplate in code but there's a much easier way to set up our binding using what's called a CollectionViewSource.
Using the CollectionViewSource
A CollectionViewSource is a proxy for the CollectionView which manages the currency (the position) in the list of objects (or rows if using a DataTable). It has a property called Source which can be set in our code behind. This way, we can set up CollectionVieSources in XAML for all our data lists and bind them to the corresponding controls all in XAML. Then at runtime in our code we set the Source properties and only at that time does the data pull from the database.
We need three CollectionViewSource's in our Master/Detail example, one for the Customers (MasterView) one for Orders (DetailView) and one for the list of Employees (EmployeeLookup). On the DetailView we specify the Source as the MasterView with a Path set here to 'Orders'. This is similar to how we chain master/detail BindingSources in Winforms development.
So our XAML will be changed to:
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="242" Width="367" Name="Window1"> <Window.Resources> <CollectionViewSource x:Key="MasterView" /> <CollectionViewSource x:Key="DetailView" Source="{Binding Source={StaticResource MasterView}, Path='Orders'}" /> <CollectionViewSource x:Key="EmployeeLookup" /> </Window.Resources> <Grid DataContext="{Binding Source={StaticResource MasterView}}" > ...<ListView Name="lstDetails" Grid.Row="1" Grid.ColumnSpan="2" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Source={StaticResource DetailView}}"> ... <ListView.View> <GridView>... <GridViewColumn Header="Employee" Width="115" > <GridViewColumn.CellTemplate> <DataTemplate> <ComboBox IsEditable="False" Name="cboEmployee" IsSynchronizedWithCurrentItem="False" SelectedValue="{Binding Path=EmployeeID}" ItemsSource="{Binding Source={StaticResource EmployeeLookup}}" DisplayMemberPath="Name" SelectedValuePath="EmployeeID" Margin="-6,0,-6,0"/> </DataTemplate> </GridViewColumn.CellTemplate> ...
Now we just set the Source properties of our MasterView and EmployeeLookup in our code behind. It's easy to grab references to the CollectionViewSources by accessing the Window.Resources dictionary.
Class Window1 Private Sub Window1_Loaded() Handles MyBase.Loaded 'Returns List(Of Customer) and sets the Master CollectionViewSource, ' the DetailView already has it's source set to the MasterView with ' the child path specified in the XAML. Dim masterViewSource = CType(Me.Resources("MasterView"), CollectionViewSource) masterViewSource.Source = CustomerFactory.GetCustomers() 'Returns List(Of Employee and sets the EmployeeLookup CollectionViewSource Dim employeeViewSource = CType(Me.Resources("EmployeeLookup"), CollectionViewSource) employeeViewSource.Source = EmployeeFactory.GetEmployees() End Sub End Class
This separation also allows us to also easily swap out our data source collections at runtime if necessary.
I show an example of how to use CollectionViewSources and create a fully functional master/detail WPF form in this video and this sample so take a look.
There are over 1100 people registered for the Silicon Valley code camp this year! Wow! That's a couple hundred more than last year. This is a free event people, so there's no excuse not to come out to Foothill college this year. And it's not all Microsoft technologies (of course not --we're in Silicon Valley for heaven's sake!) so there's a lot of variety of talks and a diverse crowd of developers to network with. And people are coming from all over. The home page has an interactive Virtual Earth map on it that shows how far attendees and speakers are coming from to attend. We have attendees coming from as far as New Jersey. Woah.
Tim Ng from the Visual Basic Team will also be delivering a couple talks, one on LINQ fundamentals and one called Introducing F# both on Saturday. Check out the full schedule here.
I'll be speaking all on Saturday as well and I've got three topics (all Visual Studio of course). These are the same talks I did at SDC in early October: LINQ to Everything, the ever popular Conquering XML with Visual Basic 9 and finally Taking Advantage of LINQ and Open XML in Office 2007.
There's also these cool "Lightening talks" they're trying out this year which are 5 minute demos/talks. I'll probably do one or 10 ;-). I can think of a lot of tips and tricks just around XML Literals and Office development. Of course I could also steal some of Sara's tips and promote her book a bit too (proceeds go to charity).
There's also a BBQ on Saturday evening so it should be a lot of fun. I expect all the evangelists and developer community people to be there, so don't be square. It's your FREE chance to ask us your tough development questions. So get to registering, will ya?!
I posted two more WPF "Forms over Data" videos onto the Developer Center that you should check out.
How Do I: Edit Tabular Data in WPF? - learn how to create a simple data grid in WPF for editing tabular data using Visual Studio 2008 Service Pack 1.
How Do I: Create a Master-Detail Data Entry Form in WPF? - learn how to create a master-detail (or one-to-many) data entry form in WPF using Visual Studio 2008 Service Pack 1.
Subscribe to the How Do I video feed.
Our party was a blast on Halloween night. Alan and I were a little torn up:
But luckily we had a rabbi and a priest at the party to help (my parents, yes -- my mom is the rabbi):
But most creative was our neighbor Scott who dressed up as Al Davis:
Unfortunately I didn't take as many pictures as I had hoped -- I was running around most of the night. And most of the house is creepy lighting so the camera flash blew all that away, but there were a few good ones that we got.
The haunted tunnel we built was too dark to come out on my camera but I'll see if some of the others had better luck. Next year, I'm going to try and build a space ship :-) Now... time to pack it all up.... may take me until Christmas. ;-)
Enjoy! (I did)