在WP 8.1中只有Silverlight App支持操作剪贴板的API,Runtime App并不支持。不过,在WP 10中也引入了可以操作剪贴板的API。

顺便说点题外话,有人会说,我8.1的开发还没学了,Win10又来了,那我怎么学得过来?放心,学得过来的D,因为只要你对有WPF的基础,再加上对RT App的学习就足够应付Win 10上的app开发了。实际上,面向10的API都是在8.1的基础上增加一点新的API而已,你不需要学习新的知识就可以直接玩10的开发了。

而且,你在10上依然可以继续使用8.1的API。当然我指的是RuntimeApp,Silverlight的API有可能将来不用。虽然RT API使用起来看着和以往的.net类库没什么区别,而实际上RT库是基于COM的形式存在的,如果你足够细心的话,通过在RT应用发生异常的时候,你都会看到来自COM的HRESULT值,在调用试的时候,你也能看到许多RT库的类型显示为COM对象。这是很有力的证据。

所以我大胆估计,RT库就是为了优化性能而出现的,随同新的编译器和.net 4.6的发布,.net的性能会有更可观的优化,这是肯定的事情。而对于Win 10,微软也确实在努力优化,尽管现在是预览版,问题肯定会多一些,但我使用Build 10041后,确实感觉到反应速度快了很多,因为我的笔记本配置不算很高,对性能是有一点儿敏感的。

好了,屁话说得太多了,为了避免大家向我扔石头(如果你要扔的话,建议你扔玛瑙),下面我再简单说说“通用应用程序项目”(UAP),耍W10开发必须了解这个。

通过框架是在面向8.1(VS UD2)的时候出现,在这个通用应用中是分为三个项目的,相信大家有所了解。

1、面向Windows Phone的应用。

2、面向Windows的应用。

3、共享代码。

平台共用的代码通常会放到第三个项目中,不过这样做有时候也觉得代码管理起来不方便。

 

在面向10的项目中直接就合并成一个项目,而不再按平台分开,直接就统一到一个项目中,生成的应用程序你喜欢在手机上运行也行,在平板上运行也行,没有平板在普通PC上耍也可以。

安卓自动读取剪贴板demo app 读取剪贴板_安卓自动读取剪贴板demo

在VS的“对象浏览器”窗口中,你会发现有一个名为Universal App Platform的子集,它就是包含通用API的各个库的集合,把多个平台可以共享的API都放到一起了,开发者就不必去管理共享代码了。

当然你也可以继续使用面向8.1的项目,这些项目模板在“Store Apps”节点下面。

 

好了,有了上面的介绍,再进入咱们今天的正题就轻松了。

首先,参照上面截图自己新项一个Windows 10的空白应用程序项目。

这里我给出的示例比较简单,就是复制和粘贴一张图片。还是和以前一样,用XAML代码来布局UI。



<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
            <Image Name="imgSrc" Width="200" Height="200" />
            <Button Content="复制" Click="OnCopy"/>
        </StackPanel>
        <StackPanel Grid.Row="1" VerticalAlignment="Top" Orientation="Horizontal">
            <Image Name="imgDes" Width="200" Height="200"/>
            <Button Content="粘贴" Click="OnSet"/>
        </StackPanel>
    </Grid>



然后在页面加载后从项目目录中读出一个图像文件。



public MainPage()
        {
            this.InitializeComponent();
            this.Loaded += OnPageLoaded;
        }

        private async void OnPageLoaded(object sender, RoutedEventArgs e)
        {
            // 加载项目中的图片
            StorageFile imgFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///1.jpg"));
            // 打开文件流
            IRandomAccessStream stream = await imgFile.OpenReadAsync();
            // 显示图像
            BitmapImage bmp = new BitmapImage();
            bmp.DecodePixelWidth = 250;
            await bmp.SetSourceAsync(stream);
            this.imgSrc.Source = bmp;
            // 将流对象放入Tag属性中暂存,稍后会用得上
            this.Tag = RandomAccessStreamReference.CreateFromStream(stream.CloneStream());
            stream.Dispose();
        }



文件流一般来说,用完就应该马上关闭,不要借着别人的钱不还,但是,我们随后要把文件流中的内容写入剪贴板,而文件流被关闭后又无法操作了,于是,我调用CloneStream方法直接将流复制一份,这样原来的文件流可以关闭了,并且不影响后面的操作。并且赋值给Tag属性,随后在其他代码中可以从Tag属性取出流对象。
下面代码将流中的数据写入剪贴板:



