0

Silverlight 4 – Accessing System Devices with Com Interop, such as a Scanner.

Posted by Brian Lagunas on Feb 20, 2010 in - Dotnet, - Silver Light  | View Original Article
 

Yesterday I gave a presentation on some new features in Silverlight 4.  During this presentation I mentioned the newly available ability to interoperate with Office applications such as Outlook and Excel though the new ComAutomationFactory that is in Silverlight 4.  Someone in the audience asked the question, “Can I access my scanner”, and of course I said yes; then he said, “how?”, and I said, “I will get back to you”.  Well here I am, with code!

A couple of things to note; this feature requires your application to run as an Out Of Browser (OOB) with elevated permissions. Also, there is no IntelliSense for your COM objects. So make sure you have the documentation to the API you are trying to use. So lets get started.

Creating an OOB Application

First thing you need to do is crack open VS 2010 Beta 2 and create a new Silverlight project. Make sure you are targeting the .NET Framework 4 it is a Silverlight 4 application.

image

For this test project we don’t need to host this in a website.

image

The next thing you need to do is right-click on the project and choose “Properties”.  Check the “Enable Running Application out of browser”

image

Now click the Out of Browser Settings button and set “Require Elevated Trust when running outside the browser.”

image

Next add a button to the application to install our OOB application.

<Button x:Name="btnInstall" Content="Install Me" Click="btnInstall_Click" />

if (Application.Current.InstallState == InstallState.NotInstalled)
               Application.Current.Install();

Now run the application and install it by clicking the install button you just created.  When you start the installation, you will be prompted to install the app, and whether or not you want to create some shortcuts. Just say yes, we trust ourselves, sort of.

image

The next thing we want to do is enable debugging our OOB application.  So right click your project and chose properties –> Debug –> Installed out of browser application –> YourApplication

image

The final step is to add a reference to Microsoft.CSharp.dll so we can use the dynamic keyword.  Look for it in C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Libraries\Client\.  Now on to fun stuff.

Send an Email with Outlook

First create a form that will take your user input for the “To” and “Message” data, and a button to send the message.

<StackPanel>
    <StackPanel Margin="5">
        <TextBlock Text="To:" />
        <TextBox x:Name="txtTo" />
    </StackPanel>
    <StackPanel Margin="5">
        <TextBlock Text="Message" />
        <TextBox x:Name="txtMessage" Height="200" />
    </StackPanel>            
    <Button x:Name="btnSend" Content="Send Message" Click="btnSend_Click" />
</StackPanel>

Then handle the button click event as follows.

private void btnSend_Click(object sender, RoutedEventArgs e)
{
    using (dynamic outlook = ComAutomationFactory.CreateObject("Outlook.Application"))
    {
        dynamic mail = outlook.CreateItem(0);            
        mail.To = txtTo.Text;
        mail.Subject = "Hello, from Silverlight";
        mail.HTMLBody = txtMessage.Text;
        mail.Display();
    }
}

And you done.  Of course this will only display the message, but just call mail.Send() to actually send it; one thing to mention is to make sure you have Outlook open when you hit send or bad things will happen.

Send Data to Excel, edit it, and update Silverlight

Now this little trick is cool. We will have a data source, send it to excel for display and editing, then send the updated data back to our Silverlight application and update the UI.  Now this is a poor mans implementation for demo reasons.

First lets create our UI and populate it with data.  This is what mine looks like.

image

Lets code up the Launch Excel button.

bool firstTime = true;
private void LaunchExcel(object sender, RoutedEventArgs e)
{
   // create an instance of excel
   dynamic excel = ComAutomationFactory.CreateObject("Excel.Application");
   excel.Visible = true;  // make it visible to the user.
 
   // add a workbook to the instance 
   dynamic workbook = excel.workbooks;
   workbook.Add();
 
   dynamic sheet = excel.ActiveSheet; // get the active sheet
 
   dynamic cell = null;
 
   int i = 1;
 
   // iterate through our data source and populate the excel spreadsheet
   foreach (Entity item in CustomerList.ItemsSource)
   {
       cell = sheet.Cells[i, 1]; // row, column
       cell.Value = item.CustomerName;
       cell.ColumnWidth = 25;
 
       cell = sheet.Cells[i, 2];
       cell.Value = item.UnitSales;
       i++;
   }
 
   // add a chart
   dynamic sheetShapes = sheet.Shapes;
   sheetShapes.AddChart(-4100, 200, 2, 400, 300);
 
   // wire up an event handler to the Excel SheetChanged event
   if (firstTime)
   {
       excel.SheetChange += new SheetChangedDelegate(SheetChangedEventHandler);
       string sheetName = sheet.Name;
 
       firstTime = false;
   }
 
   ExcelButton.IsEnabled = true;
}

