Chapter 10. System Integration and Device Support

Silverlight 4 adds a number of new features that allow developers to integrate their applications with a user's system. These features include notifications, interaction with legacy COM applications and libraries, access to a user's web camera and microphone, and better access to the operating system such as enabling the Silverlight application as a drop target. In this chapter we will discuss and try out a number of these new features.

Notification (Toast) API

A toast is a small informational window you can display to notify users of various events, typically in the bottom-right corner of the screen (on Windows) and the upper right corner (on Mac OS). Toast notifications have become very popular in software design, so support for them has been added in Silverlight 4. Let's take a look at toast notifications in Silverlight and run through an example.

Try It Out: Implementing Toast Notifications.

In this example, we will create an application with a single button; when the button is pressed, it will display a toast window for 5 seconds.

  1. Create a new Silverlight application in Visual Studio 2010. Name it NotifyApplication and allow Visual Studio to create an ASP.NET web application called NotifyApplication.Web to host your application.

  2. Add a Button to the LayoutRoot Grid, and set its Width to 200, Height to 30, and Content to "Display Notify Window." Right-click on event name in the XAML and select Navigate to Event Handler to wire up the click event to Button_Click.

    <Grid x:Name="LayoutRoot" Background="White">
        <Button
            Height="30"
            Width="200"
            Content="Display Notify Window"
            Click="Button_Click" />
    </Grid>
  3. Now we need to define the toast notification window. To do this, we'll create a new user control that will define the layout of the window. To start, add a new user control to the Silverlight project by right-clicking on the NotifyApplication project and selecting Add New Item. When the Add New Item dialog appears, select Silverlight User Control and name the user control NotifyWindow.xaml, as shown in Figure 10-1.

    Adding the NotifyWindow user control

    Figure 10.1. Adding the NotifyWindow user control

  4. After you create NotifyWindow, you can start defining the layout. First, set the Height of the user control to 75 and the Width to 300.

    <UserControl x:Class="NotifyApplication.NotifyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Height="75" Width="300">
    
        <Grid x:Name="LayoutRoot" Background="White">
    
        </Grid>
    </UserControl>
  5. Next, add a Border to the LayoutRoot Grid, set the Background to #DDDDDD, the BorderBrush to Black, and the BorderThickness to 2. Within the Border, we'll add another Grid control with three rows defined.

    <Grid x:Name="LayoutRoot" Background="White">
        <Border
            Background="#DDDDDD"
            BorderBrush="Black"
            BorderThickness="2">
    
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="0.113*"/>
                    <RowDefinition Height="0.306*"/>
                    <RowDefinition Height="0.582*"/>
                </Grid.RowDefinitions>
    
            </Grid>
        </Border>
    </Grid>
  6. Now we'll add four more controls to the nested grid—a Rectangle and a TextBlock, which are completely cosmetic, and two additional TextBlocks, one for the toast notification header and one for the description. Add these controls and set the properties as indicated in the following code. The end result should look like what you see in Figure 10-2.

    <Grid x:Name="LayoutRoot" Background="White">
        <Border
            Background="#DDDDDD"
            BorderBrush="Black"
            BorderThickness="2">
    <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="0.113*"/>
                    <RowDefinition Height="0.306*"/>
                    <RowDefinition Height="0.582*"/>
                </Grid.RowDefinitions>
    
                <Rectangle
                    Fill="#FF747474"
                    Stroke="White"
                    StrokeThickness="0"
                    Grid.ColumnSpan="2"/>
    
                <TextBlock
                    TextWrapping="Wrap"
                    Text="x"
                    HorizontalAlignment="Right"
                    Margin="0,0,5,0"
                    Grid.Row="1"
                    FontFamily="Verdana"
                    FontWeight="Bold"
                    FontSize="13"/>
    
                <TextBlock
                    Name="Header"
                    TextWrapping="Wrap"
                    Text="Header Text"
                    Grid.Row="1"
                    FontWeight="Bold"
                    VerticalAlignment="Bottom"
                    FontSize="13"
                    Margin="5,0,5,0"
                    FontFamily="Tahoma"/>
    
                <TextBlock
                    Name="Description"
                    TextWrapping="Wrap"
                    Text="Notification Text"
                    Grid.Row="2"
                    FontSize="11"
                    FontFamily="Verdana"
                    Margin="5,0,5,0"/>
            </Grid>
        </Border>
    </Grid>
    
    The finished NotifyWindow user control

    Figure 10.2. The finished NotifyWindow user control

  7. Now we can turn our attention to the MainPage.xaml.cs code behind file, in particular to the Button_Click delegate we wired up earlier. First we need to make certain that the application is running outside of the browser. We can do that by checking the Boolean property App.Current.IsRunningOutOfBrowser.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (App.Current.IsRunningOutOfBrowser)
        {
    
        }
    }
  8. Next we'll create an instance of the NotificationWindow class, then an instance of the NotifyWindow user control and set the Text of the Header and Description. To make sure the NotificationWindow is set to the correct size, we'll set the height and width equal to the NotifyWindow height and width. Finally, we'll set the content of the NotificationWindow to the instance of our NotifyWindow user control and execute the NotificationWindow's Show method, passing it 5000, which indicates the window should display for 5 seconds.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (App.Current.IsRunningOutOfBrowser)
        {
            NotificationWindow notify = new NotificationWindow();
    
            NotifyWindow win = new NotifyWindow();
            win.Header.Text = "Custom Message Header";
            win.Description.Text = "This is a custom description.";
    notify.Width = win.Width;
            notify.Height = win.Height;
    
            notify.Content = win;
            notify.Show(5000);
        }
    }
  9. We are now ready to test the application. However, since Silverlight notification works only with applications running outside of the browser, we must enable out-of-browser support for the application. To do this, open the project properties for the NotifyApplication Silverlight project and check the box next to "Enable running application out of the browser," as shown in Figure 10-3.

    Settings for the NotifyApplication project

    Figure 10.3. Settings for the NotifyApplication project

  10. Save the project settings and press F5 to test the application. When the application is started, right-click on it and install it locally. When the application reopens outside of the browser, click on the "Display Notify Window" button. If all goes well, you should see the notification window in the bottom right-hand corner of your screen, as shown in Figure 10-4.

    The running NotifyApplication application

    Figure 10.4. The running NotifyApplication application

Webcam/Microphone Access

Silverlight 4 supports capture from web cams and microphones installed on the local user's machine. In this section, we'll explore this new feature and walk through an example together.

CaptureDeviceConfiguration Class

The CaptureDeviceConfiguration class provides developers with some helper functionality related to web cam and microphone access, including the ability to list the different audio and video capture devices installed on the user's machine. In addition, it provides functions that verify and request permission to access the local devices. For example, if you'd like to request device access, you can call the RequestDeviceAccess method, or if you need to check whether access has already been granted, you can inspect the AllowedDeviceAccess property.

CaptureSource Class

The CaptureSource class provides audio and video capture functionality from a given capture device, including methods for starting and stopping capture as well as for taking static images from the captured video. In order to explore these methods, let's work through an example together.

Try It Out: Accessing a User's Web Camera and Microphone

In this example we'll access the web camera and microphone installed on the local user's machine, capture the source from those devices, and display it in our Silverlight application. Let's get started!

  1. Create a new Silverlight application in Visual Studio 2010. Name it CameraAccess and allow Visual Studio to create an ASP.NET web application called CameraAccess.Web to host your application.

  2. Define two columns and two rows as shown in the following code, and set the Background of the Grid to #333333.

    <Grid x:Name="LayoutRoot" Background="#333333">
    
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="240*" />
            <RowDefinition Height="60*" />
        </Grid.RowDefinitions>
    
    </Grid>
  3. Add a Rectangle to the first row that spans both columns. Next, add a Button with the Content "Start Capture" to the first column of the second row. Add another Button with the Content "Stop Capture" to the second column of the second row. Set the Margin for all three controls to 5 to provide some spacing between the controls. Also, set the Click event to StartCapture and StopCapture appropriately. The completed user interface should appear as shown in Figure 10-5.

    <Grid x:Name="LayoutRoot" Background="#333333">
    
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="240*" />
            <RowDefinition Height="60*" />
        </Grid.RowDefinitions>
    
        <Rectangle
            Name="CaptureDisplay"
            Fill="White"
            Grid.ColumnSpan="2"
            Margin="5" />
    <Button
            Click="StartCapture"
            Content="Start Capture"
            Grid.Row="1"
            Grid.Column="0"
            Margin="5" />
    
        <Button
            Click="StopCapture"
            Content="Stop Capture"
            Grid.Row="1"
            Grid.Column="1"
            Margin="5" />
    </Grid>
    The finished CameraAccess project UI

    Figure 10.5. The finished CameraAccess project UI

  4. Make certain the two click events have the delegates in the code behind file, MainPage.xaml.cs, as shown below. In addition, add a variable for CaptureSource named source.

    public partial class MainPage : UserControl
    {
        CaptureSource source;
    
        public MainPage()
        {
            InitializeComponent();
        }
    private void StartCapture(object sender, RoutedEventArgs e)
        {
    
        }
    
        private void StopCapture(object sender, RoutedEventArgs e)
        {
    
        }
    }
  5. Now we'll add the logic to the StartCapture delegate. First check to see if there is already a capture source established; if so, we need to stop it. Next, create a new instance of the CaptureSource, then set VideoCaptureDevice to the system default video capture device and AudioCaptureDevice to the default audio capture device.

    private void StartCapture(object sender, RoutedEventArgs e)
    {
        if (source != null)
        {
            source.Stop();
        }
    
        source = new CaptureSource();
        source.VideoCaptureDevice =
            CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
        source.AudioCaptureDevice =
            CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
    }
  6. With the capture devices set, we now need to create a VideoBrush, which will be used to fill our Rectangle control. Set the source of the VideoBrush to the capture source and set the Fill property to the VideoBrush.

    private void StartCapture(object sender, RoutedEventArgs e)
    {
        if (source != null)
        {
            source.Stop();
        }
    
        source = new CaptureSource();
        source.VideoCaptureDevice =
            CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
        source.AudioCaptureDevice =
            CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
    VideoBrush video = new VideoBrush();
        video.SetSource(source);
        CaptureDisplay.Fill = video;
    }
  7. Next we need to see if we have permission to access the client's capture device. If not, we'll try to request access. If we gain access, we'll start the capture source.

    private void StartCapture(object sender, RoutedEventArgs e)
    {
        if (source != null)
        {
            source.Stop();
        }
    
        source = new CaptureSource();
        source.VideoCaptureDevice =
            CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
        source.AudioCaptureDevice =
            CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
    
        VideoBrush video = new VideoBrush();
        video.SetSource(source);
        CaptureDisplay.Fill = video;
    
        if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
            CaptureDeviceConfiguration.RequestDeviceAccess())
        {
            source.Start();
        }
    }
  8. We now have logic to start the capture, but we need a way to stop it. This is much more straightforward. First, we check that a source exists; if it does, we then execute the Stop method.

    private void StopCapture(object sender, RoutedEventArgs e)
    {
        if (source != null)
        {
            source.Stop();
        }
    }
  9. Press F5 to start the application. When it is displayed as shown in Figure 10-6, press the Start Capture button.

    The web camera capture application

    Figure 10.6. The web camera capture application

  10. If access hasn't already been granted, you'll be prompted with the consent dialog as in Figure 10-7, asking for permission to access the camera and microphone.

    Consent dialog for camera and microphone access

    Figure 10.7. Consent dialog for camera and microphone access

  11. If all goes well, you should see video captured as shown in Figure 10-8. When you are ready to stop the capture, simply press the Stop Capture button.

    CameraAccess application capturing audio and video

    Figure 10.8. CameraAccess application capturing audio and video

