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

TextBoxにMaxLengthをByte長にしたい。

環境/言語:[C#2.0]
分類:[.NET]

こんにちは。アべです。

TextBoxを継承してコントロールを作成しております。

基底のTextBoxのMaxLengthプロパティが文字数なので、
これをANSI長のByte数単位にしたいと思うのですが、
可能でしょうか?

文字列をANSI長にしてByte単位として比較することは
可能なのですが、どこのメソッドをオーバーライドして
処理を書けばよいかわからずに困っています。

OnKeyPressメソッドやProcessCmdKeyメソッドでは、
いちいちキーの種類を見て処理を分けるのは、面倒なので、
入力後のデータを見てある条件を満たさない場合は、
入力を無効にするというコードを書きたいと思います。

イメージとしては、MaxLengthが5の場合は、
ANSI長で5Byteの文字が入るので、「abcdef」と
打つと「abcde」となり、「f」の入力が無視される
形にしたいです。同じように全角文字も。
また、Ctrl+Vによるペーストでも入らない
ようにしたい。

このような実装が標準のTextBoxを継承して出来るのでしょうか?
すみませんが、ご存知の方アドバイスをお願いします。

以上
2006/06/09(Fri) 16:34:30 編集(投稿者)

WM_CHARでバイト数をチェックするのかな。
(WM_PASTEも必要)

じゃんぬさんのところにWM_CHARを使ったサンプルがあるので参考にどうぞ。
http://jeanne.wankuma.com/tips/textbox/permitchars.html


C#ではやったことないけど、VC2005のMFCでは似たようなサンプルをつくったことはあります。
↓のスレの時にやってみた。
http://hpcgi1.nifty.com/MADIA/Vcbbs/wwwlng.cgi?print+200602/06020029.txt
有難うございます。

頂いたサンプルでは数字のみを入力することができますが
ANSI長のある文字数に達したら入力できないということが
簡単には出来ないため上手く実装ができませんでした。

はじくキーとはじかないキーの違いを制御しきれないです。
(BSキーやHome、End、Ins、Delete、Left、Right、Up、Down、、、、)

また、クリップボードの情報を貼り付けると上手く制御
出来なかったです。

今のところ、上手いやり方がなく困っています。
>ANSI長のある文字数に達したら入力できないということが
>簡単には出来ないため上手く実装ができませんでした。
現在の文字列から文字バイト数を取って、最大バイト数を超えているかどうかをチェックし、
超えていない場合は何もしない、超えている場合に制御します。
そのときに、
> (BSキーやHome、End、Ins、Delete、Left、Right、Up、Down、、、、)
かどうかを判定するんですが、、、どうやるか忘れてしまいました。

MFCでよければ、載せてもいいのですけどねぇ。。。(今は手元にない)
(MFCといってもほとんどWin32API使う処理なんですが。)
■No16177に返信(kiyo7447さんの記事)
> 頂いたサンプルでは数字のみを入力することができますが
> ANSI長のある文字数に達したら入力できないということが
> 簡単には出来ないため上手く実装ができませんでした。

Blueさんの回答は、

> 文字列をANSI長にしてByte単位として比較することは
> 可能なのですが、どこのメソッドをオーバーライドして
> 処理を書けばよいかわからずに困っています。

の返答にすぎないでしょう。
全部を参考にしろとは言っていないのでは????(適正ってやつですな)

> はじくキーとはじかないキーの違いを制御しきれないです。
>(BSキーやHome、End、Ins、Delete、Left、Right、Up、Down、、、、)

これはキーの判定をしてください。
もともと制限制御というのは面倒くさいものです。

> また、クリップボードの情報を貼り付けると上手く制御出来なかったです。

これは実装が悪いだけでしょう。貼り付けたらいけません。
今あるText+SelectedText+貼り付けた場合を加味して削るんでしょ。
なぜ試行前に貼り付けるんですか?
過去に同じ質問がありましたので、紹介させていただきます。

最大テキストボックス
http://dobon.net/vb/bbs/log3-16/9718.html
2006/06/11(Sun) 14:20:39 編集(投稿者)

勉強のためにもC++/CLIで書いてみました。
これなら十分参考になると思います。
    
public ref class ByteLimitTextBox : public System::Windows::Forms::TextBox
{
private:
    int LimitBytes_; // 最大入力バイト数
public:
    ByteLimitTextBox() : LimitBytes_( 0 ) {}
    // 最大入力バイト数
    property int LimitBytes
    {
        void set( int value ) { this->LimitBytes_ = value; }
        int get() { return this->LimitBytes_; }
    }
    // 選択中文字列のバイト数
    property int SelectedBytes
    {
        int get()
        {
            return System::Text::Encoding::GetEncoding( L"Shift_JIS" )->GetByteCount( this->SelectedText );
        }
    }
    // 現在文字列のバイト数
    property int TextBytes
    {
        int get()
        {
            return System::Text::Encoding::GetEncoding( L"Shift_JIS" )->GetByteCount( this->Text );
        }
    }
    // 入力可能バイト数
    property int InputBytes
    {
        int get()
        {
            return this->LimitBytes - this->TextBytes + this->SelectedBytes;
        }
    }
protected:
    virtual void WndProc( System::Windows::Forms::Message% m ) override
    {
        switch ( m.Msg )
        {
        case WM_CHAR:
            if ( this->LimitBytes > 0 )
            {
                KeyPressEventArgs^ eKeyPress = gcnew  KeyPressEventArgs( ( Char )m.WParam.ToInt32() );
                this->OnChar( eKeyPress );
                if ( eKeyPress->Handled ) return;
            }
            break;
        case WM_PASTE:
            if ( this->LimitBytes > 0 )
            {
                this->OnPaste( gcnew System::EventArgs() );
                return;
            }
            break;
        }
        TextBox::WndProc( m );
    }
    virtual void OnChar( KeyPressEventArgs^ e )
    {
        // コントロールキーは処理対象外
        if ( Char::IsControl( e->KeyChar ) ) return;

        // 入力可能バイト数がない
        if ( this->InputBytes <= 0 )
        {
            ::MessageBeep( MB_OK );
            e->Handled = true;
        }
        // 1バイトのみ入力可能である
        if ( this->InputBytes == 1 )
        {
            // 2バイト文字であるか
            array< Char >^ c = { e->KeyChar };
            if ( System::Text::Encoding::GetEncoding( L"Shift_JIS" )->GetByteCount( c ) == 2 )
            {
                ::MessageBeep( MB_OK );
                e->Handled = true;
            }
        }
    }
    virtual void OnPaste( System::EventArgs^ e )
    {
        // クリップボードの文字列が入力可能であるか
        String^ s = Clipboard::GetDataObject()->GetData( System::Windows::Forms::DataFormats::Text )->ToString();
        if ( s != nullptr && s->Length )
        {
            int l = System::Text::Encoding::GetEncoding( L"Shift_JIS" )->GetByteCount( s );
            if ( this->InputBytes >= l )
            {
                this->SelectedText = s;
                return;
            }
            ::MessageBeep( MB_OK );
        }
    }
};
即興で組んだやつですが、参考になると思います。
Paste の処理は、別途必要です。(そのうち、このあたりをまとめて、サイトにアップしておきます)

using System.ComponentModel;

namespace Jeanne.Windows.Forms {

    public class ExTextBox : System.Windows.Forms.TextBox {

      #region コンストラクタ 

        private System.ComponentModel.Container components = null;

        public ExTextBox() {
            this.components = new System.ComponentModel.Container();
            this.MaxByteLength = int.MaxValue;
        }

      #endregion

      #region MaxByteLength プロパティ 

        private int _MaxByteLength;

        [Category("動作")]
        [DefaultValue(int.MaxValue)]
        [Description("エディット コントロールに入力できる最大文字バイト数を指定します。")]
        [RefreshProperties(RefreshProperties.Repaint)]
        public int MaxByteLength {
            get {
                return this._MaxByteLength;
            } set {
                this._MaxByteLength = value;

                int iMaxLength = (int)System.Math.Ceiling(value / 2);

                if (iMaxLength > this.MaxLength) {
                    this.MaxLength = iMaxLength;
                }
            }
        }

      #endregion

      #region MaxLength プロパティ (override) 

        [RefreshProperties(RefreshProperties.Repaint)]
        public override int MaxLength {
            get {
                return base.MaxLength;
            } set {
                base.MaxLength = value;

                int iMaxByteLength = value * 2;

                if (iMaxByteLength > this.MaxByteLength) {
                    this.MaxByteLength = iMaxByteLength;
                }
            }
        }

      #endregion

      #region Dispose メソッド (override) 

        protected override void Dispose(bool disposing) {
            if (disposing) {
                if (this.components != null) {
                    components.Dispose();
                }
            }

            base.Dispose(disposing);
        }

      #endregion

      #region IsInputChar メソッド (override) 

        protected override bool IsInputChar(char charCode) {
            base.IsInputChar(charCode);

            return false;
        }

      #endregion

      #region ProcessDialogChar メソッド 

        protected override bool ProcessDialogChar(char charCode) {
            base.ProcessDialogChar(charCode);

            System.Text.Encoding sjisEncoding = System.Text.Encoding.GetEncoding("Shift_JIS");
            int inputByteCount = sjisEncoding.GetByteCount(charCode.ToString());
            int textByteCount  = sjisEncoding.GetByteCount(this.Text);

            if (char.IsControl(charCode)) {
                return false;
            }

            if ((textByteCount + inputByteCount) > this.MaxByteLength) {
                return true;
            }

            return false;
        }

      #endregion

    }

}
2006/06/11(Sun) 14:01:42 編集(投稿者)

IsInputChar,ProcessDialogCharていうのがあるんですね。

それと
> components
はなんで必要なんでしょうか?あまり理解できていないのでご説明お願いします。
# C++/CLIに移植してみたら、TextBox::Disposeはprivateメンバなんでoverrideできなかった。


一応私のやつはダメダメ(プロパティとかめちゃくちゃ)なんですが、選択中の文字列の上書きも考慮してあります。
VCの質問スレ http://forums.belution.com/ja/vc/000/350/39s.shtml の
たけさん(00035046)の
> ただ、最大数入力済みの状態で文字を全部選択しての文字入力等
> が出来ませんのでまだ改良の余地がありそうです・・・
を考慮した。
■No16211に返信(Blueさんの記事)
> IsInputChar,ProcessDialogCharていうのがあるんですね。
> では、Pasteのもあるのかな?

ないですので、OnPaste を用意する必要があります。

> はなんで必要なんでしょうか?あまり理解できていないのでご説明お願いします。
> # C++/CLIに移植してみたら、TextBox::Disposeはprivateメンバなんでoverrideできなかった。

このまま特に拡張するものがないのであれば、必要ないです。
現状は、意味のないリソースを持っているにすぎません。

> 一応私のやつはダメダメ(プロパティとかめちゃくちゃ)なんですが、選択中の文字列の上書きも考慮してあります。

このあたりは、私のコードは完全に手抜きです。
OnChar だけであれば、私のサンプル コードに、SelectedText.Length を加味すれば OK でしょう。

if ((textByteCount + inputByteCount - this.SelectedText.Length) > this.MaxByteLength) {

# Paste がないので意味がないですけどね。
# このあたりこそ、元質問者自身で実装して頂きたい部分なのですが... (メソッド 1 本ですし)
> this.SelectedText.Length
ではバイト数は取れないのでは?

int selectByteCount = sjisEncoding.GetByteCount( this.SelectedText );

if ( ( textByteCount + inputByteCount - selectByteCount ) > this.MaxByteLength )
■No16214に返信(Blueさんの記事)
> ではバイト数は取れないのでは?

ああ、そうでした。
とりあえず、こんな感じのものを作ってみました。

 入力可能な文字バイト数を設定する
 http://jeanne.wankuma.com/tips/textbox/maxbytelength.html
2006/06/13(Tue) 19:47:06 編集(投稿者)

■No16243に返信(じゃんぬねっとさんの記事)
> とりあえず、こんな感じのものを作ってみました。

> inputText.Substring(0, remainByteCount);
ここですが、バイト数と文字数なんでちょっとやばいかもしれませんね。
やるとしたら、じゃんぬさん自作のLeftB関数でしょうか?
(泣き別れ考慮しないといけませんが。)

追記)
C++/CLIのもついでに載せてもらうとうれしいです。
■No16245に返信(Blueさんの記事)
> ここですが、バイト数と文字数なんでちょっとやばいかもしれませんね。

また、やっちゃいましたねw (進歩ないなぁw)

> やるとしたら、じゃんぬさん自作のLeftB関数でしょうか?
> (泣き別れ考慮しないといけませんが。)

私より私のサイトに詳しいですねw

> 追記)
> C++/CLIのもついでに載せてもらうとうれしいです。

はい、2005 対応の時についでにやる予定です。
私的に優先順位は、C# > VB > C++/CLI > J# です。
■No16246に返信(じゃんぬねっとさんの記事)
> 私より私のサイトに詳しいですねw
たまたま目にしただけですよ。
# http://f57.aaa.livedoor.jp/~jeanne/bbs/faq.cgi?mode=al2&namber=3975
# で、ちょっと話が出てきましたからね。
有難うございます。

正しく動作する形で実装できました。
で、解決?

> 正しく動作する形で実装できました。
結局どのような実装したんでしょうか?
じゃんぬさんのヤツをつかったならば
> inputText.Substring(0, remainByteCount);
をどのように解決したのか教えてくれますか?

<掲示板規則引用>
質問の回答に対して必ずフィードバック(結果報告、返事、お礼)をしてください(できるだけ早く)。
また、自己解決した時も解決法を明記してください。
</掲示板規則引用>
■No16264に返信(Blueさんの記事)
> 結局どのような実装したんでしょうか?
> じゃんぬさんのヤツをつかったならば
> > inputText.Substring(0, remainByteCount);
> をどのように解決したのか教えてくれますか?

ああ... 恥ずかしくて報告していなかったのですが、これについては修正致しました。
いつの時期に、どのように解決したのかで変わってきそうですが...

