Windows 10 UWP 10 of N: Drag and Drop
今天來介紹個Windows APP重大的功能~就是拖拉!現在很多裝置都是觸控(Touch)為操控而直覺的手製(Gesture) 漸漸地變得越來越重要!
在Windows 8.1也是有支援拖拉但...只限定在App之中!而在Windows 10 的APP已經像是Win32的傳統應用程式進行拖拉。
請看以下圖片示範
先建立三個區塊並放入ListView(使用Red Green Blue為區分三大區塊!)
接者示範拖曳圖片進去APP的最左邊的ListView(Red)可以看到在拖曳進去APP的時候會顯示Copy的小Popup!
但拖曳到中間(Green)以及右邊(Blue)的ListView就無法丟入圖片的處理!
左邊的圖片拖曳之後丟下(Drop)所做的簡單UI。
當第一張圖片拖入中間的ListView時候(還未丟下的行為)在左圖呈現,而右邊的圖片則是將第二張圖片拖曳並放入到最右邊的ListView的呈現畫面。
那這麼方便的功能請參考以下的Code
x:Class="UWP_Sample1.View.DragAndDropPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWP_Sample1.View"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:UWP_Sample1.Model"
DataContext="{Binding Source={StaticResource DragDropPageVM}}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="SampleDataTemplate" x:DataType="model:SampleModel">
<StackPanel Margin="10">
<TextBlock Text="{x:Bind Title}"/>
<TextBlock Text="{x:Bind SubTitle}"/>
<Image Source="{x:Bind ItemImageSource}"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0" Tag="Col0Lv" Header="Listview1" AllowDrop="True" CanDragItems="True" SelectionMode="Extended" ItemTemplate="{StaticResource SampleDataTemplate}" ItemsSource="{x:Bind DragDropPageVM.SampleCol0Collection}" DragOver="{x:Bind DragDropPageVM.ListViewBase_DragOver}" DragItemsStarting="{x:Bind DragDropPageVM.ListViewBase_DragItemsStarting}" Drop="{x:Bind DragDropPageVM.ListViewBase_Drop}"/>
<ListView Grid.Column="1" Tag="Col1Lv" Header="Listview2" AllowDrop="True" CanDragItems="True" SelectionMode="None" ItemTemplate="{StaticResource SampleDataTemplate}" ItemsSource="{x:Bind DragDropPageVM.SampleCol1Collection}" DragOver="{x:Bind DragDropPageVM.ListViewBase_DragOver}" Drop="{x:Bind DragDropPageVM.ListViewBase_Drop}"/>
<ListView Grid.Column="2" Tag="Col2Lv" Header="Listview3" AllowDrop="True" CanDragItems="True" SelectionMode="None" ItemTemplate="{StaticResource SampleDataTemplate}" ItemsSource="{x:Bind DragDropPageVM.SampleCol2Collection}" DragOver="{x:Bind DragDropPageVM.ListViewBase_DragOver}" Drop="{x:Bind DragDropPageVM.ListViewBase_Drop}"/>
</Grid>
</Page>
AllowDrop、CanDragItems為True。AllowDrop就如同字面意思依樣是否允許被丟下(Drop)、而CanDragItems就是是否能夠拖曳List內的Item。
目前設定在第一個的ListView的SelectionMode為Extended就是可以被多選的狀況。然後我定了三個ListView的ItemTemplate在Page的Resource內並使用x:Bind進行事件的Binding;DragOver以及Drop的事件分別處理的是拖曳到該Control所觸發的和丟下所觸發的事件!
接者到ViewModel的各個事件來說明詳細的Code撰寫。
DragOver就是拖曳物件到該Control的時候觸發的事件,我只有設定在listview的Tag是col0lv的時候並且DataView是包含StorageItem就可以進行Operation。這邊需要注意的事是StorageItems是可以為任何物件所以在如果要做更加深入的拖曳進APP的Item判斷可能就得要確認附檔名了!
DataOperation目前所支援的動作如上。
接者來做Drop的事件處理
這邊要注意Col0Lv的處理比較簡單!就是將拖曳進該控制項的物件解析成BitmapImage並再放入SampleModel的ImageSource。
而Col1Lv以及Col2Lv需要特別注意!解析文字的原因是因為在DragItemOver的事件來決定的!
在Col0Lv的XAML我們有x:Bind DragItemsStarting事件,這個事件就是在Col0Lv的Item開始被拖曳的時候將該Title設定為傳送的ID代碼!( 這邊最好是使用GUID來避免錯誤的拖曳資料,由於個人懶惰所以使用Title[檔名]作為識別的GUID )這樣就可以組合成GUID的列表來作為Col0Lv丟Item給Col1Lv和Col2Lv的依據了!所以在Col1Lv和Col2Lv的Drop事件就是拆解字串並將Col0Lv的Item轉送到個別的Item中。
最後ViewModel完整的Code如下
{
private ObservableCollection<SampleModel> _SampleCol0Collection;
public ObservableCollection<SampleModel> SampleCol0Collection
{
get { return _SampleCol0Collection; }
}
private ObservableCollection<SampleModel> _SampleCol1Collection;
public ObservableCollection<SampleModel> SampleCol1Collection
{
get { return _SampleCol1Collection; }
}
private ObservableCollection<SampleModel> _SampleCol2Collection;
public ObservableCollection<SampleModel> SampleCol2Collection
{
get { return _SampleCol2Collection; }
}
public DragAndDropPageViewModel()
{
_SampleCol0Collection = new ObservableCollection<SampleModel>();
_SampleCol1Collection = new ObservableCollection<SampleModel>();
_SampleCol2Collection = new ObservableCollection<SampleModel>();
}
public void ListViewBase_DragOver(object sender, DragEventArgs e)
{
if (sender is ListView)
{
switch ((sender as ListView).Tag.ToString())
{
case "Col0Lv":
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var isImages = false;
foreach (StorageFile item in await e.DataView.GetStorageItemsAsync())
{
if (item.FileType.ToLower() == ".jpg" || item.FileType.ToLower() == ".jpeg" || item.FileType.ToLower() == ".gif" || item.FileType.ToLower() == ".png")
isImages = true;
else
isImages = false;
}
if (isImages)
e.AcceptedOperation = DataPackageOperation.Copy;
else
e.AcceptedOperation = DataPackageOperation.None;
}
break;
case "Col1Lv":
case "Col2Lv":
if (e.DataView.Contains(StandardDataFormats.Text))
e.AcceptedOperation = DataPackageOperation.Move;
break;
}
}
}
public async void ListViewBase_Drop(object sender, DragEventArgs e)
{
if (sender is ListView)
{
switch ((sender as ListView).Tag.ToString())
{
case "Col0Lv":
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await e.DataView.GetStorageItemsAsync();
foreach (StorageFile item in storageItems)
{
var bImg = new BitmapImage();
await bImg.SetSourceAsync(await item.OpenReadAsync());
_SampleCol0Collection.Add(new SampleModel()
{
Title = item.DisplayName,
SubTitle = item.ContentType,
ItemImageSource = bImg
});
}
}
break;
case "Col1Lv":
if (e.DataView.Contains(StandardDataFormats.Text))
{
var titles = await e.DataView.GetTextAsync();
var itemsToMove = titles.Split(',');
foreach (var moveItemTitle in titles.Split(','))
{
var tempItem = _SampleCol0Collection.First(i => i.Title == moveItemTitle);
_SampleCol1Collection.Add(tempItem);
_SampleCol0Collection.Remove(tempItem);
}
}
break;
case "Col2Lv":
if (e.DataView.Contains(StandardDataFormats.Text))
{
var titles = await e.DataView.GetTextAsync();
var itemsToMove = titles.Split(',');
foreach (var moveItemTitle in titles.Split(','))
{
var tempItem = _SampleCol0Collection.First(i => i.Title == moveItemTitle);
_SampleCol2Collection.Add(tempItem);
_SampleCol0Collection.Remove(tempItem);
}
}
break;
}
}
}
public void ListViewBase_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
if (sender is ListView)
{
var items = string.Join(",", e.Items.Cast<SampleModel>().Select(i => i.Title));
e.Data.SetText(items);
e.Data.RequestedOperation = DataPackageOperation.Move;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyChange([CallerMemberName]string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
***以上Code以及說明都有可能隨著Windows 10 的版本以及Visual Studio 2015版本有所調整!***
參考資料 MSDN
下次再分享Windows 10 的新技術拉~