Mimic Disabled Focus Behaviour in WinForms - vb.net

In accordance to this Post I'm trying to mimic the behavior of
Enabled = False
without actually disable the Control. (In my case a multiline TextBox)
The next I'm trying to accomplish is to mimic the focus behavior by mouse of a disabled control. If I click on a disabled control it won't get the focus and the control that previously had focus won't loose the focus.
What I came up with so far: I can intercept the WM_SETFOCUS message in WndProc so my control won't recieve focus.
Private Const WM_SETFOCUS = &H7
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If Me.ReadOnly AndAlso (m.Msg = WM_SETFOCUS) Then Exit Sub
MyBase.WndProc(m)
End Sub
The problem with that is, that the previous contol lost the focus, which isn't intended. How do I prevent that even the click by mouse will do anything in the focus behaviour? Is there any way to do this?
Update: 06.08.12
As suggested by Justin I solved the problem by changing it to a label in an autoscroll panel. A minimal code example is as followed:
Imports System.Windows.Forms
Public Class ScrollableDisabledTextBox
Inherits TextBox
Private xLabel As Label
Private xPanel As Panel
Public Sub New()
InizializeComponent()
End Sub
Private Sub InizializeComponent()
xPanel = New Panel
xPanel.AutoScroll = True
xPanel.BorderStyle = BorderStyle.FixedSingle
xLabel = New Label
xLabel.Enabled = False
xLabel.AutoSize = True
xPanel.Controls.Add(xLabel)
Me.Me_SizeChanged()
End Sub
Private Sub Me_EnabledChanged() Handles Me.EnabledChanged
If Me.Enabled Then
Me.Show()
xPanel.Hide()
Else
xPanel.Show()
Me.SendToBack()
Me.Hide()
End If
End Sub
Private Sub Me_TextChanged() Handles Me.TextChanged
xLabel.Text = Me.Text
End Sub
Private Sub Me_SizeChanged() Handles Me.SizeChanged
xPanel.Size = Me.Size
xLabel.MaximumSize = New System.Drawing.Size(xPanel.Size.Width, 0)
End Sub
Private Sub Me_ParentChanged() Handles Me.ParentChanged
xPanel.Location = Me.Location
'If parent changed multiple times, remember to remove panel from old parent!
If Not Me.Parent.Controls.Contains(xPanel) Then
Me.Parent.Controls.Add(xPanel)
End If
End Sub
End Class

I do not believe what you want to do is possible. If you do not have focus, then the scrolling will not work.
However, I posit that you should rethink your original problem. Why not use an AutoSize = true, MaximumSize.Width = ParentWidth label (which could be disabled) inside of a panel that will autoscroll. This sounds like what you are really looking for.

Related

Combobox custom control with a lid

