C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[9]: カスタムコントロールについて


(過去ログ 172 を表示中)

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■98922 / inTopicNo.1)  カスタムコントロールについて
  
□投稿者/ くま (123回)-(2022/01/20(Thu) 05:17:18)

分類:[VB.NET/VB2005 以降] 

現在VB.net .net framework 4.8 Windowsフォームで開発しています。
機能が増えてきたため一部をカスタムコントロールにしようかと思っていますが以下の状態を簡単に再現する事はできますでしょうか?

1. カスタムコントロールをDesigner.vbで
Inherits System.Windows.Forms.UserControl
を変更し「Panel」とする
Inherits System.Windows.Forms.Panel

2. この中にTextBoxコントロールを入れる
3. TextBoxコントロールのプロパティ、イベント、メゾットをすべて
カスタムコントロールの物として公開する。
※↑これがうまくできない

カスタムコントロールの継承は「Panel」なので
Panelコントロールのプロパティ、イベント、メゾットが表示されます。

行いたいのは逆なんです。
(継承はTextBoxだが、カスタムコントロール上はPaneの中にTextBoxが入っている状態)

TextBox全部のプロパティ、イベント、メゾットを書き出すだけで大変で...
それ以外の方法がありますでしょうか?
引用返信 編集キー/
■98923 / inTopicNo.2)  Re[1]: カスタムコントロールについて
□投稿者/ shu (1268回)-(2022/01/20(Thu) 07:57:15)
No98922 (くま さん) に返信

TextBoxBaseとかTextBoxから派生しないと
それは無理です。
Panel内にTextBoxを貼り付けることが重要なので
あれば公開するプロパティをすべて実装する必要があります。

引用返信 編集キー/
■98924 / inTopicNo.3)  Re[1]: カスタムコントロールについて
□投稿者/ KOZ (201回)-(2022/01/20(Thu) 08:03:16)
2022/01/20(Thu) 08:08:38 編集(投稿者)
No98922 (くま さん) に返信
> TextBox全部のプロパティ、イベント、メゾットを書き出すだけで大変で...
> それ以外の方法がありますでしょうか?

Vs2019 だと

(1) TextBox を継承するコントロールを作成

Public Class PanelTextBox
    Inherits TextBox
End Class

(2) PanelTextBox をマウスで選択して右クリック→クイックリファクタリング→上書きを生成する

(3) メンバーが列挙されたダイアログが開くので OK ボタンを押す

(4) メンバーが生成される

あとは MyBase の部分を置換してご自由に。

イベントはダメっぽいですね。自分で列挙するしかないです。

引用返信 編集キー/
■98925 / inTopicNo.4)  Re[2]: カスタムコントロールについて
□投稿者/ KOZ (202回)-(2022/01/20(Thu) 08:15:45)
No98924 (KOZ) に返信

あー、これは Overridable なものしか列挙されないですね。残念。
引用返信 編集キー/
■98926 / inTopicNo.5)  Re[1]: カスタムコントロールについて
□投稿者/ 魔界の仮面弁士 (3281回)-(2022/01/20(Thu) 10:02:08)
No98922 (くま さん) に返信
> 3. TextBoxコントロールのプロパティ、イベント、メゾットをすべて
> カスタムコントロールの物として公開する。
メゾット(mezot)ではなく
メソッド(method)ですね。


> TextBox全部のプロパティ、イベント、メゾットを書き出すだけで大変で...

手抜き実装案ですが…SplitContainer コントロールの Panel1 / Panel2 プロパティのように
内包コントロールを直接公開することで、お茶を濁すのは駄目でしょうか。


具体的には、デザイン時に TextBox1 の Modifiers プロパティの値を
Friend から Private へと縮小しておいた上で、
その TextBox1 そのものを Public ReadOnly Property で公開します。

<System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)>
Public ReadOnly Property PanelTextBox As TextBox
 Get
  Return Me.TextBox1
 End Get
End Property

こうすると、デザイン時に .PanelTextBox.BackColor や .PanelTextBox.Text を設定して保存することができます。
イベントの割り当ては、コードウィンドウ上部のドロップダウンではなく、
プロパティウィンドウの稲妻アイコンからの指定になります。

継承Panel そのもののメンバーとして扱いたいとなると…愚直に一つ一つのメンバーを再公開していくしか無いですが。
引用返信 編集キー/
■98927 / inTopicNo.6)  Re[3]: カスタムコントロールについて
□投稿者/ KOZ (203回)-(2022/01/20(Thu) 11:15:23)
継承元を UserControl → Panel にした時点で UserControl が持つデザイン機能が使えなくなっちゃいます。
そもそもやりたいことは何だったのでしょう?
引用返信 編集キー/
■98929 / inTopicNo.7)  Re[2]: カスタムコントロールについて
□投稿者/ radian (1回)-(2022/01/20(Thu) 15:21:19)
2022/01/20(Thu) 15:30:07 編集(投稿者)