# C++/CLI と J# については、しばしお待ちをw
2006/06/15(Thu) 15:19:12 編集(投稿者)
2006/06/15(Thu) 15:18:06 編集(投稿者)

■No16265に返信(じゃんぬねっとさんの記事)
> これについては修正致しました。
あれ?どこが変更されているか良くわからなくなってきた。。。(汗)

とおもったら、いつの間にかLeftByteになっていた。
2006/06/15(Thu) 15:38:11 編集(投稿者)

■No16266に返信(Blueさんの記事)
> > これについては修正致しました。
> は
> > if (this.MaxLength * 2 > this.MaxByteLength) {
> ですね。

修正部分は、そこではないハズです。(;^-^)
そこは、最初に提示した時点でそのような実装だったと思います。

> この場合、MaxLengthを設定しない場合どうなるんでしょうか?

設定しない場合は、32767 になりますよね。
ああ、ひょっとして「0」の場合のことですか... 0 の場合は無制御になるんですね。
初期値が 32767 だったので、0 の場合は入力不可になるのかと思っていましたよ。(今まで)

# 以下追記?

> あれ?どこが変更されているか良くわからなくなってきた。。。(汗)
> とおもったら、いつの間にかLeftByteになっていた。

あ、それですそれです。(=^o^)
みなさん、有難うございます。

クリップボードの動きも含めて解決しました。

(補足ですが、プログラムからTextプロパティを変更した場合も
MaxLengthが動く形で実装しました。)

有難うございました。
解決済み!

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