none
RichTextBox の派生クラス内でテキストの保護(編集不可)を設定したいが、falseが戻る RRS feed

  • 質問

  • お世話になります。

    System.Windows.Forms.RichTextBoxクラスから派生したクラスで下記のようにして編集不可領域をセットしましたが、falseが返ります。

    派生クラス内では色々行っていますが(コードが長い)ので手短に書けません。

    セットできない理由は、なにが考えられるでしょうか?

    Windows10 C# WinForm VS2010

            // テキスト保護領域
            public bool SetSelectionProtected(bool sw, int start, int len)
            {
                Select(start, len);
                SelectionProtected = sw;
                Protected += new EventHandler(RichTextBox1_Protected);
    
                return SelectionProtected;
            }
    
            private void RichTextBox1_Protected(object sender, EventArgs e)
            {
                //保護されている部分を編集しようとした時
                MessageBox.Show("ここは変更できません。");
            }

    2019年4月5日 3:54

回答

  • OnTextChanged内のTextColorSet.keywordが以前の質問で参照しているコードと同じままなら、RichTextの\protectタグが考慮されていないので保護が消えるのと、選択幅をゼロにしてるので選択領域が変わってしまっているのでSelectionProtectedはfalseになる。

    protected override void OnTextChanged(EventArgs e)
    {
        if (textChangeBool == false)
            return;
    
        textChangeBool = false;
    
        m_nContentLength = this.TextLength;
    
        int nCurrentSelectionStart = SelectionStart;
        int nCurrentSelectionLength = SelectionLength;
    
        m_bPaint = false;
    
        m_nLineStart = nCurrentSelectionStart;
        while ((m_nLineStart > 0) && (Text[m_nLineStart - 1] != '\n'))
            m_nLineStart--;
        m_nLineEnd = nCurrentSelectionStart;
        while ((m_nLineEnd < Text.Length) && (Text[m_nLineEnd] != '\n'))
            m_nLineEnd++;
        m_nLineLength = m_nLineEnd - m_nLineStart;
        m_strLine = Text.Substring(m_nLineStart, m_nLineLength);
    
        try
        {
            int currentSelectionStart = SelectionStart;
            int currentSelectionLength = SelectionLength;
    
            //元のRithTextBox.Rtfに設定された\Protectを考慮していないので保護範囲が消える
            Rtf = TextColorSet.keyword(Text, keyword, number/*, this*/);
    
    
            Select(currentSelectionStart, currentSelectionLength);
            Focus();
        }
        catch (Exception ex)
        {
    
        }
    
        SelectionLength = 0; //ここで選択文字列をゼロにしているので元の選択範囲と一致しなくなる
        m_nCurSelection = SelectionStart;
    
        textChangeBool = true;
        if (TextChanged != null)
        {
            TextChanged(this, e);
        }
    
        m_bPaint = true;
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク ferret001 2019年4月16日 0:21
    2019年4月7日 0:37

すべての返信

  • 素のRichTextなら問題なく動作しているのですか?
    派生させてどのように弄っているかは外部の人は判りません。
    ソースコードを全て提示できないなら、素のRichTextBoxから変更してしまっているところを無効にするなどして、原因を自分で調べるしかないです。

    考えられること

    • 引数swがもともとFalse
    • WndProcでEM_SETCHARFORMATメッセージを無効にしてる
    • WndProcでEM_GETCHARFORMATメッセージを無効にしてる
    class Rich : RichTextBox
    {
        public Rich()
        {
            Protected += new EventHandler(RichTextBox1_Protected);
        }
    
        public bool SetSelectionProtected(bool sw, int start, int len)
        {
            Select(start, len);
            SelectionProtected = sw;
    
            return SelectionProtected;
        }
    
    
        private void RichTextBox1_Protected(object sender, EventArgs e)
        {
            //保護されている部分を編集しようとした時
            MessageBox.Show("ここは変更できません。");
        }
    
        const uint EM_SETCHARFORMAT = 0x0444;
        protected override void WndProc(ref Message m)
        {
    
            if (m.Msg == EM_SETCHARFORMAT)
            {
                m.Result = IntPtr.Zero;
                return;
            }
            base.WndProc(ref m);
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2019年4月5日 12:57
  • お世話になります。

    派生クラス部分は長文の為、控えていました。

    単に WndProc の m.result = IntPtr.Zero をコメントにしただけではなさそうでした。

            protected override void WndProc(ref System.Windows.Forms.Message m)
            {
                if (m.Msg == 0x00f)
                {
                    if (m_bPaint)
                        base.WndProc(ref m);
                    // else
                    //     m.Result = IntPtr.Zero;                  <- ここをコメントにしましたが、変わりません
                }
                else
                    base.WndProc(ref m);
            }

    可能でしたら、下記の派生クラスのコード見てください。

        public class SyntaxRichTextBox : System.Windows.Forms.RichTextBox
        {
            private static bool m_bPaint = true;
            private string m_strLine = "";
            private int m_nContentLength = 0;
            private int m_nLineLength = 0;
            private int m_nLineStart = 0;
            private int m_nLineEnd = 0;
            private string m_strKeywords = "";
            private int m_nCurSelection = 0;
    
    
            // 編集行の色(前景色..true / 背景色..false)
            public bool bLineColorFlag = true;
            public Color cLineColor = Color.Yellow;
    
            // ドロップ時の再描画で使う変数
            public string fontMei = "";
            public string fontSize = "12";
    
    
            // マウスで選択したテキスト長
            public int textLen = 0;
    
            // WM_PAINT
            private const int WM_PAINT = 0x000F;
            private const int WM_IME_NOTIFY = 0x0282;
    
            // Mouse
            private const int WM_LBUTTONDOWN = 0x0201;
            private const int WM_LBUTTONUP = 0x0202;
            private const int WM_MOUSEMOVE = 0x0200;
            private const int WM_MOUSEWHEEL = 0x020A;
            private const int WM_RBUTTONDOWN = 0x0204;
            private const int WM_RBUTTONUP = 0x0205;
    
    
            private const int EM_GETLINE = 0xc4;
            private const int EM_GETLINECOUNT = 0xba;
            private const int EM_LINEFROMCHAR = 0xc9;
            private const int EM_LINEINDEX = 0xbb;
            private const int EM_LINELENGTH = 0xc1;
            private const int EM_REPLACESEL = 0xc2;
    
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
    
    
            // キーワードの色設定
            bool textChangeBool = true;
            public List<string> keyword = new List<string>();
            public List<string> number = new List<string>();
    
    
            public SyntaxRichTextBox()
            {
                // RichEditText は、透明化や背景に画像をサポートしていないようです。
                //SetStyle(ControlStyles.SupportsTransparentBackColor, true);
                //this.BackColor = Color.Transparent;
    
                Protected += new EventHandler(RichTextBox1_Protected);
            }
    
            // テキスト保護領域
            public bool SetSelectionProtected(bool sw, int start, int len)
            {
                Select(start, len);
                SelectionProtected = sw;
    
                return SelectionProtected;
            }
    
            private void RichTextBox1_Protected(object sender, EventArgs e)
            {
                //保護されている部分を編集しようとした時
                MessageBox.Show("ここは変更できません。");
            }
    
            // 現在のカーソルの行を取得
            public int CurrentLine
            {
                get
                {
                    // 「GetLineFromCharIndex」は「0」から始まる数値を返すので、
                    // 実際はプラス1した値を返す
                    return GetLineFromCharIndex(SelectionStart) + 1;
                }
            }
    
            // 現在の行におけるカーソルが何文字目かを取得 (v2.0~)
            public int CurrentColumn
            {
                get
                {
                    // 現在のカーソルの文字インデックスから現在の行の
                    // 最初の文字インデックスを減じ、さらにプラス1した値を返す
                    return SelectionStart - GetFirstCharIndexOfCurrentLine() + 1;
                }
            }
            // カーソル移動の検出を知らせるイベント
            public event EventHandler CursorPositionChanged;
    
            // カーソル移動の検出イベントを発生させるメソッド
            protected virtual void OnCursorPositionChanged(EventArgs e)
            {
                // イベントが未発生であれば、発生させる
                if (CursorPositionChanged != null)
                {
                    CursorPositionChanged(this, e);
                }
            }
            // 既存の「OnSelectionChanged」メソッドをオーバーライドする
            protected override void OnSelectionChanged(EventArgs e)
            {
                // 「OnCursorPositionChanged」メソッドを実行する
                OnCursorPositionChanged(e);
    
                // 選択範囲が1文字以上あれば、本来の「OnSelectionChanged」メソッドを実行
                if (SelectionLength > 0)
                {
                    base.OnSelectionChanged(e);
                }
            }
            // ////////////////////////////////////////////////////////////////////////////////////////
    
            // //////////////////////////////////////////////////////////////////
            // カーソルの移動・マウスクリックで行に背景色
            //
            // ↑キーで行のハイライト
            // //////////////////////////////////////////////////////////////////
            public void BkLine(int /*line*/mmm)
            {
                SendMessage(this.Handle, 0x000B, 0, 0);
    
                // 現在のカーソル位置保存
                int pos = SelectionStart;
    
                //int start = GetFirstCharIndexFromLine(line);
                int current = (int)SendMessage(this.Handle, EM_LINEFROMCHAR, -1, 0);
                int start = (int)SendMessage(this.Handle, EM_LINEINDEX, current + 1, 0);
                int len = (int)SendMessage(this.Handle, EM_LINELENGTH, /*SelectionStart*/start, 0);
    
                // 背景を消す
                SelectAll();
                SelectionBackColor = Color.White;
                if (start > -1 && len > 0)
                {
                    Select(start, len);         // 変更したい文字列を選択
                    if (bLineColorFlag)
                    {
                        SelectionStart = start;
                        SelectionLength = len;
                        bool abc = SelectionColor.IsNamedColor;
                        SelectionColor = cLineColor;
                    }
                    else
                        SelectionBackColor = cLineColor; // 色を設定
                }
    
                SendMessage(this.Handle, 0x000B, 1, 0);
                Refresh();
                SelectionStart = pos;
            }
            // //////////////////////////////////////////////////////////////////
            // ↓キーで行のハイライト
            // //////////////////////////////////////////////////////////////////
            public void BkLine2()
            {
                SendMessage(this.Handle, 0x000B, 0, 0);
    
                // 現在のカーソル位置保存
                int pos = SelectionStart;
    
                //int start = GetFirstCharIndexFromLine(line - 1);
                int current = (int)SendMessage(this.Handle, EM_LINEFROMCHAR, -1, 0);
                int start = (int)SendMessage(this.Handle, EM_LINEINDEX, current - 1, 0);
                int len = (int)SendMessage(this.Handle, EM_LINELENGTH, start/*SelectionStart*/, 0);
    
                // 背景を消す
                SelectAll();
                SelectionBackColor = Color.White;
                if (start > -1 && len > 0)
                {
                    Select(start, len); // 変更したい文字列を選択
                    if (bLineColorFlag)
                        SelectionColor = cLineColor;
                    else
                        SelectionBackColor = cLineColor; // 色を設定
                }
    
                SendMessage(this.Handle, 0x000B, 1, 0);
                Refresh();
                SelectionStart = pos;
            }
            // //////////////////////////////////////////////////////////////////
            // マウスクリックでで行のハイライト
            // //////////////////////////////////////////////////////////////////
            public void BkLine3()
            {
                SendMessage(this.Handle, 0x000B, 0, 0);
                //Enabled = false;
    
                // 現在のカーソル位置保存
                int pos = SelectionStart;
                // 現在の行位置
                int line = GetLineFromCharIndex(SelectionStart);
                if (Lines.Length == 0)
                    return;
    
                // Get the length of the current line:
                int len = (int)SendMessage(this.Handle, EM_LINELENGTH, SelectionStart, 0);
                int current = (int)SendMessage(this.Handle, EM_LINEFROMCHAR, -1, 0);
                int start = (int)SendMessage(this.Handle, EM_LINEINDEX, current, 0);
    
                //int start = GetFirstCharIndexFromLine(line);
                // 背景を消す
                SelectAll();
                SelectionBackColor = Color.White;
                Select(start, len); // 変更したい文字列を選択
                if (bLineColorFlag)
                    SelectionColor = cLineColor;
                else
                    SelectionBackColor = cLineColor; // 色を設定
    
                //Enabled = true;
                SendMessage(this.Handle, 0x000B, 1, 0);
                Refresh();
                SelectionStart = pos;
            }
            // //////////////////////////////////////////////////////////////////////////////////////////////
    
            // 指定行の文字数を返す
            public int MojiLength(int Start)
            {
                SendMessage(this.Handle, 0x000B, 0, 0);
    
                // 現在のカーソル位置保存
                //int pos = SelectionStart;
    
                //int start = GetFirstCharIndexFromLine(line);
                //int current = (int)SendMessage(this.Handle, EM_LINEFROMCHAR, -1, 0);
                SelectionStart = Start;
                int start = (int)SendMessage(this.Handle, EM_LINEINDEX, Start + 1, 0);
                int len = (int)SendMessage(this.Handle, EM_LINELENGTH, SelectionStart, 0);
    
                //string moji = SelectedText;
                return len;
            }
    
            // 日本語が勝手に確定してしまう原因は、どうやら下記ハンドラが呼び出されると入力が確定されるみたい
            // ここが原因のようです
            protected override void OnMouseMove(MouseEventArgs e)
            {
                //    textLen = SelectedText.Length;      // マウスで選択した文字数
    
                //    base.OnMouseMove(e);
            }
    
            /// <summary>
            /// WndProc
            /// </summary>
            /// <param name="m"></param>
            protected override void WndProc(ref System.Windows.Forms.Message m)
            {
                if (m.Msg == 0x00f)
                {
                    if (m_bPaint)
                        base.WndProc(ref m);
                    // else
                    //     m.Result = IntPtr.Zero;                  <- ここをコメントにしましたが、変わりません
                }
                else
                    base.WndProc(ref m);
            }
    
            // カーソル移動の検出を知らせるイベント
            public event EventHandler TextChanged;
    
            /// <summary>
            /// OnTextChanged
            /// </summary>
            /// <param name="e"></param>
            protected override void OnTextChanged(EventArgs e)
            {
                if (textChangeBool == false)
                    return;
    
                // ここが、ここを呼ぶのでセット
                textChangeBool = false;
    
                // Calculate shit here.
                m_nContentLength = this.TextLength;
    
                int nCurrentSelectionStart = SelectionStart;
                int nCurrentSelectionLength = SelectionLength;
    
                m_bPaint = false;
    
                // Find the start of the current line.
                m_nLineStart = nCurrentSelectionStart;
                while ((m_nLineStart > 0) && (Text[m_nLineStart - 1] != '\n'))
                    m_nLineStart--;
                // Find the end of the current line.
                m_nLineEnd = nCurrentSelectionStart;
                while ((m_nLineEnd < Text.Length) && (Text[m_nLineEnd] != '\n'))
                    m_nLineEnd++;
                // Calculate the length of the line.
                m_nLineLength = m_nLineEnd - m_nLineStart;
                // Get the current line.
                m_strLine = Text.Substring(m_nLineStart, m_nLineLength);
    
    
                // キーワードの色設定 ////////////////////////////////////////////
                try
                {
                    //this.Focus();
                    //    Enabled = false;              これが有効だと、カーソルがsyntaxRichTextBox2に飛んでしまう??    なんで???      なのでコメントにする
                    //現在の選択状態を覚えておく
                    int currentSelectionStart = SelectionStart;
                    int currentSelectionLength = SelectionLength;
                    // 文字キーワードの色(マジェンタ)
                    Rtf = TextColorSet.keyword(Text, keyword, number/*, this*/);
    
    
                    //選択状態を元に戻す
                    Select(currentSelectionStart, currentSelectionLength);
                    //textBox.Font = font;
                    //    Enabled = true;
                    Focus();
                }
                catch (Exception ex)
                {
    
                }
    
                SelectionLength = 0;
                m_nCurSelection = SelectionStart;
    
                // SetUneditRange();
    
                textChangeBool = true;
                // ///////////////////////////////////////////////////////////////
    
                // イベントが未発生であれば、発生させる
                if (TextChanged != null)
                {
                    TextChanged(this, e);
                }
    
                m_bPaint = true;
            }
            public void SetUneditRange()
            {
                // コメント文字
                if (Text.Contains("#####"))
                {
                    int nCurrentSelectionStart = SelectionStart;
                    int nCurrentSelectionLength = SelectionLength;
    
                    int start = Text.IndexOf("############");
                    int end = start + "############ /* 区切り文字:結果は、ここより上に記入".Length;     // text.IndexOf("##", start + 12);
                    if (start > -1 && end > -1)
                    {
                        // 履歴は編集不可にする
                        Select(start, Text.Length);
                        SelectionProtected = true;
    
                        SelectionLength = 0;
                        m_nCurSelection = SelectionStart;
                    }
                }
            }
            /// <summary>
            /// Process a line.
            /// </summary>
            private void ProcessLine()
            {
                // Save the position and make the whole line black
                int nPosition = SelectionStart;
                SelectionStart = m_nLineStart;
                SelectionLength = m_nLineLength;
                SelectionColor = Color.Black;
    
                if (Settings.EnableIntegers)
                    ProcessRegex("\\b(?:[0-9]*\\.)?[0-9]+\\b", Settings.IntegerColor);
    
                SelectionStart = nPosition;
                SelectionLength = 0;
                SelectionColor = Color.Black;
    
                m_nCurSelection = nPosition;
            }
    
            /// <summary>
            /// Process a regular expression.
            /// </summary>
            /// <param name="strRegex">The regular expression.</param>
            /// <param name="color">The color.</param>
            private void ProcessRegex(string strRegex, Color color)
            {
                Regex regKeywords = new Regex(strRegex, RegexOptions.IgnoreCase | RegexOptions.Compiled);
                Match regMatch;
    
                for (regMatch = regKeywords.Match(m_strLine); regMatch.Success; regMatch = regMatch.NextMatch())
                {
                    // Process the words
                    int nStart = m_nLineStart + regMatch.Index;
                    int nLenght = regMatch.Length;
                    SelectionStart = nStart;
                    SelectionLength = nLenght;
                    SelectionColor = color;
                }
            }
        }

    2019年4月6日 3:10
  • OnTextChanged内のTextColorSet.keywordが以前の質問で参照しているコードと同じままなら、RichTextの\protectタグが考慮されていないので保護が消えるのと、選択幅をゼロにしてるので選択領域が変わってしまっているのでSelectionProtectedはfalseになる。

    protected override void OnTextChanged(EventArgs e)
    {
        if (textChangeBool == false)
            return;
    
        textChangeBool = false;
    
        m_nContentLength = this.TextLength;
    
        int nCurrentSelectionStart = SelectionStart;
        int nCurrentSelectionLength = SelectionLength;
    
        m_bPaint = false;
    
        m_nLineStart = nCurrentSelectionStart;
        while ((m_nLineStart > 0) && (Text[m_nLineStart - 1] != '\n'))
            m_nLineStart--;
        m_nLineEnd = nCurrentSelectionStart;
        while ((m_nLineEnd < Text.Length) && (Text[m_nLineEnd] != '\n'))
            m_nLineEnd++;
        m_nLineLength = m_nLineEnd - m_nLineStart;
        m_strLine = Text.Substring(m_nLineStart, m_nLineLength);
    
        try
        {
            int currentSelectionStart = SelectionStart;
            int currentSelectionLength = SelectionLength;
    
            //元のRithTextBox.Rtfに設定された\Protectを考慮していないので保護範囲が消える
            Rtf = TextColorSet.keyword(Text, keyword, number/*, this*/);
    
    
            Select(currentSelectionStart, currentSelectionLength);
            Focus();
        }
        catch (Exception ex)
        {
    
        }
    
        SelectionLength = 0; //ここで選択文字列をゼロにしているので元の選択範囲と一致しなくなる
        m_nCurSelection = SelectionStart;
    
        textChangeBool = true;
        if (TextChanged != null)
        {
            TextChanged(this, e);
        }
    
        m_bPaint = true;
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク ferret001 2019年4月16日 0:21
    2019年4月7日 0:37
  • gekka 様、お世話になります。

    > TextColorSet.keywordが以前の質問で参照しているコードと同じままなら

    その通りです。その節はお世話になりました。

    > protectタグが考慮されていないので保護が消える・・

    とのことですが、保護とはどのようなことを指すのでしょうか?

    rtf情報をどこかにコピーし戻す?でも、戻した時に元に戻ってしまうと思う・・・

    2019年4月8日 3:33