VB.NET Custom Menu in Panel - vb.net

Hi I'm implementing a custom menu in a panel like this image (link) below..
heres the link http://i.imgur.com/5OlRk9c.png
My question is, how can I detect that the user clicks on another part of my form excepts the menu panel and buttons(inside the red circle).
I already used the LostFocus event but nothing happens.
Please help.

You can trap mouse messages before they get routed to the controls via IMessageFilter. Then you can check to see if the cursor position is inside or outside the bounds of your panel. Here's a simple example with Panel1:
Public Class Form1
Private WithEvents filter As New MyFilter
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Application.AddMessageFilter(filter)
End Sub
Private Sub filter_LeftClick() Handles filter.LeftClick
Dim rc As Rectangle = Panel1.RectangleToScreen(Panel1.ClientRectangle)
If Not rc.Contains(Cursor.Position) Then
Debug.Print("Click outside of Panel1")
End If
End Sub
Private Class MyFilter
Implements IMessageFilter
Public Event LeftClick()
Private Const WM_LBUTTONDOWN As Integer = &H201
Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
Select Case m.Msg
Case WM_LBUTTONDOWN
RaiseEvent LeftClick()
End Select
Return False
End Function
End Class
End Class

Related

How to raise click event of a panel by clicking on its controls in Vb.net

I've a custom TableLayoutPanel:
Public Class CustomTLP
Inherits TableLayoutPanel
Private labelText As Label = New Label()
Public Sub New([variousParam])
[...]
labelText.Text = "Hello Dolly!"
Me.Controls.Add(labelText, 0, 0)
End Sub
End Class
And, in another class, I create a new CustomTLP and its mouse click handler
Dim w As CustomTLP = New CustomTLP (Me, dName)
aFlowLayout.Controls.Add(w)
AddHandler w.MouseClick, AddressOf Me.ABeautifulOperation
The problem is that when i click on the CustomTLP label, the handler doesn't detect the event.
The only solution that came to my mind is to set ABeautifulOperation as public and call it from a label-click handler, but I don't think is an elegant solution... Is there a way to raise the clickevent of the panel? Something like this ( in CustomTLP):
AddHandler labelText.Click, AddressOf labelClicked
[...]
Private Sub labelClicked(sender As Object, e As EventArgs)
' Raise Me.MouseClick
End Sub
As suggested by GSerg, just call the base OnClick() method when your Label is clicked:
Private Sub labelClicked(sender As Object, e As EventArgs)
Me.OnClick(e)
End Sub
Here's a VB version of the Custom Label that will ignore mouse events, thus allowing the parent control to handle them:
Public Class CustomLabel
Inherits Label
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_NCHITTEST As Integer = &H84
Const HTTRANSPARENT As Integer = (-1)
If m.Msg = WM_NCHITTEST Then
m.Result = New IntPtr(HTTRANSPARENT)
Else
MyBase.WndProc(m)
End If
End Sub
End Class

Any ListView Event which fires on change of Item count?

Is there an event in Win Forms that can fire when the count of items in ListView change?
I got this link helpful a little but I cannot understand the answer as it is in c#.
Reffered Link
The answer in the link says about making some private function I guess..
If there is any event available than it would be more helpful than making a function or something.
Any help is greatfully accepted..
Option 1
You can put the logic of add/remove in specific methods and write the codes there; the codes that you want to run when count of items changed.
Option 2
You can inherit from ListView and override WndProc and raise a custom ItemsCountChanged event when receiving LVM_INSERTITEM, LVM_DELETEITEM and DELETEALLITEMS.
Public Class ListViewEx
Inherits ListView
Private Const LVM_FIRST As Integer = &H1000
Private Const LVM_INSERTITEM As Integer = LVM_FIRST + 77
Private Const LVM_DELETEITEM As Integer = LVM_FIRST + 8
Private Const DELETEALLITEMS As Integer = LVM_FIRST + 9
Public Event ItemsCountChanged As EventHandler
Protected Overridable Sub OnItemsCountChanged(ByVal e As EventArgs)
RaiseEvent ItemsCountChanged(Me, e)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
Select Case m.Msg
Case LVM_INSERTITEM, LVM_DELETEITEM, DELETEALLITEMS
OnItemsCountChanged(EventArgs.Empty)
End Select
End Sub
End Class
Then you can subscribe for the event and do what you need:
Private Sub listView1_ItemsCountChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles listView1.ItemsCountChanged
'Do stuff here
End Sub
Note
You can enhance the above implementation and also create ItemInserted or ItemDeleted events.
You can extract more information from mesages, for example you can find inserted or deleted item index and include in event args.
For cases that you don't want to inherit from ListView you can do the same job using a NativeWindow and passing an existing ListView to the native window and assign list view handle to the window.
I know i'm late but this is by far the best solution:
Private Sub ListView1_Invalidated(sender As Object, e As EventArgs) Handles ListView1.Invalidated
nbr.Text = ListView1.Items.Count 'nbr.text is a Label Control
End Sub

