I am trying to draw a highlighted border around a custom textbox control so that I can reuse the highlighting feature for each new program I create. My approach so far has been to override the paint event in the control library (dll) after the custom property I have created is set. The code for the control is below.
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
Imports System.ComponentModel.Design
<ToolboxBitmap(GetType(Button))>
Public Class Textbox_Custom
Inherits System.Windows.Forms.TextBox
Public Event OnEnterKeyPress()
Public Event MissingInfo_Change As EventHandler
Dim iMissing_Info As Boolean
Dim iCharacterInput As Cinput
Public Property CharacterInput As Cinput
'<Browsable(True), DefaultValue("AllowAll")>
Get
Return Me.iCharacterInput
End Get
Set(ByVal value As Cinput)
Me.iCharacterInput = value
End Set
End Property
Public Property Missing_Info As Boolean
'<Browsable(True), DefaultValue(True)>
Get
Return iMissing_Info
End Get
Set(value As Boolean)
iMissing_Info = value
**MyBase.Refresh()**
End Set
End Property
Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
MyBase.OnKeyPress(e)
If Asc(e.KeyChar) = 13 Then
RaiseEvent OnEnterKeyPress()
End If
Select Case Me.iCharacterInput
Case Cinput.CharactersOnly
If IsNumeric(e.KeyChar) Then
e.Handled = True
End If
Case Cinput.NumericOnly
If Not IsNumeric(e.KeyChar) And Asc(e.KeyChar) <> 8 Then
e.Handled = True
End If
End Select
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
**If iMissing_Info = True Then**
Dim rect As New Rectangle(New Point(0, 0), New Size(Me.Size.Width + 2, Me.Size.Height + 2))
Dim pen As New Pen(Brushes.OrangeRed, 2)
e.Graphics.DrawRectangle(pen, rect)
e.Dispose()
End If
End Sub
End Class
Public Enum Cinput
AllowAll
NumericOnly
CharactersOnly
End Enum
While debugging I have set a breakpoint in the OnPaint override (lines **), but it never hits it. I then put a breakpoint in the Set section of the Missing_Info property where I am trying to invalidate the control to redraw. It does hit the MyBase.Refresh breakpoint so I don't understand what I've missed.
I realize there have been several other posts on this topic, but from what I can tell they seem to require putting panels behind the control. I feel like I should be able to include this action in a custom control and not have to code a new highlighting section for each new project. Thanks for any help in advance.
In the end I decided to just go with changing the control background to a semi-transparent red color which should be obvious enough for what I'm doing.
Related
EDIT: After I found what I was really looking for, I edited my initial question so to describe better what I finaly wanted to do.
I am working on a UserControl and I want to place a DesignerVerb at it's properties, like TreeView control have. How can I do this? Is it possible?
Well, here is a simple example...
1. If we haven't done already, we should add a reference to System.Design. Goto Reference Manager > Assemblies > Framework and find System.Design. Check it and click OK.
2. Into our UserControl code, we make sure that we already have Imports System.ComponentModel and Imports System.ComponentModel.Design references.
3. Over our UserControl class, we add a Designer attribute, to specify our ControlDesigner for this UserControl.
Imports System.ComponentModel
Imports System.ComponentModel.Design
<Designer(GetType(MyControlDesigner))>
Public Class UserControl1
'Our UserControl code in here...
End Class
4. Under our UserControl class, we create a new class by the name "MyControlDesigner" which will be our ControlDesigner.
Public Class MyControlDesigner
End Class
5. Now, for example, lets create a Verb which will Dock and Undock our UserControl in ParentForm.
Public Class MyControlDesigner
Inherits System.Windows.Forms.Design.ControlDesigner 'Inherit from ControlDesigner class.
Private MyVerbs As DesignerVerbCollection
Public Sub New()
End Sub
Public Overrides ReadOnly Property Verbs() As DesignerVerbCollection
Get
If MyVerbs Is Nothing Then
MyVerbs = New DesignerVerbCollection 'A new DesignerVerbCollection to use for our DesignerVerbs.
MyVerbs.Add(New DesignerVerb("Dock In ParentForm", New EventHandler(AddressOf OnMyCommandLinkClicked))) 'An Event Handler for Docking our UserControl.
MyVerbs.Add(New DesignerVerb("Undock in ParentForm", New EventHandler(AddressOf OnMyCommandLinkClicked))) 'An Event Handler for Undocking our UserControl.
MyVerbs(1).Visible = False 'We hide second Verd by default.
End If
Return MyVerbs
End Get
End Property
Private Sub OnMyCommandLinkClicked(ByVal sender As Object, ByVal args As EventArgs)
Dim _UserControl As UserControl1 = CType(Me.Control, UserControl1) 'Reference to our UserControl1 Class, so we can access it's Properties and Methods.
If _UserControl.Dock = DockStyle.None Then 'If UserControl is Undocked then...
_UserControl.Dock = DockStyle.Fill 'Dock UserControl in ParentForm.
MyVerbs(0).Visible = False 'Hide "Dock In ParentForm" DesignerVerb.
MyVerbs(1).Visible = True 'Show "Undock in ParentForm" DesignerVerb.
Else
_UserControl.Dock = DockStyle.None 'Undock UserControl.
MyVerbs(1).Visible = False 'Hide "Undock in ParentForm" DesignerVerb.
MyVerbs(0).Visible = True 'Show "Dock in ParentForm" DesignerVerb.
End If
End Sub
End Class
6. Then we Build our project and we add our UserControl into our test Form.
You do not necessarily need to create a custom designer to access the various designer services exposed by the WinForm design environment. All you need is an instance of a IServiceProvider Interface. All classes that have System.ComponentModel.Component in their ancestry expose the Site Property. The Site Property is an instance of type ISite that itself inherits from IServiceProvider.
Most of the design services are defined by the interfaces documented in the System.ComponentModel.Design Namespace. Others like the BehaviorService Class are buried in the documentation and have to be specifically sought out.
Using a proper designer class has its advantages in that it automatically integrates into the design model and encapsulates that functionality. The technique shown below has the drawback of needing to know the proper time that services are accessible. The first temporal criteria is that the host designer has finished loading. This is achieved by using a combination the host's IsLoaded property and LoadComplete events. The second is knowing when the host is finished adding your component to the design surface. The setting of the Site Property is part of design transaction. When this transaction is completed, the component's designer is accessible. For this, you use the host's TransactionClosed Event.
With that stated, the entry point is to override the inherited Site Property so that you can gain access to the service provider. This example gains references to the designer host, its selection service, and the controls default designer. The default designer allows you to add a DesignerVerb to its Verb collection.
Imports System.ComponentModel
Imports System.ComponentModel.Design
Public Class DemoControl : Inherits Control
Public Sub New()
MyBase.New()
BackColor = Color.Red ' just so we can see it
End Sub
#Region "Designer Services"
Private designerHost As Design.IDesignerHost
Private myDesigner As Design.IDesigner
Private designerSelectionService As Design.ISelectionService
Private Shared customDesignerVerb1 As Design.DesignerVerb
Public Overrides Property Site As ISite
Get
Return MyBase.Site
End Get
Set(value As ISite)
MyBase.Site = value
If value Is Nothing Then ' being removed from the design surface
DetachDesignerServices()
Else ' being added to the design surface
designerHost = CType(value.GetService(GetType(Design.IDesignerHost)), Design.IDesignerHost)
If designerHost IsNot Nothing Then
If designerHost.Loading Then
' the designer has not finished loading,
' postpone all other connections until it has finished loading
AddHandler designerHost.LoadComplete, AddressOf DesignerHostLoaded
Else
' designerHost loaded, but is in the in the process of creating this instance
If designerHost.InTransaction Then
AddHandler designerHost.TransactionClosed, AddressOf DesignerTransactionClosed
Else
AttachDesignerServices()
End If
End If
End If
End If
End Set
End Property
Private Sub DesignerHostLoaded(sender As Object, e As EventArgs)
RemoveHandler designerHost.LoadComplete, AddressOf DesignerHostLoaded
AttachDesignerServices()
End Sub
Private Sub DesignerTransactionClosed(sender As Object, e As DesignerTransactionCloseEventArgs)
RemoveHandler designerHost.TransactionClosed, AddressOf DesignerTransactionClosed
AttachDesignerServices()
End Sub
Private Sub AttachDesignerServices()
myDesigner = designerHost.GetDesigner(Me)
If customDesignerVerb1 Is Nothing Then
customDesignerVerb1 = New Design.DesignerVerb("Verb1", AddressOf DesignerVerb1EventHandler)
End If
If myDesigner IsNot Nothing AndAlso
Not myDesigner.Verbs.Contains(customDesignerVerb1) Then
myDesigner.Verbs.Add(customDesignerVerb1)
End If
designerSelectionService = CType(designerHost.GetService(GetType(Design.ISelectionService)), Design.ISelectionService)
If designerSelectionService IsNot Nothing Then
AddHandler designerSelectionService.SelectionChanged, AddressOf DesignerSelectionChanged
End If
End Sub
Private Sub DetachDesignerServices()
If designerSelectionService IsNot Nothing Then
RemoveHandler designerSelectionService.SelectionChanged, AddressOf DesignerSelectionChanged
designerSelectionService = Nothing
End If
If designerHost IsNot Nothing Then
RemoveHandler designerHost.LoadComplete, AddressOf DesignerHostLoaded
designerHost = Nothing
End If
If myDesigner IsNot Nothing Then
myDesigner = Nothing
End If
End Sub
Private Sub DesignerSelectionChanged(sender As Object, e As EventArgs)
Static shownCount As Int32
If designerSelectionService.GetComponentSelected(Me) AndAlso shownCount < 2 Then
MessageBox.Show("I've been selected." & If(shownCount = 0, " This will show one more time on selecting.", ""))
shownCount += 1
End If
End Sub
Private Sub DesignerVerb1EventHandler(sender As Object, e As EventArgs)
MessageBox.Show("Verb1 Cicked")
End Sub
#End Region ' "Designer Services
End Class
I have a scrollable panel with an image/picture box inside.
I wanted to zoom in and zoom out the image without the buttons disappearing on the lower left of the panel. by the way, the image is in it's actual size.
If I make it a scrollable panel that can be zoomed in and out, will i still be able to get the coordinates of the image by not using the coordinates of the screen but the coordinates of the actual image?
Pls help me
I'm not sure what you mean by
zoom in and zoom out the image without the buttons disappearing
But you can use this control created by Bob Powell. His site seems to be offline now but I found this code:
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms
Namespace bobpowell.net
'/ <summary>
'/ ZoomPicBox does what it says on the wrapper.
'/ </summary>
'/ <remarks>
'/ PictureBox doesn't lend itself well to overriding. Why not start with something basic and do the job properly?
'/ </remarks>
Public Class ZoomPicBox
Inherits ScrollableControl
Private _image As Image
<Category("Appearance"), Description("The image to be displayed")> _
Public Property Image() As Image
Get
Return _image
End Get
Set
_image = value
UpdateScaleFactor()
Invalidate()
End Set
End Property
Private _zoom As Single = 1F
<Category("Appearance"), Description("The zoom factor. Less than 1 to reduce. More than 1 to magnify.")> _
Public Property Zoom() As Single
Get
Return _zoom
End Get
Set
If value < 0 OrElse value < 1E-05 Then
value = 1E-05F
End If
_zoom = value
UpdateScaleFactor()
Invalidate()
End Set
End Property
Private Sub UpdateScaleFactor()
If _image Is Nothing Then
Me.AutoScrollMargin = Me.Size
Else
Me.AutoScrollMinSize = New Size(CInt(Me._image.Width * _zoom + 0.5F), CInt(Me._image.Height * _zoom + 0.5F))
End If
End Sub 'UpdateScaleFactor
Private _interpolationMode As InterpolationMode = InterpolationMode.High
<Category("Appearance"), Description("The interpolation mode used to smooth the drawing")> _
Public Property InterpolationMode() As InterpolationMode
Get
Return _interpolationMode
End Get
Set
_interpolationMode = value
End Set
End Property
Protected Overrides Sub OnPaintBackground(pevent As PaintEventArgs)
End Sub 'OnPaintBackground
' do nothing.
Protected Overrides Sub OnPaint(e As PaintEventArgs)
'if no image, don't bother
If _image Is Nothing Then
MyBase.OnPaintBackground(e)
Return
End If
'Set up a zoom matrix
Dim mx As New Matrix(_zoom, 0, 0, _zoom, 0, 0)
mx.Translate(Me.AutoScrollPosition.X / _zoom, Me.AutoScrollPosition.Y / _zoom)
e.Graphics.Transform = mx
e.Graphics.InterpolationMode = _interpolationMode
e.Graphics.DrawImage(_image, New Rectangle(0, 0, Me._image.Width, Me._image.Height), 0, 0, _image.Width, _image.Height, GraphicsUnit.Pixel)
MyBase.OnPaint(e)
End Sub 'OnPaint
Public Sub New()
'Double buffer the control
Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.ResizeRedraw Or ControlStyles.UserPaint Or ControlStyles.DoubleBuffer, True)
Me.AutoScroll = True
End Sub 'New
End Class 'ZoomPicBox
End Namespace 'bobpowell.net
I am trying to make a reusable control similar to an Outlook-style sidebar. I have a CustomPanel. I also have a CustomCollectionControl, that inherits from flow layout panel. At design time I would like to add (x) CustomPanels to my CustomCollectionControl, through the properties window.
When I try to add from the (Collection) list in the properties window, it will show up in the list, but it will not add it to the control that is on the form.
Here is my code so far.
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Public Class CustomCollectionControl
Inherits FlowLayoutPanel
''' <summary>
''' Required designer variable.
''' </summary>
Private _mComponents As Container = Nothing
Private _mCustompanels As CustomPanelCollection
Public Sub New()
' This call is required by the Windows.Forms Form Designer.
InitializeComponent()
SetStyle(ControlStyles.DoubleBuffer, True)
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
_mCustompanels = New CustomPanelCollection(Me)
Padding = New Padding(0)
End Sub
#Region "Component Designer generated code"
''' <summary>
''' Required method for Designer support - do not modify
''' the contents of this method with the code editor.
''' </summary>
Private Sub InitializeComponent()
_mComponents = New System.ComponentModel.Container()
End Sub
#End Region
<EditorBrowsable(EditorBrowsableState.Always)> _
<Browsable(True)> _
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
<Bindable(True)> _
Public Property CustomPanels() As CustomPanelCollection
Get
Return _mCustompanels
End Get
Set(value As CustomPanelCollection)
_mCustompanels = value
End Set
End Property
Protected Overrides Sub OnResize(e As EventArgs)
MyBase.OnResize(e)
End Sub
End Class
Public Class CustomPanelCollection
Inherits CollectionBase
Private _mControl As CustomCollectionControl
Private _mCustomCollectionControl As CustomCollectionControl
Friend Sub New(control As CustomCollectionControl)
_mCustomCollectionControl = control
End Sub
Default Public ReadOnly Property Item(index As Integer) As CustomPanel
Get
Return DirectCast(List(index), CustomPanel)
End Get
End Property
Public Function Contains(cPanel As CustomPanel) As Boolean
Return List.Contains(cPanel)
End Function
Public Function Add(cPanel As CustomPanel) As Integer
Dim i As Integer
i = List.Add(cPanel)
cPanel.Control = _mCustomCollectionControl
Return i
End Function
Public Sub Remove(cPanel As CustomPanel)
List.Remove(cPanel)
cPanel.Control = Nothing
End Sub
End Class
Public Class CustomPanel
Inherits Panel
Friend Control As CustomCollectionControl
Public Sub New()
' TODO Set Stuff!
Height = 100
BorderStyle = BorderStyle.FixedSingle
Margin = New Padding(0)
Padding = New Padding(0)
Dim cBtn As New Button
cBtn.Height = 30
Controls.Add(cBtn)
cBtn.Dock = DockStyle.Top
End Sub
End Class
I need to find out when a CustomPanel is added through the properties window during design time, how to update the control with the changes?
The basic problem is that in order for the flow-layout logic to work on your panels, they need to be in the base control's ControlCollection. If/When you expose this thru the properties IDE the standard collection editor allows any control to be added to it.
Your CustomPanels() property on the other hand, allows only CustomPanel controls but they get stored in a different collection, so they do not show up on the form.
The SmartTag action to only add CustomPanel is a very viable workaround if it adds to the Controls collection. I am not sure how many of the standard Panel properties you want them to be able to edit, and since there is no way to specify the child button properties, there doesnt seem much difference between the collection editor and the SmartTag. I assume this is because it is a work in progress and/or removed to post a minimal example.
Another way is to get rid if the extra collection and use a custom collection editor which will restrict the type of control to what you want. This is shown below.
Notes:
I changed the generic names to make it easier to read. CustomCollectionControl is now FlowLayoutPanelEx and CustomPanel is FlowPanel.
Your Buttons arent hooked up to anything, nor are they exposed, so I am not sure how you plan to use them.
Since all that the FlowPanel does is store that one button, why not omit it and just add buttons of a certain size?
There are several other issues with the code(e.g. CustomPanel/FlowPanel should implement IDisposable since it is creating stuff). These and other issues are ignored in order to focus on implementing a minimal custom collection editor.
FlowLayoutPanelEx and FlowPanel:
' collection editor will need this:
Imports System.ComponentModel.Design
Public Class FlowLayoutPanelEx
Inherits FlowLayoutPanel
Public Sub New()
' This call is required by the Windows.Forms Form Designer.
' {PL} - no, it is not
'InitializeComponent()
SetStyle(ControlStyles.DoubleBuffer, True)
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Padding = New Padding(0)
End Sub
<EditorBrowsable(EditorBrowsableState.Always),
Browsable(True),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Bindable(True),
Editor(GetType(FlowPanelCollectionEditor),
GetType(System.Drawing.Design.UITypeEditor))>
Public Overloads Property Controls() As ControlCollection
Get
Return MyBase.Controls
End Get
Set(value As ControlCollection)
End Set
End Property
End Class
Public Class FlowPanel
Inherits Panel
' ToDo: implememt IDisposable
Private myBtn As Button
' allow user to specify the text for the child button
Public Property ButtonText As String
Get
If myBtn IsNot Nothing Then
Return myBtn.Text
Else
Return String.Empty
End If
End Get
Set(value As String)
myBtn.Text = value
End Set
End Property
Public Sub New()
' TODO Set Stuff!
Height = 100
BorderStyle = BorderStyle.FixedSingle
Margin = New Padding(0)
Padding = New Padding(0)
Height = 40
myBtn = New Button
myBtn.Height = 30
Controls.Add(myBtn)
myBtn.Dock = DockStyle.Top
End Sub
End Class
The way you have it, the user can change any FlowPanel property in the Collection Editor including those you have explicitly set. I dont know enough about what you ultimately want to do to offer alternatives other than it seems like perhaps the Panel is cosmetic and maybe a Button alone would suffice.
Note the additional Editor attribute on the Controls property. This tells VS to use that collection editor:
Public Class FlowPanelCollectionEditor
Inherits CollectionEditor
Public Sub New(t As Type)
MyBase.New(t)
End Sub
' *** Magic happens here: ***
' override the base class to SPECIFY the Type allowed
' rather than letting it derive the Types from the collection type
' which would allow any control to be added
Protected Overrides Function CreateNewItemTypes() As Type()
Dim ValidTypes As Type() = {GetType(FlowPanel)}
Return ValidTypes
End Function
Public Overrides Function EditValue(context As ITypeDescriptorContext,
provider As IServiceProvider,
value As Object) As Object
Return MyBase.EditValue(context, provider, value)
End Function
End Class
Results:
The collection editor adds only FlowPanels:
As you can see, the new ButtonText property can be set from the collection editor. When the controls are added to the Controls collection for use on the form, ButtonText shows on the buttons:
Note that the user can still drag a TextBox or whatever to your FlowLayoutPanelEx and it will accept it. This is another of those "other issues" mentioned above.
An article on CodeProject, Enhanced CollectionEditor Framework provides a fairly comprehensive overview of collections and custom collection editors.
It includes a custom collection editor framework but it wont handle this situation as is. If you remove NotOverridable from the CreateNewItemTypes method and recompile, you should be able to inherit from EnhancedCollectionEditor and use some of the other features it provides.
It is not really needed; as the code above shows there is not much involved in restricting the Type allowed. The article might be of value though as you modify and refine FlowPanel and the button into their final form. (Disclaimer: I wrote the article).
I am adding this here because I cannot do it in the comments because there is too much text and images. Also, maybe someone coming here from a search engine will be able to get an idea of what to do.
This is what I wanted to achieve with the control:
Closed
Open
And here is the edited code to allow the (flat style) buttons to be clicked and open the parent panel. This is a very crude method of doing it, but I put it together to check if it worked before I tied up too much time in it:
' collection editor will need this:
Imports System.ComponentModel.Design
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.Drawing
Public Class FlowLayoutPanelEx
Inherits FlowLayoutPanel
Public Sub New()
SetStyle(ControlStyles.DoubleBuffer, True)
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
Padding = New Padding(0)
BackColor = Color.FromKnownColor(KnownColor.ControlDark)
End Sub
<EditorBrowsable(EditorBrowsableState.Always),
Browsable(True),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Bindable(True),
Editor(GetType(FlowPanelCollectionEditor),
GetType(System.Drawing.Design.UITypeEditor))>
Public Overloads Property Controls() As ControlCollection
Get
Return MyBase.Controls
End Get
Set(value As ControlCollection)
End Set
End Property
End Class
Public Class HeaderButton
Inherits Button
Public Property BtnID As Integer
Public Property BtnColor As System.Drawing.Color
Public Event ButtonClicked(sender As HeaderButton, buttonID As Int32)
Private Sub clicked(sender As Object, e As EventArgs) Handles Me.Click
RaiseEvent ButtonClicked(Me, BtnID)
End Sub
End Class
Public Class FlowPanel
Inherits Panel
' ToDo: implememt IDisposable
Private myBtn As HeaderButton
' allow user to specify the text for the child button
Public Property ButtonText As String
Get
If myBtn IsNot Nothing Then
Return myBtn.Text
Else
Return String.Empty
End If
End Get
Set(value As String)
myBtn.Text = value
End Set
End Property
Public Sub New()
BorderStyle = BorderStyle.FixedSingle
Margin = New Padding(0)
Padding = New Padding(0)
Height = 32
BackColor = Color.FromKnownColor(KnownColor.Info)
myBtn = New HeaderButton
AddHandler myBtn.ButtonClicked, AddressOf Me.ItemButtonClicked
myBtn.Height = 30
myBtn.Margin = New Padding(0)
myBtn.Padding = New Padding(0)
myBtn.Dock = DockStyle.Top
myBtn.FlatStyle = FlatStyle.Flat
BackColor = Color.FromKnownColor(KnownColor.Control)
Controls.Add(myBtn)
End Sub
Public Sub ItemButtonClicked(ByVal btn As HeaderButton, ByVal buttonID As Int32)
If btn.Parent.Height = 32 Then
btn.Parent.Height = 200
Else : btn.Parent.Height = 32
End If
End Sub
End Class
Public Class FlowPanelCollectionEditor
Inherits CollectionEditor
Public Sub New(t As Type)
MyBase.New(t)
End Sub
' *** Magic happens here: ***
' override the base class to SPECIFY the Type allowed
' rather than letting it derive the Types from the collection type
' which would allow any control to be added
Protected Overrides Function CreateNewItemTypes() As Type()
Dim ValidTypes As Type() = {GetType(FlowPanel)}
Return ValidTypes
End Function
Public Overrides Function EditValue(context As ITypeDescriptorContext,
provider As IServiceProvider,
value As Object) As Object
Return MyBase.EditValue(context, provider, value)
End Function
End Class
There is so much more that I have to do, like displaying changes to the controls in the designer, implementing Idisposable, adding a collapsible button on the side, and passing the height value of the panel through the form so it will open the full height. I'm probably going to draw the buttons to get some effects that are not available with the standard button.
I'm working on a VB.Net application. I need to create an UserControl I've named signalIO, with a TextBox and a Label, with some properties to change contents of TextBox and Label, change BackColor, etc.
When I add signalIO into the Form of my application, the signalIO control works fine. If I change the parameter StatusSignal from False to True, I can see the backcolor of textbox into control changed from Red to Green, if I change the name of the Signal, I can see text into Label changed correctly.
But if I run on debug the application, I cannot see the changes after the Set methods of the properties.
This is the code I've used:
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.ComponentModel.Design
Imports System.Windows.Forms
<Designer("System.Windows.Forms.Design.ParentControlDesigner,System.Design", GetType(IDesigner))> _
Public Class controlIO
Inherits UserControl
Private status As Boolean
Private ID As Integer = 0
Private label As String
Public Property labelSignal As String
Get
Return labelIO.Text
End Get
Set(value As String)
label = value
labelIO.Text = label
updateControl()
Invalidate()
End Set
End Property
Private Sub updateControl()
Me.Refresh()
boxIO.Refresh()
labelIO.Refresh()
End Sub
Public Property statusSignal As Boolean
Get
Return status
End Get
Set(value As Boolean)
status = value
If status Then boxIO.BackColor = Color.Green Else boxIO.BackColor = Color.Red
updateControl()
Invalidate()
End Set
End Property
Public Property signalIO As Integer
Get
Return ID
End Get
Set(value As Integer)
ID = value
boxIO.Text = CStr(ID)
updateControl()
Invalidate()
End Set
End Property
End Class
I cannot understand why if I change the property statusSignal, for example, I can see the change of the color of boxIO into FormDesigner of the application, but this not changed during execution of the application.
I wish you can understand my question.
I need a multiline TextBox which is always disabled, but it shouldn't paint itself in gray, but I want to keep its designer choosen color.
I previously had the same requirement with an always-black Label (no multiline) and so I inherited from Label like:
Imports System.ComponentModel
Public Class LabelDisabled
Inherits Label
Sub New()
InitializeComponent()
Enabled = False
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
' always draw it black
e.Graphics.DrawString(Me.Text, Me.Font, Brushes.Black, 0, 0)
End Sub
End Class
That works fine. Now I want the same thing but with a multiline label, so I chose to inherit from TextBox:
Imports System.ComponentModel
Public Class CustomControl1
Inherits TextBox
Sub New()
InitializeComponent()
'Paint never fires anyway
'Enabled = False
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim brush As New SolidBrush(Me.ForeColor)
e.Graphics.DrawString(Me.Text, Me.Font, brush, 0, 0)
End Sub
End Class
Now the Paint event is never fired in the CustomControl1 - TextBox inherited - control.
Why can't I get the Paint event?
Also, if I want to make the Enabled property invisible and not-settable by the user, I do:
<Browsable(False),
DefaultValue(False)>
Public Overloads Property Enabled As Boolean
Get
Return False
End Get
Set(ByVal value As Boolean)
End Set
End Property
But this way, neither I can set the "real" Enabled property, I mean the backing field.
I've found a solution. It looks like a TextBox disables the Paint event even for subclasses. But you can force the WM_PAINT bit calling SetStyle:
Public Class DisabledTextBox
Inherits TextBox
Public Sub New()
InitializeComponent()
Enabled = False
SetStyle(ControlStyles.Selectable, False)
SetStyle(ControlStyles.UserPaint, True)
End Sub
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Dim brush As New SolidBrush(Me.ForeColor)
e.Graphics.DrawString(Me.Text, Me.Font, brush, 0, 0)
End Sub
End Class
It works perfectly as expected :)
here is your answer:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
e.Graphics.FillRectangle(Brushes.LightGray, Me.DisplayRectangle)
Dim sf As New StringFormat
sf.FormatFlags = StringFormatFlags.NoWrap
sf.HotkeyPrefix = Drawing.Text.HotkeyPrefix.Show 'if Mnemonic property is set to true
sf.HotkeyPrefix = Drawing.Text.HotkeyPrefix.Hide 'or none if Mnemonic property is set to false
sf.LineAlignment = StringAlignment.Center 'horizontal alignment
sf.Alignment = StringAlignment.Center ' vertical ...
Dim rect As Rectangle = Me.DisplayRectangle ' this is your text bounds for setting your text alignement using StringFormat(sf)
e.Graphics.DrawString("Something", Me.Font, Brushes.DarkOliveGreen, rect, sf)
End Sub