Before I say a word, let me tell you that Dave from Tangible Software has been sending me free copies of InstantVB and Instant C# for years and they posted a testimonial from me on their site. That is my only involvement with them, but there you have it.
Now, don't let Dave know, but while I've been meaning to use this software for a long time, I hadn't recently, because every project that I thought would be bi-lingual wasn't. Until recently, when I switched over to writing my tutorials and videos in VB, and I wanted to be able to grab something I'd written in C# and get its equivalent in VB.
Yow! this program is easy, accurate and blazingly fast (25K lines/minute)
I really want to ship a copy with every book I write so that folks can stop worrying about which language the book is written in. This is not freeware (its note even all that inexpensive) but it is amazing (they state that on our 101 C# samples they convert 99.98% without adjustment!) and it is making my life a whole lot easier.
I'll be interested to hear from VB programmers in coming weeks if they read any of my code and have the experience of "yes, that is technically correct code, but no VB programmer would ever write it like that."
Net Net: look forward to more of my work in both C# and VB.
-j
I have set out on an integrated set of videos, tutorials and blog posts on the subject of using Expression Blend from the point of view of Silverlight 2 Programmers. To get things going, I created a 3 part video series based on ScottGu's extended blog tutorial and I have a tutorial on the topic due to publish early next week.
I am now in the process of creating more videos in which I will be exploring how Blend can make certain tasks far easier for the Silverlight programmer; keep an eye out for my video on Blend and Data binding.
I will express a certain resistance to starting this, and I've been trying to figure out why. Part of it is that I'm very comfortable in Visual Studio and much of it is that nearly all the books on Blend are targeted at Designers rather than at Programmers. It is my goal to overcome those concerns for developers who share these concerns, and to integrate Blend into my toolset; using Blend for its strengths, and Visual Studio where it is stronger, and, eventually, to think of them as two faces of a single semi-integrated tool.
I'm not sure what will happen when Visual Studio's design surface is fully read/write. I think by then I'll be so comfortable using Blend's features that I'll continue to do so for some tasks. I'd be interested in your experiences as well.
Apparently Radio 1's Big Weekend is a big thing in England, and to launch the festivities they've made The Big Zoomy Photo Thing a central piece of the action (as you may guess, this is Silverlight/Zoom incognito).
(picture cropped to save room)
The implementation is beautiful. Click, roll, zoom. To give you a quick sense of it, here are two images. The first is the starter, I then zoomed in on the control panel (pointed to by the red arrows I added.
The progressive rendering is just mind-blowing. Every time I see it. And it is a blast to see it in a live usage, even if they don't have any really interesting pictures up yet (they will soon).
One of the folks who did a lot of the programming is a buddy (he refuses to be credited) and I will work on talking him into an interview about the programming experience. Stay tuned. I know that Tim Heuer is also taking a long look at Zoom so we'll have a good deal more on this very soon.
Aha! Okay, I may not fully understand all the requirements, but the following demo will show how to dynamically create a user control and then have that user control close itself, remove itself from the containing page and fire an event to the page so that the page can clean up any associated other controls that might be left laying about.
This is based on the User Control sample that goes with the video that hasn't yet been posted (you don't mind that, do you?) but will be in a couple days. I'll strip it down so as not to get hung up in the parts we don't care about.
First, let's look at the effects. When the application begins there is just a single button marked "Create".
Clicking on that button creates two text blocks and two user controls,
In the UserControl video the User Controls are quite nicer looking but here we're interested in their ability to self-destruct; hence the close button.
When you click the close button, not only does the User Control remove itself from its parent panel's children collection, it raises an event to which the page can subscribe so that it can clean up anything else that might be lingering about; in this case the text block (which is not part of the control). Thus, if I close the upper control, I want also to remove the "Event Address" prompt.
Here's how it all works. I assume we have the custom control already and I add the button to it. The key is to give that user control its own EventArgs type (to hold its unique ID ) and thus also give it a delegate and an event.
public partial class AddressUserControl : UserControl
{
public class AddressEventArgs : RoutedEventArgs
{
public object Tag { get; private set; }
public AddressEventArgs(object theTag)
{
this.Tag = theTag;
}
}Notice both that AddressEventArgs is derived from RoutedEventArgs adn that it is nested within AddressUserControl (my User Control). It has a constructor and a public property called Tag (to parallel the idea that the control itself has a Tag of type object).
We now give the control a delegate and an event
public delegate void AddressEventHandler(object o, AddressEventArgs e);
public event AddressEventHandler Closed;
The closed event is what the page will subscribe to, in order to be alerted when the control is closed. This event is fired as part of the control's handling of the button's click event,
public AddressUserControl()
{
InitializeComponent();
Close.Click += new RoutedEventHandler(Close_Click);
}
void Close_Click(object sender, RoutedEventArgs e)
{
Panel parent = this.Parent as Panel;
if (parent != null)
{
parent.Children.Remove(this);
if (Closed != null && this.Tag != null)
{
Closed(this, new AddressEventArgs(this.Tag));
}
}
}
In the constructor we wire up the Close.Click; our internal handler for when the button is pressed. That handler does two things; it first makes sure we're in a panel, and if so, it removes us from the panel. It then checks to see if anyone has registered with our Closed event and that our Tag is not null; if so then it fires the Closed event to anyone who is interested.
Creating The Control Dynamically
All of the above falls into place when you see the control created dynamically. The XAML has nothing but the stack panel to hold the dynamically created controls,
Page.xaml
<UserControl x:Class="UserControlDemo.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:jl="clr-namespace:UserControlDemo;assembly=UserControlDemo"
Width="600" Height="800">
<StackPanel x:Name="MasterContainer" Background="White">
<Button x:Name="Create" Content="Create" Width="60" Height="40" HorizontalAlignment="Left"/>
</StackPanel>
</UserControl>Here's how the control is created in Page.xaml.cs:
void Create_Click(object sender, RoutedEventArgs e)
{
TextBlock tb = new TextBlock();
tb.Text = "Event Address";
tb.FontFamily = new FontFamily("Verdana");
tb.FontSize = 24;
tb.HorizontalAlignment = HorizontalAlignment.Left;
tb.Margin = new Thickness(15, 0, 0, 0);
tb.Tag = "1";
MasterContainer.Children.Add(tb);
AddressUserControl auc = new AddressUserControl();
auc.Tag = "1";
auc.Closed += new AddressUserControl.AddressEventHandler(auc_Closed);
MasterContainer.Children.Add(auc);
tb = new TextBlock();
tb.Text = "Billing Address";
tb.FontFamily = new FontFamily("Verdana");
tb.FontSize = 24;
tb.HorizontalAlignment = HorizontalAlignment.Left;
tb.Margin = new Thickness(15, 0, 0, 0);
tb.Tag = "2";
MasterContainer.Children.Add(tb);
auc = new AddressUserControl();
auc.Tag = "2";
auc.Closed += new AddressUserControl.AddressEventHandler(auc_Closed);
MasterContainer.Children.Add(auc);
}Unpacking this, we start by dynamically creating a textblock and adding it to the stack panel. We then create an AddressUserControl and assign it the same tag as the TextBlock and then we register with the user control's closed event, passing in the name of the method to be invoked when that event is raised (auc_closed). Finally, we add the user control to the stack panel.
This is repeated for the second text block and the second user control.
When the user clicks on the button, the user control takes care of removing itself from the stack panel, but it also fires the Closed event, which the page has now registered for. Per the registration, the method auc_Closed is called,
void auc_Closed(object o, AddressUserControl.AddressEventArgs e)
{
foreach (UIElement uie in MasterContainer.Children)
{
TextBlock tb = uie as TextBlock;
if (tb != null)
{
if (tb.Tag.ToString().Equals(e.Tag.ToString()))
{
MasterContainer.Children.Remove(uie);
break;
}
}
}
}
Auc_Closed iterates through the stack panel's children collection looking for textBlocks. If it finds one it checks the Tag against the tag in the AddressEventArgs (put there when the event was fired) and if they match, then it removes that text block from the stack panel as well.
Sweet.
I've put the entire source code Here
Ben H. asked a question within a comment to a previous blog post.
Suppose, there is a button on the main form and when the user clicks on the button, a new container gets instantiated and the new container appears on the top of the main form. However, there is also a "Close" button on that container that when the user clicks on, the container closes. And here is my question. When the user clicks on the the "Close" button, I'll set the visibility of that Container to Collapsed, however I need to raise some kind of event in that Close button that the main form (who originally created the Container), will respond to this event that the container has been closed, therefore I need to remove it from the Children.Remove(Container) and then set the container to null.
I don't know how to raise that event in the "Close" button and how to respond to it in the main form.
A sample or explanation or snippet or pointer to some info is much appreciated!
I fully admit that I may not have answered this completely, but after a few minutes of noodling with it, here is what I have... which may be a good start or may be off point but perhapsinteresting anyway.
I created a page.xaml with a grid (one row, two columns) and a button,
<UserControl x:Class="DynamicControls.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="Create" Content="Create"
Width="50" Height="30" Grid.Row="0"
Grid.Column="0" />
</Grid>
</UserControl>
In the code behind I created an event handler for the button,
private int id = 0;
public Page()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
Create.Click +=new RoutedEventHandler(Create_Click);
}
The job of the event handler is to dynamically create a container that holds two buttons, one of which is the close button.
void Create_Click(object sender, RoutedEventArgs e)
{
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
sp.VerticalAlignment = VerticalAlignment.Center;
sp.Width = 300;
sp.Height = 500;
sp.SetValue(Grid.RowProperty, 0);
sp.SetValue(Grid.ColumnProperty, 1);
sp.Tag = (++id).ToString();
sp.Background = new SolidColorBrush(Colors.Cyan);
Button btn = new Button();
btn.Width = 50;
btn.Height = 30;
btn.Content = "Close";
btn.Margin = new Thickness(5.0, 0, 0, 0);
btn.Tag = id.ToString();
btn.Click += new RoutedEventHandler(btn_Click);
sp.Children.Add(btn);
btn = new Button();
btn.Width = 50;
btn.Height = 30;
btn.Content = "Hello";
btn.Margin = new Thickness(5.0, 0, 0, 0);
btn.Click += new RoutedEventHandler(otherBtn_click);
sp.Children.Add(btn);
LayoutRoot.Children.Add(sp);
}
Note that the stack panel is assigned a unique ID in its TAG property as is the button
We'll come back to what this is for in just a moment. After adding the stack panel we add two buttons. One to close the stack panel, and another just to have something else in the stack panel (in this case a button that knows how to change its background color.
Note that each of the buttons is added to the stack panel's children collection and the stack panel itself is added to the grid's children collection.
sp.Children.Add(btn);
LayoutRoot.Children.Add(sp);
Events
Each of the buttons has its own event, and its own event handler. The second button has an event handler cleverly named "otherBtn_click" (it was late, I was tired...)
btn.Click += new RoutedEventHandler(otherBtn_click);
That event handler picks one of six colors and sets the background for the button,
void otherBtn_click(object sender, RoutedEventArgs e)
{
int randomNumber;
Random r = new Random();
randomNumber = r.Next(0,5);
List<Color> myColors = new List<Color>();
myColors.Add(Colors.Magenta);
myColors.Add(Colors.Purple);
myColors.Add(Colors.Red);
myColors.Add(Colors.Gray);
myColors.Add(Colors.Green);
myColors.Add(Colors.Blue);
Button btn = sender as Button;
if ( btn != null )
{
btn.Background = new SolidColorBrush(myColors[randomNumber]);
btn.Foreground = new SolidColorBrush(Colors.Black);
}
}
Close The Door!
The first button is the one that answers the question. Its job is to close the form.
To be explicit, when the user clicks on the Close button, we want the page to be alerted. To accomplish this, we assign an event to the button
btn.Click += new RoutedEventHandler(btn_Click);
The event handler looks through all the elements in the children of the page to find any of type stack panel. If it finds a stack panel it looks to see if the stack panel's Tag holds an ID that matches the ID of the button that fired the event. If so, it has the right stack panel and it removes it from the page's children collection and <poof> it's gone.
void btn_Click(object sender, RoutedEventArgs e)
{
foreach (UIElement uie in LayoutRoot.Children)
{
StackPanel sp = uie as StackPanel;
Button btn = sender as Button;
if (sp != null && btn != null)
{
if (sp.Tag.ToString().Equals(btn.Tag.ToString()))
{
LayoutRoot.Children.Remove(uie);
break;
}
}
} // end foreach
} // end btn_Click
There may be easier ways, but this works quite cleanly. As an exercise for the reader I suggest implementing this so that each click of the Create button creates a stack panel in a different grid location, so that you can have a few open and see that each close button closes the correct one.
Here's the complete source code
Page.xaml
<UserControl x:Class="DynamicControls.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="Create" Content="Create" Width="50" Height="30" Grid.Row="0" Grid.Column="0" />
</Grid>
</UserControl>
Page.xaml.cs
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Collections.Generic;
namespace DynamicControls
{
public partial class Page : UserControl
{
private int id = 0;
public Page()
{
InitializeComponent();
Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
Create.Click +=new RoutedEventHandler(Create_Click);
}
void Create_Click(object sender, RoutedEventArgs e)
{
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Horizontal;
sp.VerticalAlignment = VerticalAlignment.Center;
sp.Width = 300;
sp.Height = 500;
sp.SetValue(Grid.RowProperty, 0);
sp.SetValue(Grid.ColumnProperty, 1);
sp.Tag = (++id).ToString();
sp.Background = new SolidColorBrush(Colors.Cyan);
Button btn = new Button();
btn.Width = 50;
btn.Height = 30;
btn.Content = "Close";
btn.Margin = new Thickness(5.0, 0, 0, 0);
btn.Tag = id.ToString();
btn.Click += new RoutedEventHandler(btn_Click);
sp.Children.Add(btn);
btn = new Button();
btn.Width = 50;
btn.Height = 30;
btn.Content = "Hello";
btn.Margin = new Thickness(5.0, 0, 0, 0);
btn.Click += new RoutedEventHandler(otherBtn_click);
sp.Children.Add(btn);
LayoutRoot.Children.Add(sp);
}
void btn_Click(object sender, RoutedEventArgs e)
{
foreach (UIElement uie in LayoutRoot.Children)
{
StackPanel sp = uie as StackPanel;
Button btn = sender as Button;
if (sp != null && btn != null)
{
if (sp.Tag.ToString().Equals(btn.Tag.ToString()))
{
LayoutRoot.Children.Remove(uie);
break;
}
}
} // end foreach
} // end btn_Click
void otherBtn_click(object sender, RoutedEventArgs e)
{
int randomNumber;
Random r = new Random();
randomNumber = r.Next(0,5);
List<Color> myColors = new List<Color>();
myColors.Add(Colors.Magenta);
myColors.Add(Colors.Purple);
myColors.Add(Colors.Red);
myColors.Add(Colors.Gray);
myColors.Add(Colors.Green);
myColors.Add(Colors.Blue);
Button btn = sender as Button;
if ( btn != null )
{
btn.Background = new SolidColorBrush(myColors[randomNumber]);
btn.Foreground = new SolidColorBrush(Colors.Black);
}
}
} // end class
}
In what may be a first (at least for me) I've secured agreement with the very generous folks I work for here and at O'Reilly to loosely join the tutorials and the chapters of our forthcoming book Programming Silverlight (co-authored by Tim Heuer, O'Reilly 2008) to create an enhanced approach to learning tool.
Let me be clear: the goal is to provide more for less; it is not the goal to push you to buy the book; and you have my word that the tutorials will not be limited, constrained or curtailed in service to making the book somehow more worthwhile.
My hope is that the book will have additional value, but the tutorials, like the videos will stand on their own. If we do it right, together they will supplement one another.
Here's how it will work.
Phase 1 - Tutorials and Draft Chapters
During Phase 1 I will continue to publish tutorials on Silverlight.NET as well as the first drafts of chapters from Programming Silverlight for those who are curious or who would like to provide feedback (more on providing feedback to come).
Note that these chapters will not have gone through technical edit nor copyedit, and will be quite rough; the final chapters will be updated for Silverlight RTW (Release To Web, as opposed to Beta), numerous rounds of technical edit, development edit and copyedit.
Phase 2 - Publication and Beyond
In Phase 2, the 1st Edition of the book will be published, ideally providing cohesion, depth and detail that is simply not possible in the tutorials. It is my plan that the tutorials will continue, supplementing the material already on line, drawing on and extending the book as Silverlight evolves, and serving as both a supplement to the existing edition and a preview of future editions.
Schedule
It is important to understand that the writing schedule for this book does not in any way reflect my knowledge of the timing on the release of Silverlight Beta 2 nor subsequent releases of Silverlight. Further, the current schedule is subject to change and will change. It always does. The Table of Contents will change as well. I'll post both within a week or so.
In my new video I demonstrate how you can create controls dynamically at run time. The technique is very straight forward, everything you can create declaratively in XAML you can also create dynamically in C#.
xaml
<Button x:Name="Button1" Width="50" Height="30" Content="Click Me" Grid.Row="3" Grid.Column="0" />
C#
Button button2 = new Button();
button2.Width = 75;
button2.Height = 30;
button2.Content = "No, click me!";
button2.SetValue(Grid.RowProperty, 0);
button2.SetValue(Grid.ColumnProperty, 1);
LayoutRoot.Children.Add(button2);
Why Not Dynamically Instantiate All of 'Em?
The case for why you must dynamically instantiate some objects is clear: there are times you just can't know at design time what kind of object you'll need: you must respond to the user's choices and actions. That of course raises the question of why not eschew the XAML altogether and dynamically instantiate all your objects.
There are two answers to this. The first is that it is faster and easier to declare the button in XAML. The second, more important answer is that the XAML is "toolable" -- that is, the XAML can be read and understood by, for example, Expression Blend, while the C# cannot; which makes developing large applications far easier and far more scalable.
If I open the project that created these two buttons in Blend, this is what I see:
Blend can read the grid, and the button declared in XAML but is totally unaware of the button created in code.
Conclusion?
The short conclusion is to create objects declaratively at design time when possible, and dynamically at runtime when necessary. In the abstract this can seem confusing, but when writing code, it is never ambiguous.
In about a week, I'll be posting a video on creating a form that responds to keyboard shortcuts (as an intro to a video on user controls).
This is also covered in the tutorial User Controls)
As a challenge to those of you who are already comfortable with styles and data-binding and who "don't need no stinkin' video" -- I've posted a broken version of the program for you to debug. <chortle>
Your mission, should you decide to accept it, is to find the bug, and fix it.
In the video I show this broken version and why it is broken, and then I fix it; but if you'd like to get ahead of the curve and try your hand... have at it. It's a bit sneaky.
What is supposed to happen when you run the app is that if you hit Control-C the address of the Computer Museum is filled in and if you hit Control-M the address of Microsoft is filled in. But it don't. <cackle>
I received an email today asking that I post a reference to Tim Heuer's excellent blog entry on Silverlight Cross Domain Policy File Helpers. My hope is that you are all subscribed to Tim's blog as it is excellent and he and I now share the job that is officially referred to as "Developer Community Liaison for Silverlight" (or something like that).
Tim's blog entries are great, and he and I will be co-authors on Programming Silverlight (O'Reilly). He'll also be "specializing" in Line of Business questions as well as Silverlight Serialization, Network Issues and Web/Web Services issues.
Oh! Don't forget to check out his podcasts!
[The links have been fixed. Thank you for your patience, and I apologize for the inconvenience.]
I very much enjoyed my double presentation at the Microsoft Health and Life Sciences Conference in Atlantic City,
where I promised, as always, to post my slides and source code.
During the first session we covered adding controls to a form and events and event bubbling; here is the source code.
During the second session we did not cover all the material we hoped to. We did cover data binding in some detail, and here is the starter code and the completed application.
You may want to check the following videos and tutorials to supplement the presentation:
NB: I have videos coming in the next couple weeks on Styles and Templates as well
Thanks again,
A quick announcement and some words of thanks. I'm pleased to say that Tutorial #5: Keyboard Input and User Controls is now available for reading on line or as a PDF.
The thanks go first to Karen Corby whose amazing presentation at Mix I shamelessly stole as the basis for the tutorial (see my blog entry about Karen) and to Silverlight Cream for such a glowing review that my ego can not resist reproducing it here
Jesse Liberty posted this amazing Tutorial on User Controls ... it's like a treatise or something... wow... download the PDF and code and then run away somewhere for a weekend and try to absorb everything in this tutorial, and then I tell you... good luck! ... great material as usual
Reviews in Perspective
Which leads me to a quick story about writing. Shortly after I wrote my first book (Teach Yourself C++ In 21 Days) I received two emails. The first said
I have read 5 books on C++, and I always get stuck on pointers. Yours is the first one I understand...
The second said,
I don't know what it is you do for a living, but it isn't programming or writing, because you are incompetent at both.
I hung them both up on my wall.
Today I had the great enjoyment to make two presentations to the Microsoft Health and Life Sciences Conference here in Atlantic City.
While showing DataBinding, I became interested in demonstrating the effects of INotifyPropertyChanged, The basic mechanism is this; you add a PropertyChangedEventHandler to your business object and each time you set a bound property you invoke that event, passing in a reference to the business object and an instance of PropertyChangedEventArgs initialized with the name of the property
The bound Silverlight controls will respond to this event and update themselves to the new value. But how to test this?
We tried making a new browser, but no go; we suspected (correctly) that the UI was pointing to two different objects. I proved this to myself by modifying the constructor to generate a random number and store it in a member variable, which I display.
When you attempt to create two views on the data (by hitting control N on the browser or by copying the URL to a new tab) you generate a new object as you can see by the new ID.
To ensure that we're seeing the UI update in response to changes to the same object, it is easiest to just add two new rows, one that shows the value of IsPublished and the other that shows Quantity on Hand. When you change the original, the duplicate should be informed through the event.
We can now run the program again and when we change the isPublished or QuantityOnHand properties, we should see that immediately reflected in the second control. Turn off the notification and the reflection should stop.

