在Win10 App开发中,微软新增了系统PC文件与UWP 之间的文件拖拽行为,它支持将系统磁盘上的文件以拖拽的形式拖入App中并处理,在前不久的微软build 2015开发者大会上微软展示的UWP版微信的拖拽文件就是使用的这个功能,接下来,我们一起看看该功能是怎么实现的。
首先我们要介绍的是 DragEventArgs 这个类,是在拖拽中为我们提供数据和UI样式定制的,在拖拽事件中,事件参数对象就是该类型,这个类在Win10中增加了一个新的接口的继承-> IDragEventArgs2 接口,该接口中提供如下新的成员属性:
internal interface IDragEventArgs2 { DataPackageOperation AcceptedOperation { get; set; } DataPackageView DataView { get; } DragUIOverride DragUIOverride { get; } DragDropModifiers Modifiers { get; } DragOperationDeferral GetDeferral(); }
其中有三个比较重要的:
- AcceptedOperation :这个是获取或设置指定拖动事件发起方可执行哪些操作,值是 DataPackageOperation 枚举类型。可以制定四种操作类型(None,Move,Copy,Link),指定不同类型时在拖拽时会产生不同的图标样式。
- DataPackageView :这个属性是用来获取拖拽进来的对象的数据的,根据它可以拿到拖拽对象。
- DragUIOverride :这个是用来自定义拖拽时的UI外观的,可以改变拖拽时的图标、提示语、是否显示图标和提示语等。
要想让元素接受拖拽对象到它自己本身,我们要设置元素的AllowDrop属性为True,拖拽会触发四种事件:DragEnter(进入接受拖拽的范围)、DragOver(处于接受拖拽范围)、Drop(松开鼠标)、DragLeave(离开接受拖拽的范围),整体的拖拽流程是这样的:
而我们需要订阅该元素的Drop、DragOver两个事件,DragOver事件会在拖拽对象到该元素时一直被触发,而当鼠标松开拖拽时会触发Drop事件。
接下来,我们演示下从系统磁盘文件夹拖拽一些vcf联系人到我们的App中。
界面上放置三个区域:接受拖拽区、文件显示区、拖拽删除区
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" > <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="8*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--接受拖拽的区域 Start--> <Border AllowDrop="True" Drop="VcBorder_Drop" DragOver="VcBorder_DragOver" Background="{ThemeResource ToolTipForegroundThemeBrush}" > <TextBlock Text="请尝试拖动vcf名片到这里" RequestedTheme="Dark" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <!--接受拖拽的区域 End--> <!--拖拽过来的文件显示区 Start--> <Grid Grid.Row="1" > <ListView x:Name="VcList" CanDragItems="True" DragItemsStarting="VcList_DragItemsStarting" ItemsSource="{x:Bind VCards }" > <ListView.ItemTemplate> <DataTemplate x:DataType="local:LinkManModel"> <Grid Margin="0,8"> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="3*"/> </Grid.ColumnDefinitions> <Grid> <Image Source="Assets/I Am 1%.jpg" /> <Image Source="{x:Bind Img}" /> </Grid> <Grid Margin="12,0" Grid.Column="1"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock> <Run Text="姓名:"/> <Run Text="{x:Bind Name}"/> </TextBlock> <TextBlock Grid.Row="1"> <Run Text="Phone:"/> <Run Text="{x:Bind Phone}"/> </TextBlock> <TextBlock Grid.Row="2"> <Run Text="Email:"/> <Run Text="{x:Bind Email}"/> </TextBlock> </Grid> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> <!--拖拽过来的文件显示区 End--> <!--拖拽删除区 Start--> <Border Grid.Row="2" x:Name="DelBorder" AllowDrop="True" Drop="DelBorder_Drop" DragOver="DelBorder_DragOver" Background="{ThemeResource ToolTipForegroundThemeBrush}"> <TextBlock Text="请拖动名片到这里来删除" RequestedTheme="Dark" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <!--拖拽删除区 End--> </Grid>
后台代码:
public sealed partial class MainPage : Page { public ObservableCollection<LinkManModel> VCards = new ObservableCollection<LinkManModel>(); public MainPage() { this.InitializeComponent(); } /// <summary> /// 拖拽完成 /// </summary> private async void VcBorder_Drop(object sender, DragEventArgs e) { Debug.WriteLine("[Info] Drop"); if (e.DataView.Contains(StandardDataFormats.StorageItems)) { Debug.WriteLine("[Info] DataView Contains StorageItems"); var items = await e.DataView.GetStorageItemsAsync(); //文件过滤 只取vcf文件 PS:如果拖过来的是文件夹 则需要对文件夹处理 取出文件夹文件 items = items.OfType<StorageFile>() .Where(s => s.FileType.Equals(".vcf")).ToList() as IReadOnlyList<IStorageItem>; if (items != null && items.Any()) { //添加VCard await AddVCard(items); } } } /// <summary> /// 添加VCard /// </summary> /// <param name="items"></param> /// <returns></returns> private async Task AddVCard(IReadOnlyList<IStorageItem> items) { foreach (var item in items) { #region 图片的处理 //var storageFile = item as StorageFile; //var bitmapImage = new BitmapImage(); //await bitmapImage.SetSourceAsync(await storageFile.OpenAsync(FileAccessMode.Read)); #endregion var linkMan = new LinkManModel(); var storageFile = item as StorageFile; var stream = await storageFile.OpenStreamForReadAsync(); using (StreamReader reader = new StreamReader(stream)) { var str = reader.ReadToEnd(); var vcard = VCard.Parse(str); var info = vcard.Properties; linkMan.Name = info.FirstOrDefault(s => s.Name == "FN") == null ? null : info.FirstOrDefault(s => s.Name == "FN").EncodedValue; linkMan.Phone = info.FirstOrDefault(s => s.Name == "TEL") == null ? null : info.FirstOrDefault(s => s.Name == "TEL").EncodedValue; linkMan.Email = info.FirstOrDefault(s => s.Name == "EMAIL") == null ? null : info.FirstOrDefault(s => s.Name == "EMAIL").EncodedValue; var photoStr = info.FirstOrDefault(s => s.Name == "PHOTO") == null ? null : info.FirstOrDefault(s => s.Name == "PHOTO").EncodedValue; if (photoStr != null) linkMan.Img = await convertToImage(photoStr); } if (linkMan != null) { VCards.Add(linkMan); } } } /// <summary> /// base64 To BitmapImage /// </summary> private async static Task<BitmapImage> convertToImage(string strimage) { try { byte[] bitmapArray; bitmapArray = Convert.FromBase64String(strimage); MemoryStream ms = new MemoryStream(bitmapArray); InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream(); //将randomAccessStream 转成 IOutputStream var outputstream = randomAccessStream.GetOutputStreamAt(0); //实例化一个DataWriter DataWriter datawriter = new DataWriter(outputstream); //将Byte数组数据写进OutputStream datawriter.WriteBytes(bitmapArray); //在缓冲区提交数据到一个存储区 await datawriter.StoreAsync(); //将InMemoryRandomAccessStream给位图 BitmapImage bitmapImage = new BitmapImage(); bitmapImage.SetSource(randomAccessStream); return bitmapImage; } catch { return null; } } /// <summary> /// 进入到接受拖拽区 /// </summary> private void VcBorder_DragOver(object sender, DragEventArgs e) { Debug.WriteLine("[Info] DragOver"); //设置操作类型 e.AcceptedOperation = DataPackageOperation.Copy; //设置提示文字 e.DragUIOverride.Caption = "拖放此处即可添加文件 o(^▽^)o"; ////是否显示拖放时的文字 默认为true //e.DragUIOverride.IsCaptionVisible = true; ////是否显示文件图标,默认为true //e.DragUIOverride.IsContentVisible = true; ////Caption 前面的图标是否显示。默认为 true //e.DragUIOverride.IsGlyphVisible = true; ////自定义文件图标,可以设置一个图标 //e.DragUIOverride.SetContentFromBitmapImage(new BitmapImage(new Uri("ms-appx:///Assets/copy.jpg"))); } /// <summary> /// 要删除的项 /// </summary> LinkManModel DelItem; /// <summary> /// 开始拖拽Item 以准备删除 /// </summary> private void VcList_DragItemsStarting(object sender, DragItemsStartingEventArgs e) { DelItem = e.Items.FirstOrDefault() as LinkManModel; } /// <summary> /// 拖拽删除完成 /// </summary> private void DelBorder_Drop(object sender, DragEventArgs e) { VCards.Remove(DelItem); } /// <summary> /// 进入拖拽删除区 /// </summary> private void DelBorder_DragOver(object sender, DragEventArgs e) { //设置操作类型 e.AcceptedOperation = DataPackageOperation.Move; e.DragUIOverride.Caption = "删除"; e.DragUIOverride.IsContentVisible = false; } } public class LinkManModel { public string Name { get; set; } public string Email { get; set; } private string _Phone; public string Phone { get { return string.IsNullOrEmpty(_Phone)? null : Regex.Replace(_Phone, @"(?im)(/d{3})(/d{4})(/d{4})", "$1***$3"); } set { _Phone = value; } } public BitmapImage Img { get; set; } }
效果: