泥庭

2014年12月1日

Room metro #28 でしゃべってきました。

Filed under: .NET, WPF, 勉強会, 未分類 — タグ: , , , — yone64 @ 11:12 午後

先日行われた、Room metro #28 「XAML Day」で、しゃべってきました。

ネタかぶりしなさそうなWF(Window Workflow Foundation)がらみのはずなのに、まさかのネタかぶりとかありましたが、実際はWPFのコードしか書いてないのでかぶってないはず(

資料は、↓で公開しています。

また、デモに使ったコードは↓です。

https://github.com/yone64/Metro28

2013年12月21日

WPFだからできること

Filed under: .NET, WPF — タグ: , , , — yone64 @ 6:58 午後

この記事は、XAML Advent Calendarの第21日目の記事です。

WPFは、XAMLでUIを定義するプラットフォームの始祖にしてもっとも強力な機構を持っています。
そこで、他プラットフォームでは使えないWPFならではの記述を2つ紹介します。

  1. SetterでBindingを設定
  2. DataTemplateでDataTypeを指定

サンプルコードはこちら

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

実行結果はこちら。

キャプチャ3

まずひとつ目から、
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に上げてあります。

2013年5月11日

マルチタッチでお絵かき

Filed under: .NET, ストアアプリ, Windows8 — タグ: , , — yone64 @ 1:09 午前

前回の続き。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);
}

実行結果

スクリーンショット (10)

補足

MSDNにある通り、PointerReleasedイベントの代わりにPointerCanceledやPointerCaptureLostイベントが発生することがあるので、きちんとそちらのイベントも対応する必要があります。

WordPress.com Blog.