A couple readers of my blog and folks eager to learn more about Silverlight 2 have written to me asking for 2 things:
1. Tutorials and Videos on the specific topics of interest to them (great, keep 'em coming) and
2. Greater transparency in my schedule of what I'll be releasing
Normally, I don't like to say "I'll be taping, or writing about this subject or that, because the schedule inevitably changes; for any number of reasons: other things rise on the priority list, something is about to change in the way it works so I decide to wait, someone else is about to cover that topic, etc. etc.
All of that said, since I've been asked, here is my tentative list of what I hope to release in the upcoming weeks
Tutorials
- Keyboard input events on controls
- User controls
- Expression Blend for Silverlight Programmers
HDI Videos
- Creating Controls Dyanmically (in code vs. XAML)
- Styles
- Templates
- Keyboard input events
- Creating User Controls
- Expression Blend For Silverlight Programmers
- Recreating ScottGu's tutorial on Blend as a video
I'll probably add some, and I can't guarantee the order, but that is the plan, at least right now. My hope is to turn these out quickly to provide a good base of material.
Thanks for your patience and support.
-jesse
I'm pleased to say that we've posted a new How Do I Video on Binding Data to Silverlight 2 Controls.
In the very beginning of this video I say it is about "wiring up events" but I'm confused and befuddled -- it is, of course, about binding UI objects to business objects. This is clarified quickly and there is no confusion once we get past the first few moments.
Update Saturday 4/19. This video was not encoded properly. We've re-encoded it, and I took the opportunity to fix the beginning of the video and eliminate the confusion. The video should be up and correct no later than Monday or Tuesday in all formats. I very much appreciate your patience.
Next