Windowsフォームデザイナーでツールボックスからカスタムコントロールを追加する場合の注意点

Visual Studio Community 2013で、C#で、Windows Formsでアプリ書いているんだが、なかなかやっかいなはまり方することが多くて困る。

今日はListViewを、継承してカスタムしたクラスに置き換えようとしてはまった。

カスタムコントロールを作るまでは、MSDNに書いてある感じでまあ問題ない。

方法 : 既存の Windows フォーム コントロールから継承する

問題はこのカスタムコントロールを、フォームデザイナーを使って、ツールボックスから追加しようとした場合に起きる。

上記の手順で追加した場合、カスタムコントロールはWindowsフォーム本体と同じ名前空間に属する。それ自体は自然だし問題ない。

だがフォームデザイナーがどうも、コントロールとフォーム本体が同じ名前空間に属する場合を想定していないようで、おかしなコードを出力する。

namespace MyApp
{
    partial class MyApp
    {
        ...

        private void InitializeComponent()
        {
            this.listView1 = new MyApp.ListView();

            ...
        }

        ...
    }
}

このコードが生成される、 myapp.designer.cs ファイルの名前空間MyApp になっているので、コンパイラMyApp.MyApp.ListView を見ようとしてエラーになる。

型名 'ListView' は型 'MyApp.MyApp' に存在しません。

対策としては、手動で書き換えるしかなさそうだ……。

今は大丈夫だけど、これからコントロールが増えてきたらどうしよう。

  • フォームデザイナーなんて使わず、手動で書く。
  • WPFに移行する。

どっちがいいだろうかね。困った。

おまけ

カスタムコントロールの方のクラスはまあこんな感じになるわけだが、

namespace MyApp
{
    public partial class ListView : System.Windows.Forms.ListView
    {
        public ListView()
        {
            InitializeComponent();
        }
    }
}

間違ってこう書くと、名前が被ってエラーになる。

using System.Windows.Forms;

namespace MyApp
{
    public partial class ListView : ListView
    {
        public ListView()
        {
            InitializeComponent();
        }
    }
}
'MyApp.ListView' と 'MyApp.ListView' を含む、循環する基本クラスの依存関係です。

まあ、ちゃんと文法わかってればない間違えなんだけど。

追記。

どうも、フォームデザイナーで修正する度に書き換えられちゃう模様。本気でどうにかしないと……。

C#のcatchがちょっと面白い

最近C#をちょっと書いているのだが、まったく未知のところからやっているとつい変なコードを書いてしまう。

先日は、こんなコードを書いた。

using System; // for Exception

class Program
{
    private static void Main() {
        try {
            throw new Exception("error!");
        }
        catch (Exception) {
        }
    }
}

見ての通り、catch句に受け取る型名しか書いていない。なんでこんなことが起きるのかとTwitterでつぶやいたら、こんな感じで教えていただいた。

実用的には、あるクラスの例外だけ再スローして、ほかの例外は処理する、などに使えるのだろう。

using System;

class ExceptionA : Exception
{
    // 関係ないけどコンストラクタいちいち書かないといけないのなんとかならんの?
    public ExceptionA(string message) : base(message) {
    }
}

class Program
{
    private static void Main() {
        try {
            throw new ExceptionA("error!");
        }
        catch (ExceptionA) {
            // こうすると再スローできる
            throw;
        }
        catch (Exception e) {
            // ExceptionA以外のExceptionクラスの例外は、処理する
            Console.Write(e);
        }
        // こうすると残りぜんぶ取れる
        catch {
        }
    }
}

型のゆるい言語を使っていると、catchした後にクラスを見て処理とか考えてしまうが、型の厳しい言語ではこういうやり方があるのだろう。

型の厳しい言語はほとんど使ったことがないので勉強になる。

追記

タイトルをちょっと修正した。メソッドじゃないのにcatch()とかおかしかったので。

メソッドといえば、メソッドのパラメータにクラス・型だけ書くのはできないようだ。まあ、意味ないしね。

// コンパイルすると、
// ... error CS1001: ID がありません。
// となる。
using System;

class Program
{
    private static void Main() {
    }

    private void Method(Exception) {
    }
}

QuickRunでC#叩けるようにする……までもなかった話

QuickRunはC#デフォルトで対応していた

なにを間違ったかC#書いてて、基本はIDEでいいのだけど、ちょっとしたことを試すのにQuickRunが使いたくなったのであちこち検索して設定してみた。

のだが、さらに調べるとそもそもQuickRunは現在デフォルトでC#に対応していた。

最初動かなくて気付かなかったがそれはPATHを設定していなかっただけだった。のでPATHを設定。

PATHには、C:\Windows\Microsoft.NET\Framework64\v4.0.30319 とか、csc.exe がある場所を指定すればいい。