As you can see we are hooking into the Sheet changed event so we can respond to when the data is update in excel. So here is the code for that.

delegate void SheetChangedDelegate(dynamic excelSheet, dynamic rangeArgs);
 
// event handler for the sheet changed event
// looks at the data and creates a new items source to rebind to the datagrid
private void SheetChangedEventHandler(dynamic excelSheet, dynamic rangeArgs)
{
    dynamic sheet = excelSheet;
 
    string sheetName = sheet.Name;
 
    dynamic range = rangeArgs;
 
    dynamic rowValue = range.Row;
 
    Entity[] entities = CustomerList.ItemsSource as Entity[];
    Entity[] newEntities = new Entity[10];
 
    dynamic col2range = sheet.Range("B1:B10");
 
    for (int i = 0; i < 10; i++)
    {
        Entity newEntity = new Entity();
        newEntity.CustomerName = entities[i].CustomerName;
        newEntity.PhoneNumber = entities[i].PhoneNumber;
 
        dynamic item = col2range.Item(i + 1);
        newEntity.UnitSales = Convert.ToInt32(item.Value);
 
        newEntities[i] = newEntity;
    }
 
    CustomerList.ItemsSource = newEntities;
    CustomerList.SelectedIndex = Convert.ToInt32(rowValue) - 1;
 
    UpdateNotification.Text = "Data updated from Excel spreadsheet";
}

Now, I will click the Launch Excel button, and edit my data.

image

Now if I look back in my Silverlight application, I will now see that all of my data in the grid has been updated.

image

Lets Open a Program

Using the WScript.Shell API we can execute any command and open any program.  In this example lets open Notepad and write some text to it.  So first I am going to create a simple UI to allow a user to enter some text, then click a button to send it to Notepad.

<StackPanel>
   <TextBlock Text="Enter text to send to NotePad." />
   <TextBox x:Name="txtTextToSend" />
   <Button x:Name="txtOpenProgram" Content="Open NotePad" Click="txtOpenProgram_Click"  />
</StackPanel>

private void txtOpenProgram_Click(object sender, RoutedEventArgs e)
{
    using (dynamic shell = ComAutomationFactory.CreateObject("WScript.Shell"))
    {
        shell.Run(@"C:\windows\notepad.exe"); //you can open anything
        shell.SendKeys(txtTextToSend.Text);
    }
}

Now lets enter some text and click that button.

image

image

Now that’s pretty cool. I am sure your mind is thinking of all the cool stuff you can do with this.

Well, this stuff is cool and all, but where is the really cool stuff? Wait no more!

Text to Speech

That’s right, I said it.  I am going to use the SAPI.SpVoice API to tap into the power of text to speech. So lets build a UI that will allow a user to enter some text. Heck lets let them control the rate and pitch of the speech.

<StackPanel>
    <TextBlock Text="Enter text to say: " />
    <TextBox x:Name="txtTextToSay" Margin="0,0,0,20" />
    <TextBlock Text="Pitch" />
    <Slider x:Name="sldrPitch" Minimum="-10" Maximum="10" Value="0" />
    <TextBlock Text="Rate" />
    <Slider x:Name="sldrRate" Minimum="-10" Maximum="10" Value="0" />
    <Button x:Name="btnSpeak" Content="Speak" Click="btnSpeak_Click" Margin="20" />
</StackPanel>

This should look something like this.

image

Lets hook up our button’s click event and make some noise.

private void btnSpeak_Click(object sender, RoutedEventArgs e)
{
   using (dynamic ISpeechVoice = ComAutomationFactory.CreateObject("SAPI.SpVoice"))
   {
       ISpeechVoice.Volume = 100;
       ISpeechVoice.Speak(string.Format("<rate speed=\"{0}\"><pitch middle=\"{1}\">{2}", Math.Round(sldrRate.Value), Math.Round(sldrPitch.Value), txtTextToSay.Text));
   }
}

If you wanted to you could create a volume control for it as well.

What? This still isn’t cool enough for you? Well I think I can satisfy your need for coolness. Enter:

Acquire an Image from your Scanner/Camera

That’s right I said it.  You can access your scanner, scan an image, and then save it to your hard drive.  Here we are using the WIA (Windows Image Acquisition). “WIA is a full-featured image manipulation component that provides end-to-end image processing capabilities.  The WIA Automation Layer makes it easy to acquire images on digital cameras, scanners, or Web cameras, and to rotate, scale, and annotate your image files.” So, enough with the talking, lets get coding.

First all I need to do it create a button that will initiate the process.

<Button x:Name="btnAquireImage" Content="Aquire Image from Scanner/Camera" Click="btnAquireImage_Click" />

Now we need to handle the button’s click event and do all the complicated code.

private void btnAquireImage_Click(object sender, RoutedEventArgs e)
{
   using (dynamic CommonDialog = ComAutomationFactory.CreateObject("WIA.CommonDialog"))
   {
       dynamic imageFile = CommonDialog.ShowAcquireImage();
       if (imageFile != null)
       {
           string filePath = string.Format("D:\\{0}.jpg", Guid.NewGuid());
           imageFile.SaveFile(filePath);
           MessageBox.Show(string.Format("Saved {0}", filePath));
       }
   }
}

So what does this actually do?  Well lets push the button and find out:

image

Well the first thing it does is asks me which device I want to get my images from.  Lets pick my scanner.

image

Well, holly crap, that’s my scanner. And I can preview and crop my image before I even get the image. Yes, that is my baby picture, it was the only thing I could find OKAY!  All my current pictures are digital.  Anyways, so once I like my settings I click scan, the scanner will do its thing and my image will be saved.

image

image

image

And there it is right where I told it to save.  How fun is that? I did you a favor and already coded all this up and packed it up in a nice little zip file.  So download the code, play with it, and write some kick ass applications!

I almost forgot to mention, when you open the source code, go into Properties –> Debug –> and select “Dynamically create the page”, then run the application, install it, and go back and change the option back to “Installed out of browser application ”.

Tags: , , , , , ,

 
0

Don’t install Visual Studio 2010 RC just yet

Posted by Brian Lagunas on Feb 10, 2010 in - Dotnet  | View Original Article
 

If you haven’t heard already, Microsoft announced that Visual Studio 2010 Release Candidate was available for MSDN subscribers and would be probably available on 10 February 2010 for the public, which is today.  But before you get all excited and start installing the latest and greatest as soon as possible, you might want to slow down and reconsider; at least if you are a Silverlight developer.

Here are my two reasons for waiting:

  • At this time, VS2010 RC does not support developing Silverlight 4 applications.  This means that at this time there is no update for Silverlight 4 runtime/tools or the WCF RIA Services or other companion frameworks (toolkit controls, etc.).  This will not be enabled until the next public build of Silverlight 4 and companion frameworks.
  • Visual Studio 2010 Beta 2 and Visual Studio RC does NOT install side-by-side. You must first uninstall VS Beta 2 then install VS2010 RC.

So, if you want to keep developing on Silverlight 4, stick with the VS Beta 2 for now. At least until the next public build Silverlight 4 is released.

Tags: , , ,

 
0

Silverlight 4 Beta – Using Silverlight as Drop Target

Posted by Brian Lagunas on Nov 23, 2009 in - Dotnet  | View Original Article
 

Have you ever wanted to give the user the ability to drag a file from you desktop or file explorer onto your Silverlight application? Well, up until now you couldn’t. What’s that sound? It’s a bird… it’s a plane… no, it’s Silverlight 4 Beta.  Yes, Silverlight 4 Beta is coming to the rescue and giving you the ability to have your Silverlight application act as a drop target. And it is so easy to do. Observe.

First just identify the UI element you want to use as the drop target, then set the AllowDrop property to true. Then just handle the Drop events.

Here is a quick example. This examples allows a user to drag a bitmap from their file system into my application and displays the images in a ScrollViewer. Here is the XAML

<StackPanel x:Name="LayoutRoot" Background="White">

    <TextBlock Text="Drop image below." FontSize="16" FontWeight="BOld" HorizontalAlignment="Center" />

    <ScrollViewer x:Name="ImagesTarget" Background="White" Width="800" MinHeight="300" 

                  VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto" AllowDrop="True">

            <ItemsControl x:Name="ImageList">

                <ItemsControl.ItemTemplate>

                    <DataTemplate>

                        <Image Source="{Binding}" Margin="5" Stretch="UniformToFill" Height="240" />

                    </DataTemplate>

                </ItemsControl.ItemTemplate>

                <ItemsControl.ItemsPanel>

                    <ItemsPanelTemplate>

                        <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"/>

                    </ItemsPanelTemplate>

                </ItemsControl.ItemsPanel>

            </ItemsControl>

        </ScrollViewer>

</StackPanel>

I create a ObservableCollection of bitmap images and use this as the ItemsSource for the ScrollViewer.

ObservableCollection<BitmapImage> _images = new ObservableCollection<BitmapImage>();

I hook into the Drop event in the load event of the form.

ImagesTarget.Drop += new DragEventHandler(ImagesTarget_Drop);

Now lets implement the Drop event to loop through all the files and add them to the list.

void ImagesTarget_Drop(object sender, DragEventArgs e)

{

    if (e.Data == null)

        return;

 

    IDataObject dataObject = e.Data as IDataObject;

    FileInfo[] files =

      dataObject.GetData(DataFormats.FileDrop) as FileInfo[];

 

    foreach (FileInfo file in files)

    {

        try

        {

            using (var stream = file.OpenRead())

            {

                var imageSource = new BitmapImage();

                imageSource.SetSource(stream);

                _images.Add(imageSource);

            }

        }

        catch (Exception)

        {

            MessageBox.Show("Not a suppoted image.");

        } 

    } 

}

Bingo… Bango… Boom, that is all there is to it. And of course, you can handle the other drag events to have more control over what is being dropped and where.

Download the Source

Tags: , ,

 
0

Silverlight 4 Beta – Accessing a users webcam and microphone

Posted by Brian Lagunas on Nov 23, 2009 in - Dotnet  | View Original Article
 

So, by now you should have heard that Silverlight 4 Beta was released at PDC 2009. One of the most interesting and fun features included in the release is the ability for you to access a users webcam and microphone. So I am going to show you have to access a list of webcams and microphones, capture video, and then take pictures and show them to the user.  But, before I can show you how to use them, you have to have the Silverlight 4 Beta framework installed.

Start here:

You can either download the source and start playing around immediately, or you can read through the post and then download the source.

First off lets start out with our view box (this spot is where our video will be shown). First create a rectangle and give it a meaningful name. I called mine ViewBox. Make it the size of the video you want to show, as you can see mine is 640×480.

<Rectangle x:Name="ViewBox" Width="640" Height="480" Fill="White"/>

Now lets create some combo boxes to show the available webcams and microphones on the users machine. I created a item template and used the FriendlyName as the binding text. The FriendlyName is the name/description of the device.

<TextBlock Margin="5" HorizontalAlignment="Center" Text="WebCam" Grid.Column="0" Grid.Row="0" />

<ComboBox x:Name="WebCamList" Grid.Row="1" Grid.Column="0">

    <ComboBox.ItemTemplate>

        <DataTemplate>

            <TextBlock Text="{Binding FriendlyName}" />

        </DataTemplate>

    </ComboBox.ItemTemplate>

</ComboBox>

 

<TextBlock Margin="5" HorizontalAlignment="Center" Text="Microphone" Grid.Column="1" Grid.Row="0" />

<ComboBox x:Name="MicrophoneList" Grid.Row="1" Grid.Column="1">

    <ComboBox.ItemTemplate>

        <DataTemplate>

            <TextBlock Text="{Binding FriendlyName}" />

        </DataTemplate>

    </ComboBox.ItemTemplate>

</ComboBox>

