Can I use the SetWindowsSubclass API to subclass a constituent control on a UserControl?

Robert Gustafson 606 Reputation points
2021-10-05T23:06:28.353+00:00

WHAT I HAVE:
Visual Basic 2019, WinForms, .NET 4+

MY PROBLEM:
I've created an extended-function rich-text UserControl that uses a RichTextBox as a child constituent control, and I want to use the SetWindowsSubclass on the constituent control to display page breaks when the text-box repaints (RichTextBox--and TextBox, for that matter--don't fire Paint events). I could create an extended control that inherits from the RichTextBox, and use that as the constituent control within my UserControl (relying on WndProc)--but that means adding an additional level of complexity and an additional project to my class-library solution--and it means end users will have to reference an additional library besides the UserControl library in order to place my UserControl into a host project! (They already have to reference an extra one on account of the spell-checker!)

I've been able to use SetWindowSubclass on a regular RichTextBox from within a form, but when I try to port the code to my UserControl code, it (apparently) sucessfully installs the subclass, but it doesn't fire the subclass procedure designed to handle the WM_PAINT message. It works fine when the RichTextBox is directly on a form, but not when it's a constituent inside a UserControl!

What am I doing wrong?! I need an answer ASAP, and in VB.NET.

PS. My tentative solution is to place the page-break drawing code inside of a Timer_Tick event that fires every so many times a second. The result is that the page-breaks have to be continually updated--even when text isn't changed, which shouldn't be necessary, but it is when done this way ('cause there's no way to know when the control will be repainted)--and there's flicker with respect to the page-break lines every time text is changed.

Windows Forms
Windows Forms
A set of .NET Framework managed libraries for developing graphical user interfaces.
1,835 questions
VB
VB
An object-oriented programming language developed by Microsoft that is implemented on the .NET Framework. Previously known as Visual Basic .NET.
2,579 questions
0 comments No comments
{count} votes

Accepted answer
  1. Robert Gustafson 606 Reputation points
    2021-10-06T04:37:02.757+00:00

    I appear to have solved the problem. The thing is, I have to use the tmrPageBreaks.Tick event of a Timer component, to plug in the subclass rather than the rtb.HandleCreated event of the RichTextBox, as follows:

    '   TIMER EVENT PROCEDURES:
    
    '   Display page breaks
    
    Private Sub tmrPageBreaks_Tick(sender As Object, e As EventArgs) Handles tmrPageBreaks.Tick
        Debug.WriteLine("INSIDE TIMER")
        SetUpSubclassIfNeeded()
        If IsRTBSubclassPresent Then
            tmrPageBreaks.Enabled = False
        End If
    

    ' DrawPageBreaks()
    End Sub

    For some reason, invoking SetUpSubclassIfNeeded()--which plugs the subclasser in if it isn't already--can't be reliably/sustainable done simply when the RichTextBox's handle is created. (Note that the timer is no longer needed once the subclasser is verified to be in place.)

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Castorix31 81,741 Reputation points
    2021-10-06T02:06:59.043+00:00

    I just tested a UserControl and a RichTextBox (RichEdit) as child
    (Public Class UserControlTest
    Inherits UserControl

    then
    LoadLibrary("msftedit.dll")
    CreateWindowEx with "RichEdit50W" class
    )

    and I have no problem to use SetWindowSubclass
    I just drew an Ellipse in WM_PAINT for testing : it is behind if I subclass the UserControl, or above for the RichEdit control :

    RichEdit subclassed :
    137897-usercontrol-richedit-paint.jpg

    UserControl subclassed :
    137962-usercontrol-richedit-paint2.jpg


  2. Robert Gustafson 606 Reputation points
    2021-10-06T02:50:27.367+00:00

    @Castorix31 :

    I'm using the standard WinForms RichTextBox-class control in the UserControl designer, not the RichEdit-class library via Win32 API at run-time using LoadLibrary or CreateWindowEx. I did everything the "conventional" way for a UserControl with constituent controls--although the Modifiers property of the RichTextBox control is set to Public so that the end user can access its members from an external host project that uses the larger control . (In case you're wondering, changing the property doesn't seem to mitigate my predicament.) So why is it that the same VB.NET Win32 code that works for the control when it's on the form by itself doesn't work when it's a constituent control inside another?! My problem is that the subclass procedure I've delegated to doesn't even get triggered even though SetWindowSubclass--which is supposed to plug it in--returns True--meaning that the installation was "successful".

    Do the following (and in VB.NET), just to make sure we're making an apples-to-apples comparison: Set up UserControlTest in the designer, dropping a WinForms RichTextBox control onto the larger control's designer space. (Don't use CreateWindowEx to create the text box via API; also experiment with different Modifiers settings for the constituent RichTextBox control.) Then subclass the constituent RichTextBox control using SetWindowSubclass (and RemoveWindowSubclass). See what happens. (And show me the subclass-relevant code so I can see if your code and mine is fundamentally any different.)

    PS.

    1. The only proviso for the SetWindowsSubclass API in the help file is that it can't be used to subclass across threads. That shouldn't be an issue since everything's on the UI thread. (Is it??)
    2. Not only does SetWindowsSubclass actually work when used on a RichTextBox control that's directly on a form by itself (but not when used on a RichTextBox control that's a constituent control), it also works on "auxillary" windows of a control whose handles aren't exposed by the control. For instance, I've created an extended control that inherits from the ComboBox control and subclasses the drop-down window of it. Is the problem specific to standard WinForms controls that are placed on UserControl controls as constituents?! Experiment and find out for me and get back to me!