在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 大家可以进来一起学习