转载

UWP/Win10新特性系列—Drag&Drop

在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(离开接受拖拽的范围),整体的拖拽流程是这样的:

UWP/Win10新特性系列—Drag&Drop

而我们需要订阅该元素的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; } } 

效果:

UWP/Win10新特性系列—Drag&amp;Drop

正文到此结束
Loading...