One button to control another button on many user controls

Hey and sorry for another strange question...
I have 25 UserControls with a Start_Button on each of them - this Start_Button can either be Visible or not depending on whether the UserControl is active. On my form1 I have a Start_All button.
I would like to simulate a click of all the UserControl's Start_Buttons which are visible only.
Instead of simulating click-events expose a method for the start-functionality and call this method from the Start_Button.Click-event. Then you can use this method from wherever you want. On this way your code remains readable and reusable.
You should also provide an Active property in your UserControl which you can simply link to your start-button's Visible-property.
Presuming that the user-controls are in a container-control like Panel:
Public Sub StartAll()
Dim allActiveUserControls =
From uc In controlPanel.Controls.OfType(Of MyUserControlType)()
Where uc.Active
For Each uc In allActiveUserControls
uc.Start()
Next
End Sub
Here is an example for the Active property:
Public Property Active As Boolean
Get
Return StartButton.Visible
End Get
Set(value As Boolean)
StartButton.Visible = value
End Set
End Property
and here are the Start method and the event-handlers:
Public Sub Start()
' Do Something ... '
End Sub
Private Sub StartButton_Click(sender As System.Object, e As System.EventArgs) Handles StartButton.Click
Start()
End Sub
Private Sub Start_All_Click(sender As System.Object, e As System.EventArgs) Handles Start_All.Click
StartAll()
End Sub

Form on top but not clickable when modal dialog is shown