> 行いたいのは逆なんです。
> (継承はTextBoxだが、カスタムコントロール上はPaneの中にTextBoxが入っている状態)

出来ません。
コントロールは親を一つしか持てないので、TextBoxをPanelの子にするには、
TextBoxを今の親から取り除く必要がありますが、
取り除かれた時点で、デザイナーからは操作出来ないものになります。
TextBoxを内包したPanelなら作れるでしょう。
ただ、同じメソッド等を実装したところで、当然TextBoxとは互換性のない型になりますが。
UserControlのままではまずいのでしょうか?

> TextBox全部のプロパティ、イベント、メゾットを書き出すだけで大変で...
> それ以外の方法がありますでしょうか?

ありません。
PanelとTextBox共通のプロパティやメソッドもあるでしょうし、
内包したなら、呼び出した時にどちらを操作するのという問題もあります。
自身で振る舞いを定義するしかないでしょう。
引用返信 編集キー/
■98930 / inTopicNo.8)  Re[3]: カスタムコントロールについて
□投稿者/ くま (125回)-(2022/01/20(Thu) 17:17:09)
shuさん、KOZさん、魔界の仮面弁士さん、radianさん
返答ありがとうございます。

No98926 (魔界の仮面弁士 さん) に返信
> メゾット(mezot)ではなく
> メソッド(method)ですね。
お恥ずかしい。いままでメゾットって言ってました...。

No98927 (KOZ さん) に返信
まずパネルに変更した理由はレイアウトと枠線のコントロールをしたかったからです。
実現できるのなら
UserControl
→Panel
→→TextBox
の構成でも構いません。

カスタムコントロールでしたい事は今回は1例で、予定としては
1.パネル内にテキストボックスを1つで1行入力のみのテキストボックスでpaddingを実装する
(複数行なら拡張ウィンドウスタイルでできるのですが...)
2.テキストボックスとボタンを1つでヘルプボタン付きテキストボックス
3.テキストボックスとラベルでラベル付きテキストボックス
4.パネル内にWebBrowserで枠付きWebBrowser
など...多数考えています。

必要な分だけともおもったんですが、パターンを考えるとどんどん増えてしまって
いっそ全部できればと思った次第です。

必ずベースとなるコントロールがあるのでできればそれはそのまま公開する形で
あと付随する物だけ追加で...ともっていたんですが
(1.だと全部テキストボックスのプロパティですし)

別のスレにも書いたんですが、カスタムコントロール系も動作安定しなくって
Visual Studio 2019で問題なかったコードが表示は崩れる、動作はおかしい、など
コードの問題かVisual Studio 2021の問題か切り分けだけで大変で

やっぱり手間をかけるか魔界の仮面弁士さんの案でしょうかね...
引用返信 編集キー/
■98931 / inTopicNo.9)  Re[4]: カスタムコントロールについて
□投稿者/ KOZ (204回)-(2022/01/20(Thu) 17:44:12)
No98930 (くま さん) に返信
>1.パネル内にテキストボックスを1つで1行入力のみのテキストボックスでpaddingを実装する
>(複数行なら拡張ウィンドウスタイルでできるのですが...)

複数行にして CR,LF,TAB の入力をキャンセルすれば1行 TextBox に見えるのでは?
TAB は AcceptsTab プロパティを False にすれば入ってきませんし、
制限をかけるメッセージは WM_SETTEXT,WM_PASTE,WM_CHAR くらいかと。(WM_IME_CHAR も?)

以下3つは 1. ができればなんとかなりそうですよ。

>2.テキストボックスとボタンを1つでヘルプボタン付きテキストボックス
>3.テキストボックスとラベルでラベル付きテキストボックス
>4.パネル内にWebBrowserで枠付きWebBrowser

もっとも弁士さんの案がお手軽ではあります。
引用返信 編集キー/
■98932 / inTopicNo.10)  Re[5]: カスタムコントロールについて
□投稿者/ KOZ (205回)-(2022/01/20(Thu) 18:07:34)
No98931 (KOZ) に返信
> 制限をかけるメッセージは WM_SETTEXT,WM_PASTE,WM_CHAR くらいかと。(WM_IME_CHAR も?)

EM_REPLACESEL もありましたね。
引用返信 編集キー/
■98933 / inTopicNo.11)  Re[6]: カスタムコントロールについて
□投稿者/ KOZ (206回)-(2022/01/20(Thu) 20:18:27)
ちょっと書いてみました。250行くらいです。

Imports System.Text
Imports System.ComponentModel
Imports System.Runtime.InteropServices

