Help us understand the problem. What is going on with this article?

MSBuildで始めるWPF(C#、VB)

More than 3 years have passed since last update.

はじめに

ここ最近で、Visual Studioを使用せずにWPF(XAML)アプリケーションの作り方を勉強していた。
大よそ何となくやりたいことは出来るようになったのでこの際まとめておく。
日本語記事も少ないことだし。
ちなみに、csc.exeやvbc.exeでのコンパイル方法(オプション)も何となく把握できている方が理解は進むと思う。

MSBuildの各種オプション

MSBuild コンパイラオプション一覧
MSBuild Project Properties一覧
MSBuild .Targetsファイル

参考文献

XAML + WPFアプリケーションをコマンドラインでビルドする方法
テキストエディタでWPF + XAMLなアプリケーションを開発する
メインウィンドウから子ウィンドウを開く
WPF(VB.net & xaml)をMSBuildでコンパイル

MSBuildの環境について

ここからダウンロードしてインストールできる。
VisualStudioでも可。
インストールした後のパスは大体、

C:\Program Files\MSBuild\14.0\Bin\msbuild.exe
C:\Program Files(x86)\MSBuild\14.0\Bin

にある。
ここからPathに突っ込むなりbat作るなりして自由に使う。
ちなみにWindows7に標準でインストールされているMSBuildは.Net Framework3.5相当になるので結構古く、注意。

ビルドの実行

基本的にはプロジェクトファイルに移動してmsbuildコマンドを実行すればよい。

C:\> cd C:\sample
C:\sample

C:\sample> msbuild

同じフォルダにprojectファイルが複数存在する場合は、msbuildのコマンドライン引数にそのままファイル名を渡せばいいが、滅多にそれを行うことはないだろう。
他の詳細については下記を参照のこと。
MSBuild コンパイラオプション一覧

雛型

MSBuildのプロジェクトファイルの雛型は以下となる。
拡張子はC#、VB共用のprojかC#用のcsproj、VB用のvbprojとなる。

<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- コンパイラオプション -->
    </PropertyGroup>

    <ItemGroup>
        <!-- コンパイルするファイル、using、Imports設定 -->
    </ItemGroup>

    <!-- .targetファイル -->
    <Import Project= />

</Project>

じっくり見てみると複雑そうに見えて構造は結構単純。
上の方はいまいち理解が足りないけど、おまじないとしておいておくのが吉。多分変えることもそう多くはない。
文法こそ異なるがやったことあるのならNW.jsやElectronのpackage.jsonと似た感じで構築できる。

PropertyGroup(コンパイラオプションについて)

このPropertyGroupに含まれるものはアプリケーションの名前などプロフィールや、csc.exe、vbc.exeでのコンパイラオプションとほぼ同一のものとなる。
どのようなものや機能があるのかは、
MSBuild Project Properties一覧
を見て確認すること。
以下では実際に作ったアプリケーションの例として載せる。

project.csproj
<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- 出力ファイル名 -->
        <AssemblyName>txtLret</AssemblyName>
        <!-- 出力ファイルのファイル形式(Winexe=GUIプログラミング(WPF)) -->
        <OutputType>Winexe</OutputType>
        <!-- 出力パス -->
        <OutputPath>.\</OutputPath>
        <!-- 使用する.NetFrameworkのバージョン -->
        <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
        <!-- コンパイルをデバッグで行うかリリースで行うか -->
        <Configuration>Release</Configuration>
        <!-- コード最適化の有無(true=有) -->
        <Optimize>true</Optimize>
    </PropertyGroup>

    <ItemGroup>
        <!-- コンパイルするファイル、using、Imports設定 -->
    </ItemGroup>

    <!-- .Targetsファイル -->
    <Import Project= />

</Project>
project.vbproj
<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- 出力ファイル名 -->
        <AssemblyName>txtLret</AssemblyName>
        <!-- 出力ファイルのファイル形式(Winexe=GUIプログラミング(WPF)) -->
        <OutputType>Winexe</OutputType>
        <!-- 出力パス -->
        <OutputPath>.\</OutputPath>
        <!-- 使用する.NetFrameworkのバージョン -->
        <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
        <!-- コンパイルをデバッグで行うかリリースで行うか -->
        <Configuration>Release</Configuration>
        <!-- コード最適化の有無(true=有) -->
        <Optimize>true</Optimize>
        <!-- VBのOption Infer(型推論)をON=trueにする(デフォルトOFF=false) -->
        <OptionInfer>true</OptionInfer>
        <!-- VBのオーバーフローチェックを無効化する(デフォルトOFF=false) -->
        <RemoveIntegerChecks>true</RemoveIntegerChecks>
    </PropertyGroup>

ItemGroup

ここには、プログラムの部品として必要なソースやライブラリを追加する。
これのItem名としては、

<ApplicationDefinition Include="app.xaml" />
<Page Include="index.xaml" />
<Compile Include="main.vb" />
<Reference Include="System" />
<Import Include="System" />

などがある。などというのは、僕もこれ以外に何が存在するのかも把握できていないため。恐らく、csc.exeやvbc.exeでアイテムとして追加できるものであれば、対応するオプションが存在するはず。

これを上から説明していく。

ApplicationDefinition

ここにはエントリポイントとしてxamlを呼び出すxamlファイルを定義する。

<ApplicationDefinition Include="app.xaml" />

このxamlファイルは少々特殊で、本当にエントリポイントを指定するためだけのファイルになりそれ以外には何も記述をしない。

app.xaml
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="index.xaml"
/>

ちなみに、ここを見る限りだと、あるプログラムに変換されるようで、自動的に標準のxamlファイルにエントリポイントを渡すところまでを行うようだ。

ちなみにApplicationDefinitionはあるプログラムに変換されるといったように、そこを自力で書ければApplicationDefinitionプロパティがプロジェクトファイル内に存在しなくてもWPFプログラムを作ることは可能。これは後述する。

Page

ページファイル、つまりxamlファイルはここにIncludeする。
必要な分だけIncludeする。

<Page Include="index.xaml" />

起動時に立ち上がるページ(エントリポイント)はApplicationDefinitionで指定したxaml内に記述する。

Compile

コンパイルファイル、つまりc#やVBソースファイルはここに該当する。
ワイルドカードを指定することも可能。

<Compile Include="main.cs" />
<Compile Include="main.vb" />
<Compile Include="*.vb />

Reference

必要なライブラリの参照をここに定義する。
ちなみにWPFでプログラミングを行う場合は下記の参照が必ず必要となるので必ず追加すること。

<Reference Include="System" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />

Import

名前空間の追加。
その名の通り、C#のusingやVBのImportsに相当する物。
ここに追加すれば、グローバルで拡張されることになるので、追加する物はある程度吟味した方がいいのかもしれない。
ちなみにVBの"Microsoft.VisualBasic"名前空間も標準で追加されないので必要であれば追加しておく。

<Import Include="System" />
<Import Include="Microsoft.VisualBasic" />

.targets

最後に、.targetsと呼ばれるファイルを指定する。
正直、ここは僕も理解できていないけれど、公式を読む感じでは、使用する言語に合わせて、

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

のどちらかにしておけば間違いはなさそうだ。

サンプルソース

これらを踏まえて一つのサンプルソースを載せておく。フォルダ構成としては下記のようになる。

sample\
    project.csproj 又は project.vbproj
    app.xaml
    index.xaml
    main.cs 又は main.vb
project.csproj
<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- 出力ファイル名 -->
        <AssemblyName>txtLret</AssemblyName>
        <!-- 出力ファイルのファイル形式(Winexe=GUIプログラミング(WPF)) -->
        <OutputType>Winexe</OutputType>
        <!-- 出力パス -->
        <OutputPath>.\</OutputPath>
        <!-- 使用する.NetFrameworkのバージョン -->
        <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
        <!-- コンパイルをデバッグで行うかリリースで行うか -->
        <Configuration>Release</Configuration>
        <!-- コード最適化の有無(true=有) -->
        <Optimize>true</Optimize>
    </PropertyGroup>

    <ItemGroup>
        <!-- エントリポイント指定用XAMLファイル -->
        <ApplicationDefinition Include="app.xaml" />

        <!-- XAMLファイル -->
        <Page Include="index.xaml" />

        <!-- CSファイル -->
        <Compile Include="main.cs" />

        <!-- 参照の追加 -->
        <Reference Include="System" />
        <Reference Include="System.Xaml" />
        <Reference Include="WindowsBase" />
        <Reference Include="PresentationCore" />
        <Reference Include="PresentationFramework" />
    </ItemGroup>

    <!-- .targets -->
    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

</Project>
project.vbproj
<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- 出力ファイル名 -->
        <AssemblyName>txtLret</AssemblyName>
        <!-- 出力ファイルのファイル形式(Winexe=GUIプログラミング(WPF)) -->
        <OutputType>Winexe</OutputType>
        <!-- 出力パス -->
        <OutputPath>.\</OutputPath>
        <!-- 使用する.NetFrameworkのバージョン -->
        <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
        <!-- コンパイルをデバッグで行うかリリースで行うか -->
        <Configuration>Release</Configuration>
        <!-- コード最適化の有無(true=有) -->
        <Optimize>true</Optimize>
        <!-- VBのOption Infer(型推論)をON=trueにする(デフォルトOFF=false) -->
        <OptionInfer>true</OptionInfer>
        <!-- VBのオーバーフローチェックを無効化する(デフォルトOFF=false) -->
        <RemoveIntegerChecks>true</RemoveIntegerChecks>
    </PropertyGroup>

    <ItemGroup>
        <!-- エントリポイント指定用XAMLファイル -->
        <ApplicationDefinition Include="app.xaml" />

        <!-- XAMLファイル -->
        <Page Include="index.xaml" />

        <!-- VBファイル -->
        <Compile Include="main.vb" />

        <!-- 参照の追加 -->
        <Reference Include="System" />
        <Reference Include="System.Xaml" />
        <Reference Include="WindowsBase" />
        <Reference Include="PresentationCore" />
        <Reference Include="PresentationFramework" />

        <!-- 名前空間の追加 -->
        <Import Include="System" />
        <Import Include="Microsoft.VisualBasic" />
    </ItemGroup>

    <!-- .targets -->
    <Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" />

</Project>
app.xaml(C#、VB)
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="index.xaml"
/>
index.xaml(C#、VB)
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="txtLret"
    Title="テキストルーレット"
    Width="620"
    Height="440"
    ResizeMode="NoResize"
    FontFamily="Meiryo UI"
    FontSize="16"
    Foreground="#000099"
    Background="#3399FF"
>
    <Window.Resources>
        <Style TargetType="TextBox">
            <Setter Property="Width" Value="580" />
            <Setter Property="HorizontalAlignment" Value="Center" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Height" Value="24" />
            <Setter Property="Margin" Value="10" />
        </Style>
    </Window.Resources>
    <StackPanel>
        <TextBlock
            Margin="0,10,0,5"
            HorizontalAlignment="Center"
            Text="ルーレットしたい値を入れてね。"
        />
        <TextBox
            AcceptsReturn="True"
            VerticalScrollBarVisibility="Auto"
            Height="260"
            Name="txtList"
        />
        <WrapPanel HorizontalAlignment="Center">
            <Button Width="200" Click="lretMain" Content="Let's ルーレット!" />
            <Button Width="100" Click="lretDefault" Content="MH武器" />
        </WrapPanel>
        <TextBox Name="resultList" />
    </StackPanel>
</Window>
main.cs(C#)
using System;
using System.Windows;

partial class txtLret{
    Random rand=new Random();

    void lretMain(object sender,RoutedEventArgs e){
        string[] txtLists=txtList.Text.Split('\n');
        resultList.Text=txtLists[rand.Next(txtLists.Length)];
    }

    void lretDefault(object sender,RoutedEventArgs e){
        txtList.Text=
@"大剣
太刀
片手剣
双剣
ランス
ガンランス
ハンマー
狩猟笛
操虫棍
穿龍棍
ライトボウガン
ヘビィボウガン
弓";
    }
}
main.vb(VB)
Option Strict On

Imports System
Imports System.Windows

Class txtLret
    Dim rand As New Random()

    Sub lretMain()
        Dim txtLists As String()=Split(txtList.Text,vbLf)
        resultList.Text=txtLists(rand.Next(txtLists.Length))
    End Sub

    Sub lretDefault()
        txtList.Text= _
$"大剣
太刀
片手剣
双剣
ランス
ガンランス
ハンマー
狩猟笛
操虫棍
穿龍棍
ライトボウガン
ヘビィボウガン
弓"

    End Sub
End Class

ApplicationDefinitionを指定しないでビルドする。

ApplicationDefinitionを指定しなかった場合はプログラムのエントリポイントが通常通りMain関数となる。そこから内部的にApplicationDefinitionが行っていることをダイレクトにやってあげれば良い。
やることとしては特に難しくないのでこれでも良いのかもしれない。
ちなみにXAMLのWindow.x:Classに指定したクラス名はそのページファイルの実体にもなる。すなわち、そのクラス名でnewすれば、そのページ/XAMLもnewできるということ。

main.cs
class Entry{
    [STAThread]
    static void Main(){
        var app=new Application();
        var win=new txtLret();
        win.InitializeComponent();
        app.Run(win);
    }
}
main.vb
Module Entry
    <STAThread>
    Sub Main()
        Dim app As New Application()
        Dim win As New txtLret()
        app.Run(win)
    End Sub
End Module

最低限やるべきことはそれぞれこれ位。
(STAThreadについてはこちらでを参照。)
ちなみにVBの方は[Window].InitializeComponentが無くても読みこむことには問題が無いみたい。

Mainでページファイルを扱うことができるということはXAMLのWindow内に定義していたプロパティもMainの中で定義できるということです。次のサンプルではこれも踏まえて載せておく。

サンプルソースEX

フォルダ構成としては下記のようになる。ファイル構成としてはapp.xamlが抜けただけである。

sample\
    project.csproj 又は project.vbproj
    index.xaml
    main.cs 又は main.vb
project.csproj
<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- 出力ファイル名 -->
        <AssemblyName>txtLret</AssemblyName>
        <!-- 出力ファイルのファイル形式(Winexe=GUIプログラミング(WPF)) -->
        <OutputType>Winexe</OutputType>
        <!-- 出力パス -->
        <OutputPath>.\</OutputPath>
        <!-- 使用する.NetFrameworkのバージョン -->
        <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
        <!-- コンパイルをデバッグで行うかリリースで行うか -->
        <Configuration>Release</Configuration>
        <!-- コード最適化の有無(true=有) -->
        <Optimize>true</Optimize>
    </PropertyGroup>

    <ItemGroup>
        <!-- XAMLファイル -->
        <Page Include="index.xaml" />

        <!-- CSファイル -->
        <Compile Include="main.cs" />

        <!-- 参照の追加 -->
        <Reference Include="System" />
        <Reference Include="System.Xaml" />
        <Reference Include="WindowsBase" />
        <Reference Include="PresentationCore" />
        <Reference Include="PresentationFramework" />
    </ItemGroup>

    <!-- .targets -->
    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />

</Project>
project.vbproj
<?xml version="1.0" encoding="utf-8" ?>
<Project
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
    DefaultTargets="Build"
>

    <PropertyGroup>
        <!-- 出力ファイル名 -->
        <AssemblyName>txtLret</AssemblyName>
        <!-- 出力ファイルのファイル形式(Winexe=GUIプログラミング(WPF)) -->
        <OutputType>Winexe</OutputType>
        <!-- 出力パス -->
        <OutputPath>.\</OutputPath>
        <!-- 使用する.NetFrameworkのバージョン -->
        <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
        <!-- コンパイルをデバッグで行うかリリースで行うか -->
        <Configuration>Release</Configuration>
        <!-- コード最適化の有無(true=有) -->
        <Optimize>true</Optimize>
        <!-- VBのOption Infer(型推論)をON=trueにする(デフォルトOFF=false) -->
        <OptionInfer>true</OptionInfer>
        <!-- VBのオーバーフローチェックを無効化する(デフォルトOFF=false) -->
        <RemoveIntegerChecks>true</RemoveIntegerChecks>
    </PropertyGroup>

    <ItemGroup>
        <!-- XAMLファイル -->
        <Page Include="index.xaml" />

        <!-- VBファイル -->
        <Compile Include="main.vb" />

        <!-- 参照の追加 -->
        <Reference Include="System" />
        <Reference Include="System.Xaml" />
        <Reference Include="WindowsBase" />
        <Reference Include="PresentationCore" />
        <Reference Include="PresentationFramework" />

        <!-- 名前空間の追加 -->
        <Import Include="System" />
        <Import Include="Microsoft.VisualBasic" />
    </ItemGroup>

    <!-- .targets -->
    <Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" />

</Project>
index.xaml(C#、VB)
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="txtLret"
>
    <Window.Resources>
        <Style TargetType="TextBox">
            <Setter Property="Width" Value="580" />
            <Setter Property="HorizontalAlignment" Value="Center" />
        </Style>
        <Style TargetType="Button">
            <Setter Property="Height" Value="24" />
            <Setter Property="Margin" Value="10" />
        </Style>
    </Window.Resources>
    <StackPanel>
        <TextBlock
            Margin="0,10,0,5"
            HorizontalAlignment="Center"
            Text="ルーレットしたい値を入れてね。"
        />
        <TextBox
            AcceptsReturn="True"
            VerticalScrollBarVisibility="Auto"
            Height="260"
            Name="txtList"
        />
        <WrapPanel HorizontalAlignment="Center">
            <Button Width="200" Click="lretMain" Content="Let's ルーレット!" />
            <Button Width="100" Click="lretDefault" Content="MH武器" />
        </WrapPanel>
        <TextBox Name="resultList" />
    </StackPanel>
</Window>
main.cs(C#)
using System;
using System.Windows;
using System.Windows.Media;

class Entry{
    [STAThread]
    static void Main(){
        var app=new Application();
        var win=new txtLret();
        new Action<txtLret>(w=>{
            w.Title="テキストルーレット";
            w.Width=620;
            w.Height=440;
            w.ResizeMode=ResizeMode.NoResize;
            w.FontFamily=new FontFamily("Meiryo UI");
            w.FontSize=16;
            w.Foreground=new SolidColorBrush(Color.FromArgb(0xFF,0x00,0x00,0x99));
            w.Background=new SolidColorBrush(Color.FromArgb(0xFF,0x33,0x99,0xFF));
            w.InitializeComponent();
        })(win);
        app.Run(win);
    }
}

partial class txtLret{
    Random rand=new Random();

    void lretMain(object sender,RoutedEventArgs e){
        string[] txtLists=txtList.Text.Split('\n');
        resultList.Text=txtLists[rand.Next(txtLists.Length)];
    }

    void lretDefault(object sender,RoutedEventArgs e){
        txtList.Text=
@"大剣
太刀
片手剣
双剣
ランス
ガンランス
ハンマー
狩猟笛
操虫棍
穿龍棍
ライトボウガン
ヘビィボウガン
弓";
    }
}
main.vb(VB)
Option Strict On

Imports System
Imports System.Windows
Imports System.Windows.Media

Module Entry
    <STAThread>
    Sub Main()
        Dim app As New Application()
        Dim win As New txtLret()
        With win
            .Title="テキストルーレット"
            .Width=620
            .Height=440
            .ResizeMode=ResizeMode.NoResize
            .FontFamily=New FontFamily("Meiryo UI")
            .FontSize=16
            .Foreground=New SolidColorBrush(Color.FromArgb(&HFF,&H00,&H00,&H99))
            .Background=New SolidColorBrush(Color.FromArgb(&HFF,&H33,&H99,&HFF))
        End With
        app.Run(win)
    End Sub
End Module

Class txtLret
    Dim rand As New Random()

    Sub lretMain()
        Dim txtLists As String()=Split(txtList.Text,vbLf)
        resultList.Text=txtLists(rand.Next(txtLists.Length))
    End Sub

    Sub lretDefault()
        txtList.Text= _
$"大剣
太刀
片手剣
双剣
ランス
ガンランス
ハンマー
狩猟笛
操虫棍
穿龍棍
ライトボウガン
ヘビィボウガン
弓"

    End Sub
End Class
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした