UWP Code : ทำ Drag & Drop ด้วย C# บน Windows 10 (UWP)
ผมได้มีโอกาสทำแอปบน Windows 10 แอปนึง ก็แอป immortal table ของผมนั่นแหละ กำลัง Port มายัง Windows 10 พอทำแล้ว ได้เรียนรู้หลายๆอย่างสนุกดีครับ ซึ่งเป็นช่วงที่มีข่าวร้ายๆเกี่ยวกับแพลตฟอร์มนี้ต่อเนื่องเลยทีเดียว แต่จะเป็น developer ก็ต้องเปิดใจครับ ไม่ยึดติด ฮ่าๆ ส่วนตัวผมก็เชีย Microsoft อยู่ เดี๋ยวบทความนี้จะมาสอนเรื่องทำ drag & drop ด้วย C# บน Windows 10 เป็น UWP ซึ่งผมพึ่งได้มาประยุกต์ใช้ในการทำแอปของผมด้วย เป็นการทบทวนความรู้ของผมไปในตัวด้วยครับ
รู้จักกับ Universal Windows Platform
ขอเกริ่นนำสักเล็กน้อย เกี่ยวกับ UWP พูดง่ายๆคือ ทำแอปแบบครอบจักรวาลของ Windows ในแอปเดียว หมายความว่า สามารถนำแอปที่ทำเสร็จ ไปรันได้ในแพลตฟอร์มในสาย Windows ได้เลยทั้งหมด (ง่ายขนาดนั้นเลยหรอ) ซึ่งในที่นี้ Microsoft ใช้ Windows 10 เป็นจุดขาย และเรียกอุปกรณ์ในแต่ละตระกูลเป็น device family เช่น มือถือ คอม xbox ดังนั้นแน่นอนว่าต่อจากนี้ถ้าจะทำแอปบน Windows 10 ก็ควรจะทำแบบ UWP เรียกได้ว่า ถ้าคอนเซ็ปทั้งหมดมันทำได้จริง ก็ถือว่าเป็นจุดขายที่ไม่ธรรมดาเลย
อ่านเพิ่มเติม
https://msdn.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide
Drag & Drop
อะไรคือ Drag&drop มันคือการลากวาง เช่น ลากโฟลเดอร์ ลากไฟล์ไปมาใส่โฟลเดอร์ อันนี้ก็ใช่นะครับ เรียกว่าเป็นปฏิสัมพันธ์แบบนึงที่พบเห็นได้ทั่วไปเลย จนไปอยู่ในมือถือระบบสัมผัสก็ยังคงคอนเซ้ปเดิม แต่ drag drop นี้ก็ไม่ได้เหมาะไปเสียหมดนะครับ ต้องใช้ให้เหมาะสมด้วย แถมยังสื่อสารกับผู้ใช้หน้าใหม่ยากด้วย คนเข้ามาใช้ครั้งแรกก็อาจจะไม่รู้ว่าสามารถลากอันนี้ได้นะ ดังนั้นควรมี guideline บอกผู้ใช้หน้าใหม่ด้วยจะดีมากครับ
เริ่มต้นโปรเจค
เปิด Visual Studio 2015 ขึ้นมาครับหรือเวอชันสูงกว่า ตอนนี้ VS 2015 community มีให้โหลดฟรีด้วยนะ
ให้ทำการ New project และเลือก สร้างแบบ Visual C# > Windows > Universal แล้วเลือก Blank App
กำหนด Layout
หลังจากสร้าง project มา ก็จะได้ project ปล่าวๆมาครับ ให้กำหนด XAML ดังนี้ครับ โดยกำหนด จะมี TextBlock ไว้ด้านบน ตั้งชื่อว่า txt_hello และมีปุ่มไว้ด้านล่าง ชื่อว่า btn เราจะลาก TextBlock ด้านบนไปแปะปุ่มด้านล่าง แล้วปุ่มจะเปลี่ยนสี สั้นๆง่ายๆแบบนี้เลย
MainPage.xaml
<Page x:Class="dragexample.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:dragexample" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <TextBlock x:Name="txt_hello" Text="Hello" FontSize="96" Margin="0,50,0,0" HorizontalAlignment="Center" VerticalAlignment="Top"></TextBlock> <Button x:Name="btn" Content="Ok" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,50"></Button> </Grid> </Page>
Event
แน่นอนว่า เราต้องมาจัดการเรื่อง event ของการ drag ซึ่งไม่ต้องห่วงไปครับ เขามีให้ครบครันเลย ทั้ง เริ่มลาก ลากเข้า ลากออก ปล่อย วิธีการเพิ่ม event สามารถทำได้หลายวิธี เช่น ทำผ่านตัว GUI ก็ได้ โดยกดที่ object TextBlock แล้วไปเลือกที่ไอคอนรูปสายฟ้า ด้านขวา จะปรากฏรายชื่อ event แล้วก็จะเห็น event drag อยู่ด้วย มี DragEnter DragLeave DragOver Drop มีให้ใช้อย่างสนุกสนานเลยทีเดียวถ้าดับเบิ้ลคลิกไปที่ช่อง มันจะทำการ generate method ให้ แต่เดียวก่อนนะ อีกวิธี คือการทำ delegate อันนี้จะดู (เท่กว่านิดนึง) ผมเลือกวิธีหลัง
ประกาศตัวแปรพระเอก
MainPage.xaml.cs
ประกาศเป็น Global variable
IAsyncOperation<DataPackageOperation> dragOperation;
กำหนด event สำหรับสำหรับต้นทาง
โดยเราจะต้องจัดการกับ event 2 ตัวบน TextBlock คือ
DragStarting คือ ตอนเริ่มลาก
PointerMoved คือ ตอน เริ่มจะลาก และกำลังเลื่อนนิ้วไปบนจอ (ขณะลาก)
โดยเมื่อเราเริ่มลากมันจะเข้า PointMoved ก่อน ไปเรียก StartDragAsync แล้ว DragStarting ก็จะทำงาน จากนั้นเมื่อเราเลื่อนนิ้วไปบนจอ มันก็จะเรียด PointerMoved รัวๆ แต่จะเห็นว่า dragOperation ไม่ได้เป็น null มันก็เลยไม่ได้ทำงานอะไร
// set drag starting. txt_hello.DragStarting += delegate (UIElement sender, DragStartingEventArgs e) { e.Data.RequestedOperation = DataPackageOperation.Copy; }; // set point move. txt_hello.PointerMoved += delegate (object sender, PointerRoutedEventArgs e) { if (e.Pointer.IsInContact && (dragOperation == null)) { dragOperation = txt_hello.StartDragAsync(e.GetCurrentPoint(txt_hello)); dragOperation.Completed = DragCompleted; } };
แล้วก็อย่าลืมมา implement method หลังจาก drag เสร็จแล้วด้วย ทำเสร็จแล้วก็ให้ dragOperation = null
private void DragCompleted(IAsyncOperation<DataPackageOperation> asyncInfo, AsyncStatus asyncStatus) { dragOperation = null; // if drag success to target. //(asyncStatus == AsyncStatus.Completed) && (asyncInfo.GetResults() == DataPackageOperation.Copy); }
ถ้าจะมีการเก็บข้อมูลด้วย เช่น ลากไอเท็มไปไว้ที่ตัวละคร เราสามารถดักได้ที่ DragStrating ก็สร้างตัวแปรมาอีกสักตัวนึงเก็บ ว่ากำลังลากคืออะไร และอย่าลืมต้องมีการไปเคลียค่าที่ DragCompleted ด้วย
กำหนด event สำหรับสำหรับปลายทาง
ที่ปลายทางก็ต้องกำหนด ที่ Button เป้าหมายที่จะวาง หลักๆ คือต้องกำหนด event ดังนี้
drop หมายถึงตอนปล่อย
dragEnter คือ ตอนลากเข้ามาในเป้าหมาย ยังไม่ปล่อย
dragLeave คือ ลากออกไป
// Drag enter over. btn.DragEnter += delegate (object sender, Windows.UI.Xaml.DragEventArgs e) { bool accept = (dragOperation != null) && e.DataView.Contains(StandardDataFormats.Text); if (accept) { // Drag on here but not drop yet. } e.AcceptedOperation = accept ? DataPackageOperation.Copy : DataPackageOperation.None; e.DragUIOverride.IsCaptionVisible = false; }; // Drag exit from grid. btn.DragLeave += delegate (object sender, Windows.UI.Xaml.DragEventArgs e) { if (dragOperation != null) { // Drag leave } }; // Drop item on grid. btn.Drop += delegate (object sender, Windows.UI.Xaml.DragEventArgs e) { bool accept = (dragOperation != null) && e.DataView.Contains(StandardDataFormats.Text) ; if (accept) { // Drop here change color. btn.Background = new SolidColorBrush(Colors.Red); } e.AcceptedOperation = accept ? DataPackageOperation.Copy : DataPackageOperation.None; };
Allow Drag and Drop
อย่าลืมไปเปิดอนุญาติ Drag & Drop ไม่อย่างนั้นอาจใช้งานไม่ได้ ให้เลือกที่ TextBlock แล้วสังเกตที่ Properties เมนู Miscellaneous จากนั้นติ๊ก CanDrag
TextBlock xaml
<TextBlock x:Name="txt_hello" Text="Hello" FontSize="96" Margin="0,50,0,0" HorizontalAlignment="Center" VerticalAlignment="Top" CanDrag="True"></TextBlock>
ที่ปลายทางก็ต้องไปติ๊กเหมือนกัน ให้เลือกที่ Button แล้วสังเกตที่ Properties เมนู Layout จากนั้นติ๊ก AllowDrop
Button xmal
<Button x:Name="btn" Content="Ok" FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,50" AllowDrop="True"></Button>
Sample Project
พี่ Microsoft ทำ sample ไว้อย่างดีมากๆ มีเยอะมากๆด้วย ลองเข้าไปดูตัวอย่างได้ครับ
https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlDragAndDrop
สรุป
การทำ drag and drop ไม่ได้ซับซ้อน และตรงไปตรงมามากครับ มี event ให้จัดการได้ครอบคลุม แค่ implement event ที่ต้นทาง เช่น DragStarting , PointMoved แล้วก็ปลายทางเช่น Drop เป็นต้น ส่วน event อื่นๆ อยู่ที่จะนำไปประยุกต์ใช้งานครับ