// 从Tag属性中取出文件流
            RandomAccessStreamReference stream = this.Tag as RandomAccessStreamReference;
            if (stream != null)
            {
                // 创建数据包装
                DataPackage package = new DataPackage();
                // 放入内容
                package.SetBitmap(stream);
                // 将数据放入ClipBoard
                Clipboard.SetContent(package);
            }



下面代码从剪贴板读出数据,即“粘贴”:



// 从clip board中取出数据
            DataPackageView pack = Clipboard.GetContent();

            // 如果Count为0,表示剪贴板中没有可用的内容
            if (pack.AvailableFormats.Count == 0)
            {
                return;
            }
            else
            {
                foreach (string format in pack.AvailableFormats)
                {
                    System.Diagnostics.Debug.WriteLine(format);
                }
            }
            // 验证格式是否正确
            if (pack.AvailableFormats.Where(s=>s == StandardDataFormats.Bitmap).Count() > 0)
            {

                // 读出流
                RandomAccessStreamReference streamref = await pack.GetBitmapAsync();
                using (IRandomAccessStream streamIn = await streamref.OpenReadAsync())
                {
                    // 显示图片
                    BitmapImage bmp = new BitmapImage();
                    bmp.DecodePixelWidth = 250;
                    await bmp.SetSourceAsync(streamIn);
                    this.imgDes.Source = bmp;
                }
                Clipboard.Clear(); //清除剪贴板中的内容

            }



完事了,例子就这么简单。结果如下图。

安卓自动读取剪贴板demo app 读取剪贴板_ui_02

 

下面就给大家介绍一下知识点。

1、操作剪贴板用的是Clipboard类(命名空间:Windows.ApplicationModel.DataTransfer),这个类是静态的,也就是说它的成员也是静态的,不用new就可以直接用。

2、要向剪贴板写入数据,就用SetContent方法,要从剪贴板中读出数据就用GetContent方法。

3、在写入的时候,数据是通过DataPackage类来包装的,而读取时则是通过DataPackageView类来操作的。那么,这两个家伙有什么区别呢?

安卓自动读取剪贴板demo app 读取剪贴板_ui_03

看到了吧,一个是SetXXXXX,一个是GetXXXXXX。因此,DataPackage是用于创建数据包的,而DataPackageView是用来读数据包的。

4、操作也不难,写什么类型的数据就用什么方法,如写文本用SetText,读文本用GetTextAsync。本示例是读写图像,所以写的时候调用SetBitmap方法,读的时候调用GetBitmapAsync方法。注意,图像的内容是用RandomAccessStreamReference类来封装的流对象,通过静态的CreateFromStream方法可以从现存的流创建新的RandomAccessStreamReference实例。

5、用DataPackageView方法检索内容之前,应当访问AvailableFormats属性,检查一下剪贴板上的内容是否可用,或者是空的。该属性是一个字符串列表,如果列表中元素个数为0,说明剪贴板中的数据无效。

6、Clipboard.Clear()方法清理剪贴板中的内容。

 

随后,我把示例源代码上传,大家可以下载下来玩玩。


===============================

这里补充一下,现在Win10和VS2015都是预览版,如果你害怕出现小问题,那你得谨慎使用,如果你像我这样,不干正经事,只是体验一下,可以在电脑上装来耍耍。我比较穷,只有一台笔记本,买不起新电脑,就花几百块钱买了一台国产的山寨平板,8寸的屏幕是小了点,不过还是可以当电脑用的。所以我的笔记本用来开发实验,山寨板用来看看电子书,看看电影电视剧,和林妹妹聊聊天,还是可以用的。

要注意的是,装Win10预览版一定要装最新发布的Build 10041版本,否则面向Win 10通用应用的XAML设计器无法加载。一开始我也犯了这个错误,后来查看一下清单中的XML文件,才发现它要求的是Build 10030以上的版本,而大于这个版本的就只有最近发布的Build 10041了。

另外,有一件事,需要向各位读者表示道歉的,有位细心的朋友发现《精通C# 5.0》中第54页,定义的Book结构有一个属性应该是ISBN,我弄错为ISBM。这是我的疏忽,偏偏编辑在审稿时没有注意,我在定稿时也没看到。因此,向各位表示歉意。