在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 拖动打开文件-LMLPHP

而我们需要订阅该元素的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();
//实例化一个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 拖动打开文件-LMLPHP

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

04-26 14:59