DOBON.NET DOBON.NETプログラミング掲示板過去ログ

カスタムコントロールの作成について

環境/言語:[OS : Windows XP Professional / 言語 : Visual Basic .NET / .NET Framework : 1.1]
分類:[.NET]

【解決したい問題】

お世話になります。

現在勉強と一部仕事を兼ねてTextBoxを継承したカスタムコントロールの作成に初挑戦しています。
# まずは、試験的にIsNumeric(数値(金額))用に拡張中です。

挑戦するにあたってWEB検索などで多少ですが情報収集をしたところ
Key〜やTextChangedで入力チェックをしている物が多い(?)ようでした。
入力中の段階で常にチェックをかけてはじくという事なのかな…と。

自分の場合は、入力は自由にさせて最後にOnValidatingで
・入力エラー(IsNumericがFalse)ならe.Cancel = True
・エラーでなければ"#,##0"でフォーマット
といった具合で進めています。
# 一般的に見て仕様としてどうなのかは疑問(自信なし)ですが。とりあえず試験的に…

で、ようやく本題(質問)に入りますが
上記だとテキストボックス(MyTextBox)がフォーカスを取得して検証イベントが
発生しないと入力制限やフォーマットがかからないと思いますが、
たとえばデータ修正フォームなどで修正データの値が直接Textプロパティに
セットされた場合にも同様の処理を行うのにはどうすればいいのでしょうか?
# MyTextBox1.Text = "10000" とされた場合も "10,000" にするイメージなんですが。

【解決するために何をしたか】

その場の思い付きだけで
Textプロパティを Shadows とか Overrides してIsNumeric(Value)が
Trueならフォーマットする?とか考えてみましたが、検討違い…??


今回もうまく文章をまとめる事ができず、
ちんたらとした質問文で申し訳ありませんがよろしくお願い致します。
■No16933に返信(典武1点さんの記事)
http://jeanne.wankuma.com/tips/textbox/permitchars.html

ここでどうですか?
■No16933に返信(典武1点さんの記事)
> Key〜やTextChangedで入力チェックをしている物が多い(?)ようでした。
> 入力中の段階で常にチェックをかけてはじくという事なのかな…と。

場合によりけりでしょうね。
いくら、Validating イベントでも、下記ような仕様のままでは、
返ってアクセシビリティ、ユーザビリティの低下に繋がります。

> 自分の場合は、入力は自由にさせて最後にOnValidatingで
> ・入力エラー(IsNumericがFalse)ならe.Cancel = True

検証が成功しない限り、フォーカスが移動できないユーザビリティの悪さを解決したいところですね。

> 上記だとテキストボックス(MyTextBox)がフォーカスを取得して検証イベントが
> 発生しないと入力制限やフォーマットがかからないと思いますが、
> たとえばデータ修正フォームなどで修正データの値が直接Textプロパティに
> セットされた場合にも同様の処理を行うのにはどうすればいいのでしょうか?
> # MyTextBox1.Text = "10000" とされた場合も "10,000" にするイメージなんですが。

これも、返ってアクセシビリティの低下に繋がりそうです。
これだったら、MaskedEdit のようなコントロールを使った方が良いです。

> その場の思い付きだけで
> Textプロパティを Shadows とか Overrides してIsNumeric(Value)が
> Trueならフォーマットする?とか考えてみましたが、検討違い…??

これは、絶対に無理です。
TextChanged イベントで遅いとするならば、WM_CHAR などを捕える他ありません。
Kazu さん回答ありがとうございます。

紹介いただいたリンク先のサイトは時折拝見していましたが、このサンプルは見てませんでした。
ユーザー定義のように入力許可する文字を設定できて便利だと思いました。
が、WndProcなどまだ未体験な部分もあり…Windowsメッセージを処理??…う〜ん、申し訳ありません。勉強します。


じゃんぬねっと さん回答ありがとうございます。

上記にも書きましたが、時折サンプルなど拝見させていただきながら
勉強している事もあるので、回答を付けていただいて誠に恐縮です。

> 検証が成功しない限り、フォーカスが移動できないユーザビリティの悪さを解決したいところですね。
仰るとおりだと実感致しました。検証が成功しないと終了Buttonすら押せないような状態になるところでした。
そうなるとやはり入力中の段階で制御するが無難なんでしょうか…
一応、今のValidatingでも改善策を考えていますが、もし何か助言いただける事があれば幸いです。

> これだったら、MaskedEdit のようなコントロールを使った方が良いです。
これもまた未体験なんですが(パッと見ツールボックス等には見当たらないのでなにか参照を追加する?)
今回の場合はTextBoxではなく、このコントロールを継承した方が良いという事でしょうか?
また検索が下手なだけだと思いますが、情報が少ないので
参考リンクなどあれば教えていただけないでしょうか?


あと今回の経緯についてもう少し説明させていただきますと、金額入力(数値チェック)用のTextBoxで
1. フォーカス取得時はフォーマット("#,##0")を解除する。
2. フォーカス喪失時はフォーマット("#,##0")する。
3. Textプロパティの値を設定した時はフォーマット("#,##0")して表示される。
4. Textプロパティの値を取得する時はフォーマット("#,##0")を解除したものを取得する。
以上の事が実現できないか?との相談(課題)を同じく初級者の同僚から受け、
それなら勉強中のカスタムコントロールで対応できないかと思った次第です。

1、2については、じゃんぬねっとさんご指摘のとおり問題も多々ありますがちょっとずつ実現に向かえそうですが
3、4については、案なしです。MaskedEditの情報収集中ですが、何か方法があれば助言いただけないでしょうか?
こんなのはいかがでしょう?
フォーカスを持ってるとき、不細工ですが終電なので^^;

-----
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim tmpText As TextBoxEx = New TextBoxEx

        tmpText.Parent = Me
        tmpText.Location = New Point(100, 50)

    End Sub
End Class


Public Class TextBoxEx
    Inherits System.Windows.Forms.TextBox

    Public Sub New()
        Me.SetStyle(ControlStyles.UserPaint, True)
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
        MyBase.OnPaint(e)
        If Not Me.Focused And IsNumeric(Me.Text) Then
            e.Graphics.DrawString(Format(CInt(Me.Text), "###,###"), Me.Font, Brushes.Black, 0, 0)
        Else
            e.Graphics.DrawString(Me.Text, Me.Font, Brushes.Black, 0, 0)
        End If
    End Sub

End Class
自己レス+解説です。

私が示したサンプルですと、画面に表示するときにのみ影響を受けます。
Textプロパティは常にカンマ無しで、フォーカスを失っていて尚且つ、
数値の場合###,###形式になります。
この方法だと1-4の全てに対応できます。

ただし入力中だと何故かフォントサイズが異常になりますし、ForeColorが動作しない。(理由不明^^;)
ContainsFocusプロパティがTrueの時もちゃんとOnPaint発生してるんだけど・・・

MaskedEditは昔のVBの頃からあります。VB.NET 2003であったかどうか忘れました。
入力チェックというより、特定のフォーマットでしか入力できないテキストボックスです。
#VS2005にはMaskedTextBoxってのがありました。

一応参考URL
http://dobon.net/vb/melma/dotnet58.txt
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/maskededit_control.asp

ちなみに、前回のサンプルの以下の場所を修正してください。ForeColorを無視していたので^^;
> e.Graphics.DrawString(Format(CInt(Me.Text), "###,###"), Me.Font, Brushes.Black, 0, 0)
             ↓
Dim fc As SolidBrush = New SolidBrush(Me.ForeColor)
e.Graphics.DrawString(Format(CInt(Me.Text), "###,###"), Me.Font, fc, 0, 0)
fc.Dispose()
■No16986に返信(dedさんの記事)
典武1点さんこんにちは
>3. Textプロパティの値を設定した時はフォーマット("#,##0")して表示される。
>4. Textプロパティの値を取得する時はフォーマット("#,##0")を解除したものを取得する。


http://jeanne.wankuma.com/tips/textbox/permitchars.html
ここのコードに

Private Sub Me_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Leave
Dim tx As String = Format(CInt(Me.Text), "#,##0")
MyBase.Text = Me.Text
MsgBox(Val(Me.Text))
Me.Text = tx
End Sub
Public ptxt As String
Public Overrides Property Text() As String
Get
ptxt = MyBase.Text
Return Format(CInt(ptxt)) 'CStr(instr(ptxt))
End Get
Set(ByVal value As String)
ptxt = Format(CInt(value), "#,##0")
MyBase.Text = ptxt

End Set
End Property

これを付け足せば うまくいきましたよ(深くテストしてませんが)
お世話になります。
一つずつ自分なりに理解するのに時間がかかってしまい、連絡が遅くなり大変申し訳ありません。


dedさん回答ありがとうございます。

なるほど、描画イベントで見た目上のみ編集をするのでText値はそのままという事ですよね。
Graphicsオブジェクトを使ったちょっとした図形のお絵かきなど多少勉強した事はありましたが
今回のようなケースでもこのようなアプローチの仕方もあるんですね。勉強になります。
サンプルの動作もイメージできましたので、作成中のクラスに反映させてみようと思います。
あと参考リンクも紹介していただきありがとうございます。上記が落ち着き次第さっそく読んでみます。


Kazuさん回答ありがとうございます。

参考リンクのコードを自分の目的に合わせてアレンジするといった事がまったくの未熟なため
今回仕様のサンプルまで提示していただき誠に恐縮しています。
ただ、ツールボックスに追加してテストフォームに貼り付けようとしたところ
「呼び出しのターゲットが例外をスローしました。」とのメッセージが表示されてしまいます。
まず間違いなく自分のチョンボだと思いますが…調査中です。


