Windows 10 UWP 10 of N: Drag and Drop

  • 743
  • 0
  • UAP
  • 2021-04-30

Windows 10 UWP 10 of N: Drag and Drop

今天來介紹個Windows APP重大的功能~就是拖拉!現在很多裝置都是觸控(Touch)為操控而直覺的手製(Gesture) 漸漸地變得越來越重要!

在Windows 8.1也是有支援拖拉但...只限定在App之中!而在Windows 10 的APP已經像是Win32的傳統應用程式進行拖拉。

請看以下圖片示範

image_thumb1

先建立三個區塊並放入ListView(使用Red Green Blue為區分三大區塊!)

image_thumb4

接者示範拖曳圖片進去APP的最左邊的ListView(Red)可以看到在拖曳進去APP的時候會顯示Copy的小Popup!

image_thumb7

但拖曳到中間(Green)以及右邊(Blue)的ListView就無法丟入圖片的處理!

image_thumb10

左邊的圖片拖曳之後丟下(Drop)所做的簡單UI。

image_thumb13image_thumb18

當第一張圖片拖入中間的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>
在22行可以看到只放入三個Listview為拖拉的主要接收Control!而這三個Listview都必須要設定

AllowDrop、CanDragItems為True。AllowDrop就如同字面意思依樣是否允許被丟下(Drop)、而CanDragItems就是是否能夠拖曳List內的Item。

目前設定在第一個的ListView的SelectionMode為Extended就是可以被多選的狀況。然後我定了三個ListView的ItemTemplate在Page的Resource內並使用x:Bind進行事件的Binding;DragOver以及Drop的事件分別處理的是拖曳到該Control所觸發的和丟下所觸發的事件!

接者到ViewModel的各個事件來說明詳細的Code撰寫。

        {
            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;
                }
            }
        }

DragOver就是拖曳物件到該Control的時候觸發的事件,我只有設定在listview的Tag是col0lv的時候並且DataView是包含StorageItem就可以進行Operation。這邊需要注意的事是StorageItems是可以為任何物件所以在如果要做更加深入的拖曳進APP的Item判斷可能就得要確認附檔名了!

    {
        //
        // Summary:
        //     No action. Typically used when the DataPackage object requires delayed rendering.
        None = 0,
        //
        // Summary:
        //     Copies the content to the target destination.
        Copy = 1,
        //
        // Summary:
        //     Copies the content to the target destination and deletes the original.
        Move = 2,
        //
        // Summary:
        //     Creates a link for the data.
        Link = 4
    }

DataOperation目前所支援的動作如上。

接者來做Drop的事件處理

        {
            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;
                }
            }
        }

這邊要注意Col0Lv的處理比較簡單!就是將拖曳進該控制項的物件解析成BitmapImage並再放入SampleModel的ImageSource。

而Col1Lv以及Col2Lv需要特別注意!解析文字的原因是因為在DragItemOver的事件來決定的!

        {
            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;
            }
        }

在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 的新技術拉~