ラベル C# の投稿を表示しています。 すべての投稿を表示
ラベル C# の投稿を表示しています。 すべての投稿を表示

2014年1月7日火曜日

C# 閉じるイベントのキャンセル

親子関係のフォームを使うアプリケーションで、子のフォームを自由に出したり消したりしたい。
それだけならボタンを押されるたびにフォームの生成か破棄をすればいいだけなのだけども、
そのフォームの確保しているリソース量が多いので新しく作るたびに確保するのもなあ、と思った。
というわけで、閉じるイベントをキャンセルしてフォームを非表示にしてみる。

参考サイト
C# - フォームが閉じられるのをキャンセルする

/*---------------------------------------------------------------------------*/
this.FormClosing += delegate(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
sender.Visible = !sender.Visible;
}
/*---------------------------------------------------------------------------*/

フォーム終了時に呼び出されるイベントを追加し、
その中でFormClosingEventArgsのCancelプロパティをtrueにする。
こうするとフォームを閉じるのがキャンセルされる。
そのあとにフォームのVisibleプロパティを反転させれば完璧さ!とか思ったらだめだった。
これをやってしまうとどんなときでもフォームが閉じなくなるので、アプリケーションを終了できない!

親の閉じるボタンを押されたときは一緒に閉じてほしいので、その辺を判断できないものか。
FormClosingEventArgsの中身を調べてみたらあったぞう。

/*---------------------------------------------------------------------------*/
this.FormClosing += delegate(object sender, FormClosingEventArgs e)
{
// 閉じるボタンを押されたときに終了する
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
this.Visible = !this.Visible;
}
}
/*---------------------------------------------------------------------------*/

FormClosingEventArgsのCloseReasonプロパティがどうしてフォームが閉じるのか教えてくれる。
自分の閉じるボタンを押されたときはUserClosingが入っているようなので、その時だけ動作。

一応期待通りの動作はこれでできた。

C# 閉じるボタンを無効にする

親フォームと子フォームを作って、子フォームを消されたりすると困る状況になったので、
閉じるボタンを無効にしてみた。

参考サイト
フォームの「閉じる」ボタンを無効にする: .NET Tips: C#, VB.NET

色々方法があるようだけど、一番強力そうな方法を採用。

/*---------------------------------------------------------------------------*/
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
const int CS_NOCLOSE = 0x200;

System.Windows.Forms.CreateParams createParams = base.CreateParams;
createParams.ClassStyle |= CS_NOCLOSE;

return createParams;
}
}
/*---------------------------------------------------------------------------*/

フォームが作成されるときのパラメータを弄って閉じれなくしてしまっている。
閉じるボタンの無効はもちろんのこと、「Alt」+「F4」も無効になる。

2013年12月20日金曜日

C# 定数

C#をやっていると、たまにメンバの定数ってどう宣言すんだっけ? となる。ぼけか?
とにかく、もうそんなことにならないためにメモ。

