読者です 読者をやめる 読者になる 読者になる

Every day is a new day

ひつじのプログラミング日記。

WPFで矩形トラッカーを描画する(MVVM) その1

WPF C# XAML MVVM

前回の続きで、コードビハインドで実装された矩形トラッカーを、今度はDataBinding(MVVM)でやってみたいと思います。

まずは、トラッカー描画の要となるMouseLeftButtonDown、MouseLeftButtonUp、MouseMoveイベントをViewModelとBindingするのにMouseBindingを使おうと思ったのですが、これらのイベントが存在しなかった・・・。

<!--こんな感じでBindingしたかったけど・・・-->
<Grid Background="White">
    <Grid.InputBindings>
        <MouseBinding Gesture="LeftDoubleClick" 
                      Command="{Binding LeftDoubleClickCommand}" />
    </Grid.InputBindings>
</Grid>

 
というわけで、こちらこちらを参考にして、EventTriggerを使うことにしました。

<i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseLeftButtonDown" >
        <i:InvokeCommandAction
              Command="{Binding MouseLeftButtonDownCommand}" />
    </i:EventTrigger>
    <i:EventTrigger EventName="MouseLeftButtonUp" >
        <i:InvokeCommandAction
              Command="{Binding MouseLeftButtonUpCommand}" />
    </i:EventTrigger>
    <i:EventTrigger EventName="MouseMove" >
        <i:InvokeCommandAction
              Command="{Binding MouseMoveCommand}" />
    </i:EventTrigger>
</i:Interaction.Triggers>

  • MainViewModel.cs
private RelayCommand _mouseLeftButtonDownCommand;
public RelayCommand MouseLeftButtonDownCommand
{
    get
    {
        return _mouseLeftButtonDownCommand 
            ?? (_mouseLeftButtonDownCommand = new RelayCommand(
            () =>
            {
                // マウス左ボタンダウン
            }));
    }
}

private RelayCommand _mouseLeftButtonUpCommand;
public RelayCommand MouseLeftButtonUpCommand
{
    get
    {
        return _mouseLeftButtonUpCommand 
            ?? (_mouseLeftButtonUpCommand = new RelayCommand(
            () =>
            {
                // マウス左ボタンアップ
            }));
    }
}

private RelayCommand _mouseMoveCommand;
public RelayCommand MouseMoveCommand
{
    get
    {
        return _mouseMoveCommand 
            ?? (_mouseMoveCommand = new RelayCommand(
            () =>
            {
                // マウス移動
            }));
    }
}


RelayCommandクラスについては、こちらを参考にさせて頂きました。

これらを実装し、イベントが飛んでくることまで確認できたのですが、ここであることに気付きました。
・・・あれっ?これじゃマウスボタンをダウンした位置やマウス移動中の座標わからなくない?矩形作れなくない?

つまりViewModel側でMouseEventArgsやMouseButtonEventArgsを取りたいということなのですが、これはまた次回です・・・。

WPFで矩形トラッカーを描画する

C# WPF XAML

マウスをドラッグすると点線で表示されたりする矩形領域のことですが、WPFC#、だとなかなか見つからなかったりします。

というわけで実装してみました。

GitHub - nejimakidori/TrackerTest: WPFにおけるトラッカー描画のテストです。


f:id:buribaries3:20170128101202g:plain



コードは以下。

<Window x:Class="TrackerTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="ContentPanel"
          Background="Transparent" 
          MouseLeftButtonDown="ContentPanel_MouseLeftButtonDown" 
          MouseLeftButtonUp="ContentPanel_MouseLeftButtonUp" 
          MouseMove="ContentPanel_MouseMove">

        <Rectangle Name="rectangle1" 
                   HorizontalAlignment="Left" VerticalAlignment="Top" 
                   StrokeDashArray="2,2" Stroke="Blue" StrokeThickness="3" 
                   Visibility="Collapsed" />
    </Grid>