ちなみに参考までにお答えいただければ幸いなんですが
カスタムコントロールを作成しながらちょっとずつ動作テストしたい時など皆さんはどうしているのでしょうか?
チョコチョコ修正するたびに一度ツールボックスから削除して、アイテムの追加と削除で
もう一度修正後のアイテムを追加して、また修正したら削除して…追加して…(参照設定なども)
今のところ自分の場合はこんな感じなんですが。普通??なんでしょうか?
> カスタムコントロールを作成しながらちょっとずつ動作テストしたい時など皆さんはどうしているのでしょうか?
> チョコチョコ修正するたびに一度ツールボックスから削除して、アイテムの追加と削除で
> もう一度修正後のアイテムを追加して、また修正したら削除して…追加して…(参照設定なども)
> 今のところ自分の場合はこんな感じなんですが。普通??なんでしょうか?

通常はプロジェクト参照をすると思います。
ソリューションにコントロールプロジェクトとテストアプリケーションプロジェクトを追加して作業します。
アプリケーションプロジェクトからは参照の追加ダイアログで「プロジェクト」タブからコントロールプロジェクトを選択します。

コントロールは利用する側からすると完成していなければならないレベルのものですので、
まず上記でコントロールを完成させてから、それを利用するソリューションを開発します。
#必ずではなく、コンポーネント層から言うとという意味です。
その際はアセンブリ参照をします。
同時開発なら一つのソリューションでプロジェクト参照すればよいと思います。
まどかさんもレスしていますが、補足です。

私の場合は以下のステップを踏んでいます。

1.同一プロジェクト内で作成(同一ファイル内/別ファイルは、修正規模による)
  →修正/検証が楽
2.一通り実装し終わったら、同一ソリューションの専用プロジェクトを作成(まどかさんの方法と一緒)
  →ここでIDE上でプロパティ設定とかを確認
3.アセンブリ参照できる状態に持っていく
  →本番

こんな感じですね。
■No17041に返信(dedさんの記事)
正解かどうか 分かりませんが私の場合は

カスタムコントロールをテストする為に テスト用のプロジェクトを一つ立ち上げ
それに 参照を追加しテストします どちらもデバッグで開けたままにしておき
不具合があれば まず必ずテスト用のデバッグを中止し カスタムコントロール側で修正してビルドしてからテスト用をビルドすると 改良が反映されています

これが正解かどうかは?ですが 楽です
まどかさん、回答ありがとうございます。

> コントロールは利用する側からすると完成していなければならないレベルのものですので、
もちろんあたりまえの事なのかもしれませんが、もっともだなと実感致しました。
しっかりと肝に銘じて勉強(作成)して行きたいと思います。


Kazuさん、dedさん、度々のご親切な回答本当にありがとうございます。なんとか動作検証できました。
それと作業手順についての回答もありがとうございます。とても参考になりました。

> dedさん
自分のイメージするポイントでイベントが発生しているか?に注目してテストしましたが
いまさらながら、再描画というのはあらゆる場所で行われるんですね。十分対応可能だと思いました。

> Kazuさん
Textプロパティでの制御は、最初の書き込みのとおり思い付きはしたんですけど…
まだまだ基本クラスと派生クラスの継承など勉強不足な部分も多いようで
そもそもMyBaseとMeの使い分けなどボロボロだったみたいです。そういった意味でも勉強になりました。

出来の悪い初級者で色々とご迷惑おかけしましたm(_ _)m

ご迷惑ついでに最後にもう一つお聞きしてよろしいでしょうか?
回答いただいた中で「アセンブリ参照」というキーワードが今ひとつよく分かりません。
うまく理解するための参考リンクやアドバイスなどいただけないでしょうか?
■No17063に返信(典武1点さんの記事)
> ご迷惑ついでに最後にもう一つお聞きしてよろしいでしょうか?
> 回答いただいた中で「アセンブリ参照」というキーワードが今ひとつよく分かりません。
> うまく理解するための参考リンクやアドバイスなどいただけないでしょうか?

とりあえず 「アセンブリ」 とは、EXE や DLL だと思って頂いて良いです。
お世話になっています。

じゃんぬねっとさん回答ありがとうございます。

> とりあえず 「アセンブリ」 とは、EXE や DLL だと思って頂いて良いです。
通常の参照設定とは別に、なにか特殊な参照法でもあるのか?と勝手に難しく考えていました。
回答文をきちんと理解できるよう、今後はこういったキーワード(用語)についても勉強します。


今回のスレッドでは予想以上にたくさんの事を学べました。回答してくださった皆様本当にありがとうございました。
これ以降も、半角チェック・全角チェック……最終的にはフリガナ取得??…と
目標に向けて頑張りたいと思います。
申し訳ありません!チェック忘れてました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板