nprogram’s blog

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

WPFで、自作イベントの作り方 [C#][WPF]

はじめに

WPFで、自作イベントを作成する方法を調べてみました。

参考サイト

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

https://msdn.microsoft.com/ja-jp/library/ms742806(v=vs.100).aspx

  • あと、tocsworld 様のCLRイベントとWPF Routedイベントの違いの記事はすごく勉強になります。 tocsworld.wordpress.com

  • あと、英語のページだけどこれもよさげ。 www.c-sharpcorner.com

WPFで簡単なUserControlの作り方 [C#][WPF]

UserControlは便利

繰り返し使用するUI部品は、UserControlにして何度も使用できるようにしたほうが便利です。

例えば、MainWindow.xamlを以下のように記載した場合、同じような表記が繰り返し発生します。 これをUserControlに置き換えます。

<Window x:Class="WpfApp5.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:WpfApp5"
        mc:Ignorable="d"
        Title="MainWindow" SizeToContent ="WidthAndHeight">
    <StackPanel Width="Auto" Height="Auto" Margin="10">
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="Name : "/>
            <TextBlock Text="Ichiro"/>
            <Button Content="Show Message"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="Name : "/>
            <TextBlock Text="Jiro"/>
            <Button Content="Show Message"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal" Margin="10">
            <TextBlock Text="Name : "/>
            <TextBlock Text="Saburo"/>
            <Button Content="Show Message"/>
        </StackPanel>
    </StackPanel>
</Window>

ユーザーコントロールは以下のようにプロジェクトに追加します。 プロジェクトのプロパティで、追加を選択して、ユーザーコントロールを選択。 f:id:nprogram:20180409214834p:plain

次に、ユーザーコントロールのxamlファイルを以下のように書き換えて、

<UserControl x:Class="WpfApp5.UserControl1"
             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" 
             xmlns:local="clr-namespace:WpfApp5"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel Orientation="Horizontal" Margin="10">
        <TextBlock Text="Name : "/>
        <TextBlock Text="Ichiro"/>
        <Button Content="Show Message"/>
    </StackPanel>
</UserControl>

MainWindow.xamlも以下のように書き換えます。

<Window x:Class="WpfApp5.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:WpfApp5"
        mc:Ignorable="d"
        Title="MainWindow" SizeToContent ="WidthAndHeight">
    <StackPanel Width="Auto" Height="Auto" Margin="10">
        <local:UserControl1/>
        <local:UserControl1/>
        <local:UserControl1/>
    </StackPanel>
</Window>

[表示結果]

f:id:nprogram:20180409215435p:plain

これですと、全部表示が同じですので、UserControlの外側から値を設定できるようにさせます。

(1) MainWindow.xaml書き換え

<Window x:Class="WpfApp5.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:WpfApp5"
        mc:Ignorable="d"
        Title="MainWindow" SizeToContent ="WidthAndHeight">
    <StackPanel Width="Auto" Height="Auto" Margin="10">
        <local:UserControl1 DataContext="Ichiro"/>
        <local:UserControl1 DataContext="Jiro"/>
        <local:UserControl1 DataContext="Saburo"/>
    </StackPanel>
</Window>

(2) UserControl1.xaml書き換え

<UserControl x:Class="WpfApp5.UserControl1"
             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" 
             xmlns:local="clr-namespace:WpfApp5"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" Name="parent">
    <StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=parent}" Margin="10">
        <TextBlock Text="Name : "/>
        <TextBlock Text="{Binding DataContext}"/>
        <Button Content="Show Message"/>
    </StackPanel>
</UserControl>

[表示結果]

f:id:nprogram:20180409221143p:plain

ここで、DataContext="{Binding ElementName=parent}"で、ElementNameプロパティにparentを指定することで、UserControlのUI要素をバインディング・ソースにしてデータ・バインディングを行えます。 StackPanel以下のUIアイテムから、上記のDataContextに対して、直接バインディングできるようです。 これは、コードが短くなります。

あと、上のxamlコードは次にようにも書き換えることができます。 RelativeSourceは、バインディング ターゲットの位置に対して相対的な位置を指定することにより、バインディング ソースを取得または設定します。 例えば {RelativeSource Self} は自身を参照し、{RelativeSource FindAncestor} では自分の要素を内包する、親の要素を対象に指定します。 以下は、UserControlを指定したパターン

<UserControl x:Class="WpfApp5.UserControl1"
             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" 
             xmlns:local="clr-namespace:WpfApp5"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel Orientation="Horizontal" Margin="10">
        <TextBlock Text="Name : "/>
        <TextBlock Text="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
        <Button Content="Show Message"/>
    </StackPanel>
</UserControl>

以下のサイトを参考にしました

  • 独自の依存関係プロパティを作成する

http://yujiro15.net/YKSoftware/tips_DependencyProperty.html

qiita.com

d.hatena.ne.jp

Git

需要はなさそうだけど、一応Gitにもコードを上げます。 github.com

Visual Studioの便利なショートカットまとめ

Visual Studioのショートカットを覚えると、プログラミング時にとても便利です。

特に便利だと思ったのが、コードリーディング時に使用する ・定義へ飛んだ後、戻るときはCtrl + -

qiita.com

あと、コードスニペットも便利です。 C#のWPFで、依存関係プロパティを作成したい場合は、propdpで作成できます。(VS2017で確認済み)

ほかにもたくさんあるようです。 minus9d.hatenablog.com

Visual Studio 2017のプロジェクト テンプレートで、簡単プロジェクトひな形作成

はじめに

MVVMを意識したWPFのプロジェクトを作成するのは結構大変です。もともとVisual Studioのプロジェクトで用意されているWPFプロジェクトは、MVVMを意識したプロジェクトになっていないためです。(´;ω;`)

そこで、自分で、プロジェクトのテンプレートを作成するのがらくちんです。

MVVMを意識したWPFのプロジェクトのひな形作成方法は以下のページが素晴らしいです。
http://yujiro15.net/YKSoftware/MVVM_Tree.html

<MVVMを意識したWPFプロジェクトひな形構成例>
f:id:nprogram:20180220233509p:plain

プロジェクトテンプレート作成方法は、上記のようなプロジェクトのひな形を作成した状態で、 Visual Studio 2017のメニューのプロジェクトから、テンプレートのエクスポートを選択します。 プロジェクトのテンプレートをラジオボタンで選択します。 f:id:nprogram:20180220233644p:plain

テンプレートオプション画面で、テンプレートの説明を入力した後、完了ボタンを押してください。 f:id:nprogram:20180220233700p:plain

自作したプロジェクトテンプレートを、新しいプロジェクト作成時に使用できるようになります。 f:id:nprogram:20180220233838p:plain

なお、NuGetで外部ライブラリ(例えばPrism.WPF)を登録することもできますので、プロジェクトを新規作成するたびに、NuGetで外部ライブラリをインストールする必要がなくなります。(とてもありがたい。)

ReactivePropertyが便利すぎてとても助かる [C#][WPF]

ReactivePropertyの使い方を学習していきます

画面

f:id:nprogram:20180210194943p:plain

コード

[MainWindowViewModel.cs]

using Reactive.Bindings;
using System;
using System.Linq;
using System.Reactive.Linq;

namespace ReactivePropertySample
{
    class MainWindowViewModel
    {
        public ReactiveProperty<string> Input { get; private set; }
        public ReactiveProperty<string> Output { get; private set; }
        public ReactiveCommand ClearCommand { get; private set; }
        public ReactiveCommand SetCommand { get; private set; }

        public MainWindowViewModel()
        {
            // ViewModelクラスのコンストラクタでReactiveProperty間の関連を定義

            // ReactivePropertyを作成する基本的な方法は、以下の2つ
            // 1. new演算子を使って生成する
            //    コンストラクタの引数にデフォルト値を指定する。指定しない場合は、その型のでデフォルト値が使われる)
            // 2. IObservable<T> に対してToReactiveProperty拡張メソッドを呼ぶ
            this.Input = new ReactiveProperty<string>(""); // デフォルト値を指定してReactivePropertyを作成
            this.Output = this.Input
                .Delay(TimeSpan.FromSeconds(1)) // 1秒間待機して
                .Select(x => x.ToUpper()) // 大文字に変換して
                .ToReactiveProperty(); // ReactiveProperty化する


            // CommandのExecuteが呼ばれたときの処理はSubscribeメソッドで指定します。
            // 先ほど作成したViewModelのInputプロパティが空じゃないときに、Inputプロパティを空にするCommandを追加したコードは以下のようになります。
            this.ClearCommand = this.Input
                .Select(x => !string.IsNullOrWhiteSpace(x)) // Input.Valueが空じゃないとき
                .ToReactiveCommand(); // 実行可能なCommandを作る

            // Commandの動作を定義する
            this.ClearCommand.Subscribe(_ => this.Input.Value = "");


            // 現在の日付を取得する
            DateTime dtToday = DateTime.Today;

            // 実行可能なCommandを生成する
            this.SetCommand = new ReactiveCommand();

            // Commandの動作を定義する
            this.SetCommand.Subscribe(_ => this.Input.Value += dtToday.ToString());
        }
    }
}

[MainWindow.xaml]

<Window x:Class="ReactivePropertySample.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:ReactivePropertySample"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    
    <StackPanel>
        <Label Content="入力" />
        <TextBox Text="{Binding Input.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <Label Content="出力" />
        <TextBlock Text="{Binding Output.Value}"/>
        <Button Content="SET Today Date" Command="{Binding SetCommand}" />
        <Button Content="CLEAR Text" Command="{Binding ClearCommand}" />
    </StackPanel>
</Window>

他のファイルは変更ありません。

ReactivePropertyの勉強は以下のサイトがおすすめです。

  • MVVMとリアクティブプログラミングを支援するライブラリ「ReactiveProperty v2.0」オーバービュー blog.okazuki.jp

  • MVVMをリアクティブプログラミングで快適にReactivePropertyオーバービュー blog.okazuki.jp

tmori3y2.hatenablog.com

qiita.com

WPF/C#/MVVM/Prismで、簡単なWPFアプリケーションを作ります [C#][WPF]

WPF/C#/Prismで、簡単なWPFアプリケーションを作ります

以下のサイトにも記載があるように、WPFアプリケーションでMVVMのアプリケーションを作成するのは難しいと思います。

そのため、PrismというMVVM基盤ライブラリを使用します。

qiita.com

本アプリケーションでは、Prismを使用しているため、NuGetからPrism.Wpfというライブラリをプロジェクトにインストールしてください。

画面の見た目

f:id:nprogram:20180207100504p:plain

プロジェクト構成

f:id:nprogram:20180207100908p:plain

コード

[MainWindow.xaml]

<Window x:Class="WpfApp3.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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Text="{Binding LeftValue}" TextAlignment="Center" VerticalContentAlignment="Center"/>
        <Label Grid.Row="1" Content="+" HorizontalAlignment="Center" VerticalContentAlignment="Center"/>
        <TextBox Grid.Row="2" Text="{Binding RightValue}" TextAlignment="Center" VerticalContentAlignment="Center"/>
        <Label Grid.Row="3" Content="=" HorizontalAlignment="Center" VerticalContentAlignment="Center"/>
        <TextBox Grid.Row="4" Text="{Binding AnswerValue}" IsEnabled="False" TextAlignment="Center" VerticalContentAlignment="Center"/>
        <Button Grid.Row="5" Content ="足し算開始" Command ="{Binding CalcCommand , Mode=OneWay}"/>
    </Grid>
</Window>

MainWindowクラスのコンストラクタにおいて、DataContext にViewModelクラスのインスタンスを設定しています。

[MainWindow.xaml.cs]

using System.Windows;

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

            this.DataContext = new ViewModel();
        }
    }
}

[ViewModel.cs]

using Prism.Commands;
using Prism.Mvvm;

namespace WpfApp3
{
    class ViewModel : BindableBase
    {
        public Model model;

        public ViewModel()
        {
            model = new Model();
        }

        private double _leftValue;
        public double LeftValue
        {
            get { return _leftValue; }
            set { this.SetProperty(ref this._leftValue, value); }
        }

        private double _rightValue;
        public double RightValue
        {
            get { return _rightValue; }
            set { this.SetProperty(ref this._rightValue, value); }
        }

        private double _answerValue;
        public double AnswerValue
        {
            get { return _answerValue; }
            set { this.SetProperty(ref this._answerValue, value); }
        }

        private DelegateCommand calcComamnd;
        public DelegateCommand CalcCommand
        {
            get { return calcComamnd = calcComamnd ?? new DelegateCommand(CalcExecute); }
        }

        private void CalcExecute()
        {
            AnswerValue = model.Calc(LeftValue, RightValue);
        }
    }
}

[Model.cs]

namespace WpfApp3
{
    class Model
    {
        public double Calc(double x, double y)
        {
            return x + y;
        }
    }
}

Google Test導入方法 [VS2017] [Cmake] [C++]

環境について

以下の環境で、Google Testを使用してみます。

  • Windows 10
  • Visual Studio 2017 Community
  • Cmake 3.10.2
  • C++

なお、Visual Studio 2017 バージョン 15.5 以降では、Google Test が C++ ワークロードでのデスクトップ開発の既定のコンポーネントとして Visual Studio IDE に統合されており、より簡単にGoogle Testの導入が可能です。以下の記事に詳細を記載しました。

nprogram.hatenablog.com

Google Testのライブラリファイル生成手順

まず、Google Testを、GitHubからダウンロードします。 Zipファイルでダウンロードしてください。 ダウンロード後は、アクセス制限がかからないフォルダまで移動してください。 この後、Cドライブ直下など、アクセス制限のあるフォルダで作業すると、Visual Studioでビルドするときに、エラーになってビルドできなくなる可能性があるためです。 github.com

次に、以下のホームページでCMakeをダウンロードします。(例 : cmake-3.10.2-win64-x64.msi)
https://cmake.org/

環境変数の設定は、インストール時の設定で解決できそうです。
f:id:nprogram:20180131041231p:plain

この後、Visual Studioを用いてビルドします。
Cmake 3.10.2でビルドできるVisual Studioのバージョンは、以下のとおり。
本情報は、cmake公式ページのドキュメントのHELP(V3.10)のVisual Studio Generatorsの項目に記載があります。
https://cmake.org/documentation/

  • Visual Studio 6
  • Visual Studio 7
  • Visual Studio 7 .NET 2003
  • Visual Studio 8 2005
  • Visual Studio 9 2008
  • Visual Studio 10 2010
  • Visual Studio 11 2012
  • Visual Studio 12 2013
  • Visual Studio 14 2015
  • Visual Studio 15 2017

(1) コマンドプロンプトを開き、ダウンロードしたGoogle Testまで移動。 <your dir path>googletest-master\googletest
(2) googletest-master\googletestのまでcdコマンドで移動
(3) mkdir buildで、ワーキングディレクトリ作成
(4) cd buildでワーキングディレクトリに移動
(5) cmake .. -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF -G "Visual Studio 15 Win64"でビルドする

<コマンドプロンプト出力結果>

<working dir path>googletest-master\googletest\build>cmake .. -DBUILD_GTEST=ON -DBUILD_GMOCK=OFF -G "Visual Studio 15 Win64"
-- Selecting Windows SDK version 10.0.15063.0 to target Windows 10.0.16299.
-- The CXX compiler identification is MSVC 19.11.25508.2
-- The C compiler identification is MSVC 19.11.25508.2
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.11.25503/bin/HostX86/x64/cl.exe
-- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.11.25503/bin/HostX86/x64/cl.exe -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.11.25503/bin/HostX86/x64/cl.exe
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.11.25503/bin/HostX86/x64/cl.exe -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Found PythonInterp: C:/Program Files/Python36/python.exe (found version "3.6.2")
-- Looking for pthread.h
-- Looking for pthread.h - not found
-- Found Threads: TRUE
-- Configuring done
-- Generating done
CMake Warning:
  Manually-specified variables were not used by the project:

    BUILD_GMOCK
    BUILD_GTEST


-- Build files have been written to: <working dir path>googletest-master/googletest/build

(6) ワーキングディレクトリにプロジェクトファイルgtest.slnが作成されるので、開きます。

(7) プロジェクトのALL_BUILDをビルドします。ライブラリファイルは、build\Debugbuild\Releaseに作成されます。pdbファイルを含めた中間ファイルは、build\gtest.dirに作成されます。
* gtestd.lib * gtest.pdb * gtest_maind.lib * gtest_main.pdb Releaseフォルダ以下にも以下のように作成されます。 gtest.lib gtest_main.lib

Visual StudioでGoogle Testのライブラリファイルを使用する

本方法は参考ページに非常に詳しく記載されています。

  Sample
  │  Sample.sln
  └─Sample
     │  Sample.cpp
     │  Sample.vcxproj
     │  Sample.vcxproj.filters
     │  ReadMe.txt
     │  stdafx.cpp
     │  stdafx.h
     │  targetver.h
     │
     ├─include     // googletest-master\googletest\include をコピー
     │
     └─lib         // googletest-master\googletest\buildのDebugとReleaseフォルダをコピー(※1)
         ├─Debug
         └─Release

(※1) pdbファイルが必要な場合は、```build\gtest.dir```から取得してください

(1) 構成を[すべての構成] に設定し、INCLUDEディレクトリの追加
ソリューション右クリック -> プロパティ -> 構成プロパティ -> C/C++ -> [追加のインクルードディレクトリ] -> gtestのincludeディレクトリを追加
※$(ProjectDir)を使用して追加した方がよい
例:$(ProjectDir)include

(2) 構成を[すべての構成] に設定し、libファイルのリンク設定
ソリューション右クリック -> プロパティ -> 構成プロパティ -> リンカー -> [追加のライブラリディレクトリ] -> gtestのlibのディレクトリを追加 $(Configuration)を利用して追加した方がよい
例:$(ProjectDir)lib\$(Configuration)
ソリューション右クリック -> プロパティ -> 構成プロパティ -> リンカー -> 入力 -> [追加の依存ファイル] ->

(2-1) 構成を[Release] に設定し、下記を追加 gtest.lib gtest_main.lib

(2-2) 構成を[Debug] に設定し、下記を追加 gtestd.lib gtest_maind.lib

(3) ランタイムライブラリのリンク方法の指定を変更
ソリューション右クリック -> プロパティ -> 構成プロパティ -> C/C++ -> コード生成 -> [ランタイムライブラリ] ->
(3-1) 構成を[Release] に設定し、[マルチスレッド (/MT)] を設定
(3-2) 構成を[Debug] に設定し、[マルチスレッド デバッグ (/MTd)] を設定

(4) 以下のようにコードを書いてSHIFT + F5で実行してください。なお、Google-TestはWin64のものをダウンロードしているので、
プラットフォームは、x64環境でないと正しく動作しません。Google Testが正しく動作することを確認しました。

[コード]

#include "stdafx.h"
#include "gtest/gtest.h"


TEST(Test1, test1_1) {
    int num1 = 1;
    int num2 = 1;

    ASSERT_EQ(num1, num2);  // 成功する

    num2 = 2;

    ASSERT_EQ(num1, num2);  // 失敗する

}

[実行結果] f:id:nprogram:20180131061012p:plain

参考ページ

以下のページを参考にさせていただきました。ありがとうございます。 qiita.com