Public Class PaddingTextBox
    Inherits TextBox

    Public Sub New()
        MyBase.AutoSize = False
        SetStyle(ControlStyles.FixedHeight, True)
    End Sub

    <Browsable(True)>
    <EditorBrowsable(EditorBrowsableState.Always)>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)>
    Public Shadows Property Padding As Padding
        Get
            Return MyBase.Padding
        End Get
        Set(value As Padding)
            MyBase.Padding = value
        End Set
    End Property

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.Style = cp.Style Or ES_MULTILINE
            Return cp
        End Get
    End Property

    Private _AutoSize As Boolean = True

    Public Overrides Property AutoSize As Boolean
        Get
            Return _AutoSize
        End Get
        Set(value As Boolean)
            If AutoSize <> value Then
                _AutoSize = value
                SetStyle(ControlStyles.FixedHeight, value)
                OnAutoSizeChanged(EventArgs.Empty)
            End If
        End Set
    End Property

    Protected Overrides Sub OnHandleCreated(e As EventArgs)
        MyBase.OnHandleCreated(e)
        DoLayout()
    End Sub

    Protected Overrides Sub OnPaddingChanged(e As EventArgs)
        MyBase.OnPaddingChanged(e)
        DoLayout()
    End Sub

    Protected Overrides Sub OnFontChanged(e As EventArgs)
        MyBase.OnFontChanged(e)
        DoLayout()
    End Sub

    Private Sub DoLayout()
        AdjustHeight()
        Dim r As Rectangle = ClientRectangle
        r = DeflateRect(r, Padding)
        Dim rect As New RECT(r)
        SendMessage(Handle, EM_SETRECT, IntPtr.Zero, rect)
    End Sub

    Private Sub AdjustHeight()
        If Not AutoSize Or Multiline Then Return

        FontHeight = Font.Height

        Dim hdc As IntPtr = CreateCompatibleDC(IntPtr.Zero)
        Dim hFont As IntPtr = Font.ToHfont()
        Dim oldFont As IntPtr = SelectObject(hdc, hFont)
        Try
            Dim tm As New TEXTMETRICW()
            GetTextMetricsW(hdc, tm)

            Dim cs As Size = ClientSize
            cs.Height = tm.tmHeight + Padding.Top + Padding.Bottom
            ClientSize = cs

        Finally
            SelectObject(hdc, oldFont)
            DeleteObject(hFont)
            DeleteDC(hdc)
        End Try
    End Sub

    Private Shared Function DeflateRect(rect As Rectangle, padd As Padding) As Rectangle
        rect.X += padd.Left
        rect.Y += padd.Top
        rect.Width -= padd.Horizontal
        rect.Height -= padd.Vertical
        Return rect
    End Function

    Protected Overrides Sub OnResize(e As EventArgs)
        MyBase.OnResize(e)
        DoLayout()
    End Sub

    Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
        If Not Multiline Then
            Select Case e.KeyChar
                Case CHAR_CR, CHAR_LF, CHAR_TAB
                    e.Handled = True
                    Return
            End Select
        End If
        MyBase.OnKeyPress(e)
    End Sub

    Private Function RemoveControlChar(value As String) As String
        Dim sb As New StringBuilder(value.Length)
        For Each c As Char In value
            Select Case c
                Case CHAR_CR, CHAR_LF, CHAR_TAB
                Case Else
                    sb.Append(c)
            End Select
        Next
        Return sb.ToString()
    End Function

    Protected Overrides Sub WndProc(ByRef m As Message)
        If Multiline Then
            MyBase.WndProc(m)
        Else
            Select Case m.Msg
                Case WM_SETTEXT, EM_REPLACESEL

                    Dim setText As String = Marshal.PtrToStringUni(m.LParam)
                    setText = RemoveControlChar(setText)
                    Dim ptr As IntPtr = Marshal.StringToCoTaskMemAuto(setText)
                    Try
                        Dim msg As Message = Message.Create(m.HWnd, m.Msg, m.WParam, ptr)
                        MyBase.WndProc(msg)
                    Finally
                        Marshal.FreeCoTaskMem(ptr)
                    End Try

                Case WM_PASTE
                    If Clipboard.ContainsText() Then
                        Dim pasteText As String = RemoveControlChar(Clipboard.GetText())
                        Dim ptr As IntPtr = Marshal.StringToCoTaskMemAuto(pasteText)
                        Try
                            Dim msg As Message = Message.Create(m.HWnd, EM_REPLACESEL, New IntPtr(1), ptr)
                            MyBase.WndProc(msg)
                        Finally
                            Marshal.FreeCoTaskMem(ptr)
                        End Try
                    End If

                Case Else
                    MyBase.WndProc(m)
            End Select
        End If
    End Sub

    Private Const CHAR_CR As Char = ChrW(&HD)
    Private Const CHAR_LF As Char = ChrW(&HA)
    Private Const CHAR_TAB As Char = ChrW(&H9)

    Private Const WM_SETTEXT As Integer = &HC
    Private Const WM_PASTE As Integer = &H302

    Private Const EM_SETRECT As Integer = &HB3
    Private Const EM_REPLACESEL As Integer = &HC2
    Private Const ES_MULTILINE As Integer = &H4

    <DllImport("Gdi32")>
    Private Shared Function CreateCompatibleDC(hDC As IntPtr) As IntPtr
    End Function

    <DllImport("User32")>
    Private Shared Function GetDC(hWnd As IntPtr) As IntPtr
    End Function

    <DllImport("User32")>
    Private Shared Function ReleaseDC(hWnd As IntPtr, hDC As IntPtr) As Boolean
    End Function

    <DllImport("Gdi32")>
    Public Shared Function DeleteDC(hdc As IntPtr) As Boolean
    End Function

    <DllImport("Gdi32")>
    Public Shared Function SelectObject(hdc As IntPtr, hgdiobj As IntPtr) As IntPtr
    End Function

    <DllImport("Gdi32")>
    Public Shared Function DeleteObject(hObject As IntPtr) As Boolean
    End Function

    <StructLayout(LayoutKind.Sequential)>
    Private Structure TEXTMETRICW
        Public tmHeight As Integer
        Public tmAscent As Integer
        Public tmDescent As Integer
        Public tmInternalLeading As Integer
        Public tmExternalLeading As Integer
        Public tmAveCharWidth As Integer
        Public tmMaxCharWidth As Integer
        Public tmWeight As Integer
        Public tmOverhang As Integer
        Public tmDigitizedAspectX As Integer
        Public tmDigitizedAspectY As Integer
        Public tmFirstChar As UShort
        Public tmLastChar As UShort
        Public tmDefaultChar As UShort
        Public tmBreakChar As UShort
        Public tmItalic As Byte
        Public tmUnderlined As Byte
        Public tmStruckOut As Byte
        Public tmPitchAndFamily As Byte
        Public tmCharSet As Byte
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Private Structure RECT
        Public Left As Integer, Top As Integer, Right As Integer, Bottom As Integer

        Public Sub New(r As Rectangle)
            Left = r.Left : Top = r.Top : Right = r.Right : Bottom = r.Bottom
        End Sub

        Public Function ToRectangle() As Rectangle
            Return Rectangle.FromLTRB(Left, Top, Right, Bottom)
        End Function
    End Structure

    <DllImport("Gdi32")>
    Private Shared Function GetTextMetricsW(hdc As IntPtr, ByRef lptm As TEXTMETRICW) As Boolean
    End Function

    <DllImport("User32", CharSet:=CharSet.Auto)>
    Private Shared Function SendMessage(hwnd As IntPtr, wMsg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
    End Function

    <DllImport("User32", CharSet:=CharSet.Auto)>
    Private Shared Function SendMessage(hwnd As IntPtr, wMsg As Integer, wParam As IntPtr, ByRef lParam As RECT) As IntPtr
    End Function

End Class

引用返信 編集キー/
■98934 / inTopicNo.12)  Re[7]: カスタムコントロールについて
□投稿者/ くま (126回)-(2022/01/20(Thu) 23:17:46)
No98933 (KOZ さん) に返信
> ちょっと書いてみました。250行くらいです。
複数行コントロールで1行のみとは発想の転換ですね。
でも250行はちょっとじゃないですよ(笑)
サンプルまで本当にありがとうございます。

引用返信 編集キー/
■98935 / inTopicNo.13)  Re[8]: カスタムコントロールについて
□投稿者/ KOZ (207回)-(2022/01/21(Fri) 08:32:43)
細かい不具合がありますが使われるんだったら直して使ってやってください。

・SetStyle(ControlStyles.FixedHeight, AutoSize AndAlso (Not Multiline))
・AutoSize は Shadows にしたほうがいいかも
・Multiline もオーバーライドして SetStyle
・大きな Padding を入力して EM_SETRECT の領域がなくなったときの挙動が不自然


引用返信 編集キー/
■98936 / inTopicNo.14)  Re[9]: カスタムコントロールについて
□投稿者/ くま (127回)-(2022/01/21(Fri) 10:27:16)
No98935 (KOZ さん) に返信
今テストしていますが、ほぼサンプルで行けそうです。
ありがとうございます。

今回は
1. TextBoxのPaddingはKOZ さんのサンプルを採用
2.〜4.等は基本魔界の仮面弁士 さんの案で
あとは、頑張って写して対応とします。

アドバイス等ありがとうございます。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -