65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Visual Studio LightSwitch 中从扫描仪和摄像头获取图像

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (17投票s)

2011 年 10 月 26 日

Ms-PL

12分钟阅读

viewsIcon

75794

downloadIcon

12

本文介绍了如何从扫描仪和摄像头获取图像,以及如何将它们存储到 LightSwitch 应用程序的数据库中。

PhotoManagerLS.jpg

引言

借助 Microsoft Visual Studio LightSwitch 2011,您可以构建各种处理数据的应用程序。现代业务应用程序通常需要包含图像,以表示产品、联系人或其他数据项。在 LightSwitch 中,您可以轻松地将图片存储到类型为 Image 的实体属性中。这是一种新的业务类型,允许将 Jpeg 和 Png 格式的图像文件上传到应用程序的数据库。图像可以来自磁盘上的文件,LightSwitch 提供了一个方便的 Image Editor 控件来上传它们,也可以来自扫描仪和网络摄像头等设备。在本文中,您将学习如何通过 Silverlight 4 中提供的功能扩展 LightSwitch 应用程序,从而能够处理图像采集设备。您将创建一个名为 Photo Manager 的应用程序,该应用程序可以帮助您跟踪磁盘上的图片,并且还可以捕获设备上的图像。创建同一解决方案内的自定义控件需要 Visual Studio 2010 Professional 或更高版本。

背景

本文介绍的示例应用程序将只包含一个名为 Photo 的实体,该实体代表一个图像。将创建三个屏幕:一个用于向集合添加图像的数据录入屏幕,一个搜索屏幕和一个可编辑网格屏幕(这使得编辑现有图像条目更加容易)。数据录入屏幕除了允许从磁盘选择图像文件外,还允许从扫描仪和网络摄像头捕获图像。这需要对 Silverlight 4 有一定的熟悉度,因为您需要调用特定的 API 来处理网络摄像头,但没有直接支持扫描仪设备;关于这一点,利用 Silverlight 4 中的 COM 自动化(这是 Silverlight 4 的新功能)来使用 WIA (Windows Image Acquisition) API。通过使用 WIA,您可以调用操作系统允许处理此类设备的 API。一旦从扫描仪或网络摄像头获取了图像,就必须将其转换为 Silverlight 4 可接受的格式。为此,我们使用 CodePlex 上提供的开源库,名为 .NET Image Tools for Silverlight。该库提供了许多易于在 Silverlight 4 中处理图像的对象,避免了重复造轮子,从而节省了大量时间。下载后,将 zip 存档解压缩到磁盘上的一个文件夹中,以便以后能够轻松地添加对必需程序集的引用。由于应用程序使用 COM 自动化,它需要提升的权限,本文描述的功能仅在应用程序作为桌面客户端运行时才可用。这里有很多内容需要展示,所以我假设您熟悉 Visual Studio 开发环境中的概念,例如创建解决方案、项目、添加引用等。

LightSwitch

从 LightSwitch 的角度来看,我们将首先创建一个处理设备的 Silverlight 4 类库,并公开一个用于处理网络摄像头的自定义控件。稍后,该控件将利用可扩展性添加到应用程序的数据录入屏幕中。您基本上会有一个包含 Silverlight 类库和引用了另一个项目的 LightSwitch 应用程序的解决方案。

创建 Silverlight 类库和扫描仪服务

在 Visual Studio 2010 中要做的第一件事是创建一个名为 PhotoManager 的空白解决方案。接下来,您可以添加一个类型为 Silverlight Class Library 的新项目,名为 PhotoService ,如下图所示:

LSPhotoMan1.jpg

Visual Studio 2010 会要求您指定类库的 Silverlight 版本,选择 Silverlight 4 并继续。此时,您会看到一个默认类已添加到项目中。在解决方案资源管理器中,右键单击代码文件名(Class1.vbClass1.cs,取决于选择的编程语言),然后选择 重命名。类的名称将是 ScannerService。在编写代码之前,您需要添加对 Image Tools 库的以下程序集的引用:

  • ImageTools.dll
  • ImageTools.Utils.dll
  • ImageTools.IO.Jpeg.dll
  • ImageTools.IO.Bmp.dll

还有其他程序集可用于将图片编码和解码为不同的文件格式,但这已经足够了。现在让我们关注 ScannerService 类。它将实现一个名为 Scan 的方法,该方法将调用 COM 自动化以从 Windows 访问 WIA API,并将扫描过程的结果存储到磁盘上的文件和类型为 System.Byte() 的属性中。字节数组实际上是 LightSwitch 接受图像的方式。此外,该类还需要实现 INotifyPropertyChanged 接口。通过这种方法,当上述属性的值发生变化时,会向客户端发送通知;LightSwitch 客户端也会收到通知,并更新 Image Editor Image Viewer 控件的内容。让我们开始编写这个:

Option Strict Off
 
Imports System.Runtime.InteropServices.Automation
Imports System.Windows.Media.Imaging
Imports System.Runtime.CompilerServices
Imports System.Windows.Threading
Imports System.IO
Imports ImageTools
Imports ImageTools.IO.Jpeg
Imports ImageTools.IO.Png
Imports ImageTools.IO
Imports System.ComponentModel
Imports ImageTools.IO.Bmp
 
Public Class ScannerService
    Implements INotifyPropertyChanged
 
    ''' 
    ''' Fired when the image acquisition completes successfully
    ''' 
    ''' <remarks>
    Public Event AcquisitionCompleted()
 
    ''' 
    ''' Fired when the image acquisition fails for some reasons
    ''' 
    ''' <remarks>
    Public Event AcquisitionFailed()
 
    Protected Sub OnPropertyChanged(ByVal strPropertyName As String)
        If Me.PropertyChangedEvent IsNot Nothing Then
            RaiseEvent PropertyChanged_
	      (Me, New System.ComponentModel.PropertyChangedEventArgs(strPropertyName))
        End If
    End Sub
 
    Public Event PropertyChanged(sender As Object, _
	e As System.ComponentModel.PropertyChangedEventArgs) _
	Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
 
    Private _acquiredImage As Byte()
 
    ''' 
    ''' Returns the acquired image under a form that is accepted 
    ''' by the LightSwitch Image control
    ''' 
    ''' <value>Byte()</value>
    ''' <returns>
    ''' <remarks>
    Public Property AcquiredImage As Byte()
        Get
            Return _acquiredImage
        End Get
        Set(value As Byte())
            _acquiredImage = value
            OnPropertyChanged("AcquiredImage")
        End Set
    End Property

请记住,在 Visual Basic 中处理 COM 自动化时需要 Option Strict Off 指令。注意还公开了两个事件,仅用于通知扫描过程的进度(完成或失败)。下一步是编写 Scan 方法;您会看到它调用稍后会解释的其他方法。

    ''' 
    ''' Acquires an image from scanner. Stores the result in the 
    ''' <seealso cref="acquiredimage"> property and returns the pathname for the image file
    ''' 
    ''' <returns>String</returns>
    ''' <remarks>Available only if the application is running out-of-browser</remarks>
    Public Function Scan() As String
        'If not out-of-browser:
        If AutomationFactory.IsAvailable = False Then
            RaiseEvent AcquisitionFailed()
            Return Nothing
        End If
 
        'Gets a reference to the WIA dialog
        Try
 
            Dim commonDialog As Object = AutomationFactory.CreateObject("WIA.CommonDialog")
 
            'Show the dialog for scanning inmages
            Dim imageFile As Object = commonDialog.ShowAcquireImage()
 
            'If the result is not null,
            If imageFile IsNot Nothing Then
                'Saves the result as an image to disk
 
                Dim filePath As String = BuildFileName()
                imageFile.SaveFile(filePath)
                commonDialog = Nothing
 
                'Converts the image file into a byte array
                Me.AcquiredImage = ConvertImageToByteArray(filePath)
                RaiseEvent AcquisitionCompleted()
                Return filePath
            Else
                RaiseEvent AcquisitionFailed()
                Return Nothing
            End If
        Catch ex As Exception
            Throw
        End Try
    End Function

AutomationFactory 类允许理解应用程序是否作为桌面客户端运行。如果它没有作为桌面客户端运行(IsAvailable = False),则代码将引发 AcquisitionFailed 事件并返回一个 null 对象。这实际上是一个双重检查,因为这也可以在 LightSwitch 客户端中完成,但如果另一个开发人员忘记在那里添加检查,它会很有用。如果它是一个桌面客户端,代码将通过 AutomationFactory.CreateObject 方法创建一个 WIA.CommonDialog 对象实例,然后调用其 ShowAcquireImage 方法,该方法会显示默认的图像采集对话框。注意代码随后如何将图像保存到磁盘(请记住它以 Bitmap 格式存储)。BuildFileName 是一个根据当前日期/时间构建增量文件名的 \*\*方法。\*\*保存后,代码将采集结果分配给 AcquiredImage 属性。这是通过首先通过一个名为 ConvertImageToByteArray 的方法将磁盘上的文件转换为字节数组来实现的。以下代码显示了如何构建增量文件名:

    ''' 
    ''' Constructs a file name starting from today's date and time
    ''' 
    ''' <returns>
    ''' <remarks>
    Private Function BuildFileName() As String
        Dim tempString As New Text.StringBuilder
        tempString.Append(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))
        tempString.Append("\")
        tempString.Append(Date.Now.Year.ToString)
        tempString.Append(Date.Now.Month.ToString)
        tempString.Append(Date.Now.Day.ToString)
        tempString.Append("_")
        tempString.Append(Date.Now.Hour.ToString)
        tempString.Append(Date.Now.Minute.ToString)
        tempString.Append(Date.Now.Second.ToString)
        tempString.Append(".bmp")
        Return GetUniqueFilename(tempString.ToString)
    End Function
 
    Private Function GetUniqueFilename(ByVal fileName As String) As String
        Dim count As Integer = 0    'a counter   
        Dim name As String = String.Empty
        'If the original file name does not exist...        
        If System.IO.File.Exists(fileName) Then
            'Get details about the file name
            Dim currentFileInfo As New System.IO.FileInfo(fileName)
            'if it has extension...            
            If Not String.IsNullOrEmpty(currentFileInfo.Extension) Then
                'takes the file name without extension
                name = currentFileInfo.FullName.Substring_
			(0, currentFileInfo.FullName.LastIndexOf("."c))
            Else        'otherwise uses the current file name
                name = currentFileInfo.FullName
            End If
            'Iterates until the file name exists
            While System.IO.File.Exists(fileName)
                count += 1
 
                fileName = name + "_" + count.ToString() + currentFileInfo.Extension
            End While
        End If
        Return fileName
    End Function

BuildFileName 只是根据当前日期/时间构建文件名。为了避免重复,会调用一个名为 GetUniqueFileName 的附加方法。这确保了给定文件首先不存在于磁盘上;如果存在,则通过附加增量数字(1、2、3 等)来生成新的文件名,直到确保文件名是唯一的。以下是 ConvertImageToByteArray 方法的代码:

    ''' 
    ''' Converts an image file into a Byte array, which is accepted in LightSwitch
    ''' 
    Private Function ConvertImageToByteArray(fileName As String) As Byte()
        Dim bm As New BmpDecoder()
        Dim inputImg As New ExtendedImage
        Using fs1 As New FileStream(fileName, FileMode.Open)
            bm.Decode(inputImg, fs1)
 
            Dim enc As New JpegEncoder
            Using ms As New MemoryStream
                enc.Encode(inputImg, ms)
                Return ms.ToArray()
            End Using
        End Using
    End Function

此方法至关重要:它从指向先前捕获的图像文件的 FileStream 对象开始,使用 Image Tools 库中的 BmpDecoder.Decode 方法将流解码为 ExtendedImage 类型对象,该对象也由该库公开。接下来,假设您想使用 Jpeg 格式,将使用 JpegEncoder.Encode 方法将位图内容以 Jpeg 图像的形式写入 MemoryStream 对象。写入 MemoryStream 很重要,因为该对象公开了一个名为 ToArray 的方法,该方法将图像转换为字节数组。这正是 LightSwitch 可以存储到 Image 类型属性中的内容。最后一步是在类的构造函数中填充编码器和解码器集合,如下所示:

    Public Sub New()
 
        Decoders.AddDecoder(Of JpegDecoder)()
        Decoders.AddDecoder(Of BmpDecoder)()
        Encoders.AddEncoder(Of BmpEncoder)()
        Encoders.AddEncoder(Of JpegEncoder)()
    End Sub

扫描仪服务类已完成。下一步是构建一个自定义控件,该控件将在 LightSwitch 屏幕中使用,以从网络摄像头捕获图像。

创建用于网络摄像头交互的自定义控件

选择 项目添加新项 以向项目添加新的 Silverlight 用户控件。您选择 Silverlight User Control 模板,如下图所示:

LSPhotoMan2.jpg

基本上,该控件将提供用户界面,用于从可用设备列表中选择网络摄像头,并允许开始和停止视频捕获。用户界面的 XAML 代码如下所示:

<UserControl x:Class="DelSole.PhotoService.WebcamControl"
    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"
    d:DesignHeight="300" d:DesignWidth="480">
    <!--Dividing the main grid into three columns-->
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        
        <Border CornerRadius="6" BorderBrush="Black" BorderThickness="2">
            <StackPanel >
                <TextBlock Text="Available video devices:" 
			Foreground="Blue" FontWeight="SemiBold" />
                <ListBox Name="VideoDevicesListBox" ItemsSource="{Binding}" 
			Margin="0,10,0,0">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <!-- This is data bound to the FriendlyName property
                        of the collection of video devices-->
                            <TextBlock Text="{Binding FriendlyName}"/>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </StackPanel>
        </Border>

        <!--This last StackPanel nests the box for showing the
        webcam output and for showing the still images collection-->
        <StackPanel Grid.Column="1">
            <!--This rectangle will show the actual webcam output-->
            <Border BorderBrush="Black" BorderThickness="2" CornerRadius="6">
                <Rectangle Width="320" Height="240" Name="WebcamBox"/>
            </Border>

            <Border BorderBrush="Black" BorderThickness="2" CornerRadius="6" >
            <StackPanel Orientation="Horizontal">
                <StackPanel.Resources>
                    <!--Defines a common set of properties for each button-->
                    <Style x:Key="ButtonStyle" TargetType="Button">
                        <Setter Property="Width" Value="80"/>
                        <Setter Property="Height" Value="30"/>
                        <Setter Property="Margin" Value="5"/>
                    </Style>
                </StackPanel.Resources>
                <Button Name="StartButton" Content="Start"  
			Style="{StaticResource ButtonStyle}" />
                <Button Name="StopButton" Content="Stop"  
			Style="{StaticResource ButtonStyle}" />
                <Button Name="ShotButton" Content="Get picture"  
			Style="{StaticResource ButtonStyle}" />
            </StackPanel>
            </Border>
        </StackPanel>
    </Grid>
</UserControl>

除了几个按钮(每个按钮用于一个特定的、不言自明的操作)之外,请注意 ListBox 控件是如何进行数据绑定的,并在运行时填充。ListBox 的数据模板包含一个 TextBlock 控件,该控件绑定到代码隐藏中解释的可用设备集合的 FriendlyName 属性。此时,您的设计器应如下图所示:

LSPhotoMan3.jpg

从代码隐藏的角度来看,现在您将实现与您在扫描仪服务类中看到的类似的成员。主要区别在于代表图像的属性是依赖属性,因为这在使用自定义控件时很合适,并且提供了最佳的数据绑定支持。这是代码的第一部分:

Imports ImageTools.IO
Imports ImageTools.IO.Jpeg
Imports System.IO, ImageTools.ImageExtensions
Imports ImageTools.IO.Png
 
Partial Public Class WebcamControl
    Inherits UserControl
 
    ''' 
    ''' Fired when the Webcam completes capturing an image to a WriteableBitmap object
    ''' 
    ''' <remarks>
    Public Event CaptureCompleted()
 
    Private WithEvents capSource As CaptureSource
 
    Private capturedImageProperty As DependencyProperty = _
            DependencyProperty.Register("CapturedImage", _
		GetType(Byte()), GetType(WebcamControl), Nothing)
 
    ''' 
    ''' Returns the still image taken from the Webcam under a form 
    ''' that is accepted by the LightSwitch Image Editor control
    ''' 
    ''' <value>
    ''' <returns>Byte()</returns>
    ''' <remarks>
    Public ReadOnly Property CapturedImage As Byte()
        Get
            Return CType(GetValue(capturedImageProperty), Byte())
        End Get
    End Property
 
    Public Sub New()
        InitializeComponent()
 
        Encoders.AddEncoder(Of JpegEncoder)()
        Decoders.AddDecoder(Of JpegDecoder)()
        Encoders.AddEncoder(Of PngEncoder)()
    End Sub

注意定义了 CaptureSource 类型的文件。这是 Silverlight 4 中的一个新对象,代表选定的网络摄像头设备。一旦用户控件加载,就会创建该类的实例,这也是用可用设备列表填充 ListBox 的点:

    Private Sub SilverlightWebcamControl_Loaded_
	(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        'Retrieves the list of available video devices
        Me.VideoDevicesListBox.ItemsSource = _
	CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices()
 
        'Creates a new capture source
        Me.capSource = New CaptureSource()
    End Sub

CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices 返回一个 ReadonlyCollection VideoCaptureDevice 对象,每个对象代表一个网络摄像头。此时,您可以开始处理 Button.Click 事件。首先是开始和停止(请参见代码中的注释):

    Private Sub StartButton_Click(sender As System.Object, _
	e As System.Windows.RoutedEventArgs) Handles StartButton.Click
        If Me.capSource IsNot Nothing Then
            'If a device is already capturing, then stop it
            Me.capSource.Stop()
 
            'Set capture devices taking selected items from ListBoxes
            Me.capSource.VideoCaptureDevice = _
		DirectCast(VideoDevicesListBox.SelectedItem, VideoCaptureDevice)
 
            'Creates a VideoBrush for showing video output
            Dim webcamBrush As New VideoBrush()
            webcamBrush.SetSource(Me.capSource)
            'Fills the rectangle with the video source
            WebcamBox.Fill = webcamBrush
 
            'It's a good idea requesting user permission before starting capture
            If CaptureDeviceConfiguration.AllowedDeviceAccess _
		OrElse CaptureDeviceConfiguration.RequestDeviceAccess() Then
                Me.capSource.Start()
            End If
        End If
 
    End Sub
 
    Private Sub StopButton_Click(sender As System.Object, _
	e As System.Windows.RoutedEventArgs) Handles StopButton.Click
        Me.capSource.Stop()
    End Sub

为了从选定的网络摄像头获取静态图像,您调用 CaptureSource.CaptureImageAsync 方法,然后处理 CaptureSource.CaptureImageCompleted 事件,以便将捕获的图像转换为字节数组:

    Private Sub ShotButton_Click(sender As System.Object, _
	e As System.Windows.RoutedEventArgs) Handles ShotButton.Click
        If Me.capSource IsNot Nothing Then
            Try
                'Captures a still image
                Me.capSource.CaptureImageAsync()
 
            Catch ex As InvalidOperationException
                MessageBox.Show("You need to start capture first")
            Catch ex As Exception
 
            End Try
 
        End If
 
    End Sub
 
    Private Sub capSource_CaptureImageCompleted(ByVal sender As Object,
                  ByVal e As System.Windows.Media.
                  CaptureImageCompletedEventArgs) Handles capSource.CaptureImageCompleted
 
        Try
            'Gets the instance of the captured image
            Dim converted = e.Result.ToImage
 
            'Encodes the image to Jpeg
            Dim encoder As New JpegEncoder()
 
            'Converts the image to a byte array, which is accepted by LightSwitch
            Using ms As New MemoryStream
                encoder.Encode(converted, ms)
                Me.SetValue(Me.capturedImageProperty, ms.ToArray)
            End Using
 
            RaiseEvent CaptureCompleted()
        Catch ex As Exception
            Throw e.Error
        End Try
    End Sub

请注意,在事件处理程序中,通过 Image Tools 库的 ToImage 扩展方法,将捕获的图像(e.Result)转换为 ExtendedImage 对象。然后,它按照您在扫描仪服务类中看到的方式进行编码。请记住,LightSwitch 支持 Jpeg 和 Png 图像格式,因此您不限于 JpegEncoder。现在是时候在 LightSwitch 客户端应用程序中使用这个类库了。在进入下一节之前,请生成项目并确保没有引发任何错误。

创建 LightSwitch 应用程序

此时,您可以向解决方案添加一个新的 LightSwitch 项目,选择 LightSwitch Application 模板,如下图所示:

LSPhotoMan4.jpg

新项目准备就绪后,单击 创建新表。定义一个名为 Photo 的新实体,包含三个属性:Picture (必需,类型为 Image)、Description (类型为 String)和 DateTaken (类型为 Date):

LSPhotoMan5.jpg

现在,您将添加三个屏幕(使用设计器工具栏上的 Screen 按钮):一个名为 Create New Photo 的数据录入屏幕,一个名为 Search Photos 的搜索屏幕,以及一个名为 Editable Photos Grid 的可编辑网格屏幕。仅举一例,这是添加数据录入屏幕的方法:

LSPhotoMan6.jpg

特别是对于数据录入屏幕,这是将添加自定义 Silverlight 控件的地方,并将使用两个按钮来启动扫描仪服务和网络摄像头采集控件。话虽如此,双击解决方案资源管理器中的 Create New Photo 屏幕,然后展开 Rows Layout 元素下的下拉列表,以便您能够选择 New Custom Control 命令,如下图所示:

LSPhotoMan7.jpg

此时将出现 Add Custom Control 对话框。单击 Add Reference,然后添加对之前创建的 Silverlight 类库项目的引用,最后选择 WebcamControl 元素:

LSPhotoMan8.jpg

由于您不会真正将任何数据源绑定到控件,因此您可以保持数据绑定路径不变。添加控件后,在 Properties 窗口中,取消选中 Is Visible 复选框。这是因为用户将决定何时打开控件以通过网络摄像头抓取图片。现在您可以添加两个按钮;确保 Screen Command Bar 已展开,然后选择 AddNew Button。为新按钮指定一个名称,例如 AcquireFromScanner

LSPhotoMan9.jpg

重复相同的步骤添加另一个名为 AcquireFromWebcam 的按钮。添加这两个按钮后,您还可以用自定义图标替换默认图标。例如,源代码使用 Visual Studio 2010 附带的图像库中的图标。此时,双击 Acquire From Scanner 按钮,您将被重定向到代码编辑器。您首先处理 CanExecute 方法钩子,以检查客户端是否在桌面运行,然后处理 Execute 方法钩子以运行扫描仪(请参阅代码中的注释):

        Private Sub AcquireFromScanner_CanExecute(ByRef result As Boolean)
            ' Write your code here.
            result = AutomationFactory.IsAvailable
        End Sub
 
        Private Sub AcquireFromScanner_Execute()
            Dim scanService As New DelSole.PhotoService.ScannerService
 
            'When the scanner service fires the event, the Picture property
            'of the current photo is assigned with the AcquiredImage property
            'of the scanner class
            AddHandler scanService.AcquisitionCompleted, Sub()
                          Me.PhotoProperty.Picture = scanService.AcquiredImage
        End Sub
            Try
                Dispatchers.Main.Invoke(Sub()
                     Try
                         'Invokes the Scan method
                          Dim imageName As String = scanService.Scan()
 
                          'This code is executed after the AcquisitionComplete 
                          'event is intercepted and asks if the user wants to
                          'delete the scanned image file and just keep the in-memory pic
                          Dim wantToErase As MessageBoxResult = _
			ShowMessageBox("Do you want to delete the _
			scanned image file from disk?", "", MessageBoxOption.OkCancel)
                          Select Case wantToErase
                               Case Is = Windows.MessageBoxResult.OK
                                   IO.File.Delete(imageName)
                               Case Else
                                   Exit Select
                          End Select
 
                          'This error is thrown if no scanner is turned on
                          Catch ex As System.Runtime.InteropServices.COMException
                               If ex.ErrorCode = -2145320939 Then
                                   ShowMessageBox("Ensure that your scanner _
					is plugged-in and turned on.")
                               Else
                                   ShowMessageBox(ex.Message)
                               End If
                                   Catch ex As Exception
                               ShowMessageBox(ex.Message)
                           End Try
                      End Sub)
            Catch ex As Exception
                ShowMessageBox("The following error occurred: " & _
				Environment.NewLine & ex.Message)
            End Try
        End Sub

您必须从 Dispatcher 运行采集代码,因为它将使用适当的线程并避免无效的跨线程调用。接下来的代码处理 Acquire From  Webcam 按钮的 Execute 方法钩子:

    Private Sub AcquireFromWebcam_Execute()
        ' Write your code here.
    
        'Retrieves the instance of the custom control
        Dim control = Me.FindControl("ScreenContent")
 
        'If not visible (default)
        If Not control.IsVisible Then
        'Make it visible
        control.IsVisible = True
 
        'When available...
        AddHandler control.ControlAvailable, _
		Sub(sender As Object, e As ControlAvailableEventArgs)
        'Get the instance of the button and change its text
        Dim currentButton = Me.FindControl("AcquireFromWebCam")
        currentButton.DisplayName = "Hide WebCam"
   
        'Get the instance of the WebcamControl
        Dim webcamControl = CType(e.Control, DelSole.PhotoService.WebcamControl)
 
        'When the CaptureCompleted event is fired,
        AddHandler webcamControl.CaptureCompleted, Sub()
        'The Picture property is assigned with the CapturedImage property
        Me.PhotoProperty.Picture = webcamControl.CapturedImage
 
        End Sub
    End Sub
    Else
        'If already visible, restore it
        control.IsVisible = False
            Dim currentButton = Me.FindControl("AcquireFromWebCam")
            currentButton.DisplayName = "Acquire From WebCam" 
        End If
End Sub

代码中的注释应该足以让您理解,只需注意如何根据控件的状态(可见或隐藏)在运行时更改控件上的属性(例如 DisplayName)。

测试应用程序

最后,您可以按 F5 测试应用程序。打开数据录入屏幕时,您将能够启动扫描仪采集对话框和网络摄像头控件。后者如下图所示:

LSPhotoMan10.jpg

您首先需要选择一个可用设备,然后单击 Start。准备就绪后,单击 Get Picture。此时,运行时将向用户界面发送通知,Image  Editor 控件将自动显示图片。完成后不要忘记单击 Stop。还要查看屏幕命令栏上的按钮,它的文本会根据控件的状态而变化。下图显示了可编辑网格屏幕,您可以在其中查看和编辑收藏中可用图片的列表:

LSPhotoMan11.jpg

关注点

Visual Studio LightSwitch 允许通过可扩展性和 Silverlight 4 为业务应用程序添加大量功能。本文重点介绍了如何轻松地从设备采集图片,从而丰富您的实体(如文档、图片或产品表示),使您的应用程序比以往任何时候都更酷。

历史

  • 2011年10月26日:初稿
© . All rights reserved.