</Window>

  • MainWindow.xaml.cs
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private double _downedX = 0;
        private double _downedY = 0;
        
        private void ContentPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 矩形初期値設定
            Point p = e.GetPosition(this);
            _downedX = p.X;
            _downedY = p.Y;
            rectangle1.Margin = new Thickness(p.X, p.Y, 0, 0);
            rectangle1.Width = 0;
            rectangle1.Height = 0;
            rectangle1.Visibility = Visibility.Visible;
        }

        private void ContentPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // 矩形非表示
            rectangle1.Width = 0;
            rectangle1.Height = 0;
            rectangle1.Visibility = Visibility.Hidden;
        }

        private void ContentPanel_MouseMove(object sender, MouseEventArgs e)
        {
            // 移動中矩形サイズ
            if (rectangle1.Visibility == Visibility.Visible)
            {
                Point p = e.GetPosition(this);
                if (p.X - _downedX >= 0 && p.Y - _downedY >= 0)
                {
                    // マウスダウン座標から見て右下
                    rectangle1.Margin = new Thickness(_downedX, _downedY, 0, 0);
                    rectangle1.Width  = p.X - _downedX;
                    rectangle1.Height = p.Y - _downedY;
                }
                else if (p.X - _downedX < 0 && p.Y - _downedY >= 0)
                {
                    // マウスダウン座標から見て左下
                    rectangle1.Margin = new Thickness(p.X, _downedY, 0, 0);
                    rectangle1.Width  = _downedX - p.X;
                    rectangle1.Height = p.Y - _downedY;
                }
                else if (p.X - _downedX >= 0 && p.Y - _downedY < 0)
                {
                    // マウスダウン座標から見て右上
                    rectangle1.Margin = new Thickness(_downedX, p.Y, 0, 0);
                    rectangle1.Width  = p.X - _downedX;
                    rectangle1.Height = _downedY - p.Y;
                }
                else
                {
                    // マウスダウン座標から見て左上
                    rectangle1.Margin = new Thickness(p.X, p.Y, 0, 0);
                    rectangle1.Width  = Math.Abs(_downedX - p.X);
                    rectangle1.Height = Math.Abs(_downedY - p.Y);
                }
            }
        }
    }

今回はコードビハインドで実装してみましたが、次はこれのMVVM化に挑んでみます。

Visitor パターン

デザインパターン

引用

訪問者のこと。

廃れることは無いとは思いますが、最近はあまり使われてないかも。

パターンの内容

デザインパターンを勉強していると、頭がこんがらがってきます。どれもこれも似たようなものに見えるからです。

なんというか、とにかく実装依存とインターフェースを分けろとまあ、コレに尽きるような感じがします。

しかしこれは、インターフェース部分を抽象化し過ぎると、かえってコードが理解しにくくなるというデメリットがあるような気がします。上記に引用させて頂いたサイトのソースコードがそう。 はっきり言って、デザインパターンを適用していないほうのがわかりやすい

結局、何をどうしたいのかという設計に依存する話で・・・、なんてまとめてしまうと、デザインパターンを勉強している意味がなくなってしまうので、あまり考えないことにしよう(ムナしくなるのを我慢)。

Mediator パターン

デザインパターン

引用

「仲介者」と訳される。

「同僚(仲間)」はお互いを直接は知らず、常に仲介者を介する。

あまり出番ないけれど、バシっと決まる場合も偶にある。

このパターンは、一つの大きな台紙(コンテナ)に複数のオブジェクトを貼り付けて、台紙(コンテナ)上でそれらの関連を記述するパターンです。

よく似たパターンに OBSERVER がありますが、OBSERVER はオブジェクトの状態の一貫性に着目しているのに対し、MEDIATOR は、部品オブジェクトが発生するイベントに着目したパターンと言えると思います。

パターンの内容について

Observerパターンとの違いがわからない・・・。 たぶん、Observerパターンはイベントリスナーにイベントが通知されるのに対し、Mediatorパターンはstatic的なコンテキストオブジェクトがイベントを管理する感じかな・・・。

2017/01/07追記

ん?WPFのMessengerってMediatorパターンじゃないか!?

Adapter パターン

デザインパターン

最近デザインパターンの勉強からちょっと離れていたのだけど、大規模Webアプリケーションにおける複雑性とアーキテクチャ設計に関する一考察 - Qiitaというとても参考になる記事の中で、

連携システムのインタフェースが、アプリケーション全体に漏れ出すと制御不能な状態に陥るので、Adapterなどで腐敗防止層を実装し、外部システムの影響を局所化すること。

とあり、このAdapterという単語をちゃんと理解するために、また色々と調べてみました。

ということで引用

既存のクラスに変更を加えることなく、本来関連のない型として使用するための方法。似た実装も含めて使用頻度の高い実装方法。

継承を使ってクラスを拡張する代わりに、あらかじめ拡張するメソッドをインターフェースにしておく手法。メッセージングのときに、後付けで必要なインターフェースを追加するときに使う。データ自体と操作するメソッドを分離するときによこう使われる。が、最初からわかっていれば、Interface で十分な気も。

インタフェースをそろえるためにラッパーを用意することはよくある。 普通のOOPの使い方である。

第2章では Adapter パターンを学びます。adaptという単語は日本語で「適合させる」という意味で、adapterとは「適合させるもの」という意味になります。Adapterパターンは、インタフェースに互換性の無いクラス同士を組み合わせることを目的としたパターンです。

