在Win10 App开发中,微软新增了系统PC文件与UWP 之间的文件拖拽行为,它支持将系统磁盘上的文件以拖拽的形式拖入App中并处理,在前不久的微软build 2015开发者大会上微软展示的UWP版微信的拖拽文件就是使用的这个功能,接下来,我们一起看看该功能是怎么实现的。

 

    首先我们要介绍的是DragEventArgs这个类,是在拖拽中为我们提供数据和UI样式定制的,在拖拽事件中,事件参数对象就是该类型,这个类在Win10中增加了一个新的接口的继承->IDragEventArgs2接口,该接口中提供如下新的成员属性:

1 internal interface IDragEventArgs2
2  {
3      DataPackageOperation AcceptedOperation { get; set; }
4      DataPackageView DataView { get; }
5      DragUIOverride DragUIOverride { get; }
6      DragDropModifiers Modifiers { get; }
7  
8      DragOperationDeferral GetDeferral();
9  }

其中有三个比较重要的:

  • AcceptedOperation:这个是获取或设置指定拖动事件发起方可执行哪些操作,值是DataPackageOperation枚举类型。可以制定四种操作类型(None,Move,Copy,Link),指定不同类型时在拖拽时会产生不同的图标样式。
  • DataPackageView:这个属性是用来获取拖拽进来的对象的数据的,根据它可以拿到拖拽对象。
  • DragUIOverride:这个是用来自定义拖拽时的UI外观的,可以改变拖拽时的图标、提示语、是否显示图标和提示语等。

要想让元素接受拖拽对象到它自己本身,我们要设置元素的AllowDrop属性为True,拖拽会触发四种事件:DragEnter(进入接受拖拽的范围)、DragOver(处于接受拖拽范围)、Drop(松开鼠标)、DragLeave(离开接受拖拽的范围),整体的拖拽流程是这样的:

而我们需要订阅该元素的Drop、DragOver两个事件,DragOver事件会在拖拽对象到该元素时一直被触发,而当鼠标松开拖拽时会触发Drop事件。

接下来,我们演示下从系统磁盘文件夹拖拽一些vcf联系人到我们的App中。

界面上放置三个区域:接受拖拽区、文件显示区、拖拽删除区

 1 <Grid  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
 2       <Grid.RowDefinitions>
 3           <RowDefinition Height="*"/>
 4           <RowDefinition Height="8*"/>
 5           <RowDefinition Height="*"/>
 6       </Grid.RowDefinitions>
 7  
 8       <!--接受拖拽的区域 Start-->
 9       <Border AllowDrop="True"
10               Drop="VcBorder_Drop"
11               DragOver="VcBorder_DragOver"
12               Background="{ThemeResource ToolTipForegroundThemeBrush}" >
13           <TextBlock Text="请尝试拖动vcf名片到这里" RequestedTheme="Dark" HorizontalAlignment="Center" VerticalAlignment="Center"  />
14       </Border>
15       <!--接受拖拽的区域 End-->
16  
17       <!--拖拽过来的文件显示区 Start-->
18       <Grid Grid.Row="1" >
19           <ListView x:Name="VcList" CanDragItems="True"  DragItemsStarting="VcList_DragItemsStarting"
20                     ItemsSource="{x:Bind VCards }" >
21               <ListView.ItemTemplate>
22                   <DataTemplate x:DataType="local:LinkManModel">
23                       <Grid Margin="0,8">
24                           <Grid.ColumnDefinitions>
25                               <ColumnDefinition Width="2*"/>
26                               <ColumnDefinition Width="3*"/>
27                           </Grid.ColumnDefinitions>
28                           <Grid>
29                               <Image Source="Assets/I Am 1%.jpg"  />
30                               <Image Source="{x:Bind Img}"  />
31                           </Grid>
32                           <Grid Margin="12,0" Grid.Column="1">
33                               <Grid.RowDefinitions>
34                                   <RowDefinition Height="*"/>
35                                   <RowDefinition Height="*"/>
36                                   <RowDefinition Height="*"/>
37                               </Grid.RowDefinitions>
38                               <TextBlock>
39                                     <Run Text="姓名:"/>
40                                     <Run Text="{x:Bind Name}"/>
41                               </TextBlock>
42                               <TextBlock Grid.Row="1">
43                                     <Run Text="Phone:"/>
44                                     <Run Text="{x:Bind Phone}"/>
45                               </TextBlock>
46                               <TextBlock Grid.Row="2">
47                                    <Run Text="Email:"/>
48                                    <Run Text="{x:Bind Email}"/>
49                               </TextBlock>
50                           </Grid>
51                       </Grid>
52                   </DataTemplate>
53               </ListView.ItemTemplate>
54           </ListView>
55       </Grid>
56       <!--拖拽过来的文件显示区 End-->
57  
58       <!--拖拽删除区 Start-->
59       <Border Grid.Row="2" x:Name="DelBorder"
60           AllowDrop="True"
61               Drop="DelBorder_Drop"
62               DragOver="DelBorder_DragOver"
63               Background="{ThemeResource ToolTipForegroundThemeBrush}">
64           <TextBlock Text="请拖动名片到这里来删除" RequestedTheme="Dark" HorizontalAlignment="Center" VerticalAlignment="Center"  />
65       </Border>
66       <!--拖拽删除区 End-->
67   </Grid>

后台代码:

  1 public sealed partial class MainPage : Page
  2 {
  3  
  4     public ObservableCollection<LinkManModel> VCards = new ObservableCollection<LinkManModel>();
  5  
  6     public MainPage()
  7     {
  8         this.InitializeComponent();
  9     }
 10  
 11     /// <summary>
 12     /// 拖拽完成
 13     /// </summary>
 14     private async void VcBorder_Drop(object sender, DragEventArgs e)
 15     {
 16         Debug.WriteLine("[Info] Drop");
 17  
 18         if (e.DataView.Contains(StandardDataFormats.StorageItems))
 19         {
 20             Debug.WriteLine("[Info] DataView Contains StorageItems");
 21             var items = await e.DataView.GetStorageItemsAsync();
 22  
 23             //文件过滤 只取vcf文件 PS:如果拖过来的是文件夹 则需要对文件夹处理 取出文件夹文件
 24             items = items.OfType<StorageFile>()
 25                 .Where(s => s.FileType.Equals(".vcf")).ToList() as IReadOnlyList<IStorageItem>;
 26             if (items != null && items.Any())
 27             {
 28                 //添加VCard
 29                 await AddVCard(items);
 30             }
 31         }
 32     }
 33  
 34     /// <summary>
 35     /// 添加VCard
 36     /// </summary>
 37     /// <param name="items"></param>
 38     /// <returns></returns>
 39     private async Task AddVCard(IReadOnlyList<IStorageItem> items)
 40     {
 41         foreach (var item in items)
 42         {
 43             #region 图片的处理
 44             //var storageFile = item as StorageFile;
 45             //var bitmapImage = new BitmapImage();
 46             //await bitmapImage.SetSourceAsync(await storageFile.OpenAsync(FileAccessMode.Read));
 47             #endregion
 48  
 49             var linkMan = new LinkManModel();
 50             var storageFile = item as StorageFile;
 51             var stream = await storageFile.OpenStreamForReadAsync();
 52             using (StreamReader reader = new StreamReader(stream))
 53             {
 54                 var str = reader.ReadToEnd();
 55                 var vcard = VCard.Parse(str);
 56                 var info = vcard.Properties;
 57                 linkMan.Name = info.FirstOrDefault(s => s.Name == "FN") == null ? null : info.FirstOrDefault(s => s.Name == "FN").EncodedValue;
 58                 linkMan.Phone = info.FirstOrDefault(s => s.Name == "TEL") == null ? null : info.FirstOrDefault(s => s.Name == "TEL").EncodedValue;
 59                 linkMan.Email = info.FirstOrDefault(s => s.Name == "EMAIL") == null ? null : info.FirstOrDefault(s => s.Name == "EMAIL").EncodedValue;
 60                 var photoStr = info.FirstOrDefault(s => s.Name == "PHOTO") == null ? null : info.FirstOrDefault(s => s.Name == "PHOTO").EncodedValue;
 61                 if (photoStr != null)
 62                     linkMan.Img = await convertToImage(photoStr);
 63  
 64             }
 65             if (linkMan != null)
 66             {
 67                 VCards.Add(linkMan);
 68             }
 69         }
 70     }
 71  
 72     /// <summary>
 73     /// base64 To BitmapImage
 74     /// </summary>
 75     private async static Task<BitmapImage> convertToImage(string strimage)
 76     {
 77         try
 78         {
 79             byte[] bitmapArray;
 80             bitmapArray = Convert.FromBase64String(strimage);
 81             MemoryStream ms = new MemoryStream(bitmapArray);
 82             InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();
 83             //将randomAccessStream 转成 IOutputStream
 84             var outputstream = randomAccessStream.GetOutputStreamAt(0);
 85             //实例化一个DataWriter
 86             DataWriter datawriter = new DataWriter(outputstream);
 87             //将Byte数组数据写进OutputStream
 88             datawriter.WriteBytes(bitmapArray);
 89             //在缓冲区提交数据到一个存储区
 90             await datawriter.StoreAsync();
 91  
 92             //将InMemoryRandomAccessStream给位图
 93             BitmapImage bitmapImage = new BitmapImage();
 94             bitmapImage.SetSource(randomAccessStream);
 95  
 96             return bitmapImage;
 97         }
 98         catch
 99         {
100             return null;
101         }
102     }
103  
104     /// <summary>
105     /// 进入到接受拖拽区
106     /// </summary>
107     private void VcBorder_DragOver(object sender, DragEventArgs e)
108     {
109         Debug.WriteLine("[Info] DragOver");
110         //设置操作类型
111         e.AcceptedOperation = DataPackageOperation.Copy;
112  
113         //设置提示文字
114         e.DragUIOverride.Caption = "拖放此处即可添加文件 o(^▽^)o";
115  
116         ////是否显示拖放时的文字 默认为true
117         //e.DragUIOverride.IsCaptionVisible = true;
118  
119         ////是否显示文件图标,默认为true
120         //e.DragUIOverride.IsContentVisible = true;
121  
122         ////Caption 前面的图标是否显示。默认为 true
123         //e.DragUIOverride.IsGlyphVisible = true;
124  
125         ////自定义文件图标,可以设置一个图标
126         //e.DragUIOverride.SetContentFromBitmapImage(new BitmapImage(new Uri("ms-appx:///Assets/copy.jpg")));
127     }
128  
129     /// <summary>
130     /// 要删除的项
131     /// </summary>
132     LinkManModel DelItem;
133  
134     /// <summary>
135     /// 开始拖拽Item 以准备删除
136     /// </summary>
137     private void VcList_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
138     {
139         DelItem = e.Items.FirstOrDefault() as LinkManModel;
140     }
141  
142     /// <summary>
143     /// 拖拽删除完成
144     /// </summary>
145     private void DelBorder_Drop(object sender, DragEventArgs e)
146     {
147         VCards.Remove(DelItem);
148     }
149  
150     /// <summary>
151     /// 进入拖拽删除区
152     /// </summary>
153     private void DelBorder_DragOver(object sender, DragEventArgs e)
154     {
155         //设置操作类型
156         e.AcceptedOperation = DataPackageOperation.Move;
157         e.DragUIOverride.Caption = "删除";
158         e.DragUIOverride.IsContentVisible = false;
159     }
160      
161 }
162  
163 public class LinkManModel
164 {
165     public string Name { get; set; }
166  
167     public string Email { get; set; }
168  
169     private string _Phone;
170     public string Phone { get { return string.IsNullOrEmpty(_Phone)? null : Regex.Replace(_Phone, @"(?im)(\d{3})(\d{4})(\d{4})", "$1***$3"); } set { _Phone = value; } }
171     public BitmapImage Img { get; set; }
172 }

效果:

 

推荐一个UWP开发群:53078485 大家可以进来一起学习