だが出力が文字化けた。調べてみると、termencoding が空だった。set encoding=utf8 していなければそれでも大丈夫だった気がするが、してたのでまあ、termencoding も設定。

set termencoding=cp932

手動設定を試していたときに :@" ではまった件

この間のKindleのセールで買ったVimテクニックバイブルをちびちび進めていて、その中にあった、Vimスクリプトをコピーして :@" で実行、というテクニックを最近使っていたのだが、

let g:quickrun_config = {
            \ ...

みたいなスクリプトでエラー (E15) が出る。

:h :@ で調べてみたところ、 :@{register} での実行は、Exコマンドを連続実行してるだけみたいな感じで、どうも行の継続は無理っぽい。

対処法が思い付かなかったが、QuickRunはVimスクリプトも当然対応しているので変なことせずにQuickRun使えば大丈夫だった。

QuickRunマジ便利という話。

Angular.jsとBootstrapを競合させずに使う

普通にやったらアウトっぽい。

知らずにBootstrapに後からAngular.js突っ込んでてうまく動かなかったので、以下の手順でなんとかした。

  1. jquery.js, bootstrap.jsを外す。
  2. bower install angular-ui-bootstrap-bower --save
  3. angular.module()の第2引数でui.bootstrapへの依存を指定。
  4. Bootstrapのオリジナルの動作指定は使えないので、ここの下の方を参考に、HTMLを書き換える。

つーか最初からちゃんとやってればよかった。

Node.js, npmインストール時の注意

NODE_PATHを設定しないとnpm install -g ...で入れたモジュールは動作しない。こんな感じ。

# on ~/.bash_profile, ~/.zprofile

# npmは重いし、このコマンドの結果は頻繁に変わるわけではないので、生で入れてもいい
export NODE_PATH=$(npm root -g)

驚くべきことにこの情報がNode.jsnpmの公式に存在しない。

そもそもNode.jsに至っては、インストール方法が公式に載っていない。なんなんだろう?

nvmを使うと楽そうだが、今回はそれほど本格的に使うわけではないのでyumで入れた。少し古いが、普通に入る。

Vimで、式用コマンドラインモードの履歴をコピーする方法

Vimでは、ノーマルモードでのp, Pによるレジスタ貼り付け以外に、挿入モードでのCTRL-Rからの貼り付けがある。

貼り付けられるレジスタにはいくつか特殊なものがあるが、特に=の挙動は変わっていて、これを入力すると、式用のコマンドラインモードになる。

このモードで入力した文字列は式として評価され、その戻り値が貼り付けられる。

簡単だが式が長くなるような計算などで便利でよく使うのだが、今回、入力した式の方を後から再利用したくなった。

コマンドラインモードの履歴を扱うには、コマンドラインウィンドウを開けばいい。

通常のコマンドラインモード用なら、q:でおっけー。:qを打ち間違えることで出てきたりするので、多分みんな知ってる。

検索用ならq/, q?.

だが、式用のキーバインドが見つからない。q=にはなにも割り当てられていないようだし。

ということで:h q:から調べてみたところ、

1. コマンドラインモードで、オプション 'cedit' で指定されたキーを使う。
   'compatible' がオフのときの既定値は CTRL-F である。

これで開けるということだった。今回の場合、i_CTRL-R_=で式用コマンドラインモードを開いた上で、CTRL-Fを入力することで行けた。

便利。とはいえq=も欲しかった。

HTML(など)で使うURIの相対的な表記方法についてまとめる

いい加減「ネットワークパス参照」とか覚えたいので。

とはいっても、基本的にはRFC 3986を読めば書いてあるのだが。

Uniform Resource Identifier (URI): 一般的構文

  • // で始まるやつ。
    • 「ネットワークパス参照」
    • 昔はあまり見なかったが、httpsや、外部でホスティングされているJavaScriptの使用が流行ったせいで最近ではけっこう見る。
    • この名前だと誰も覚えられないと思う。「スキーム相対」とかそんな感じにしてはどうか。関係ないがスキームとスキーマがどっちがどっちかも覚えられない。
  • / で始まるやつ。
    • 絶対パス参照」
    • 文書自身の位置を気にせずできるので便利。ただし、素人さんにこれで渡すと、手もとで見れない! となって面倒臭い。
    • 確かに絶対パスだけどこれもまた混乱する名前だ。「ドメイン相対」とかにしたら。それも微妙か。
  • それ以外。
    • 相対パス参照」
    • ./やら../やら、aaa/bbbやらで始まるの。
    • RFC確認して知ったけど、aaa:bbbみたいなのだと、スキーム名と混同されるるので./ちゃんとつけろ、らしい。これ考えると常に.で始めるべきかも。

それにしても問題は、人に伝えるときだ。どの表記方法も、正しい名前ではまず通用しないと思われる。

かといって十分に知れ渡った慣用名があるという感じでもない。実例を出しつつ伝えるのがベターか。