まったくインターフェースに互換性のないクラス同士を組み合わせるためのパターンです。 クライアントコードを変えずに機能拡張するのが、このパターンの目的です。クライアントコードで利用されている クラスA(Target) に、クラスB(Adaptee)の機能を使ってクラスAを 機能拡張したい場合に用います。

色々読んてみた結果・・・

うーん、ざっくり言ってラッパーってことですかね。Facadeパターンっぽくもあるのかな?でもFacadeパターンは処理をまとめてシンプルな呼び出しにするのに対し、Adapterパターンはインターフェースの統一を図っているのでしょうね。

秘密の国のアリス[第3版] 第12章

秘密の国のアリス

おうちで学べるデータベースのきほん』の次は、『暗号技術入門 第3版 秘密の国のアリス』を勉強しています。

暗号技術入門 第3版 秘密の国のアリス

暗号技術入門 第3版 秘密の国のアリス

パラパラっとめくりながら、第12章の「乱数」をピックアップ。

乱数の性質としては、以下が大事なようだ。

  • 無作為性 統計的な偏りがなく、でたらめな数列になっているという性質。
  • 予測不可能性 過去の数列から次の数を予測できないという性質。
  • 再現不可能性 同じ数列を再現できないという性質。再現するためには、数列そのものを保存しておくしかない。

驚いたのは、c言語のrand関数が上記の無作為性しか持たない線形合同法であるということ。それは確かに、この本に書いてある通り、暗号技術には使えないでしょうね。

  

あとクイズの解答を、答えを見ずにここに書いてみようと思う。果たしてあっているだろうか?w

クイズ1……サイコロ

サイコロを繰り返し投げて作る数列は、再現不可能性を持つを考えられますか。

サイコロを繰り返し投げて作る数列は、再現不可能性を持つと考えられます。 なぜなら、サイコロを投げるというのは物理的な操作で、二度と同じように投げられないからです。

クイズ2……擬似乱数生成器の弱点を探す

一方向ハッシュ関数を使って擬似乱数生成器を作れるという話を聞いたを聞いたアリスは、Fig.12-6(図があるのですが、手順は以下の通り)のような擬似乱数生成器を設計しました。乱数生成の手順は次の通りです。

 

(1)擬似乱数の種を使って内部状態を初期化する。

(2)一方向ハッシュ関数を使って内部状態のハッシュ値を得る。

(3)ハッシュ値擬似乱数として出力する。

(4)そのハッシュ値を新たな内部状態とする。

(5)必要なだけの擬似乱数が得られるまで、(2) ~ (4)を繰り返す。

 

アリスがせっかく考えた擬似乱数生成器ですが、この擬似乱数生成器が生成した擬似乱数列は「予測不可能性」を持ちません。なぜでしょうか。

一方向ハッシュ関数ハッシュ値を新たな内部状態にしてしまうと、内部状態は常に固定長になってしまう。 例えば、SHA−256なら256bitである。この場合、いつかは繰り返しが発生してしまうから・・・かなぁ?

クイズ3……乱数の基礎知識

(1)擬似乱数の種は、攻撃者に秘密にしておく必要がある。

(2)線形合同法は、暗号用の擬似乱数生成器として用いることができる。

(3)無作為性を持っている擬似乱数生成器でも、予測不可能性を持っているとは限らない。

(1)🙆‍♂️ (2)🙅‍♂️ (3)🙆‍♂️

  

で、答えなのですが、クイズ1と3はあってそうのですが、クイズ2、全然見当違いです・・・w

擬似乱数の予測不可能性は、過去の擬似乱数を元にして、次の擬似乱数を予測できないという性質のことです。アリスが考えた擬似乱数生成器では、最後に出力した擬似乱数ハッシュ値をとれば、次の擬似乱数が得られてしまいます。したがって、予測不可能性を持ちません。

焦点は予測可能か不可能というところで、ハッシュ値からまたハッシュ値を求めてしまえば、それは予測可能になってしまうということですね。なるほどぅ。

Chain of Responsibility パターン

デザインパターン

 

上司がこのパターンの名前を口にしたので、なんだ意外と有名なパターンなのか(あるいは全部知っているのか?)と思ったので今回はこのパターンにしてみた。
 
とりあえず動かしてみて・・・、うん、そんなに難しいことはやっていない。
自分で解決(Resolve)できないものは、次(next)に任せているだけだ。それが数珠つなぎになっていると。なんかリンクリストみたいな感じですかね。
 
数珠が1つだけだと、Proxyパターンのようにも見える。ただ、Proxyパターンは構造を表すのに対し、Chain of Responsibilityパターンは振る舞いを表すらしい。
 
そもそも、この構造と振る舞いの違いっていうのもあまり理解できていませんナ・・・。