What I want is a small notification message that is shown in the lower right corner when there are any messages to be shown. If there are none the notification message will not be shown. The notification message should not steal focus or block the main application.
What I have is an application that runs a Task as a kind of messageservice. This application contains multiple dialogs that opens as modal dialogs.
When a message arrives to the application it is added to a observable list. This fires an eventhandler in the form showing the notification message and it is redrawn to show the first item in the list.
When a message is read/closed it is removed from the list which fires the event again and the form is updated with the information from the first item in the list.
If the list is empty the form is hidden.
My problem is that if i get a message and the notification message form is shown, and before I close it a modal dialog is opened in the main application, my form with the notification message is still on top of everything, even the modal dialog, but it's not clickable.
I've searched and read several forums for an answer but haven't been able to come up with an answer.
A small testapplication that simulates this behaviour can be found at Github.
https://github.com/Oneleg/NotificationMessage
Some fast info:
The NotificationMessage form has:
FormBorderStyle = None
Topmost = False
Is shown with Show()
Overloads ShowWithoutActivation()
Overloads CreateParams with WS_EX_NOACTIVATE WS_EX_TOOLWINDOW WS_EX_TOPMOST
Any ideas on how I could solve this?
Looks like I'll be able to answer my own question.
The answer is to create the NotificationMessage as an application withs it's own messagepump.
Application.Run(New NotificationMessage(_messageList))
After some modifications my Main now looks like this:
Imports System.Threading
Imports System.Threading.Tasks
Public Class frmMain
Private _notificationMessage As NotificationMessage
Private _task As Task
Private _messageList As ObservableGenericList(Of String) = New ObservableGenericList(Of String)
Private ReadOnly _cancelMessages As CancellationTokenSource = New CancellationTokenSource()
Private Sub btnModal_Click(sender As System.Object, e As System.EventArgs) Handles btnModal.Click
frmModal.ShowDialog()
End Sub
Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
AddHandler _messageList.Changed, AddressOf MessageListChanged
End Sub
Private Sub NotificationMessageLoop(mess As String)
_notificationMessage = New NotificationMessage(_messageList)
_messageList.Add(mess)
Application.Run(_notificationMessage)
End Sub
Private Sub btnMessage_Click(sender As System.Object, e As System.EventArgs) Handles btnMessage.Click
Dim newMessage = String.Format("Message no {0}", _messageList.Count + 1)
If _task Is Nothing Then
_task = Task.Factory.StartNew(Sub() NotificationMessageLoop(newMessage), _cancelMessages.Token)
Else
_messageList.Add(newMessage)
End If
End Sub
Private Sub MessageListChanged()
If Not _messageList.Any Then
_cancelMessages.Cancel()
End If
End Sub
End Class
And the NotificationMessage looks like this:
Imports System.Runtime.InteropServices
Public Class NotificationMessage
Public Sub New(messages As ObservableGenericList(Of String))
InitializeComponent()
_messages = messages
AddHandler _messages.Changed, AddressOf ListChanged
End Sub
Private ReadOnly _messages As ObservableGenericList(Of String)
Private Delegate Sub ListChangedDelegate()
Private Sub ListChanged()
If InvokeRequired Then
BeginInvoke(New ListChangedDelegate(AddressOf ListChanged))
Return
End If
If _messages.Any Then
Dim message As String = _messages.First
txtMessage.Text = message
lblCounter.Text = String.Format("({0} messages)", _messages.Count)
Show()
Else
Hide()
End If
End Sub
Private Sub MessageLoad(sender As System.Object, e As EventArgs) Handles MyBase.Load
Left = Screen.PrimaryScreen.WorkingArea.Width - Width
Top = Screen.PrimaryScreen.WorkingArea.Height - Height
End Sub
Private Sub btnClose_Click(sender As System.Object, e As System.EventArgs) Handles btnClose.Click
_messages.RemoveFirst()
End Sub
#Region "Overrides"
Private Const WS_EX_NOACTIVATE = &H8000000 ' Do not steal focus
Private Const WS_EX_TOOLWINDOW = &H80 ' Makes form hidden from Alt + Tab window
Private Const WS_EX_TOPMOST = &H8 ' Makes window topmost
''' <summary> Indicates whether the window will be activated when it is shown. </summary>
''' <remarks> http://msdn.microsoft.com/en-us/library/system.windows.forms.form.showwithoutactivation.aspx </remarks>
Protected Overrides ReadOnly Property ShowWithoutActivation() As Boolean
Get
Return True
End Get
End Property
''' <summary> Override for creation parameters that are set when control handle is created. </summary>
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim params As CreateParams = MyBase.CreateParams
params.ExStyle = params.ExStyle Or WS_EX_NOACTIVATE Or WS_EX_TOOLWINDOW Or WS_EX_TOPMOST
Return params
End Get
End Property
#End Region
End Class
I now have a notification message that is only visible when there are any messages to show, doesn't steal focus when a new message arrives, is always on top and is clickable even after a modal form is opened in the main application.

Cancelling the button click event in its derived class

Very recently, I was working on a custom button, in which I had to capture the event of the button, and depending on the certain conditions, either I'll block the event or pass it on the form containing my custom button.
Here is prototype of the code I was working on:
Public Class MyCustomButton
Inherits Windows.Forms.Button
Private Sub Me_Clicked(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Click
Dim i As Integer = MsgBox("Are you sure you want to perform the operation?", MsgBoxStyle.YesNoCancel, "MyApp")
If Not i = 6 Then
'Cancel the event
Else
'Continue with the event
End If
End Sub
End Class
Now, I do not have any idea how to block the event and not allow it to pass if the users opts for "No" in the given example.
Any suggestions?
You need to override the OnClick event:
Public Class MyCustomButton
Inherits Button
Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
Dim i As Integer = MsgBox("Are you sure you want to perform the operation?", MsgBoxStyle.YesNoCancel, "MyApp")
If Not i = 6 Then
'Cancel the event
Else
MyBase.OnClick(e)
End If
End Sub
End Class