none
Easier way for multiple hovers? RRS feed

  • Question

  • I have a program with 80 icons displayed on screen at one time.

    I added the following code to display a full-sized preview on mouse hover (and clear on mouse out):

    Private Sub picThumb01_MouseHover(sender As Object, e As EventArgs) Handles picThumb01.MouseHover
            picPreview.Image = Thumbnails(0).Image
    End Sub
    
    Private Sub picThumb01_MouseLeave(sender As Object, e As EventArgs) Handles picThumb01.MouseLeave
            picPreview.Image = imgPreviewReset.Images(0)
    End Sub


    There must be a smarter way to do this than to nearly duplicate the same code 80 times (once for each icon.)

    TIA


    Thursday, March 29, 2018 6:40 PM

Answers

  • Well it's easy to write code that will write 80 lines of code to a text file for copying and pasting into an app. That is if you can increment indexes and such in a loop.

    I suppose you wouldn't require that though.

    This works but not what you want probably.

    Option Strict On
    
    Public Class Form1
    
        Dim Images As New List(Of Image)
        Dim Pbox As PictureBox
        Dim PBoxs As New List(Of PictureBox)
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.WindowState = FormWindowState.Maximized
            Dim Filez = IO.Directory.GetFiles("C:\Users\John\Desktop\Deck Of Cards")
            Dim x As Integer = 0
            Dim y As Integer = 0
            For i = 0 To Filez.Count - 1
                Pbox = New PictureBox
                With Pbox
                    .Size = Image.FromFile(Filez(i)).Size
                    .BackgroundImage = Image.FromFile(Filez(i))
                    .BackgroundImageLayout = ImageLayout.Stretch
                    .BorderStyle = BorderStyle.FixedSingle
                    .Location = New Point(x, y)
                    .Name = "picThumb" & i.ToString
                    x += 100
                    If x = 1000 Then
                        x = 0
                        y += 100
                    End If
                End With
                PBoxs.Add(Pbox)
            Next
            For i = 0 To PBoxs.Count - 1
                Me.Controls.Add(PBoxs(i))
                AddHandler PBoxs(i).MouseHover, AddressOf PBoxs_MouseHover
                AddHandler PBoxs(i).MouseLeave, AddressOf PBoxs_MouseLeave
            Next
        End Sub
    
        Dim PBoxSize As Size
        Dim PBoxsIndex As Integer = 0
    
        Private Sub PBoxs_MouseHover(sender As Object, e As EventArgs)
            For i = 0 To PBoxs.Count - 1
                If DirectCast(sender, PictureBox).Name = PBoxs(i).Name Then
                    PBoxSize = PBoxs(i).Size
                    PBoxs(i).Size = New Size(200, 200)
                    PBoxs(i).BringToFront()
                    PBoxsIndex = i
                End If
            Next
        End Sub
    
        Private Sub PBoxs_MouseLeave(sender As Object, e As EventArgs)
            PBoxs(PBoxsIndex).Size = PBoxSize
        End Sub
    
    End Class


    La vida loca

    Friday, March 30, 2018 12:00 AM

  • Option Strict On
    
    Public Class Form1
    
        Private Sub PBoxs_MouseHover(sender As Object, e As EventArgs)
            For i = 0 To PBoxs.Count - 1
                If DirectCast(sender, PictureBox).Name = PBoxs(i).Name Then
                    PBoxSize = PBoxs(i).Size
                    PBoxs(i).Size = New Size(200, 200)
                    PBoxs(i).BringToFront()
                    PBoxsIndex = i
                End If
            Next
        End Sub
    


    Ah, I figured it out (from your code). This worked:

    Private Sub Thumbnails_MouseHover(sender As Object, e As EventArgs)
        Dim PBoxsIndex As Integer = 0
        For intCnt = 0 To Thumbnails.Count - 1
            If DirectCast(sender, PictureBox).Name = Thumbnails(intCnt).Name Then
                PBoxsIndex = intCnt
            End If
        Next
        picPreview.Image = Thumbnails(PBoxsIndex).Image
    End Sub
    

    Much better than 160 mousehover/leaves. :D Thx.
    Friday, March 30, 2018 5:37 PM