Working with Captured Streams

If you're interested in actually working with the raw audio and video stream, or if you'd like to save the stream data, you'll need to create a class that will implement the AudioSink and VideoSink classes. When implementing these classes, you'll need to provide overrides to a number of callbacks, including: OnCaptureStarted, OnCaptureStopped, OnFormatChange, and OnSample. The specifics of working with the raw streams from an audio or video capture are outside the scope of this book. For more information on this topic, I recommend Pro Silverlight 4 in C# by Matthew MacDonald (Apress, 2010).

COM Interoperability

When building a rich client application, there may be times you'd like to leverage existing applications and libraries installed on the local user's machine. In Silverlight 4, you now have access to COM objects installed locally. For the most part, this is exposed through a new class called AutomationFactory, which allows you to perform a CreateObject that activates and returns a reference to a registered COM object. Let's run through an example together.

Try It Out: Executing an EXE

In this example, we'll execute an executable with Silverlight using the new COM interoperability feature. To illustrate, we will open the Notepad application.

  1. Create a new Silverlight application in Visual Studio 2010. Name it InvokeNotepad and allow Visual Studio to create an ASP.NET web application called InvokeNotepad.Web to host your application.

  2. When the project is created, you should be looking at the MainPage.xaml file. Within the LayoutRoot Grid of the Silverlight page, add a Button and set its attributes as follows, wiring up the Click event as well.

    <Grid x:Name="LayoutRoot" Background="White">
        <Button
            Content="Invoke Notepad"
            Width="100"
            Height="30"
            Name="button1"
            Click="button1_Click" />
    </Grid>
  3. In the code behind, MainPage.xaml.cs, first check that the application has elevated permissions. Next, add a using statement at the top of the code to System.Runtime.InteropServices.Automation. Now we'll create an instance of the AutomationFactory class and the WScript.Shell COM object. We can then execute the Run command and pass it the location for Notepad.exe.

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        if (App.Current.HasElevatedPermissions)
        {
            using (dynamic cmd = AutomationFactory.CreateObject("WScript.Shell"))
            {
                cmd.Run(@"C:WINDOWSNOTEPAD.EXE", 1, true);
            }
        }
    }
  4. Since the COM interoperability feature requires out-of-browser elevated permissions, open the properties of our Silverlight project, InvokeNotepad. Within the project properties, check the box next to "Enable running application out of the browser," as shown in Figure 10-9.

    Project properties for InvokeNotepad

    Figure 10.9. Project properties for InvokeNotepad

  5. Click the "Out-of-Browser Settings" button and at the bottom of the dialog, check the box next to "Require elevated trust when running outside the browser" as shown in Figure 10-10.

    Out-of-browser settings for the InvokeNotepad project

    Figure 10.10. Out-of-browser settings for the InvokeNotepad project

  6. Press F5 to test the application. When the application is displayed, click on the "Invoke Notepad" button. You'll notice that nothing happens. This is because the COM interoperability features require elevated out-of-browser execution and we are checking the HasElevatedPermissions property to avoid getting a runtime error from performing the illegal operation.

  7. Right-click within the running application and select "Install InvokeNotepad Application onto this computer" as shown in Figure 10-11.

    Installing InvokeNotepad application onto the computer

    Figure 10.11. Installing InvokeNotepad application onto the computer

  8. When prompted, click Install and you'll see the application open outside of the browser. Once again, click on the "Invoke Notepad" button. If all goes well, you should see Notepad open, as shown in Figure 10-12.

    Opening Notepad from Silverlight

    Figure 10.12. Opening Notepad from Silverlight