I have created a custom combobox with a label to cover the combobox (as it's very ugly) when it's not in use. The label, witch is the lid should show the display member. The covering and uncovering forks fine however the text on the label displayed is the previous value and not the current one. Passing over the label with the mouse, triggers the labels mouse enter event and than the display value is correct.
Here is the code for the custom control.
Public Class ComboBoxWithALid
Inherits ComboBox
Private WithEvents Lid As New Label With {.BackColor = Color.LightCyan, .ForeColor = Color.Black,
.TextAlign = ContentAlignment.MiddleCenter}
Protected Overrides Sub OnDataSourceChanged(ByVal e As EventArgs)
MyBase.OnDataSourceChanged(e)
Lid.Location = Location
Lid.Size = Size
Parent.Controls.Add(Lid)
Lid.BringToFront()
End Sub
Private Sub Lid_MouseEnter(sender As Object, e As EventArgs) Handles Lid.MouseEnter
Lid.SendToBack()
End Sub
Protected Overrides Sub OnMouseLeave(e As EventArgs)
MyBase.OnMouseLeave(e)se
Lid.Text = SelectedText
Lid.BringToFront()
End Sub
Protected Overrides Sub OnDropDownClosed(e As EventArgs)
MyBase.OnDropDownClosed(e)
Lid.BringToFront()
Lid.Text = SelectedText
End Sub
End Class
To test the control, drag the control from the tool box to your form and bind the control to any table you have
I tried to use text in place of selected text - same results
I found the solution. Change the move statement to:
Lid.Text = Items(SelectedIndex)(DisplayMember)
and is works.
SelectedText is for the highlighted portion of the text that is currently selected, not the selected item. You probably want something like this:
If SelectedIndex = -1 Then
Lid.Text = String.Empty
Else
Lid.Text = Items(SelectedIndex).ToString()
End If
Yes, you have an annoying control.

How can I apply a MouseHover and MouseLeave to two controls to act as one control?

I have two labels in my form which are placed side-by-side to act as one label. When I hover over the labels, I have a function that fades the labels to different colours, which works well. I am trying to apply the MouseHover and MouseLeave event to both labels, so that when I hover over Label1 and move to Label2 (and vice versa), the function doesn't then fade the colour back to the original colour. Currently, moving between the two labels activates MouseLeave followed by MouseHover again in the new label.
I have tried to add both labels to the event trigger, but this hasn't worked. I have also tried placing both labels in a Panel, but that then doesn't trigger the event.
Private Sub fadeHeaderIn(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Label1.MouseHover, Label2.MouseHover
Call fadeLabel("In")
End Sub
Private Sub fadeHeaderOut(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Label1.MouseLeave, Label2.MouseLeave
Call fadeLabel("Out")
End Sub
If the functionality to change the colour of part of the label existed, I'd not need the two labels, so if there is a better way of doing this altogether, I'm happy to do so. Thanks!
I have also tried placing both labels in a Panel, but that then
doesn't trigger the event.
That should work. The panel would act as the boundaries for both labels. You'll get a MouseLeave, though, when you move from the panel to the labels contained within. To prevent a false trigger, simply check if the mouse is still within the bounds of the panel. You can prevent multiple fade ins when moving from label to label by tracking the faded state with a boolean. It'd look something like this:
Public Faded As Boolean = False
Private Sub fadeHeaderIn(sender As Object, e As EventArgs) Handles Label1.MouseHover, Label2.MouseHover
If Not Faded Then
Faded = True
fadeLabel("In")
End If
End Sub
Private Sub fadeHeaderOut(sender As Object, e As EventArgs) Handles Panel1.MouseLeave
If Not Panel1.ClientRectangle.Contains(Panel1.PointToClient(Cursor.Position)) Then
If Faded Then
Faded = False
fadeLabel("Out")
End If
End If
End Sub
Here's a sample of what has been described in comments.
The Text of a control (here, derived from a standard Label) is split in two sections of the same measure. Each section can have a different color.
The active and inactive colors are custom public properties, they can be set in the designer.
Each section is tracked, meaning that the control is aware of what side the Mouse Pointer is currently hovering.
The size of the text is measured using the TextRenderer.MeasureText method. This size is used to calculate the rectangles that include the sections of text.
The Rectangle.Contains([Point]) method is then used to determine which section of the text the mouse pointer in hovering. [Point] is calculated using the MousePosition property, translated to client coordinates using the Control.PointToClient() method.
When the mouse pointer is moved from one section of text to the other (here, just two sections, more could be defined adding more rectangles), the control is Invalidated, causing a call to the OnPaint method of the control.
If the mouse pointer is not hovering a section of text, base.OnPaint(e) is called (also causing the Paint event to raise), which draws the default text with the default color.
In the OnPaint method, the Graphics region is clipped using the Rectangles that define the text sections. A subsequent call to TextRenderer.DrawText, setting the TextFormatFlags.PreserveGraphicsClipping flag, clips the text in the defined region, so just the section of text that fits in the clipping region is painted.
The Graphics.ExcludeClip() method is used here to define these clipping regions.
The TextFormatFlags.ExternalLeading and TextFormatFlags.TextBoxControl are also used to replicate the default text rendering, so the custom text is rendered in the same relative position.
This is how it behaves:
Custom Control Class to test the functionality:
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
<DesignerCategory("Code")>
Public Class LabelSplitText
Inherits Label
Private m_Text As String = String.Empty
Private m_Sections As RectangleF() = Nothing
Private m_PaintText As Boolean = False
ReadOnly flags As TextFormatFlags = TextFormatFlags.ExternalLeading Or
TextFormatFlags.PreserveGraphicsClipping Or
TextFormatFlags.TextBoxControl
Public Sub New()
InitializeComponent()
End Sub
Private Sub InitializeComponent()
ResizeRedraw = True
End Sub
Public ReadOnly Property ActiveRectangle As RectangleF
Public ReadOnly Property ActiveSide As String = String.Empty
Public Property ActiveColor As Color = Color.White
Public Property InactiveColor As Color = Color.DimGray
Protected Overrides Sub OnLayout(e As LayoutEventArgs)
MyBase.OnLayout(e)
Me.AutoSize = False
m_Text = Me.Text
End Sub
Protected Overrides Sub OnMouseEnter(e As EventArgs)
m_Text = Me.Text
Text = String.Empty
m_PaintText = True
MyBase.OnMouseEnter(e)
Invalidate()
End Sub
Protected Overrides Sub OnMouseLeave(e As EventArgs)
m_PaintText = False
Me.Text = m_Text
MyBase.OnMouseLeave(e)
End Sub
Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
MyBase.OnMouseMove(e)
Invalidate()
If m_Sections Is Nothing Then Return
Me._ActiveRectangle = If(m_Sections(0).Contains(e.Location), m_Sections(0), m_Sections(1))
End Sub
Protected Overrides Sub OnMouseClick(e As MouseEventArgs)
Me._ActiveSide = If(m_Sections(0).Contains(e.Location), "left", "right")
MyBase.OnMouseClick(e)
End Sub
Protected Overrides Sub OnPaint(e As PaintEventArgs)
If Not m_PaintText Then
MyBase.OnPaint(e)
Return
End If
Dim textSize As SizeF = TextRenderer.MeasureText(e.Graphics, m_Text, Me.Font, Me.ClientSize, flags)
m_Sections = GetTextAreaSections(textSize)
e.Graphics.ExcludeClip(Rectangle.Round(m_Sections(1)))
TextRenderer.DrawText(e.Graphics, m_Text, Me.Font, Point.Empty, GetSectionColor(0), flags)
e.Graphics.ResetClip()
e.Graphics.ExcludeClip(Rectangle.Round(m_Sections(0)))
TextRenderer.DrawText(e.Graphics, m_Text, Me.Font, Point.Empty, GetSectionColor(1), flags)
End Sub
Private Function GetSectionColor(section As Integer) As Color
Return If(m_Sections(section).Contains(PointToClient(MousePosition)),
Me.ActiveColor, Me.InactiveColor)
End Function
Private Function GetTextAreaSections(textSize As SizeF) As RectangleF()
If textSize.Width > Me.ClientSize.Width Then textSize.Width = Me.ClientSize.Width
Dim rectLeft = New RectangleF(PointF.Empty,
New SizeF(textSize.Width / 2.0F, Me.ClientSize.Height))
Dim rectRight = New RectangleF(New PointF(textSize.Width / 2.0F, 0),
New SizeF(textSize.Width / 2.0F, Me.ClientSize.Height))
Return {rectLeft, rectRight}
End Function
End Class

How to reset the close reason when close is cancelled

Question
Is it possible to reset the CloseReason provided by the FormClosingEventArgs in the FormClosing event of a modal dialog?
Symptoms
Setting the DialogResult of a modal dialog can result in an "incorrect" CloseReason if the close event have previously been cancelled.
Details
(The following code is just sample code to highlight the inconvenience)
Imagine I have a form with two buttons, OK and Cancel, displayed as a modal dialog.
Me.btnOk = New Button With {.DialogResult = Windows.Forms.DialogResult.OK}
Me.btnCancel = New Button With {.DialogResult = Windows.Forms.DialogResult.Cancel}
Me.AcceptButton = Me.btnOk
Me.CancelButton = Me.btnCancel
Any attempts to close the form will be cancelled.
If I click each button (including the [X] - close form button) in the following order, the close reasons will be as following:
Case 1
btnOk::::::::::: None
btnCancel::: None
X::::::::::::::::::: UserClosing
Now, if I repeat the steps you'll see that the UserClosing reason will persist:
btnOk::::::::::: UserClosing
btnCancel::: UserClosing
X::::::::::::::::::: UserClosing
Case 2
X::::::::::::::::::: UserClosing
btnCancel::: UserClosing
btnOk::::::::::: UserClosing
Same here. Once you click the X button the close reason will always return UserClosing.
Sample application
Public Class Form1
Public Sub New()
Me.InitializeComponent()
Me.Text = "Test"
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog
Me.MinimizeBox = False
Me.MaximizeBox = False
Me.ClientSize = New Size(75, 25)
Me.StartPosition = FormStartPosition.CenterScreen
Me.btnOpenDialog = New Button() With {.TabIndex = 0, .Dock = DockStyle.Fill, .Text = "Open dialog"}
Me.Controls.Add(Me.btnOpenDialog)
End Sub
Private Sub HandleOpenDialog(sender As Object, e As EventArgs) Handles btnOpenDialog.Click
Using instance As New CustomDialog()
instance.ShowDialog()
End Using
End Sub
Private WithEvents btnOpenDialog As Button
Private Class CustomDialog
Inherits Form
Public Sub New()
Me.Text = "Custom dialog"
Me.ClientSize = New Size(400, 200)
Me.StartPosition = FormStartPosition.CenterParent
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog
Me.MinimizeBox = False
Me.MaximizeBox = False
Me.tbOutput = New RichTextBox() With {.TabIndex = 0, .Bounds = New Rectangle(0, 0, 400, 155), .ReadOnly = True, .ScrollBars = RichTextBoxScrollBars.ForcedBoth, .WordWrap = True}
Me.btnExit = New Button With {.TabIndex = 3, .Text = "Exit", .Bounds = New Rectangle(10, 165, 75, 25), .Anchor = (AnchorStyles.Bottom Or AnchorStyles.Left)}
Me.btnOk = New Button With {.TabIndex = 1, .Text = "OK", .Bounds = New Rectangle(237, 165, 75, 25), .Anchor = (AnchorStyles.Bottom Or AnchorStyles.Right), .DialogResult = Windows.Forms.DialogResult.OK}
Me.btnCancel = New Button With {.TabIndex = 2, .Text = "Cancel", .Bounds = New Rectangle(315, 165, 75, 25), .Anchor = (AnchorStyles.Bottom Or AnchorStyles.Right), .DialogResult = Windows.Forms.DialogResult.Cancel}
Me.Controls.AddRange({Me.tbOutput, Me.btnExit, Me.btnOk, Me.btnCancel})
Me.AcceptButton = Me.btnOk
Me.CancelButton = Me.btnCancel
End Sub
Private Sub HandleExitDialog(sender As Object, e As EventArgs) Handles btnExit.Click
Me.exitPending = True
Me.Close()
End Sub
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
If (Not Me.exitPending) Then
e.Cancel = True
Me.tbOutput.Text += (String.Format("DialogResult={0}, CloseReason={1}{2}", Me.DialogResult.ToString(), e.CloseReason.ToString(), Environment.NewLine))
Me.DialogResult = Windows.Forms.DialogResult.None
End If
MyBase.OnFormClosing(e)
End Sub
Private exitPending As Boolean
Private WithEvents btnExit As Button
Private WithEvents btnCancel As Button
Private WithEvents btnOk As Button
Private WithEvents tbOutput As RichTextBox
End Class
End Class
Update
I was of the impression that if either the Form.AcceptButton or Form.CancelButton (IButtonControl) was clicked the close reason would be set to UserClosing, but this is not the case. In the following code you'll see that all it do is setting the DialogResult of the owning form to that of its own DialogResult.
Protected Overrides Sub OnClick(ByVal e As EventArgs)
Dim form As Form = MyBase.FindFormInternal
If (Not form Is Nothing) Then
form.DialogResult = Me.DialogResult
End If
MyBase.AccessibilityNotifyClients(AccessibleEvents.StateChange, -1)
MyBase.AccessibilityNotifyClients(AccessibleEvents.NameChange, -1)
MyBase.OnClick(e)
End Sub
The Control class do have a property named CloseReason but it's defined as Friend, thus not accessible.
I also thought that setting the forms DialogResult would result in a WM message being sent, but all it does is setting a private field.
So I delved into reflector and followed the stack. The following image is a highly simplified illustration.
This is how the CheckCloseDialog method looks like:
Friend Function CheckCloseDialog(ByVal closingOnly As Boolean) As Boolean
If ((Me.dialogResult = DialogResult.None) AndAlso MyBase.Visible) Then
Return False
End If
Try
Dim e As New FormClosingEventArgs(Me.closeReason, False)
If Not Me.CalledClosing Then
Me.OnClosing(e)
Me.OnFormClosing(e)
If e.Cancel Then
Me.dialogResult = DialogResult.None
Else
Me.CalledClosing = True
End If
End If
If (Not closingOnly AndAlso (Me.dialogResult <> DialogResult.None)) Then
Dim args2 As New FormClosedEventArgs(Me.closeReason)
Me.OnClosed(args2)
Me.OnFormClosed(args2)
Me.CalledClosing = False
End If
Catch exception As Exception
Me.dialogResult = DialogResult.None
If NativeWindow.WndProcShouldBeDebuggable Then
Throw
End If
Application.OnThreadException(exception)
End Try
If (Me.dialogResult = DialogResult.None) Then
Return Not MyBase.Visible
End If
Return True
End Function
As you can see the modal message loop checks the DialogResult in every cycle and if the conditions are met it will use the stored CloseReason (as observed) when creating the FormClosingEventArgs.
Summary
Yes, I know that the IButtonControl interface have a PerformClick method which you can call programmatically, but still, IMO this smells like a bug. If clicking a button is not a result of a user action then what is?
It is pretty important to understand why this is behaving the way it does, you are liable to get yourself into trouble when you rely in the CloseReason too much. This is not a bug, it is a restriction due to the way Windows was designed. One core issue is the way the WM_CLOSE message is formulated, it is the one that sets the train in motion, first firing the FormClosing event.
This message can be sent for lots of reasons, you are familiar with the common ones. But that's not where it ends, other programs can send that message as well. You can tell the "flaw" from the MSDN Library article I linked to, the message is missing a WPARAM value that encodes the intent of the message. So there isn't any way for a program to provide a reasonable CloseReason back to you. Winforms is forced to guess at a reason. It is of course an entirely imperfect guess.
That's not where it ends, the DialogResult property is a problem as well. It will force a dialog to close when any code assigns that property. But again the same problem, there isn't any way for such code to indicate the intent of the assignment. So it doesn't, it leaves in internal Form.CloseReason property at whatever value it had before, None by default.
This was "properly" implemented in .NET 1.0, there was only the Closing event and it didn't give a reason at all. But that didn't work out so well either, apps that used it chronically prevented Windows from shutting down. They just didn't know that it was inappropriate to, say, display a message box. The .NET 2.0 FormClosing event was added as a workaround for that. But it needs to work with the imperfect guess.
It is important to rate the CloseReason values, some are very accurate and some are just guesses:
CloseReason.WindowsShutdown - reliable
CloseReason.ApplicationExitCall - reliable
CloseReason.MdiFormClosing - reliable, not very useful
CloseReason.FormOwnerClosing - reliable, not very useful
CloseReason.TaskManagerClosing - complete guess, will be returned when any program sends a WM_CLOSE message, not just Task Manager
CloseReason.UserClosing - complete guess, will also be returned when your program calls the Close() method for example
CloseReason.None - it just doesn't know.
Yes, Winforms not setting the CloseReason back to None when your FormClosing event handler cancels is arguably a bug. But it isn't the kind of bug that actually really matters. Since you can't treat UserClosing and None differently anyway.
I would probably call that a bug.
As you mentioned, the CloseReason property is marked internal (or Friend in VB.Net terms) so one work-around to the problem is using Reflection to reset that value yourself:
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
If Not exitPending Then
e.Cancel = True
tbOutput.AppendText(String.Format("DialogResult={0}, CloseReason={1}{2}", _
Me.DialogResult.ToString(), e.CloseReason.ToString(), _
Environment.NewLine))
Dim pi As PropertyInfo
pi = Me.GetType.GetProperty("CloseReason", _
BindingFlags.Instance Or BindingFlags.NonPublic)
pi.SetValue(Me, CloseReason.None, Nothing)
End If
MyBase.OnFormClosing(e)
End Sub
No guarantee that this code would work on future versions of WinForms, but I'm guessing it's a safe bet these days. :-)
Private Const WM_SYSCOMMAND As Int32 = &H112
Private Const SC_CLOSE As Int32 = &HF060
'Private Const SC_MAXIMIZE As Int32 = &HF030
'Private Const SC_MINIMIZE As Int32 = &HF020
'Private Const SC_RESTORE As Int32 = &HF120
Private _commandClose As Boolean = False
Protected Overrides Sub WndProc(ByRef m As Message)
If CInt(m.Msg) = WM_SYSCOMMAND Then
If (m.WParam.ToInt32 And &HFFF0) = SC_CLOSE Then _commandClose = True
End If
MyBase.WndProc(m)
End Sub
Private Sub baseClick(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Click
Close()
End Sub
Protected Overrides Sub OnFormClosing(ByVal e As FormClosingEventArgs)
If _commandClose Then DialogResult = ' ...
MyBase.OnFormClosing(e)
End Sub
reference: MSDN - WM_SYSCOMMAND message
hmm, actually, this does work. but unlike the official docs, SC_CLOSE fires for Alt+F4, etc... as well, even though not specifically mentioned.
It does not fire when calling the Form.Close() method. therefore, working as intended.
however, it will still return UserClosing if you call the Close() method, which is by design.
note: SC_SCREENSAVE can be used to detect/prevent screensavers, along with SC_MONITORPOWER. the documentation on that seems a bit vague.

VB.Net TreeView NodeMouseClick middle mouse button

I have been using the NodeMouseClick event to handle left and right clicks on my TreeNodes for a while. Now I want to add an effect to middle clicking as well, but the NodeMouseClick event doesn't seem to fire on a middle click. Is this a known bug, or should it work fine and I'm just doing something wrong? If it is a bug (or just intended to function this way), how can I make a middle click on a TreeNode do something specific with that node?
Here's a bit of my code:
Private Sub TreeView1_NodeMouseClick(sender As Object, e As System.Windows.Forms.TreeNodeMouseClickEventArgs) Handles TreeView1.NodeMouseClick
If e.Button = Windows.Forms.MouseButtons.Left Then
Call nodeLeft(e.Node)
ElseIf e.Button = Windows.Forms.MouseButtons.Middle Then
Call nodeMiddle(e.Node)
ElseIf e.Button = Windows.Forms.MouseButtons.Right Then
Call nodeRight(e.Node)
End If
End Sub
You can try this version:
Public Class MyTreeView
Inherits TreeView
Private Const WM_MBUTTONDOWN As Integer = &H207
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = WM_MBUTTONDOWN Then
Dim p As Point = Me.PointToClient(MousePosition)
Dim mouseNode As TreeNode = Me.GetNodeAt(p)
If mouseNode IsNot Nothing Then
Me.OnNodeMouseClick(New TreeNodeMouseClickEventArgs(mouseNode, MouseButtons.Middle, 1, p.X, p.Y))
End If
End If
End Sub
End Class
It will fire the NodeMouseClick event with the middle value set for the Button property. It won't select the node though. To do that, add the line Me.SelectedNode = mouseNode above the OnNodeMouseClick call.

vb.net how do I make an application that only has a notifyicon and no windows form?

I want to make an application that only has a notifyicon and doesn't have any visible window form when it starts up. I see some example sort of like what I want to do for c#, but I don't see how to do this in vb.net project.
A form is not strictly necessary. You can instantiate a NotifyIcon and use that without creating a form:
Public Class AppContext
Inherits ApplicationContext
Private notifyIcon As NotifyIcon
Private appActive As Boolean
Public Sub New()
AddHandler Application.ApplicationExit, AddressOf OnApplicationExit
notifyIcon = New NotifyIcon()
notifyIcon.Icon = My.Resources.ActiveIcon
notifyIcon.Text = "The app is active."
AddHandler notifyIcon.MouseClick, AddressOf OnIconMouseClick
appActive = True
notifyIcon.Visible = True
End Sub
Private Sub OnApplicationExit(ByVal sender As Object, ByVal e As EventArgs)
If notifyIcon IsNot Nothing Then
notifyIcon.Dispose()
End If
End Sub
Private Sub OnIconMouseClick(ByVal sender As Object, ByVal e As MouseEventArgs)
If e.Button = MouseButtons.Left Then
appActive = Not appActive
notifyIcon.Icon = If(appActive, My.Resources.ActiveIcon, My.Resources.InactiveIcon)
notifyIcon.Text = If(appActive, "The app is active.", "The app is not active.")
Else
If MsgBox("Do you want to Exit?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
notifyIcon.Visible = False
ExitThread()
End If
End If
End Sub
End Class
And then start your app from a Sub Main:
Public Module EntryPoint
Public Sub Main()
Dim ctx As New AppContext()
Application.Run(ctx)
End Sub
End Module
Just put the form transparent and resize it to 1x1..
And add a notifyicon..
And on the Form Load Event do this:
NotifyIcon.Visible = True
Then make what ever you want..
You can create a context menu strip (A Menu when you right click on it )
PS: If you do it, You need to go on the NotifyIcon properties and set the Context Menu Strip to that you created..
Hope it helped you..