Remove/Hide Public Controls from Toolbox - vb.net

I have a problem making a custom control. When I create and build the control is in my toolbox which is kind of what I was after:
Public Class PanelBar
Inherits Panel
Private _mCusBtn As CustomButton
Public Sub New()
InitializeComponent()
_mCusBtn = New CustomButton()
AddHandler _mCusBtn.ButtonClicked, AddressOf CustomButtonClicked
Controls.Add(_mCusBtn)
Public Sub CustomButtonClicked(ByVal btn As CustomButton, ByVal buttonId As Int32)
' Do important stuff here...
End Sub
End Class
However, when It displays in the toolbox this control also displays:
Public Class CustomButton
Inherits Button
Public Property BtnId As Integer
Public Property BtnColor As Color
Public Event ButtonClicked(sender As CustomButton, buttonId As Int32)
Public Sub New()
' Set new property values
End Sub
Private Sub CustomButtonClicked(sender As Object, e As EventArgs) Handles Me.Click
RaiseEvent ButtonClicked(Me, BtnId)
End Sub
End Class
So I have tried setting the CustomButton class to friend to limit the outside access because I do not want this as a control in my toolbox and I get this error: 'btn' cannot expose type 'CustomButton' outside the project through class 'PanelBar'. on the CustomButtonClicked event of the first class.
I'm not sure if this makes sense to want to limit the scope of the controls that help make up my custom control since it is having to access the events on them. To be honest I do not recall one time that I have come across a custom control that doesn't list the controls that make it up, so I am not entirely certain this is going to be possible...but I would really appreciate any advice I can get.

To "hide" a control from the toolbox, use the ToolboxItem attribute:
<ToolboxItem(False)>
Public Class CustomButton
...
If your class inherits from Component rather than Control and you don't want them to show in the form component tray at the bottom use:
<DesignTimeVisible(False)>
Public Class FooBarItem
...

Related

Listbox breaks when setting it to a virtual instance from a class

I have a weird problem that I can't wrap my head around.
I have the following code:
Public Class Form1
Public WithEvents MyClass1 As New MyClass
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
End Sub
Private Sub MyClass_UpdateListbox() Handles MyClass1.UpdateListbox
For Each sItem as String In MyClass1.Listbox
MsgBox(sItem) 'an MsgBox shows correct items each time.
Next sItem
Me.Listbox = Me.MyClass1.Listbox 'doesn't work and breaks listbox.
Me.Listbox.Items.Clear() 'listbox is empty anyway, but has no effect.
Me.Listbox.Items.Add("event triggered") 'does nothing.
End Sub
End Class
Public Class MyClass
Public Listbox as new Listbox
Public Event UpdateListbox()
Public Sub New()
'Constructor. sub.
Me.AddItem("Populating listbox")
End Sub
Public Sub AddItem(sItem as String)
Me.Listbox.Items.Add(sItem)
RaiseEvent UpdateListbox()
End Sub
End Class
If I comment the following lines in above code, the listbox keeps adding event triggered, as expected. Of course, I don't have to remove the clear one. It will work, but then it just adds the same item. If I use a command button and call MyClass.AddItem("Something") that is correctly added too as long as the below is commented out. But if not, then once the listbox is in broken state, nothing can be added anymore.
Me.Listbox = Me.MyClass1.Listbox 'doesn't work and breaks listbox.
Me.Listbox.Items.Clear() 'listbox is empty anyway, but has no effect.
How can I use a virtual listbox and assign it to my real listbox?
Also, instead of assigning one listbox to the other, I can of course use that for each loop and add each item one by one which works, but that for each look was for debugging purpose in the first place.
EDIT:
My goal with this application is to build a Todo list with features that are not in a todolist. This is a project I build for work because there I need a tool like this. I already have a todolist that I use but I built it wrong in the past. Everything was condensed in form1, no modules no extra classes. As a result I got weird bugs that I patched with workarounds. I am now rebuilding the application from the ground up, separating tasks in its own classes so I can apply business logic and have a true OOP application. The todo list will become its own class, and managing the list etc will be handeled by this class. It interacts with controls on the form, such as buttons and listboxes. If I just use form1.listbox from the class, things break at program start. I started another question and the below code was a now deleted answer. At first I did not get it working because I did not realize the listbox crashes if I assign it the virtual instance.
So my goal is to have the todolist be handled entirely by the todolist class. It does need a way to interact with controls on form1, and that is the puzzle I'm currently trying to solve.
In the original code, the main problem is that the Field that hold the instance of a Control shown if a Form is reassigned to the instance of another ListBox Control defined in a custom class:
Me.Listbox = Me.MyClass1.Listbox
From now on, Me.Listbox points another ListBox that is not show on screen, so any attempt to update the Form's child ListBox fails, except when Me.Listbox.Items.Clear() is called - in the same procedure - after it's being reassigned, because the handle of the Owner of the ObjectCollection (the object that holds the Items shown in the ListBox) has not been updated yet. It's going to fail after the current method exits nonetheless.
As noted in comments, this is a simplified method to handle a Form and its child Controls using a handler class. The contract between the class handler and a Form is sealed by an Interface (named IFormHandler here).
A Form that implements this Interface exposes the methods defined by the Interface that allow to trigger Actions and specific behaviors, depending on the Type of Control and the implementation.
I suggest to take a look at the MVP or ReactiveUI (MVVM-derived) for WinForms Patterns.
How too proceed:
Open up the ApplicationEvents class object.
If you don't have it already, select Project -> Properties -> Application and click the View Application Events button. It will generate ApplicationEvents.vb. Find it in Solution Explorer and open it up.
It should look like this (plus a bunch of comments that explain what it's for):
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
Partial Friend Class MyApplication
End Class
End Namespace
Paste into MyApplication these lines of code:
Imports Microsoft.VisualBasic.ApplicationServices
Namespace My
Partial Friend Class MyApplication
Public SomeFormHandler As MyFormHandler(Of SomeForm)
Protected Overrides Function OnStartup(e As StartupEventArgs) As Boolean
SomeFormHandler = New MyFormHandler(Of SomeForm)
Return MyBase.OnStartup(e)
End Function
End Class
End Namespace
Add an Interface that defines the Actions (or Behaviors) that a Form must implement.
Here, the GetUsersList() method specifies that a Form that implements this Interface must return the instance of a child ListBox Control.
(To add an Interface, select Project -> Add -> New Item... and select the Interface template. Name the file IFormHandler)
Extend this Interface as needed, to add more Methods or Properties that define actions and behaviors.
Public Interface IFormHandler
Function GetUsersList() As ListBox
End Interface
A Form that implements the IFormHandler Interface implements and exposes the GetUsersList() method, which returns the instance of a ListBox Control (named usersList here)
There's nothing else to do with this Form, the control is handed over to the MyFormHandler object that is initialized with this Type.
Public Class SomeForm
Implements IFormHandler
Public Sub New()
InitializeComponent()
End Sub
Public Function GetUsersList() As ListBox Implements IFormHandler.GetUsersList
Return Me.usersList
End Function
End Class
Now, to show SomeForm, you can use the MyFormHandler class object show below.
' Set the Owner if called from another Form
My.Application.SomeFormHandler.Show(Me)
' Or without an Owner
My.Application.SomeFormHandler.Show()
To close SomeForm, you can either use its handler:
My.Application.SomeFormHandler.Close()
or close it as usual:
[SomeForm Instance].Close()
If MyFormHandler determines that the instance of SomeForm has been disposed, it creates a new one when you call its Show() method again later.
To update the ListBox Control of SomeForm, use the public methods exposed by the MyFormHandler class:
' Add a new element
My.Application.SomeFormHandler.UpdateUsersList(UpdateType.AddElement, "Some Item")
' Remove an element
My.Application.SomeFormHandler.UpdateUsersList(UpdateType.RemoveElement, "Some Item")
' Replace an element
My.Application.SomeFormHandler.UpdateUsersList(UpdateType.ReplaceElement, "New Item", "Some Item")
' Clears the ListBox
My.Application.SomeFormHandler.ClearUsersList()
All these actions generate an event that you can subscribe to when needed.
See also the example that shows how to raise a custom event when the ListBox raises one of its stardard events; SelectedIndexChanged is handled here.
See the implementation of MyFormHandler.
Generic Form handler:
A Form needs to implement the IFormHandler Interface for the MyFormHandler class to accept it as valid.
You can of course extend the Interface, to add more Actions, or build a MyFormHandler class object that uses a different Interface, or more than one.
Public Class MyFormHandler(Of TForm As {Form, IFormHandler, New})
Implements IDisposable
Private formObject As TForm
Private IsInstanceSelfClosing As Boolean = False
Public Event UsersListUpdate(item As Object, changeType As UpdateType)
Public Event UsersListIndexChanged(index As Integer)
Public Sub New()
InitializeInstance()
Dim lstBox = formObject.GetUsersList()
AddHandler lstBox.SelectedIndexChanged, AddressOf OnUsersListIndexChanged
End Sub
Private Sub InitializeInstance()
formObject = New TForm()
AddHandler formObject.FormClosing, AddressOf OnFormClosing
End Sub
Private Sub OnFormClosing(sender As Object, e As FormClosingEventArgs)
IsInstanceSelfClosing = True
Dispose()
End Sub
Public Sub UpdateUsersList(updateMode As UpdateType, newItem As Object, Optional oldItem As Object = Nothing)
If newItem Is Nothing Then Throw New ArgumentException("New Item is null")
Dim lstBox = formObject.GetUsersList()
Select Case updateMode
Case UpdateType.AddElement
lstBox.Items.Add(newItem)
Case UpdateType.RemoveElement
lstBox.Items.Remove(newItem)
Case UpdateType.ReplaceElement
If oldItem Is Nothing Then Throw New ArgumentException("Replacement Item is null")
Dim index = lstBox.Items.IndexOf(oldItem)
lstBox.Items.Remove(oldItem)
lstBox.Items.Insert(index, newItem)
Case Else : Return
End Select
RaiseEvent UsersListUpdate(newItem, updateMode)
End Sub
Public Sub ClearUsersList()
formObject.GetUsersList().Items.Clear()
End Sub
Private Sub OnUsersListIndexChanged(sender As Object, e As EventArgs)
RaiseEvent UsersListIndexChanged(DirectCast(sender, ListBox).SelectedIndex)
End Sub
Public Sub Show(Optional owner As IWin32Window = Nothing)
If formObject Is Nothing OrElse formObject.IsDisposed Then InitializeInstance()
If formObject.Visible Then
formObject.WindowState = FormWindowState.Normal
formObject.BringToFront()
Else
formObject.Show(owner)
End If
End Sub
Public Sub Close()
If formObject IsNot Nothing AndAlso (Not formObject.IsDisposed) Then
RemoveHandler formObject.FormClosing, AddressOf OnFormClosing
IsInstanceSelfClosing = False
Dispose()
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overridable Sub Dispose(disposing As Boolean)
If disposing Then
If formObject Is Nothing OrElse formObject.IsDisposed Then Return
Dim lstBox = formObject.GetUsersList()
RemoveHandler lstBox.SelectedIndexChanged, AddressOf OnUsersListIndexChanged
RemoveHandler formObject.FormClosing, AddressOf OnFormClosing
If Not IsInstanceSelfClosing Then formObject.Close()
IsInstanceSelfClosing = False
End If
End Sub
End Class
Enumerator used in MyFormHandler:
Public Enum UpdateType
AddElement
RemoveElement
ReplaceElement
End Enum

Remove Line Under ToolStrip VB.Net

I have added a ToolStrip to a form which is going to be used to add menus and set the background colour to match the forms background colour but it always displays a horizontal line under the ToolStrip which I find distracting.
My workaround so far is to use the StatusStrip and add dropdown buttons but ideally I would have liked to have used the ToolStrip as I believe this is the preferred tool for adding menus
Having researched this, I think it has something to do with the Render Property and I have read where it's been mentioned about creating an override.
Can anyone show me an example on how to achieve this in VB.Net please.
This is simply the VB.Net version of the code provided in this previous SO question.
Obviously, the line will be there at design-time on your form, but would be gone at run-time:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ToolStrip1.Renderer = New ToolStripRenderer
End Sub
Public Class ToolStripRenderer
Inherits ToolStripProfessionalRenderer
Public Sub New()
MyBase.New()
End Sub
Protected Overrides Sub OnRenderToolStripBorder(e As ToolStripRenderEventArgs)
If Not (TypeOf e.ToolStrip Is ToolStrip) Then
MyBase.OnRenderToolStripBorder(e)
End If
End Sub
End Class
End Class
An alternative would be to create a whole new class that inherits from ToolStrip and creates the renderer for you. Then the line would be gone at design-time as well. The new control would appear at the top of your ToolBox after you compile. Unfortunately, this means you'd have to delete the old ToolStrip and drag a new one (your version) onto the form and reconfigure it:
Public Class MyToolStrip
Inherits ToolStrip
Public Sub New()
MyBase.New
Me.Renderer = New ToolStripRenderer
End Sub
Public Class ToolStripRenderer
Inherits ToolStripProfessionalRenderer
Public Sub New()
MyBase.New()
End Sub
Protected Overrides Sub OnRenderToolStripBorder(e As ToolStripRenderEventArgs)
If Not (TypeOf e.ToolStrip Is ToolStrip) Then
MyBase.OnRenderToolStripBorder(e)
End If
End Sub
End Class
End Class
Thank you for explaining how to do that. I went with the second option as this seemed more convenient for what I wanted and I presume I can save that Class and reuse it on further projects.
I still need to learn the Class and explore what and how they can be used.
Public Class MyToolStrip
Inherits ToolStrip
Public Sub New()
MyBase.New
Me.Renderer = New ToolStripRenderer
End Sub
Public Class ToolStripRenderer
Inherits ToolStripProfessionalRenderer
Public Sub New()
MyBase.New()
End Sub
Protected Overrides Sub OnRenderToolStripBorder(e As ToolStripRenderEventArgs)
If Not (TypeOf e.ToolStrip Is ToolStrip) Then
MyBase.OnRenderToolStripBorder(e)
End If
End Sub
End Class
End Class
Here is a screenshot of what I am referring to
I have made the Toolstrip the same colour as the Panel I have put it into.
Underneath the Toolstrip is a white line which I find distracting and would like to be able to remove it.

WinForms - MVP - Forms Events in Form Interface

I am trying to implement MVP in a small WinForms App I am developing.
As known, I need to define an IView interface.
The IView interface should have an event that is fired when the concrete View loads.
This event is already implented in WinForms as the Load() event.
Now in some guides/tutorials I have seen they implemented it like this
Public Interface IView
Event OnPrepareView()
.....
End Interface
Public Class MainForm
Inherits Form
Implements IView
Private Sub Main_Load(sender As Object, e As EventArgs) Handles Me.Load
RaiseEvent OnPrepareView()
End Sub
Public Event OnPrepareView()
End Class
Now I was wondering if its possible to expose directly the Form Load event directly in the interface, so that IView looks like:
Public Interface IView
Event Load()
.....
End Interface
And the Load event is implemented in the Form with it's load event.
If possible how should I do it in VB?
UPDATE
For example for a Property like Iconor Function like Show which are defined in System.Windows.Forms.Form.
I would have an IView:
Public Interface IView
Property Icon as Icon
Sub Show()
End Interface
And then in my concrete implementation
Public Class FooForm
Inherits Form
Implements IView
Public Overloads Property Icon as Icon Implementes IView.Icon
Get
return MyBase.Icon
End Get
Set
MyBase.Icon = value
End Set
Public Overloads Sub Show()
MyBase.Show()
End Sub
End Class
but how can I do for the Form events?
Or should I just declare new Event in IView like:
Public Interface IView
Property Icon as Icon
Sub Show()
Event OnLoad()
End Interface
Public Class FooForm
Inherits Form
Implements IView
....
....
Public Event OnLoad() Implements IView.OnLoad
Private Sub FooForm_Load() Handles Me.Load
RaiseEvent OnLoad()
End Sub
End Class
You should declare the event in the interface, then in the Form implement the interface, then override OnLoad and raise the event. For example:
Public Interface IView
Event Load As EventHandler
End Interface
Public Class Form1
Implements IView
Public Shadows Event Load As EventHandler Implements IView.Load
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
RaiseEvent Load(Me, e)
End Sub
End Class

How do I render the inner properties of my User Control on the page?

I am designing a user control that attempts to create a filter bar with various TextBox or DropDownList elements on the page according to the sample markup below:
<gf:GridFilterBar runat="server">
<filters>
<filter Label="Field1" Type="TextBox" />
<filter Label="Field2" Type="DropDownList" />
</filters>
</gf:GridFilterBar>
Using inspiration from another post, I have created code behind that properly parses this markup and reads in the properties of each intended child control. The issue I am having is when it comes time to actually render this information on the screen. Every control I initialize from within the "New" sub of the "Filter" class never appears on the screen. When I place a breakpoint in the "New" sub and follow what is happening, I can see the Filter.New sub being traversed twice and the values being read in, but nothing else I initialize from within that sub has any effect on the page even though, as far as I can tell, it is all being created successfully. Here is a sample of the code with just the Label property being read:
Imports System
Imports System.Collections
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Public Class GridFilterBar
Inherits System.Web.UI.UserControl
Private _Filters As New FiltersClass(Me)
<PersistenceMode(PersistenceMode.InnerProperty)> _
Public ReadOnly Property Filters() As FiltersClass
Get
Return _Filters
End Get
End Property
Private Sub Page_Init(sender As Object, e As System.EventArgs) Handles Me.Init
DDL.Visible = True
End Sub
End Class
Public Class FiltersClass
Inherits ControlCollection
Public Sub New(ByVal owner As Control)
MyBase.New(owner)
End Sub
Public Overrides Sub Add(ByVal child As System.Web.UI.Control)
MyBase.Add(New Filter(child))
End Sub
End Class
Public Class Filter
Inherits HtmlGenericControl
Public Sub New(ByVal GenericControl As HtmlGenericControl)
Label = GenericControl.Attributes("Label")
Dim lit As New Literal
lit.Text = Label.ToString
Me.Controls.Add(lit)
End Sub
Public Property Label As String = String.Empty
Public Overrides Function ToString() As String
Return Me.Label
End Function
End Class
Can anyone spot what I'm doing wrong?
I was able to answer my question. I added an override sub for CreateChildControls in my main class and used a For Each loop to grab the properties set from each newly initialized "Filter"
Protected Overrides Sub CreateChildControls()
For Each filter In Filters
Dim lit As New Literal
lit.Text = filter.Label
Controls.Add(lit)
Next filter
End Sub
This relegated the Filter.New sub to simply grabbing the properties:
Public Sub New(ByVal GenericControl As HtmlGenericControl)
Label = GenericControl.Attributes("Label")
End Sub

Shadowing an event handler

I have a two windows forms classes, a base class and a derived class. The base class has an event handler which handles ValueChanged on some component. I have also written a different event handler for the same event on the derived class.
When I create an instance of the derived class and fire the event, I find that both event handlers run (the base class one and then the derived class one). But I want only the handler in the derived class to run.
Is this possible and if so how do I do it?
(This is .NET 3.5)
Thanks!
Edit: Here is what the code looks like (can't post the actual code):
Public Class BaseForm
Inherits System.Windows.Forms.UserControl
(Windows Form Designer Generated Code)
Private WithEvents myControl As New SomeOtherControl
Protected value As String
Private Sub myControl_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles myControl.ValueChanged
value = SomeLogic()
End Sub
End Class
Public Class DerivedForm
Inherits BaseForm
Private WithEvents myControl As New SomeOtherControl
Private Sub myControl_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles myControl.ValueChanged
value = myControl.Value
End Sub
End Class
You can attach multiple handlers to one event so what you are seeing is by design (you have two handlers attached to the same event so they both fire).
That being said there are ways to accomplish what you are trying to do. One way is to move the code out of your base class handler into an overridable method, and then in the derived class override the method. So your base class handler only has 1 line of code,calling the new method you made. If you inherit from the class it will call your overridden method instead.
Public Class BaseForm
Private Sub myControl_ValueChanged() Handles myControl.ValueChanged
DoSomeLogic()
End Sub
Protected Overridable Sub DoSomeLogic()
'original logic here
End Sub
End Class
Public Class ChildForm
Inherits BaseForm
Protected Overrides Sub DoSomeLogic()
'other logic here
End Sub
End Class
I don't believe you can control that.
If it were me, I think I'd intercept the event in the base class, as you are doing, then define ANOTHER event from the base class and raise that event FROM the event handler in the base class.
on return, if the event was handled (presumably by your derived class), then just exit the base event handler, otherwise, proceed with the base event handling.