nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

WPF MVVM基礎の学習

INotifyPropertyChangedインタフェースは、プロパティ値が変更されたことをクライアントに通知するインタフェースです。 ICommandインタフェースは、コマンドを定義するインタフェースです。 操作を実行するためにのExecuteメソッド、操作が実行可能かどうかを返すCanExecuteメソッド、CanExecuteの結果が変化したことを通知するCanExecuteChangedイベントを公開します。

MVVMの構造パターン

f:id:nprogram:20170918140725p:plain

ビューは、自身をデータバインディングターゲットとして、データバインディングを確立するとき、INotifyPropertyChangedを実装したオブジェクトをバインディングソースにすることで、 ソースプロパティの変更を検知できます。

ビューはICommandをボタンやその他のアクションとバインドすることで、アクション可能かどうか判断しつつ、ロジックを呼び出すことができます。

INotifyPropertyChangedやICommandは、ビューの上記の仕様を満たすために、用意されています。

MVVMを考慮した簡単なWPFアプリケーションのサンプルは以下のサイトが非常にお勧めです。

<参考サイト>

garafu.blogspot.jp

WPF 基礎の学習

WPFのデータバインディングを勉強していきます。

開発環境は、Microsoft Visual Studio Community 2015を使用します。

WPFでは、データ・ソース(=モデルなどの、データの提供元)をビュー(=WPFの場合はXAMLコード)上のUI要素と簡単に結び付けるために、データ・バインディングという仕組みを提供しています。

サンプルプロジェクトでは、バインディング要素は以下のような関係になっています。

項目 意味 サンプルでの対象
バインディングソース データの提供元のオブジェクト Slider Control
ソース・プロパティ データ提供元となる、バインディング・ソースのプロパティ Slider .Value
バインディングターゲット データの反映先のオブジェクト Label Control
ターゲットプロパティ データの反映咲となる、バインディング・ターゲットのプロパティ Labal Content

<サンプルプロジェクト> プロジェクトは、WPFプロジェクトを選択してください。

<サンプルプロジェクト(ModeプロパティがOneWayのとき)>

<Window x:Class="WPF_TEST.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_TEST"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Slider x:Name="SampleSlider">
        </Slider>

        <Label Content="{Binding ElementName=SampleSlider, Path=Value}"></Label>

    </StackPanel>
</Window>

<サンプルプロジェクト(ModeプロパティがTwoWayのとき> UpdateSourceTriggerプロパティがPropertyChangedになっているのに注意してください

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>

        <Slider x:Name="SampleSlider"/>

        <TextBox Text="{Binding ElementName=SampleSlider, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

    </StackPanel>
</Window>

<参考ページ> www.atmarkit.co.jp

C++11 文法について

以下のページに詳しく書いてあります。

cpprefjp.github.io

  • 範囲for文
  • 型推論
  • ラムダ式
  • 型alias
  • typedef ⇒ usingを使おう
  • typedefはオワコン
  • 新初期化記法
  • リストイニシャライザ
  • override

Qt Creatorを用いた自動deploy方法 (Windows 編)

Qtアプリを配布する場合は、実行ファイル(*.exe)ファイルのみでは実行できません。

アプリで使用しているDLLファイルがないためです。

そこで、DLLファイルをいざ集めようとするとこんなに大量にDLLファイルが必要になります。 <一例>

f:id:nprogram:20170906201055p:plain

そこで、windeployqtを用いて、Qt Creator上で、自動で、DLLファイルを集める方法を用いました。

以下がQt Creatorのプロジェクトのビルド設定です。

コマンド : C:\Qt\5.9.1\tool\deploy.bat

引数 : %{CurrentBuild:Name} %{CurrentProject:Name}

作業ディレクトリ : %{buildDir}

(※コマンドは任意のパスです)

f:id:nprogram:20170906200854p:plain

deploy.batファイルの中身は以下のとおりです。

 if "%1"=="デバッグ" (
set BUILD_MODE=debug
) else (
set BUILD_MODE=release
)

if not exist deploy\nul (
mkdir deploy
)

xcopy /C /Y %BUILD_MODE%\*.exe deploy\
windeployqt --%BUILD_MODE% deploy %BUILD_MODE%\%2.exe

C:\Qt\5.9.1\toolのフォルダは以下のような構成になっております。

f:id:nprogram:20170906194454p:plain

以下のページが参考になります。

qiita.com

WindowsでQtアプリを作る (デプロイ編) - Yahoo!知恵袋

Decoratorパターンについて

Decoratorパターンは、オブジェクトに付加的な責務を動的に付与します。

Observerパターンについて

Observerとは、英語で観察者を意味します。

Observer パターンとは、状態の変化を観察することを目的としたものですが、どちらかというと「観察」よりも「通知」に重点の置かれたものになっています。

あるインスタンスの状態が変化した際に、そのインスタンス自身が、「観察者」に状態の変化を「通知」する仕組みです。

<Observerパターンの定義 : クラス図>

f:id:nprogram:20170816230359p:plain

[ObserverSample.h]

#include <vector>
#include <algorithm>
#include <iostream>

class Observer {
    friend class Subject;
public:
    virtual ~Observer() {}
protected:
    virtual void update(Subject*) = 0;
};

class Subject {
private:
    std::vector<Observer*> obs_;

public:
    // Observerを登録
    void add_observer(Observer* o) {
        if (std::find(obs_.begin(), obs_.end(), o) == obs_.end()) {
            obs_.push_back(o);
        }
    }

    // Observerの登録抹消
    void delete_observer(Observer* o) {
        auto iter = std::find(obs_.begin(), obs_.end(), o);
        if (iter != obs_.end()) {
            obs_.erase(iter);
        }
    }
protected:
    // 登録されたObserverのハンドラを呼び出す
    // std::for_each ⇒ 範囲の全ての要素に、指定された関数を適用する
    // oの全ての要素にラムダ式を適用する
    // 【thisのキャプチャ】
    // メンバ関数内では、thisをキャプチャすることでラムダ式内でメンバ変数、メンバ関数(update)を使用することができます。
    // 下記のラムダ式では、thisをキャプチャして、vector[obs_]の各要素のObserverオブジェクトのupdateメソッドを使用しています。
    void notify() {
        std::for_each(obs_.begin(), obs_.end(),
            [this](Observer* o) { o->update(this); });
    }
};

// 具象サブジェクト
// 室温
class RoomTemp : public Subject {
private:
    int value_; //
public:
    RoomTemp(int initial) : value_(initial) {}
    void increment() // 室温を1度上げる
    {
        ++value_;
        notify();
    }
    void decrement() // 室温を1度下げる
    {
        --value_;
        notify();
    }
    int get_temp() const // 現在の室温
    {
        return value_;
    }
};

// 具象オブサーバクラス
// 温度表示パネル : 室温の変化に応じて室温を表示する
class TempDisp : public Observer {
public:
    ~TempDisp() { std::cout << "お疲れ様でしたー" << std::endl; }
protected:
    virtual void update(Subject* from) {
        int temp = static_cast<RoomTemp*>(from)->get_temp();
        std::cout << "現在の温度: " << temp << "℃" << std::endl;
    }
};

[main.cpp]

#include "ObserverSample.h"


int main() {
    RoomTemp room1(20);
    RoomTemp room2(30);
    TempDisp disp1;
    TempDisp disp2;
    room1.add_observer(&disp1);
    room2.add_observer(&disp2);
    room1.increment();
    room1.increment();
    room1.increment();
    room2.decrement();
    room2.decrement();
    room2.decrement();
}

実行結果

f:id:nprogram:20170816230627p:plain

以下のサイトを参考にさせていただきました。

codezine.jp

Strategy Patternについて

[Duch.h]

#include <iostream>
using namespace std;

//QuackBehavior インターフェース
class QuackBehavior
{
public:
    virtual ~QuackBehavior() {}
    virtual void quack() const = 0; //純粋仮想関数(派生クラスに)
};

//QuackBehaviorインターフェースを実装したQuack, Squeak, MuteQuack クラス
class Quack : public QuackBehavior
{
public:
    void quack() const
    {
        cout << "gua-gua" << endl;
    }
};
class Squeak : public QuackBehavior
{
public:
    void quack() const
    {
        cout << "kyu-kyu" << endl;
    }
};

//なにもしない(鳴かない)クラス(Nullオブジェクト)
class MuteQuack : public QuackBehavior
{
public:
    void quack() const
    {
        cout << "<<mute>>" << endl;//何もしていないつもりだが、確認のため、出力する
    }
};

// Duck(基底クラス)と種々の派生クラス
class Duck
{
protected:
    QuackBehavior* q;
public:
    virtual ~Duck() { delete q; };
    //virtual を入れなかったのは、Duckクラス共通の振る舞いだから
    //デフォルトの実装と派生クラス独自の実装を分けたい場合は、virtualを付ける
    void quack() const
    {
        q->quack();
    }
};

class RedHeadDuck : public Duck
{
private:
public:
    RedHeadDuck()
    {
        q = new Quack();
    }
};

class RubberDuck : public Duck
{
private:
public:
    RubberDuck()
    {
        q = new Squeak();
    }
};

class DecoyDuck : public Duck
{
private:
public:
    DecoyDuck()
    {
        q = new MuteQuack();
    }
};

[main.cpp]

#include "Duck.h"

int main(void)
{
    Duck* d1 = new RedHeadDuck();
    Duck* d2 = new DecoyDuck();
    Duck* d3 = new RubberDuck();
    //RedHeadDuck()
    //  //基底クラスにアップキャスト
    //  QuackBehavior* q = new Quack();

    // [オブジェクト] [-> : アロー演算子] [メソッド(引数1..)]
    d1->quack();
    d2->quack();
    d3->quack();
    //  q->quack(); // qはQuackBehavior*型でquackメンバ関数は仮想関数なので、派生クラス(Quack)のquackメンバ関数が動的に実行される。

    delete d1;
    delete d2;
    delete d3;

    return 0;
}

実行結果

Visual Studio CommunityからCtrl + F5を押すと、派生クラスのメソッドに従った振る舞いをします。 f:id:nprogram:20170816112833p:plain