Now lets create some buttons to start capturing our video, stop capturing the video, and one to take some pictures.

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

   <Button Margin="5" x:Name="btnStartCapture" Content="Start Capture" Click="btnStartCapture_Click" />

   <Button Margin="5" x:Name="btnStopCapture" Content="Stop Capture" Click="btnStopCapture_Click" />

   <Button Margin="5" x:Name="btnTakePicture" Content="Take Picture" Click="btnTakePicture_Click" />

</StackPanel>

Next, lets create a scroll viewer to hold a collection of pictures we take.

<ScrollViewer Width="800" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto">

   <ItemsControl x:Name="Pictures">

       <ItemsControl.ItemTemplate>

           <DataTemplate>

               <Image Source="{Binding}" Margin="5" Stretch="UniformToFill" Height="240" />

           </DataTemplate>

       </ItemsControl.ItemTemplate>

       <ItemsControl.ItemsPanel>

           <ItemsPanelTemplate>

               <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"/>

           </ItemsPanelTemplate>

       </ItemsControl.ItemsPanel>

   </ItemsControl>

</ScrollViewer>

Now that we have our UI finished up we need to start adding some code.  The first thing we need to do is create some private members.  Once will be our capture source and the other will be a collection to hold our pictures. I am using an ObservableCollection so I don’t have to worry about manually update the UI when items are added to it.

CaptureSource _captureSource;

ObservableCollection<WriteableBitmap> _pictures = new ObservableCollection<WriteableBitmap>();

Now that we have our private members, lets hook into the form’s loaded event and add some code. The first thing we need to do is create a new instance of our capture source. Next, we get a list of all our available webcams and microphones and add them to our combo boxes accordingly. Last we set the Pictures ScrollViewer items source to our private member _pictures. You should have something like the following.

void MainPage_Loaded(object sender, RoutedEventArgs e)

{

    // creating a new capture source

    _captureSource = new CaptureSource();

 

    // get list of the web cams

    WebCamList.ItemsSource = CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices();

    WebCamList.SelectedIndex = 0;

 

    // get list of microphones

    MicrophoneList.ItemsSource = CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices();

    MicrophoneList.SelectedIndex = 0;

 

    Pictures.ItemsSource = _pictures;

}

 

Now, lets handle the click event for our start capturing button. First thing we hav to do is stop and device that may be capturing. Now, we need to set our capture source’s video and audio capture instances from the available options in our combo boxes. We need to create a VisualBrush, this is what will be the video. We create a new instance of the VisualBrush and set the source to our captureSource. Next, we set the Fill of our rectangle to the VisualBrush.  now, the magic does happen until the next part. You need to request permission from the user to use their webcam. Once you have it you just start capturing.

private void btnStartCapture_Click(object sender, RoutedEventArgs e)

{

    if (_captureSource != null)

    {

        // stop whatever device may be capturing

        _captureSource.Stop();

 

        _captureSource.VideoCaptureDevice = (VideoCaptureDevice)WebCamList.SelectedItem;

        _captureSource.AudioCaptureDevice = (AudioCaptureDevice)MicrophoneList.SelectedItem;

 

        VideoBrush vidBrush = new VideoBrush();

        vidBrush.SetSource(_captureSource);

        ViewBox.Fill = vidBrush;

 

        // request access to webcam and audio devices

        if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())

        {

            _captureSource.Start();

        }

    }

}

Now, lets handle the stop capture buttons click event. This basically halts all video capturing and is very easy to implement.

private void btnStopCapture_Click(object sender, RoutedEventArgs e)

{

   if (_captureSource != null)

   {

       _captureSource.Stop();

   }

}

The last thing we need to do is enable taking pictures. So, handle the click event for the take picture button. Luckily for us, the capture source has built in support for capturing images asynchronously.

private void btnTakePicture_Click(object sender, RoutedEventArgs e)

{

   if (_captureSource != null)

   {

       _captureSource.AsyncCaptureImage((image) =>

       {

           _pictures.Add(image);

       });

   }

}

That’s it. You have now created your first Silverlight application that can communicate with a users webcam and microphone. Think of all the possibilities.  Download the code, play with it and have fun.

Download Source

Tags: , , ,

Copyright © 2010 Answer My Query All rights reserved. Maintained by Orange Brains .