UWP的扫描仪功能现在被微软划分到了[Windows Desktop Extensions for the UWP]中,如果要使用扫描仪扫描图片到自己的App中,首先我们要添加[Windows Desktop Extensions for the UWP]的引用,这个dll中的所有类都是只能在Desktop设备上才能正常运行的。添加[Windows Desktop Extensions for the UWP]
扫描仪需要用Windows.Devices.Scanners 命名空间下的成员,有几个很重要的成员先介绍下:
ImageScanner 类
- ImageScanner.DefaultScanSource //获取为此扫描仪设备选择的默认扫描源
- ImageScanner.DeviceId //获取此扫描仪设备的 PnP 设备标识符
- ImageScanner.FlatbedConfiguration //获取并设置平板扫描单元的扫描设置,如扫描分辨率和颜色模式
- ImageScanner.FromIdAsync //创建基于扫描仪设备信息 ID 的 ImageScanner 对象的实例
- ImageScanner.ScanFilesToFolderAsync //扫描文件到文件夹 ,支持进度报告
- ImageScanner.ScanPreviewToStreamAsync //扫描文件到流
ImageScannerFlatbedConfiguration 类(这个类里面有很多属性都是和设置扫描仪有关的,主要的如下,其他就不一一列举了)
- ImageScannerFlatbedConfiguration.ColorMode //获取或设置平板扫描仪的颜色模式如黑白灰度等
- ImageScannerFlatbedConfiguration.DesiredResolution //获取或设置应用程序请求的扫描仪平板的水平和垂直扫描分辨率(以 DPI 为单位)
- ImageScannerFlatbedConfiguration.Format //获取或设置从扫描仪的平板到客户端应用程序的图像数据获取的当前文件传输格式。
- ImageScannerFlatbedConfiguration.SelectedScanRegion //获取或设置选定扫描区域的原始坐标(水平和垂直)和维度(宽度和高度)(以英寸为单位)
接下来,我们需要一台扫描仪,扫描仪装好驱动,并支持WIA 2.0 .我的是TOSHIBA 2008S
然后创建一个页面,前台代码如下:
1 <Page.BottomAppBar>
2 <CommandBar>
3 <CommandBar.Content>
4 <Grid/>
5 </CommandBar.Content>
6 <AppBarButton Icon="Scan" Label="Scan Images" x:Name="Bar_ScanFiles" Click="Bar_ScanFiles_Click"/>
7 </CommandBar>
8 </Page.BottomAppBar>
9
10 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
11 <Viewbox>
12 <Grid>
13 <Path Data="M0,589.122C7.23756,591.35,15.1453,592.686,23.5452,592.686L734.433,592.686C742.831,592.686,750.74,591.35,758,589.122L746.228,633.25C746.228,642.477,735.684,650,722.639,650L35.3397,650C22.3137,650,11.7945,642.477,11.7945,633.25z M410.134,418.119L541.24,418.119 635.51,532.435 363.028,532.435z M209.217,402.719C198.74,402.719,195.772,408.064,187.462,416.255L73.5126,540.241C73.5126,547.715,81.9774,553.729,92.4548,553.729L378.964,553.729 665.473,553.729C675.95,553.729,684.484,547.715,684.484,540.241L570.516,416.255C562.228,408.064,559.258,402.719,548.757,402.719L378.964,402.719z M168.384,389.421L378.964,389.421 589.591,389.421C602.638,389.421,606.277,396.102,616.622,406.219L758,560.028C758,569.288,747.43,576.795,734.433,576.795L378.964,576.795 23.5452,576.795C10.5443,576.795,0,569.288,0,560.028L141.375,406.219C151.651,396.102,155.358,389.421,168.384,389.421z M164.949,0L378.966,0 593.029,0C606.078,-5E-06,616.621,7.50893,616.621,16.7822L616.621,358.018C616.621,367.277,606.078,374.785,593.029,374.785L378.966,374.785 164.949,374.785C151.925,374.785,141.379,367.277,141.379,358.018L141.379,16.7822C141.379,7.50893,151.925,-5E-06,164.949,0z" Stretch="Uniform" Fill="#FF434343" Width="160" Height="160" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5">
14 <Path.RenderTransform>
15 <TransformGroup>
16 <TransformGroup.Children>
17 <RotateTransform Angle="0" />
18 <ScaleTransform ScaleX="0.5" ScaleY="0.5" />
19 </TransformGroup.Children>
20 </TransformGroup>
21 </Path.RenderTransform>
22 </Path>
23 </Grid>
24 </Viewbox>
25
26 <GridView x:Name="ImgList"></GridView>
27
28 </Grid>
后台代码如下:
1 /// <summary>
2 /// 开始扫描
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 private async void Bar_ScanFiles_Click(object sender, RoutedEventArgs e)
7 {
8 var scanner = await GetScanner();
9 if (scanner == null) return;
10
11 //保存至用户图片库
12 //var results = await scanner.ScanFilesToFolderAsync(scanner.DefaultScanSource, KnownFolders.PicturesLibrary);
13
14 //取消任务的Token
15 cancellationToken = new CancellationTokenSource();
16
17 //扫描中的对话框
18 ContentDialog dialog = new ContentDialog();
19 dialog.Title = new TextBlock { Text = "扫描中...", FontSize = 14 };
20 dialog.Content = new Scaning() { Width = 200, Height = 200 };
21 dialog.PrimaryButtonText = "取消";
22 dialog.PrimaryButtonClick += (s, a) =>
23 {
24 cancellationToken.Cancel();
25 cancellationToken.Token.Register(() =>
26 {
27 dialog.Hide();
28 });
29 };
30 dialog.ShowAsync();
31 try
32 {
33 //获取预览 ->直接返回 Stream 不过图片质量很差
34 //IRandomAccessStream stream = new InMemoryRandomAccessStream();
35 //await scanner.ScanPreviewToStreamAsync(scanner.DefaultScanSource, stream).AsTask(cancellationToken.Token);
36
37 //扫描 -> 获取到扫描完成的文件
38 var files = await Scanner.ScanFilesToFolderAsync(Scanner.DefaultScanSource, KnownFolders.SavedPictures).AsTask(cancellationToken.Token);
39
40 for (int i = 0; i < files.ScannedFiles.Count; i++)
41 {
42 //创建文件读取流
43 using (var fileStream = await files.ScannedFiles[i].OpenStreamForReadAsync())
44 {
45 var bitmap = new BitmapImage();
46 await bitmap.SetSourceAsync(fileStream.AsRandomAccessStream());
47 ImgList.Items.Add(new Image { Source = bitmap });
48 }
49 //用完流后删除下图片
50 await Task.Factory.StartNew(() => File.Delete(files.ScannedFiles[i].Path));
51 }
52 }
53 catch (TaskCanceledException)
54 {
55 Debug.WriteLine("A task was canceled.");
56 }
57 catch (Exception)
58 {
59 Debug.WriteLine("扫描出错");
60 }
61 finally
62 {
63 dialog.Hide();
64 }
65 }
66
67 /// <summary>
68 /// 获取扫描仪对象
69 /// </summary>
70 /// <returns>ImageScanner</returns>
71 private async Task<ImageScanner> GetScanner()
72 {
73 try
74 {
75 var device = await DeviceInformation.FindAllAsync(DeviceClass.ImageScanner);
76 var scanner = await ImageScanner.FromIdAsync(device.FirstOrDefault().Id);
77 return scanner;
78 }
79 catch (Exception)
80 {
81 await OpenScannerWithError("你可以尝试以下操作后重试:\r\n1、未发现可用的扫描仪设备\r\n2、没有安装适合的WIA2.0兼容驱动程序",
82 true, "重试", true, "取消",
83 () => { Bar_ScanFiles_Click(null, null); });
84 return null;
85 }
86 }
87
88 /// <summary>
89 /// 弹出错误提示
90 /// </summary>
91 /// <param name="error">错误描述</param>
92 /// <param name="hasPrimaryBtn">是否具有主按钮</param>
93 /// <param name="primaryBtnText">主按钮Text</param>
94 /// <param name="hasSecondaryBtn">是否具有副按钮</param>
95 /// <param name="secondaryBtnText">副按钮Text</param>
96 /// <param name="primaryBtnClick">主按钮点击后执行的方法</param>
97 /// <param name="secondaryBtnClick">副按钮点击后执行的方法</param>
98 /// <returns>null</returns>
99 private async Task OpenScannerWithError(string error, bool hasPrimaryBtn = false, string primaryBtnText = null, bool hasSecondaryBtn = false, string secondaryBtnText = null, Action primaryBtnClick = null, Action secondaryBtnClick = null)
100 {
101 ContentDialog dialog = new ContentDialog();
102 var dialogTitle = new StackPanel();
103
104 dialogTitle.Children.Add(new TextBlock { Text = "出错啦 ( ▼-▼ )", Foreground = new SolidColorBrush(Colors.Red) });
105
106 var dialogContent = new StackPanel() { Margin = new Thickness(0, 12, 0, 0) };
107
108 dialogContent.Children.Add(new TextBlock { Text = error });
109
110 dialog.Title = dialogTitle;
111
112 dialog.Content = dialogContent;
113
114 if (hasPrimaryBtn)
115 {
116 dialog.PrimaryButtonText = "重试";
117 if (primaryBtnClick != null)
118 {
119 dialog.Hide();
120 dialog.PrimaryButtonClick += (s, a) =>
121 {
122 primaryBtnClick();
123 };
124 }
125 }
126
127 if (hasSecondaryBtn)
128 {
129 dialog.SecondaryButtonText = "取消";
130
131 dialog.SecondaryButtonClick += (s, a) =>
132 {
133 dialog.Hide();
134 if (secondaryBtnClick != null)
135 {
136 secondaryBtnClick();
137 }
138 };
139 }
140 await dialog.ShowAsync();
141 }
用到的一个UserControl –> Scaning, 是用来显示扫描中动画的,前台代码如下:
1 <UserControl
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:local="using:ScannerDeviceSample.UserControls"
5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
7 xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" xmlns:Media="using:Microsoft.Xaml.Interactions.Media"
8 x:Class="ScannerDeviceSample.UserControls.Scaning"
9 mc:Ignorable="d"
10 d:DesignHeight="200"
11 d:DesignWidth="200">
12 <UserControl.Resources>
13 <Storyboard x:Name="ScaningStoryboard" AutoReverse="True" RepeatBehavior="Forever">
14 <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName="recScanning">
15 <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
16 <EasingDoubleKeyFrame KeyTime="0:0:2" Value="-200"/>
17 </DoubleAnimationUsingKeyFrames>
18 </Storyboard>
19 </UserControl.Resources>
20
21 <Interactivity:Interaction.Behaviors>
22 <Core:EventTriggerBehavior EventName="Loaded">
23 <Media:ControlStoryboardAction Storyboard="{StaticResource ScaningStoryboard}"/>
24 </Core:EventTriggerBehavior>
25 </Interactivity:Interaction.Behaviors>
26
27 <Grid>
28 <Path Data="M2.8160041,17.083004L2.8160041,18.351004 15.944004,18.351004 15.944004,17.083004z M2.8160041,13.907004L2.8160041,15.174004 15.944004,15.174004 15.944004,13.907004z M2.8160041,10.731004L2.8160041,11.999004 15.944004,11.999004 15.944004,10.731004z M0,1.356853E-05L10.572985,1.356853E-05 10.572985,7.9669956 18.761999,7.9669956 18.761999,21.176014 0,21.176014z M11.945004,0L18.761988,6.6610196 11.945004,6.6610196z" Stretch="Uniform" Fill="Black" Width="128" Height="128" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5">
29 <Path.RenderTransform>
30 <TransformGroup>
31 <RotateTransform Angle="0" />
32 <ScaleTransform ScaleX="0.8" ScaleY="1" />
33 </TransformGroup>
34 </Path.RenderTransform>
35 </Path>
36 <Rectangle x:Name="recScanning" Height="2" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Bottom" d:LayoutOverrides="Height">
37 <Rectangle.RenderTransform>
38 <CompositeTransform/>
39 </Rectangle.RenderTransform>
40 <Rectangle.Projection>
41 <PlaneProjection/>
42 </Rectangle.Projection>
43 <Rectangle.Fill>
44 <LinearGradientBrush EndPoint="0,0.5" StartPoint="1,0.5">
45 <GradientStop Color="#331CF106" Offset="0.15"/>
46 <GradientStop Color="#331CF106" Offset="0.85"/>
47 <GradientStop Color="#FF1CF106" Offset="0.5"/>
48 </LinearGradientBrush>
49 </Rectangle.Fill>
50 </Rectangle>
51 </Grid>
52 </UserControl>
最后的效果: