【WPF】グリッド状の模様を描く その(1) CanvasのChildrenに線を追加する

投稿者: | 2011年8月8日

WPFで下のような模様を描くにはどうしたら良いだろうか?

まず、WPFでグリッド状の模様を描く上で以下の条件を課すことにしたい。

・クライアント画面いっぱいにグリッドが表示されること
・画面をリサイズしてもグリッド状の模様は画面いっぱいに表示されること
・すばやく画面を更新した場合でも表示が乱れないこと
・アンチエイリアスの無い、くっきりとした細い線で描かれること

WinFormの時は、OnPaintをオーバーライドして、Graphicsに対してDrawLine()を呼んで線を描いていく…といった手順で出来た。
しかし、WPFでは勝手が違ってしまっている。とりあえず、試行錯誤してみた過程を残しておく。

WPFアプリケーションは「Visual C# 2010 Express」で開発することが出来る。
まずはVisualStudioのメニューの「新しいプロジェクト > WPFアプリケーション」を選択してWPFアプリケーションプロジェクトを作成する。そして、今度はソリューションエクスプローラーを右クリックして「追加 > 新しい項目 > ユーザーコントロール(WPF)」を選び、「MyGridView」という名前のWPFユーザーコントロールを作った。このユーザーコントロールにグリッド状の模様を描く処理を実装していく。
そのxamlは以下のようになる。

MyGridView.xaml

<UserControl x:Class="WpfMyGridLesson.MyGridView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             Loaded="UserControl_Loaded"
             SizeChanged="UserControl_SizeChanged">

    <!-- 線のスタイル -->
    <UserControl.Resources>
        <Style x:Key="GridLineStyle" TargetType="Line">
            <Setter Property="StrokeThickness" Value="1" />
            <Setter Property="Stroke" Value="LightGray" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
        </Style>
    </UserControl.Resources>
    <Grid>
        <!-- 描画用のキャンバス -->
        <Canvas Name="myCanvas"/>
    </Grid>
</UserControl>

赤字で表記している部分がxamlエディタで手書きした部分である。さらに、プロパティウィンドウでLoadedイベントハンドラとSizeChangedイベントハンドラを追加している。

次に、C#での実装を行う。

MyGridView.xaml.cs

namespace WpfMyGridLesson
{
    /// <summary>
    /// MyGridView.xaml の相互作用ロジック
    /// </summary>
    public partial class MyGridView : UserControl
    {
        private const int GRID_SIZE = 20;

        public MyGridView()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            BuildView();
        }

        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            BuildView();
        }

        // キャンバスに線を描画する
        private void BuildView()
        {
            myCanvas.Children.Clear();


            Style lineStyle = this.FindResource("GridLineStyle") as Style;

            for (int i = 0; i < this.ActualWidth; i += GRID_SIZE)
            {
                Line line = new Line()
                {
                    X1 = i,
                    Y1 = 0,
                    X2 = i,
                    Y2 = this.ActualHeight,
                    Style = lineStyle
                };

                myCanvas.Children.Add(line);
            }

            for (int i = 0; i < this.ActualHeight; i += GRID_SIZE)
            {
                Line line = new Line()
                {
                    X1 = 0,
                    Y1 = i,
                    X2 = this.ActualWidth,
                    Y2 = i,
                    Style = lineStyle
                };

                myCanvas.Children.Add(line);
            }
        }
    }
}

26行目のBuildView()メソッドでキャンバスに対してグリッド状の線を描画している。メソッドの始めにキャンバスに存在する
図形を全てクリアしている。このメソッドをLoadedのタイミングとSizeChangedのタイミングで呼ばれるようにしておいた。

ユーザーコントロールが出来たので、最後にアプリケーション本体のxamlを書くことにする。
アプリケーション本体の記述は「MainWindow.xaml」というファイルに書いていく。
このファイルは、VisualStudioで新しくWPFアプリケーションのプロジェクトを作成したとき、自動的にプロジェクトフォルダに作成されているはずである。xamlは以下のようになる。

MainWindow.xaml

<Window x:Class="WpfMyGridLesson.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:WpfMyGridLesson"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <!-- ユーザーコントロールを配置 -->
        <my:MyGridView></my:MyGridView>
    </Grid>
</Window>

赤字で表記している部分がxamlエディタで手書きした部分である。
まず、xamlエディタでユーザーコントロールを使えるように、

xmlns:my="clr-namespace:WpfMyGridLesson"

と書いておく。そうすると「my:」というプレフィックスでユーザーコントロールを配置することが出来るようになる。
ここまでで、プロジェクトのファイル構成は以下のようになっているはずである。

このプログラムを実行すると以下のように表示される。

こうして、とりあえず表示することができたが、C#のコーディングは別に良い方法があるかもしれない。

カテゴリー: C# WPF

【WPF】グリッド状の模様を描く その(1) CanvasのChildrenに線を追加する」への6件のフィードバック

  1. 三鷹

    前半のユーザーコントロールを動かすところまでは成功しましたが、メインをどこに作ったらよいのか分からず、
    とりあえずユーザーコントロールを作ったソリューションにプロジェクトを追加してみたところ失敗です。
    どうやって本体のxamlをつくるのか手順が分かりません。

    返信
    1. okazawata 投稿作成者

      コメントありがとうございます。
      メインについては、VisualStudioでWPFアプリケーションを新規作成したときに、自動的に作成されている
      「MainWindow.xaml」というファイルに書いていきます。
      本文にも書いていますが、「xmlns:~」の記述を冒頭でしておかないとユーザーコントロールをxamlで配置できません。
      これらの点をもう一度注意深く確認して試してみてください。

      返信
  2. 田中

    色んなWPF操作を参照させていただいて感謝いたします。
    小生もWPFを業務に取り入れるべく、ネットを探っています。
    ドライバやスレッドをミヨウミマネでC#で作れるレベルですが、初心者です。
    出来れば以下についてご教授願えればと思いPOSTしました。
    背景
    1.大量の描画コマンド(万個単位)をファイルから抽出し描画する。(ファイル内容は可変)
    2.C#を使って上記抽出内容の描画をプログラミングレベルで行いたい。
    3.XAMLの補助?が必要か。
    4.描画全体の拡大・縮小を行う必要がある。
    5.ビュー領域に任意の部位を表示したい。
    OnRenderとか使いたいのですが、今一うまく行きません。
    入り口だけでも知恵を拝借出来ないでしょうか。宜しくお願いいたします。

    返信
    1. okazawata 投稿作成者

      コメントありがとうございます。
      さて、ご質問の内容ですが、おそらくGoogleMapのようなインターフェースを
      実現したいのでは?と推察しました(勘違いでしたらすみません)。
      すぐに思いつく限りだと、まずxamlでCanvasを配置しておき、そのChildrenに対して
      C#で描画オブジェクトを追加していく、という方法でしょうか。
      ただ、Canvasに万単位の描画オブジェクトを追加したときにパフォーマンスはどうか?
      という点は、まだ試したことが無いですが考慮が必要と思います。
      「4.描画全体の拡大・縮小」に関してはCanvasのRenderTransformにScaleTransformを適用するか、
      Path等のGeometryを元にする描画オブジェクトをChildrenに追加している場合は
      そのGeometryのTransformにScaleTransformを適用するかのいずれかで出来るのではないかと思います。
      「5.ビュー領域に任意の部位を表示」に関しては、おそらくWPFコントロールのScrollViewrを使えば出来るのではないかと思いますが、まだ未調査です。
      このコントロールについては個人的に興味がありますので、いずれ調査して記事で取り上げたいと思っています。

      WPFに関してはまだ分かっていない所も多く、コードを書いて試してみた事を
      日々ブログに上げているという感じですが、ご参考になりましたら幸いです。

      返信
  3. ピンバック: 【WPF】グリッド状の模様を書く その(2) OnRenderをオーバーライドする(失敗?) – ザワプロ!

  4. ピンバック: 【WPF】グリッド状の模様を描く (1)~(6)のまとめ – ザワプロ!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です