All replies

  • Hi

    The simplest way, would be to cycle through all the images, setting the same MouseHover event handler to each. Then in the handler, use the sender to decide which image has MouseHover.


    Regards Les, Livingston, Scotland

    Thursday, March 29, 2018 7:05 PM
  • Thanks.

    I'm not 100% sure how to do that, but the first step would be figuring out how to trigger a "hover" event w/o creating 80 Subs (one for every icon). After that, I can probably figure it out. :D

    TIA

    Thursday, March 29, 2018 7:35 PM
  • See if the following makes sense, should work in VS2013, will work in VS2015 and VS2017.

    Imports System.Text.RegularExpressions
    
    Public Class Form2
        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Controls.OfType(Of PictureBox).
                Where(Function(pb) pb.Name <> "picPreview").
                ToList.
                ForEach(
                Sub(pb)
                    AddHandler pb.MouseHover, AddressOf _MouseHover
                    AddHandler pb.MouseLeave, AddressOf _MouseLeave
                End Sub)
        End Sub
    
        Private Sub _MouseHover(sender As Object, e As EventArgs)
            Dim Index As Integer = CInt(Regex.Replace(CType(sender, PictureBox).Name, "[^0-9]", ""))
            '
            ' e.g. If PictureBox1 Index = 1, if PictureBox 78 Index = 78
            ' Use Index to index into your array
            '
        End Sub
        Private Sub _MouseLeave(sender As Object, e As EventArgs)
            ' do your reset
        End Sub
    End Class

    For each PictureBox the first line in the load events subscribes these picture box controls to two events. In the hover event you can get the name, extract the sole number e.g. SomePictBox11 would give us 11 etc. use this to index into the array to preview the icon.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites


    Thursday, March 29, 2018 7:47 PM
    Moderator
  • Assuming that all of the PictureBoxes are named "picThumb01" thru "picThumb80", and that they are all in the same container control, then you can use code like the following to loop through all of the picture boxes and add the handlers by parsing the name:

    Private Sub HookupThumbnailHovers(container As Control)
        Static hookSet As Boolean
        If hookSet Then Exit Sub
        For Each item In (From pictureBox In container.Controls.OfType(Of PictureBox)
                          Where pictureBox.Name.StartsWith("picThumb")
                          Let id = CInt(pictureBox.Name.Substring(8)) - 1
                          Select id, pictureBox)
            AddHandler item.pictureBox.MouseHover, Sub(sender As Object, e As EventArgs)
                                                       picPreview.Image = Thumbnails(item.id).Image
                                                   End Sub
            AddHandler item.pictureBox.MouseLeave, Sub(sender As Object, e As EventArgs)
                                                       picPreview.Image = imgPreviewReset.Images(item.id)
                                                   End Sub
        Next
        hookSet = True
    End Sub

    When you call this method you supply whatever object contains all the picture boxes.  If they are on the Form then pass the Form instance.  If they are in a panel or other control, pass that instance.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Thursday, March 29, 2018 7:50 PM
    Moderator
  • Thx for the reply. I must have implemented your code wrong. :(

    I put the "Controls.OfType" section in my existing frmLoad(), and added the "_MouseHover" sub to the end (and the Imports up top).

    My images are in an image array ("Thumbnails(0).Image" thru "Thumbnails(79).Image") and I'm not sure how to incorporate that into your code. :(

    Thursday, March 29, 2018 8:59 PM
  • Thx for the reply. This looks simpler, but I can't figure what what would trigger it?

    You say "when you call this method", but what would trigger the call (other than 80 "hover" subs?)

    TIA.

    Thursday, March 29, 2018 9:08 PM
  • First off I have zero images to try this out. The following uses a dictionary and a class.

    Imports System.Text.RegularExpressions
    
    Public Class Form2
        Private imagesDictionary As Dictionary(Of Integer, MyImage)
    
        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim items As String() = IO.Directory.GetFiles("", "*.png")
            Dim index As Integer = 1
    
            For Each file As String In items
    
                imagesDictionary.Add(index, New MyImage With
                {
                    .Index = index,
                    .Image = Image.FromFile(file)
                })
    
            Next
    
            Controls.OfType(Of PictureBox).
                Where(Function(pb) pb.Name <> "picPreview").
                ToList.
                ForEach(
                Sub(pb)
                    AddHandler pb.MouseHover, AddressOf _MouseHover
                    AddHandler pb.MouseLeave, AddressOf _MouseLeave
                End Sub)
        End Sub
    
        Private Sub _MouseHover(sender As Object, e As EventArgs)
            Dim Index As Integer = CInt(Regex.Replace(CType(sender, PictureBox).Name, "[^0-9]", ""))
            picPreview.Image = imagesDictionary(Index).Image
        End Sub
        Private Sub _MouseLeave(sender As Object, e As EventArgs)
            ' do your reset
        End Sub
    End Class
    Public Class MyImage
        Public Property Index As Integer
        Public Property Image As Image
    End Class


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Thursday, March 29, 2018 9:23 PM
    Moderator
  • There must be a smarter way to do this than to nearly duplicate the same code 80 times (once for each icon.)

    If you have 80 icons on a screen then I presume that you do not create them in the designer - they are likely created at run time. Based on the name that you used, they are likely picture box controls.  In that case, you can implement the same event handler for each one by using AddHandler at the time that it is created.

        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            For I As Integer = 0 To 7
                For J As Integer = 0 To 9
                    Dim thisPB As New PictureBox
                    thisPB.Size = New Size(20, 30)
                    thisPB.Location = New Size(I * 20 + 5, J * 30 + 5)
                    thisPB.Image = ...
                    thisPB.BackgroundImage = ...
                    AddHandler thisPB.MouseHover, AddressOf MouseHover)
                    AddHandler thisPB.MouseLeave, AddressOf MouseLeave)
                    Me.Controls.Add(thisPB)
                Next
            Next
        End Sub
    
        Private Sub MouseHover(sender As PictureBox, e As EventArgs)
            picPreview.Image = sender.Image
        End Sub
    
        Private Sub MouseLeave(sender As PictureBox, e As EventArgs)
            picPreview.Image = sender.BackgroundImage
        End Sub

    Thursday, March 29, 2018 9:28 PM
  • If you have 80 icons on a screen then I presume that you do not create them in the designer - they are likely created at run time.

    Thx for the reply.

    By default, every picbox ("picThumb01" - "picThumb80") is assigned a default "not" image before loading icons from a location selected by the user into those boxes.

    Your code, as-is, is displaying them directly on the form (but the mouseover is working.)

    I need to make a few changes but this seems to work. Thanks.

    (If successful, I'll make this as the answer.)

    Thursday, March 29, 2018 11:17 PM
  • Assuming that all of the PictureBoxes are named "picThumb01" thru "picThumb80", and that they are all in the same container control, then you can use code like the following to loop through all of the picture boxes and add the handlers by parsing the name:

    Thx. I'll give this a try a bit later.

    Thursday, March 29, 2018 11:18 PM
  • Hi,

    I recommend to use DataGridView for showing 80 icons.
    If you use it, you can handle MouseHover on the DataGridView and get Row and Column index, then show an image in that cell on a PictureBox. 

    This will make code smaller and easy to read.
     
    Regards,

    Ashidacchi -- http://hokusosha.com/

    Thursday, March 29, 2018 11:46 PM
  • Well it's easy to write code that will write 80 lines of code to a text file for copying and pasting into an app. That is if you can increment indexes and such in a loop.

    I suppose you wouldn't require that though.

    This works but not what you want probably.

    Option Strict On
    
    Public Class Form1
    
        Dim Images As New List(Of Image)
        Dim Pbox As PictureBox
        Dim PBoxs As New List(Of PictureBox)
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Me.WindowState = FormWindowState.Maximized
            Dim Filez = IO.Directory.GetFiles("C:\Users\John\Desktop\Deck Of Cards")
            Dim x As Integer = 0
            Dim y As Integer = 0
            For i = 0 To Filez.Count - 1
                Pbox = New PictureBox
                With Pbox
                    .Size = Image.FromFile(Filez(i)).Size
                    .BackgroundImage = Image.FromFile(Filez(i))
                    .BackgroundImageLayout = ImageLayout.Stretch
                    .BorderStyle = BorderStyle.FixedSingle
                    .Location = New Point(x, y)
                    .Name = "picThumb" & i.ToString
                    x += 100
                    If x = 1000 Then
                        x = 0
                        y += 100
                    End If
                End With
                PBoxs.Add(Pbox)
            Next
            For i = 0 To PBoxs.Count - 1
                Me.Controls.Add(PBoxs(i))
                AddHandler PBoxs(i).MouseHover, AddressOf PBoxs_MouseHover
                AddHandler PBoxs(i).MouseLeave, AddressOf PBoxs_MouseLeave
            Next
        End Sub
    
        Dim PBoxSize As Size
        Dim PBoxsIndex As Integer = 0
    
        Private Sub PBoxs_MouseHover(sender As Object, e As EventArgs)
            For i = 0 To PBoxs.Count - 1
                If DirectCast(sender, PictureBox).Name = PBoxs(i).Name Then
                    PBoxSize = PBoxs(i).Size
                    PBoxs(i).Size = New Size(200, 200)
                    PBoxs(i).BringToFront()
                    PBoxsIndex = i
                End If
            Next
        End Sub
    
        Private Sub PBoxs_MouseLeave(sender As Object, e As EventArgs)
            PBoxs(PBoxsIndex).Size = PBoxSize
        End Sub
    
    End Class


    La vida loca

    Friday, March 30, 2018 12:00 AM
  • I recommend to use DataGridView for showing 80 icons.

    Thx. That's a very tempting idea. But at this late stage, I'd have to do a total rewrite of my app.

    Oh well. ;)

    Friday, March 30, 2018 1:54 AM
  • Thx for the reply. This looks simpler, but I can't figure what what would trigger it?

    You say "when you call this method", but what would trigger the call (other than 80 "hover" subs?)

    TIA.


    You call it once on Form.Load.

    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Friday, March 30, 2018 11:53 AM
    Moderator
  • This works but not what you want probably.

    Thanks. After initially simply doing a copy/paste of 160 subs (80 mousehovers, 80 mouseleaves), I thought "there MUST be a more elegant solution. :D

    It seems the best advice I'm getting is an "AddHandler" for MouseHover & MouseLeave.

    Thx.

    Friday, March 30, 2018 3:31 PM
  • This works but not what you want probably.

    Thanks. After initially simply doing a copy/paste of 160 subs (80 mousehovers, 80 mouseleaves), I thought "there MUST be a more elegant solution. :D

    It seems the best advice I'm getting is an "AddHandler" for MouseHover & MouseLeave.

    Thx.


    IMHO the best advice is dont do that.

    What could it be you are making? A typewriter?

    I would say rule of thumb if you are using more than 10 controls for something you should rethink and make sure.

    Then you could always draw your own keyboard and only have one control.

    Friday, March 30, 2018 4:27 PM

  •         For i = 0 To PBoxs.Count - 1
                Me.Controls.Add(PBoxs(i))
                AddHandler PBoxs(i).MouseHover, AddressOf PBoxs_MouseHover
                AddHandler PBoxs(i).MouseLeave, AddressOf PBoxs_MouseLeave
            Next
        End Sub
    
        Dim PBoxSize As Size
        Dim PBoxsIndex As Integer = 0
    
        Private Sub PBoxs_MouseHover(sender As Object, e As EventArgs)
    

    I'm using a variation of your code (a bit overkill for my simple needs) but I need help identifying which icon I'm hovering over.

    My revised code (I only needed a blanket "hover" trigger):

    ' Towards the end of my frmMain_Load()...
    For intCnt = 0 To Thumbnails.Count - 1
        Me.Controls.Add(Thumbnails(intCnt))
        AddHandler Thumbnails(intCnt).MouseHover, AddressOf Thumbnails_MouseHover
        AddHandler Thumbnails(intCnt).MouseLeave, AddressOf Thumbnails_MouseLeave
    Next
    
    ' Added subs:
    
    Private Sub Thumbnails_MouseHover()
        picPreview.Image = Thumbnails(...).Image ' Need value to know which number icon I'm over.
    End Sub
    
    Private Sub Thumbnails_MouseLeave()
        picPreview.Image = imgPreviewReset.Images(0) ' Only 1 image in list.
    End Sub

    This triggers a hover action over all 80 icons, all going to the same sub (Good). But once I arrive, I can't determine which number icon my pointer is over (Bad.)

    TIA.

    Friday, March 30, 2018 5:19 PM

  • Option Strict On
    
    Public Class Form1
    
        Private Sub PBoxs_MouseHover(sender As Object, e As EventArgs)
            For i = 0 To PBoxs.Count - 1
                If DirectCast(sender, PictureBox).Name = PBoxs(i).Name Then
                    PBoxSize = PBoxs(i).Size
                    PBoxs(i).Size = New Size(200, 200)
                    PBoxs(i).BringToFront()
                    PBoxsIndex = i
                End If
            Next
        End Sub
    


    Ah, I figured it out (from your code). This worked:

    Private Sub Thumbnails_MouseHover(sender As Object, e As EventArgs)
        Dim PBoxsIndex As Integer = 0
        For intCnt = 0 To Thumbnails.Count - 1
            If DirectCast(sender, PictureBox).Name = Thumbnails(intCnt).Name Then
                PBoxsIndex = intCnt
            End If
        Next
        picPreview.Image = Thumbnails(PBoxsIndex).Image
    End Sub
    

    Much better than 160 mousehover/leaves. :D Thx.
    Friday, March 30, 2018 5:37 PM
  • Hi

    Her (as well as all the other code examples shown) shows the extraction of an 'ID' for any of the PictureBoxes.in this example. In this case, the 'ID' is the Name property as set in the Load event handler - it could just as easily be any other property (usually name or TAG properties are most useful for this)

    Anyway, just some more code to try to assist. This illustrates one Hover handler for all/any amount of controls.

    ' Form1 with only a ToolTip1 added to it.
    Option Strict On
    Option Explicit On
    Public Class Form1
      Dim thumbnails As New List(Of PictureBox)
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim x As Integer = 10
        Dim y As Integer = 10
        For i As Integer = 0 To 11
          Dim pb As New PictureBox
          With pb
            .Name = "picThumb" & (i + 1).ToString
            .Size = New Size(80, 80)
            .BorderStyle = BorderStyle.FixedSingle
            .BackColor = Color.LightGoldenrodYellow
            .Location = New Point(x, y)
            x += pb.Width + 5
            If x > ClientSize.Width - .Width Then
              y += ClientSize.Height + 5
              x = 10
            End If
            AddHandler .MouseHover, AddressOf MyMouseHover
            thumbnails.Add(pb)
            Controls.Add(pb)
          End With
        Next
      End Sub
      Private Sub MyMouseHover(sender As Object, e As EventArgs)
        ' find which PB has mouse hover
        Dim pb As PictureBox = DirectCast(sender, PictureBox)
        ' show it's name. Could just as easily be any othe
        'property - such as TAG property.
        ToolTip1.Show("PictureBox " & pb.Name & " has current Hover", pb)
      End Sub
    End Class


    Regards Les, Livingston, Scotland

    Friday, March 30, 2018 6:04 PM
  • What could it be you are making? A typewriter?

    I wrote an Icon Manager companion utility (for another program) a while back that I'm constantly updating based on user requests/suggestions.

    The form, displaying the maximum 80 available icons, was designed to fit on a 720p (1366x768) monitor. But some users with 4K monitors were complaining the icons are too small to see.

    So I added an "enlarged preview on MouseOver" feature so they're easier to see.

    Friday, March 30, 2018 6:40 PM

  • Option Strict On
    
    Public Class Form1
    
        Private Sub PBoxs_MouseHover(sender As Object, e As EventArgs)
            For i = 0 To PBoxs.Count - 1
                If DirectCast(sender, PictureBox).Name = PBoxs(i).Name Then
                    PBoxSize = PBoxs(i).Size
                    PBoxs(i).Size = New Size(200, 200)
                    PBoxs(i).BringToFront()
                    PBoxsIndex = i
                End If
            Next
        End Sub
    


    Ah, I figured it out (from your code). This worked:

    Private Sub Thumbnails_MouseHover(sender As Object, e As EventArgs)
        Dim PBoxsIndex As Integer = 0
        For intCnt = 0 To Thumbnails.Count - 1
            If DirectCast(sender, PictureBox).Name = Thumbnails(intCnt).Name Then
                PBoxsIndex = intCnt
            End If
        Next
        picPreview.Image = Thumbnails(PBoxsIndex).Image
    End Sub

    Much better than 160 mousehover/leaves. :D Thx.
    I forgot but you could use an Exit For in the If statement to stop the For/Next even though it seems to run PDQ.

    La vida loca

    Friday, March 30, 2018 7:28 PM
  • I forgot but you could use an Exit For in the If statement to stop the For/Next even though it seems to run PDQ

    Yep, I added that after I posted the reply. Thx.

    Friday, March 30, 2018 11:00 PM