Dropping Files on a Silverlight Application

A feature that users have grown very accustomed to in desktop applications is the ability to drag files directly onto the application and have some action take place. With web applications, in contrast, you've always been restricted to browse functionality, which is not efficient or intuitive to users. Silverlight 4, however, supports the ability to drag files directly onto a Silverlight application. Currently this is limited to files, but you can see from the API that future releases of Silverlight will most likely support additional types.

To enable drop functionality in your application, you need only enable one property on your UI element, AllowDrop. If you set AllowDrop to true on your element, your application can immediately access the drop target functionality.

Once AllowDrop has been set, you have a number of events that will allow you to work with file(s) dropped onto your application. The DragEnter, DragLeave and DragOver events are fired during the drag process, but the primary event we are concerned with is the Drop event that occurs when a file is actually dropped onto our control.

Drop Event

The Drop event occurs when a file is dropped onto a control with AllowDrop enabled. This is the primary event that is used in enabling Silverlight as a drop target. From within the Drop event you can get information about the file list from the DropEventArgs.Data property.

Let's get a closer look at enabling file drop in a Silverlight application by running through an example.

Try It Out: Enabling an Application As a Drop Target

In this example, we'll create a simple Silverlight application that contains a TextBlock. We will then enable the application as a drop target and when a file is dropped, if it is a text document with extension *.txt, we will display the contents of the file in the TextBlock control. Let's get started.

  1. Create a new Silverlight application in Visual Studio 2010. Name it SilverlightDropTarget and allow Visual Studio to create an ASP.NET web application called SilverlightDropTarget.Web to host your application.

  2. When the project is created, you should be looking at the MainPage.xaml file. First, set the AllowDrop property for the Grid to True. Then, within the LayoutRoot Grid of the Silverlight page, add a TextBlock named FileContents, and set the Margin to 10 and the TextWrapping property to Wrap.

    <Grid AllowDrop="True" x:Name="LayoutRoot" Background="White">
        <TextBlock
            Margin="10"
            TextWrapping="Wrap"
            Name="FileContents" />
    </Grid>
  3. Now turn your attention to the code behind, MainPage.xaml.cs. First add a using reference to the System.IO namespace, then wire up the Drop event in the constructor.

    public MainPage()
    {
        InitializeComponent();
        LayoutRoot.Drop += new DragEventHandler(LayoutRoot_Drop);
    }
  4. Next, in the LayoutRoot_Drop event, we'll start to work with the dropped file(s). First, we'll make certain that the data dropped on the application is not null. Next, we'll create an IDataObject set to the DragEventArgs.Data property passed into the event. We can then get the collection of files dropped using the GetData method.

    void LayoutRoot_Drop(object sender, DragEventArgs e)
    {
        if (e.Data != null)
        {
            IDataObject obj = e.Data;
            FileInfo[] files = obj.GetData(DataFormats.FileDrop) as FileInfo[];
    
        }
    }
  5. Once we have a collection of files, we can create a foreach statement to step through each file within our collection. For each instance, we will inspect the file extension. If the extension is .txt, we will open the file using a StreamReader and read the contents of the file. Once we have read the contents, we will set that to the Text property of the FileContents TextBlock.

    void LayoutRoot_Drop(object sender, DragEventArgs e)
    {
        if (e.Data != null)
        {
            IDataObject obj = e.Data;
            FileInfo[] files = obj.GetData(DataFormats.FileDrop) as FileInfo[];
            foreach (FileInfo file in files)
            {
                if (file.Extension.Equals(".txt"))
                {
                    using (Stream stream = file.OpenRead())
                    {
                        using (StreamReader reader = new StreamReader(stream))
                        {
                            FileContents.Text = reader.ReadToEnd();
                        }
                    }
                }
            }
        }
    }
  6. Now we'll test the application, but first let's inspect the file we are going to drop onto our application. The filename is DropMe.txt and it contains the text shown in Figure 10-13.

    Contents of the DropMe.txt file

    Figure 10.13. Contents of the DropMe.txt file

  7. Press F5 to start the application. Once the application is open, drag DropMe.txt onto the application. You should see the cursor displayed as shown in Figure 10-14.

    Dragging DropMe.txt onto the Silverlight application

    Figure 10.14. Dragging DropMe.txt onto the Silverlight application

  8. After you drop the file, if all goes well you should see the contents of the file displayed in the TextBlock as shown in Figure 10-15.

    File successfully dropped onto the Silverlight application

    Figure 10.15. File successfully dropped onto the Silverlight application

Summary

In this chapter, we looked at a number of new features in Silverlight that enable developers to gain access to user machine devices and operating system functionality. These features let developers make their Silverlight applications very rich and user-friendly. In the next chapter, we will start to look at Expression Blend, a new tool for designing XAML-based applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset