【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

VC# 2010でのプロジェクト作成方法

Windows Mobile勉強会(id:n7shi:20101108)で使用した資料を公開します。

  • 勉強会で発生した問題などを踏まえて、一部加筆修正しています。
  • VB.NETではmscorlibを差し替える設定が分からないため、同じ手法は使えません。
    • 【追記】コメントでVB.NET 2008での設定方法を教えていただきました。
  • VC# 2008での方法、CABの作り方など ⇒ id:n7shi:20090106
  • なぜこれで動くのかという資料 ⇒ id:n7shi:20091031

初期設定

VC# 2010でやっておくと便利な設定。

  • ツール→設定→上級者設定
  • ツール→オプション
    • チェックする:すべての設定を表示
    • テキストエディター→すべての言語→全般
      • チェックする:行番号

描画が乱れる場合は以下の設定を推奨。(問題なければ不要)

  • ツール→オプション→環境→全般
    • チェックを外す:クライアントのパフォーマンスに基づいて視覚的効果を自動的に調整する
    • チェックを外す:可能ならハードウェアのグラフィックスアクセラレータを使用する

プロジェクト作成

  1. VC# 2010 Expressを起動
  2. Windowsフォームアプリケーションを新規作成
  3. すべて保存
  4. ソリューションエクスプローラ
    1. プロジェクトを右クリック→プロパティ
      1. ビルド→詳細設定→「mscorlib.dllを参照しない」をチェック→OK
      2. 参照パスを追加: C:\Program Files\Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE
      3. アプリケーション→対象のフレームワークを変更: .NET Framework 2.0
    2. 以下を削除
      1. Properties/Resources.resx
      2. Properties/Settings.settings
      3. app.config
      4. Form1.cs/Form1.Designer.cs
  5. すべて保存
  6. VC# 2010 Expressを閉じる
  1. プロジェクトファイルをエディタで開く
  2. 内のを以下のように書き換える
<ItemGroup>
  <Reference Include="mscorlib" />
  <Reference Include="System" />
  <Reference Include="System.Data" />
  <Reference Include="System.Drawing" />
  <Reference Include="System.Windows.Forms" />
  <Reference Include="System.Xml" />
</ItemGroup>
  1. ソリューションを開く
  2. 実行してみる
  3. エラーが出てくるので、行ごと削除
  4. 実行できるようになる。

EXEをコピーすると端末で動くが、終了する手段がない。
[OK]で終了できるようにする。
WM 6.5.3ではメニューバーに[OK]が表示されるため、空メニューを作成。
Form1のコンストラクタに以下のコードを追加。

public Form1()
{
    MinimizeBox = false;
    Menu = new MainMenu();
}

後は、GUIデザイナが使えない以外は、普通に開発できる。
普段はエミュレータなしで開発して、たまにエミュレータに転送して動作確認。

エミュレータ

動作が比較的軽くてインストールの依存関係も少ないWM6がお勧め。

  • スタート→すべてのプログラム→Windows Mobile 6 SDK→Standalone Emulator Images→Japanese→Classic等

Windows 7ではショートカットが作成されない問題が何件も報告された。原因・対処法は不明。ショートカットは引数やカレントが複雑なので、EXEをクリックするだけでは起動しない。正常に作成された環境からショートカットをコピーして使うしかなさそう。

6.5.3のショートカットは独自に作成したものを提供中。しかし6.5.3は起動が重く、VGA等の解像度で表示が崩れる問題がある。

プロジェクトのDebugフォルダを共有しておくと、デバッグが簡単。

  • ファイル→構成→全般→共有フォルダ

共有フォルダはエクスプローラでStorage Card扱いとなる。
表示しっぱなしにしておいて、デバッグしたいときにEXEをクリック。

動作がおかしくなったときはソフトリセットすると、起動後にコピーしたファイル等が消えなくて便利。

GUI

※この節のコードはすべてForm1クラス内に記述することが前提。

画面サイズ指定などの決まり文句を含めたテンプレートは以下の通り。
LayoutやAutoScale関係は自動調整のために必要。

public Form1()
{
    Text = "アプリ";
    Size = new Size(240, 320);
    MinimizeBox = false;
    SuspendLayout();
    AutoScaleDimensions = new SizeF(96, 96);
    AutoScaleMode = AutoScaleMode.Dpi;
    Menu = new MainMenu();
    InitializeComponent();
    ResumeLayout(false);
}

private void InitializeComponent()
{
}

コントロール配置の定型。
buttonはInitializeComponent()以外でも使えるようにするため、外で定義。
button.Clickの行は、+=の部分で[+][=][Tab][Tab]と入力すれば簡単。

private Button button = new Button();

private void InitializeComponent()
{
    button.Text = "ボタン";
    button.Bounds = new Rectangle(10, 50, 100, 40);
    button.Click += new EventHandler(button_Click);
    Controls.Add(button);
}

void button_Click(object sender, EventArgs e)
{
    MessageBox.Show("hello");
}

その他のコントロール

  • Button, CheckBox, ComboBox, DateTimePicker, DomainUpDown, HScrollBar, Label, LinkLabel, ListBox, ListView, MonthCalendar, NumericUpDown, Panel, PictureBox, ProgressBar, RadioButton, Splitter, StatusBar, TabControl, TabPage, TextBox, ToolBar, TrackBar, TreeView, VScrollBar, WebBrowser

Windows 7では、Compact Frameworkで作成したTextBoxでIMEが使えないという問題が発生します。対処法は不明です。エミュレータや実機に転送すると問題なく入力できます。

描画

基本的なコード。
[O][V][Space][O][N][P][Enter]と入力すれば簡単。

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    using (var pen = new Pen(Color.Red))
    using (var brush = new SolidBrush(Color.Blue))
    {
        e.Graphics.DrawLine(pen, 0, 0, 20, 20);
        e.Graphics.DrawLine(pen, 0, 20, 20, 0);
        e.Graphics.FillRectangle(brush, 5, 5, 10, 10);
        e.Graphics.DrawString("abc", Font, brush, 0, 20);
    }
}

EXEと同じフォルダの画像を読み込んで表示。
test.pngはプロジェクトにDnDして、プロパティでコピー設定しておく。

private Bitmap image;

private void InitializeComponent()
{
    var exe = Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName;
    var path = Path.GetDirectoryName(exe);
    var png = Path.Combine(path, "test.png");
    image = new Bitmap(png);
}

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    e.Graphics.DrawImage(image, 5, 5);
}

一定時間ごとに動かす(ちらつく)
※ダブルバッファは各自工夫してください!

private int x = 0;

private void InitializeComponent()
{
    var timer = new Timer();
    timer.Interval = 50;
    timer.Tick += new EventHandler(timer_Tick);
    timer.Enabled = true;
}

void timer_Tick(object sender, EventArgs e)
{
    x += 4;
    if (x >= ClientSize.Width) x = 0;
    Invalidate();
}

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    using (var brush = new SolidBrush(Color.Red))
    {
        e.Graphics.FillEllipse(brush, x, 0, 16, 16);
    }
}

テクニック集

Mainに[STAThread]を指定できるようにする。
指定しないとOpenFileDialogなどが使えない。

namespace System
{
    public class STAThreadAttribute : Attribute { }
}

.NET 2.0でも拡張メソッドが使えるようにする。

namespace System.Runtime.CompilerServices
{
    public class ExtensionAttribute : Attribute { }
}

WMかどうかを判定して動作を変える。

if (Environment.OSVersion.Platform == PlatformID.WinCE) ...

EXEと同じフォルダのファイルにアクセス。

var exe = Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName;
var path = Path.GetDirectoryName(exe);
var ini = Path.Combine(path, "app.ini");

.NET 2.0では委譲型を定義しておくと便利。

namespace System
{
    public delegate void Action();
    public delegate void Action<T1, T2>(T1 a, T2 b);
    public delegate void Action<T1, T2, T3>(T1 a, T2 b, T3 c);
    public delegate TR Func<TR>();
    public delegate TR Func<T1, TR>(T1 a);
    public delegate TR Func<T1, T2, TR>(T1 a, T2 b);
}