先日行われた、Room metro #28 「XAML Day」で、しゃべってきました。
ネタかぶりしなさそうなWF(Window Workflow Foundation)がらみのはずなのに、まさかのネタかぶりとかありましたが、実際はWPFのコードしか書いてないのでかぶってないはず(
資料は、↓で公開しています。
また、デモに使ったコードは↓です。
先日行われた、Room metro #28 「XAML Day」で、しゃべってきました。
ネタかぶりしなさそうなWF(Window Workflow Foundation)がらみのはずなのに、まさかのネタかぶりとかありましたが、実際はWPFのコードしか書いてないのでかぶってないはず(
資料は、↓で公開しています。
また、デモに使ったコードは↓です。
この記事は、XAML Advent Calendarの第21日目の記事です。
WPFは、XAMLでUIを定義するプラットフォームの始祖にしてもっとも強力な機構を持っています。
そこで、他プラットフォームでは使えないWPFならではの記述を2つ紹介します。
サンプルコードはこちら
<Window x:Class="WpfSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:WpfSample.ViewModels" Title="MainWindow" Height="200" Width="250"> <Grid> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.Resources> <DataTemplate DataType="{x:Type vm:ButtonViewModel}"> <Button Content="{Binding Text}" Command="{Binding Command}"></Button> </DataTemplate> <DataTemplate DataType="{x:Type vm:TextBoxViewModel}"> <TextBox Text="{Binding Text}"/> </DataTemplate> </ItemsControl.Resources> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas></Canvas> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Canvas.Left" Value="{Binding Left}"/> <Setter Property="Canvas.Top" Value="{Binding Top}"/> <Setter Property="Canvas.Width" Value="{Binding Width}"/> <Setter Property="Canvas.Height" Value="{Binding Height}"/> </Style> </ItemsControl.ItemContainerStyle> </ItemsControl> </Grid> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using WpfSample.ViewModels; namespace WpfSample { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var text = new TextBoxViewModel { Left = 10, Top = 10, Width = 200, Height = 20, Text = "Hello" }; var button = new ButtonViewModel { Left = 90, Top = 120, Width = 100, Height = 20, Text = "Click", Command = new RelayCommand(new Action(()=> MessageBox.Show(text.Text))), }; this.DataContext = new List<ViewModelBase> { text, button}; } } }
実行結果はこちら。
まずひとつ目から、
ItemsControl.ItemsContainerSytleで定義しているように、StyleのSetterのValueにBindingを指定することができます。
これにより、ContentControlの各プロパティーとViewModelのプロパティーのBindingが可能になります。これはなかなか便利なのでおすすめ。
よく使うのは、上の例のように、ItemPanel上での位置情報をBindingで指定したり、あとはListBoxのIsSelectedをViewModel側で参照したいときとかでしょうか。
ふたつ目は、DataTemplateのDataTypeプロパティー。
このプロパティーを利用すると、ViewModelの型で適用するStyleを切り替えることができます。上の例では、TextBoxとButtonのコントロールをViewModelの型で指定しています。
動的に画面の構成が変わる画面を作成する場合は、ひとつ目のBindingの仕組みと組み合わせると便利です。
以上、他のXAMLプラットフォームからWPFに来た方は参考にしてみてください。
※ItemsControlのBinding周りは、XAML Advent Calendar 1日目のぐらばくさんのエントリーが非常にくわしいです。
なお、ソースコードの全体は、githubに上げてあります。
前回の続き。Room metro #16のハンズオン2つ目です。
今回使うのは、PointerXXXXというイベント群です。これらのイベントに共通するイベント引数PointerRoutedEventArgsからは、PointerPointクラスが取得でき、タッチポイントに関するさまざまな情報が取得できます。このクラスから取得できるPointerIdを利用して、今発生しているイベントの入力ポインターを識別できます。
private void Canvas_PointerEntered(object sender, PointerRoutedEventArgs e) { var canvas = (Canvas)sender; var pointerPoint = e.GetCurrentPoint(canvas); // 入力ポイントを一意に識別するId uint pid = pointerPoint.PointerId; }
PointerIdと描画中の線を紐づけておくことで、マルチタッチでお絵かきが可能になります。
[5/18追記]プロジェクトをGitHubにあげました。
https://github.com/yone64/RoomMetro16/tree/master/Pointer
XAML側
<Page x:Class="Pointer.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Pointer" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Canvas Background="{StaticResource ApplicationPageBackgroundThemeBrush}" PointerPressed="Canvas_PointerPressed" PointerMoved="Canvas_PointerMoved" PointerReleased="Canvas_PointerReleased" > </Page>
コードビハインド側
/// <summary> /// PointerIdと描画中の線を紐づけるDictionary /// </summary> private Dictionary<uint, Polyline> dic = new Dictionary<uint, Polyline>(); // Pointerが押されたときのイベント private void Canvas_PointerPressed(object sender, PointerRoutedEventArgs e) { // PointerPointの取得 var canvas = (Canvas)sender; var pointerPoint = e.GetCurrentPoint(canvas); // 赤色でPolylineを描画 var line = new Polyline { StrokeThickness = 3, Stroke = new SolidColorBrush(Colors.Red) }; // Polylineに頂点を追加 line.Points.Add(pointerPoint.Position); // PointerIdとPolylineを紐づけ dic[pointerPoint.PointerId] = line; // キャンバスに描画する線を追加 canvas.Children.Add(line); // MoveイベントがCanvas上で発生するようにPointerをキャプチャ canvas.CapturePointer(e.Pointer); } // Pointerが動いた時のイベント private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e) { // PointerPointの取得 var pointerPoint = e.GetCurrentPoint((UIElement)sender); var pid = pointerPoint.PointerId; // Moveイベントは押下中じゃなくても発生するので // 描画中かどうかの判断を行う。 if (dic.ContainsKey(pid)) { // 描画中のPolylineに頂点を追加する dic[pid].Points.Add(pointerPoint.Position); } } // Pointerが離された時のイベント private void Canvas_PointerReleased(object sender, PointerRoutedEventArgs e) { // PointerPointの取得 var canvas = (Canvas)sender; var pointerPoint = e.GetCurrentPoint(canvas); // PointerIdとPolylineを紐づけを解除し、描画を終了。 dic.Remove(pointerPoint.PointerId); // Pointerのキャプチャも終了する canvas.ReleasePointerCapture(e.Pointer); }
実行結果
補足
MSDNにある通り、PointerReleasedイベントの代わりにPointerCanceledやPointerCaptureLostイベントが発生することがあるので、きちんとそちらのイベントも対応する必要があります。