参考サイト
定数(C# によるプログラミング入門) - ++C++

メンバの定数を定義するためには、constとreadonlyの2つの方法がある。

/*---------------------------------------------------------------------------*/
private const int PI = 3.14;
private static readonly int PI = 3.14;
/*---------------------------------------------------------------------------*/

2つの違いは以下の通り。参考サイトからまるっとコピー!

constreadonly
ローカル変数にも使えるクラスのメンバー変数のみ。
常に静的変数と同じ扱い。static の有無を変えられる。
宣言時にのみ初期化可能。コンストラクタ内で値を書き換え可能。
コンパイル結果はリテラルと同等。コンパイル結果は変数と同等。
インスタンスを new で生成するようなものには使えない。new 可能。

ああ、bloggerのコピペするとhtml構文もコピーする機能ってこのためなのか。いまさら知った。

constは扱いがリテラル(直接記述したデータ。コンパイル時に値が展開される)なので、
バージョニング問題というものが発生するらしい。
constなデータを外部で参照している場合、コンパイル時に参照側も再コンパイルしないと、
値が変化しない現象。
privateなメンバ、ソースコード内からしか参照されない値なら問題ない。



まとめとして、privateな定数ならconst、publicな定数ならreadonlyを使用するのが無難っぽい。

2013年12月13日金曜日

C# CSVファイルを読み込み

なんてこったい。C#ではCSVファイルを読み込むのが超簡単ではないですかい。

参考サイト
CSVファイルを読み込むには?[2.0のみ、C#、VB]
TextFieldParser クラス (Microsoft.VisualBasic.FileIO) - MSDN

Microsoft.VisualBasic.FileIO名前空間のTextFieldParserというクラスを使用する。

/*---------------------------------------------------------------------------*/
TextFieldParser parser = new TextFieldParser("hoge.csv");
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData) {
   string[] row = parser.ReadFields();
}
/*---------------------------------------------------------------------------*/

これだけでオーケーさ!
処理としてはコンストラクタでファイルを読んで、フィールドの分割方法をデリミタに指定。
さらにデリミタをカンマに指定してループで最後まで読み込んでいる。
今回はCSVファイルだけど、調べてみたら色々なデータ構成に対応できそう。

今まで自力で実装してきたのはなんだったのやら。

C# アプリケーションを64bitOS上で32bitモードで動かす

以前作成したアプリケーションがwindows7上だと動かないという問題が発生した。

/*---------------------------------------------------------------------------*/
間違ったフォーマットのプログラムを読み込もうとしました。
(HRESULT からの例外: 0x800xxxxx)
/*---------------------------------------------------------------------------*/

こんなエラーが出る。
どうやらアプリケーションが64bitモードで動いているのが原因らしい。
使用しているdllが32bit環境のものだったので、それを読み込めないと怒られているようだ。

参考サイト

回避方法のひとつとして64bitOS上でも32bitモードで動かすというのがあったのでやってみた。
Expressを使用していたので、上記サイトで紹介されている方法は使用できない。
探してみたら、Expressでやる方法があったのでそちらを試す。

参考サイト
  1. メニューの「ツール(T)」から「オプション(O)」を開く。
  2. 左下の方にある「すべての設定を表示(A)」にチェックを入れる。
  3. 追加された「プロジェクトおよびソリューション」項目に移動。
  4. 「全般」の中にある「ビルド構成の詳細を表示(I)」にチェックを入れる。
  5. メニューの「ビルド(B)」から「構成マネージャ(O)」を開く。
  6. 「アクティブソリューションプラットフォーム」で「新規作成」。
  7. x86の項目を作成する。
以上。これでビルドしたら64bitOS上でも動いた。
よかったよかった。

2013年11月26日火曜日

C# SQLiteDataAdapterでUpdate

ぼちぼちSQLiteにも慣れたような気のせいのような。
Select文で取ってきたデータを処理する方法として、
SQLiteはSQLiteDataReaderを使う方法と、SQLiteDataAdapterを使う方法がある。
今回は、後者で取ってきたDataTableをいじったから
もとのデータベースに反映させたいよう、というお話。

基本的な流れ


参考サイト

SQLiteDataAdapterのUpdateメソッドにDataTableを渡せばよいとな?

/*---------------------------------------------------------------------------*/
// hoge.dbに接続
SQLiteConnection conn = new SQLiteConnection("hoge.db");
conn.open();

// fugaテーブルのデータを取ってくる
SQLiteDataAdapter adapter = new SQLiteDataAdapter("select * from fuga", conn );
DataTable data = new DataTable();
adapter.Fill(data);

// なんかデータテーブルをいじる

// 更新
adapter.Update(data);
/*---------------------------------------------------------------------------*/

で、ほんともう単純にやってみたら例外吐かれた。
なんでも「有効なUpdateCommandがないわよ。ばかね」だそうで。

SQLiteCommandBuilderでコマンドを自動生成する


参考サイト

テーブルごとに有効なコマンドを自分で設定するのなんて正気の沙汰じゃない。
そもそもそれなら自分でデータベースを直でいじるわい、と思っていたら、
自動生成してくれるクラスがあった。

/*---------------------------------------------------------------------------*/
SQLiteCommandBuilder bulider = new SQLiteCommandBuilder(adapter);
adapter.Update(data);
/*---------------------------------------------------------------------------*/

たったこれだけで勝手に各種コマンドを自動生成してくれるそうで。大助かり。
これでSQLiteDataAdapter を使ってのデータベース更新ができるようになった。

補足:PRIMARY KEYの設定


データベースのKEYの設定をちゃんとしていないと、SQLiteCommandBuilderで実は怒られる。
更新時のWHERE句の指定でプライマリキーを使用するのだと思う。多分。
ちゃんと設定してあげよう。

2013年11月25日月曜日

C# ディレクトリのファイル一覧を取得する

コンソールツールなんか作ってると、ディレクトリのファイル一覧が取得したくなる。
指定ディレクトリ(または現在のディレクトリ)のファイルに対して特定の処理を行うとか。
そんなわけでやってみた。

ファイル一覧を取得する


/*---------------------------------------------------------------------------*/
string[] files = Directory.GetFiles(directory);
/*---------------------------------------------------------------------------*/

これだけでいいんだから驚き。directoryにはディレクトリのパスが入っている。
なお、特定の拡張子のファイルだけが欲しいときはこうする。

/*---------------------------------------------------------------------------*/
string[] files = Directory.GetFiles(directory, "*.txt");
/*---------------------------------------------------------------------------*/

テキストファイルだけを取ってくる例。
ワイルドカードを使うパターンは多いので、分かる人はわざわざ調べなくても出てきそう。

現在のディレクトリのパスを取得する


GetFilesは引数としてディレクトリ名を要求する。
引数なしなら現在のディレクトリ、なんて気の利いたことはしてくれない。
なので、現在のディレクトリを取得する方法も一緒に調べた。

/*---------------------------------------------------------------------------*/
    string directory = Directory.GetCurrentDirectory();
/*---------------------------------------------------------------------------*/

うーん、まんまだ。インテリセンス見ただけで気づく人も普通にいそう。

C# foreachでDictionaryのキーと値を同時に取得する

C#ではIEnumerableインターフェースを継承してるクラスで、foreachを使用することができる。
コレクションの全ての要素を一回ずつ読み込んでくれる機能。
forと違ってループ回数の指定とか面倒なことしなくてもいいのでなにかと便利。

で、PHPとかにもある機能なんだけども、PHPだと連想配列のキーも一緒に取得できる。
ならC#でもきっとできるさ、と思って調べてみたら、あった。

参考サイト

やり方は簡単。

/*---------------------------------------------------------------------------*/
foreach (KeyValuePair<string, string> pair in dictionary)
{
// なんかする
}
/*---------------------------------------------------------------------------*/

以上。
IDictionaryの要素が<TKey,TValue>のペアで構成されているため、
それを受け取れるようにしたのがKeyValuePairだそうな。

2013年11月20日水曜日

C# SQLiteを使ってみた

「SQLite? なにそれ?」状態の自分がSQLiteを使ってみた。

SQLiteってなんぞや?


参考サイト

色々と特徴が書き出されてるけど、要はアプリケーションに組み込む形でデータベースが
使えるとさえ分かっていればいいんだな。いいんだよね?

SQLiteをダウンロード


参考サイト

公式サイトにダウンロードしに行くと、なんかたくさんファイルがあった。
ちゃんと違いがあるみたいなので、上記サイトをよく読んで自分の環境にあったやつを使おう。
すごく丁寧に解説されている。
なぜかインストーラを使うと失敗するので、今回はzip版をダウンロード。

Visual C#で使う


参考サイト

いろんなページで参照に追加する追加する言ってるけど、どうするんだと思っていたら、
本当にプロジェクトに参照を追加するだけでよかったというオチ。
  1. 適当なプロジェクトを作る
  2. 「参照の追加」の「参照」タブから解凍したSystem.Data.SQLite.dllを追加
実行する際はexeと同じフォルダにSQLite.Interop.dllを追加してやろう。
サンプルとして上記サイトに書いてあるコードを実行させてもらいました。
テーブル追加、レコード追加、読み込み、出力の流れ。
動いた。

テーブルを作成すると、
実行ファイルと同じ場所にテーブルのデータファイル(?)が出来上がる。
当然同じテーブルは作れないので連続でテストする時は注意。

2013年11月15日金曜日

C# アプリケーションを終了する

そもそもアプリを起動するためのデータが取得できなかっただとか、
アプリケーションに必須なものが取得できずアプリを終了せざるをえないこともあるだろう。
あるいは、閉じるボタン以外の場所をクリックしたいときに終了するとか。

そんなわけでどうすればアプリケーションを終了できるのかを調べてみた。

参考サイト
アプリケーション(自分自身)を終了させる: .NET Tips: C#, VB.NET

上記サイトによると、3つの方法で終了できるらしい。

/*---------------------------------------------------------------------------*/
this.Close();
System.Windows.Forms.Application.Exit();
System.Environment.Exit(0);
Environment.FailFast("イベントログに書き込む")
/*---------------------------------------------------------------------------*/

上から順番に、
  • メインフォームを閉じる
  • フォームアプリケーションの終了
  • エントリポイントを抜ける
  • 強制終了
だそうな。

上記2つはフォームアプリケーションでしか使用できないので、コンソールは下の2択。
さらにFailFastはアプリケーションの状態悪化時なんかに呼び出すべきものであるらしいので、
よほどのことがない限り、コンソールはEnvroiment.Exitでよさそう。

フォームアプリケーションは全部選択できるんだけど、上記の理由によりFailFastはなし。
Envroiment.Exitはプロセスを強制的に終了してエントリポイントを抜けるため、
呼び出した場所以降のメソッドが呼ばれない。
Closeは、そもそもメインフォーム以外の場所から呼べんじゃないですか。
参照を持っていれば話は別だけども。
ということでApplication.Exitが一番よさそう。

C# フォームアプリケーションで複数ウィンドウ

フォームアプリケーションをやっていたら、
フォームBで選択されたデータをフォームAに反映したいという状況になった。
フォームBがモーダルダイアログならShowDialogで問題ないんだけど、
フォームAと常に一緒に表示されているウィンドウなので、親子関係を構築する。



親子関係の構築の仕方は簡単。子ウィンドウのShowメソッド呼び出し時に親を渡す。

/*---------------------------------------------------------------------------*/
public FormA() {
        this.Shown += delegate(object sender, EventArgs e) {
        Form formB = new Form();
        formB.Show(this);
        };
}
/*---------------------------------------------------------------------------*/

上記の例では、コンストラクタでShownイベントハンドラを設定し、
イベントハンドラ内でフォームBを生成、親として自分自身を渡している。
コンストラクタ内で直接やると、親のインスタンスが生成されておらず、
期待通りの結果にならないので注意。

表示位置もこちらで自由に指定したいのなら、
StartPositionプロパティをFormStartPosition.Manualに設定して Location を弄る。

常にその配置に固定したいならMoveやResizeにイベントハンドラを設定。

2013年11月14日木曜日

C# FileDialogとカレントディレクトリ

C#のフォームアプリケーションではファイル操作を行うときにファイルダイアログを使うと、
専用のダイアログを開いてファイルを選択できる。

このファイルダイアログ、適当に生成して使うと、
たまげたことにアプリケーションのカレントディレクトリを変更しやがる。
相対アドレスを直接指定して画像ファイルを読み込みとかしていると、
当然のごとく読めなくなった。

こういうときは、RestorDirectoryプロパティをtrueにしてやるといい。

/*---------------------------------------------------------------------------*/
OpenFileDialog dialog = new OpenFileDialog()
{
   RestoreDirectory = true,
};
DialogResult result = dialog.ShowDialog();
/*---------------------------------------------------------------------------*/

上記のコードではOpenFileDialogで行っているが、SaveFileDialogでも同様。

ところで、newしたあとに{}で囲むとプロパティを操作できるのね。
機能名がわからんから詳しく調べられてないんだけど、なんなんだこりゃ?

2013年11月13日水曜日

C# Formあれこれ

ツール作成にはC#が便利だ! というのをどこかで聞いたような聞いていないような。
C#のフォームアプリケーションを試してみたので、メモを残しておく。



まずは単純にフォームを表示するだけ

  1. VisualStudio or VisualC#を立ち上げる。
  2. 「ファイル」→「新しいプロジェクト」→「Windowsフォームアプリケーション」
完成! あらやだ簡単。なにもないフォームを表示するだけだけど。
ここから色々いじくっていくのだ。

フォームデザイナを使う


C#にはフォームデザイナという機能があって、
メニューやテキストボックスなどのコントロールをグラフィカルに追加できる。
試しに使う方法と使わない方法を試してみたけど、使ったほうが圧倒的に楽。
デザインを変更するときもぽちぽちするだけですむのはありがたい。

  1. デザイナを開いた状態で右側の「ツールボックス」へマウスオン
  2. 使いたいコントロールをデザイナ上の好きな場所へドラッグオンドロップ

イベントハンドラを追加する


フォームアプリケーションは
ボタンを押す、指定時間経過などのイベントにしたがって処理を行う「イベント駆動方式」。
ちゃんと意味を理解したのはいまさらだったりする。我ながらひでえ。
そんなわけで、ユーザーのアクションに対して適切な処理を行うためには、
イベントを追加してやらにゃなりません。
デザイナを使用していると、これも簡単に行える。(クリック系の処理に限る)

  1. 追加したコントロールをダブルクリック。
  2. ソースコードにイベントハンドラ定義用の関数が追加され、ついでにそこが開く。
  3. 処理を書き込む。



とりあえず基本中の基本をやってみたのをまとめてみた。
これだけでも簡単なツールは作れると思う。
イベントもほしいイベントは大抵用意されているので、ちょっと調べればすぐ出てくる、はず。