Wednesday, June 30, 2010

Silverlight 4.0 Tutorial (2 of N)

Read Part 1 here

Continuing our RegistrationBooth Application, in the previous post we ended having a data grid that displays all the attendees, let’s now implement the interface for registering a new attendee

- We will create a user control for registering a new attendee, right click the Views folder in the RegistrationBooth project, select add new item, choose Silverlight User Control and name it RegisterAttendee.

- Use the VS designer to add two columns for the LayoutRoot grid, one for the labels of the controls and the other for the controls, create also five rows for the different properties.

- Add the four labels and four text boxes to the first four rows of the grid, these controls will be used for the FirstName, LastName, Email and company properties.

- The fifth row will be for the photo, will use the Silverlight 4.0 new feature that allows us to access the webcam, so add a “Photo” label in the first column of this row, and in the second column add a Grid with three columns, put a rectangle in the first column, the second column should contain a ToggleButton (Start) and Capture button, and put image control in the third column.

- What we will do is that:

1- When the user clicks the Start button, we will start displaying the video stream coming from the default web cam attached and display this video stream in the left rectangle.

2- When the users clicks the Capture button we will take a snapshot of the web cam video stream and display it in the image control to use it as a picture of the attendee

- The final UI should look like the following

Register Attendee UI

- Add events handlers for the Start and Capture buttons click events

- Modify the code to be as follows

  1. private CaptureSource cs = null;
  2. private void btnStart_Click(object sender, RoutedEventArgs e)
  3. {
  4. if (btnStart.IsChecked.Value)
  5. {
  6. StartWebcam();
  7. btnStart.Content = "Stop";
  8. }
  9. else
  10. {
  11. StopWebcam();
  12. btnStart.Content = "Start";
  13. }
  14. }
  15. private void btnCapture_Click(object sender, RoutedEventArgs e)
  16. {
  17. cs.CaptureImageAsync();
  18. }
  19. private void StartWebcam()
  20. {
  21. if (CaptureDeviceConfiguration.AllowedDeviceAccess
  22. CaptureDeviceConfiguration.RequestDeviceAccess())
  23. {
  24. VideoCaptureDevice vcd = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
  25. if (null != vcd)
  26. {
  27. cs = new CaptureSource();
  28. cs.VideoCaptureDevice = vcd;
  29. cs.Start();
  30. VideoBrush videoBrush = new VideoBrush();
  31. videoBrush.Stretch = Stretch.UniformToFill;
  32. videoBrush.SetSource(cs);
  33. videoStream.Fill = videoBrush;
  34. cs.CaptureImageCompleted += new EventHandler<CaptureImageCompletedEventArgs>(cs_CaptureImageCompleted);
  35. }
  36. else
  37. {
  38. MessageBox.Show("Error initializing Webcam");
  39. }
  40. }
  41. }
  42. private void StopWebcam()
  43. {
  44. if (null != cs)
  45. {
  46. cs.Stop();
  47. }
  48. }
  49. void cs_CaptureImageCompleted(object sender, CaptureImageCompletedEventArgs e)
  50. {
  51. capturedImage.Source = e.Result;
  52. }

- The code is pretty easy,first we request access to the device this will prompt the user requesting his approval for the webcam, after that we initialize a new CaptureSource object and set its VideoCaptureDevice to the default device, then we create a new VideoBrush that uses the capture source we created, then we use this brush to paint the rectangle, we also attach an event handler to the CaptureImageCompleted event so that we set the our image control Source to the image that we will capture (when we click the capture button).

- Include the user control in the Home.xaml page, put it below the data grid.

- Build and run the application, you will be able to start/stop the web cam and capture an image to be displayed in the image control.

Capturing Webcam

- Let’s start writing the code that will actually saves the attendee to the database, add the following code to the RegisterAttendee user control, this code define some properties that retrieves the values of the controls on the user control.

  1. public string FirstName { get { return txtFirstName.Text; } }
  2. public string LastName { get { return txtLastName.Text; } }
  3. public string Company { get { return txtCompany.Text; } }
  4. public string Email { get { return txtEmail.Text; } }
  5. public byte[] Photo
  6. {
  7. get
  8. {
  9. if (capturedImage.Source == null)
  10. return null;
  11. var bitmap = capturedImage.Source as WriteableBitmap;
  12. var img = bitmap.ToImage();
  13. byte[] ba;
  14. var encoder = new PngEncoder();
  15. MemoryStream stream = new MemoryStream();
  16. encoder.Encode(img, stream);
  17. ba = stream.ToArray();
  18. stream.Close();
  19. return ba;
  20. }
  21. }

- the Photo property make use of the ImageTools for Silverlight library, so make sure to download these dlls and reference them in the project.

- Back to the Home.xaml page, add a button Save below the user control.

- Double click the Save button and add the following code to the Click event handler

  1. private void btnSave_Click(object sender, System.Windows.RoutedEventArgs e)
  2. {
  3. Attendee newAttendee = new Attendee();
  4. newAttendee.FirstName = ucNewAttendee.FirstName;
  5. newAttendee.LastName = ucNewAttendee.LastName;
  6. newAttendee.Company = ucNewAttendee.Company;
  7. newAttendee.Email = ucNewAttendee.Email;
  8. newAttendee.Photo = ucNewAttendee.Photo;
  9. newAttendee.CreationDate = DateTime.Now;
  10. (attendeeDomainDataSource.Data as DomainDataSourceView).Add(newAttendee);
  11. attendeeDomainDataSource.SubmitChanges();
  12. }

- Build and run the application, try to register a new attendee with a photo, click the Save button, the attendee should be saved and added to the data grid.

- Another thing we need to do is to print a name tag for the newly registered attendee, we will use the new Silverlight 4.0 printing features.

- What we have to do is to specify the UI that will be printed, here we will add a new user control to the Views folder, will call it PrintNameTag, in this user control we will display the Name of the user, the company and a barcode that we will be generated based on the attendee id. the markup of the user control is shown below

  1. <Grid x:Name="LayoutRoot" Background="White">
  2. <Grid.RowDefinitions>
  3. <RowDefinition Height="60*" />
  4. <RowDefinition Height="60*" />
  5. <RowDefinition Height="120*" />
  6. </Grid.RowDefinitions>
  7. <StackPanel Grid.Row="0" Orientation="Horizontal" d:LayoutOverrides="Height">
  8. <TextBlock FontSize="24" Text="{Binding Name}" FontWeight="Bold" />
  9. </StackPanel>
  10. <TextBlock Grid.Row="1" FontSize="24" TextAlignment="Center" Text="{Binding Company}" FontWeight="Bold"/>
  11. <TextBlock Grid.Row="2" Text="{Binding Barcode}" FontSize="96" FontFamily="Free 3 of 9" TextAlignment="Center"/>
  12. </Grid>

- As you can see the TextBlocks are bounded to the attendee’s different properties, the barcode is generated by using the font “Free 3 of 9” which can be downloaded for free, this font will automatically render the barcode stripes, you better use Expression Blend to change the TextBlock font family to use “Free 3 of 9”, this way Blend will include the necessary build actions that pack the font in the xap file.

- We are now done with the UI that will be printed, to invoke the actual printing, go back to Home.xaml, change the btnSave_Click event handler as follows

  1. Attendee newAttendee;
  2. private void btnSave_Click(object sender, System.Windows.RoutedEventArgs e)
  3. {
  4. newAttendee = new Attendee();
  5. newAttendee.FirstName = ucNewAttendee.FirstName;
  6. newAttendee.LastName = ucNewAttendee.LastName;
  7. newAttendee.Company = ucNewAttendee.Company;
  8. newAttendee.Email = ucNewAttendee.Email;
  9. newAttendee.Photo = ucNewAttendee.Photo;
  10. newAttendee.CreationDate = DateTime.Now;
  11. (attendeeDomainDataSource.Data as DomainDataSourceView).Add(newAttendee);
  12. attendeeDomainDataSource.SubmitChanges();
  13. PrintDocument document = new PrintDocument();
  14. document.PrintPage += new System.EventHandler<PrintPageEventArgs>(document_PrintPage);
  15. document.Print("Attendee_" + newAttendee.Id);
  16. }
  17. void document_PrintPage(object sender, PrintPageEventArgs e)
  18. {
  19. PrintDocument document = sender as PrintDocument;
  20. e.PageVisual = new PrintNameTag() {
  21. DataContext = new {
  22. Name =newAttendee.FirstName+" "+newAttendee.LastName,
  23. Company=newAttendee.Company,
  24. Barcode = "ATT" + newAttendee.Id.ToString("00000")
  25. }
  26. };
  27. }

- First we create an instance of the PrintDocument class which represents a document to be sent to the printer, we attach a handler to the event PrintPage.

- The PrintPage is called for each page that is going to be printed, in the handler we create an instance of the user control (which is the UI that will be printed) and set its DataContext to an anonymous object that contains the needed properties (refer to this post to see how to enable binding to anonymous types in Silverlight), we assign the created user control to the property PageVisual to indicate that this user control will be the source of the print UI.

- Build and run the application, register a new attendee, you should get the print dialog to choose the printer, you can use the Microsoft XPS Document Writer for testing

The name tag

in the next post we will continue building our application

Download the application from here

2 comments:

Anonymous said...

Great work. God bless you sir.

vishal gupta said...

great code but as i tried to do it myself an exception occur as i wanted to save the image.
error was due to domaincontext.
Please help me its very important to me.
My Email Id is: er.vishal_gupta@hotmail.com

other one is:
yourvishal.gupta@gmail.com