简体   繁体   English

VB.NET - 自定义Listview / Listbox

[英]VB.NET - Customizing Listview/Listbox

So there are a few Answered questions but I still dont really understand what i'm meant to do. 所以有一些已回答的问题,但我仍然不明白我的意思。 Like how can I basically make my Listview very customized. 就像我怎么能基本上使我的Listview非常自定义。

What im trying to get it to look like is: 我试图让它看起来像是:

1

As you can see it's HEAVILLY customized - I made this in Photoshop if you were wondering and I dont EXACTLY need the Scrollbar customized or that Hover-Highlight effect as of right now but in the Future a way to do it would be needed. 正如你所看到的那样HEAVILLY定制 - 如果你想知道我在Photoshop中做了这个,我现在不需要自定义Scrollbar或Hover-Highlight效果,但是在未来我需要一种方法来做到这一点。

Any ideas how I can achieve this? 我有什么想法可以达到这个目的吗?

EDIT: The closest thing I can get it to is: 编辑:我能得到的最接近的是: 2 But theres a "FEW" issues - I cant get the Text alignment/position to look right as I need to put 2 spaces before the Text for it to actually look decent which "MIGHT" give issues for me in the future when trying to get actions on click. 但是有一个“FEW”问题 - 我不能让文本对齐/位置看起来正确,因为我需要在文本之前放置2个空格以使其实际上看起来不错,“MIGHT”在将来尝试获取时会给我带来问题点击动作。 Next, There's for some reason no option to using Borders/Grids which remove the ability to do that slightly darker gray border around the Boxes in the first image. 接下来,由于某种原因,没有选择使用边框/网格,这样就无法在第一张图像中删除在Boxes周围稍微变暗的灰色边框。 Any ideas guys? 有什么想法吗?

(What I used to make it like this is: http://www.vbforums.com/showthread.php?599375-ListBox-with-custom-items-(colors-images-text-alignment) ) (我曾经这样做的是: http//www.vbforums.com/showthread.php?599375-ListBox-with-custom-items-(colors-images-text-alignment)

Edit 2: Re-Edit: Ok I tought I got the Highlight Color sorted but it doesnt seem like it as im using FillRectangle but no matter how I put the Bounds (Which are correct) it seems to leave an extra like 1px border of White for about 1-2 seconds before its refreshed and gone. 编辑2:重新编辑:好吧我想我得到了突出显示颜色排序,但它似乎不像我使用FillRectangle,但无论我如何把边界(这是正确的)它似乎留下额外像1px边框的白色在刷新之前大约1-2秒消失。 Anyway to fix this? 有任何解决这个问题的方法吗?

Edit 3: The http://www.vbforums.com/showthread.php?599375-ListBox-with-custom-items-(colors-images-text-alignment) ListBox I was using where I "ALMOST" got it done wont work for me due to it just not being stable - If I edit anything after making the ColorListBox in the Design mode it will just not work and give me errors which is annoying. 编辑3: http ://www.vbforums.com/showthread.php?599375-ListBox-with-custom-items-(colors-images-text-alignment ListBox我正在使用的地方我“ALMOST”完成它不会因为它不稳定而为我工作 - 如果我在设计模式下制作ColorListBox之后编辑任何东西它将无法正常工作并给我带来令人讨厌的错误。 It also has the "SelectedItem" parameter as no longer an Object which ruins half of my code. 它还有“SelectedItem”参数,因为它不再是一个破坏我代码一半的Object。 Other then that if those 2 are fixable I guess it would work but I have no idea how to fix it :( 除此之外,如果那些2是可修复的,我想它会起作用,但我不知道如何解决它:(

So I reverted to a VERY basic ListBox for now with just text until you guys can help find a way to customize it like the first image above. 所以我现在只用文本恢复到非常基本的ListBox,直到你们可以帮助找到一种方法来定制它,就像上面的第一张图片一样。

It is not at all clear what you want exactly. 完全不清楚你想要什么。 Like how can I basically make my Listview very customized is vague and very broad (and why you didn't get an answer you liked). Like how can I basically make my Listview very customized是模糊和非常广泛的(为什么你没有得到你喜欢的答案)。

A picture is nice, but words describing the desired appearance would have been better. 画面很好,但描述所需外观的单词会更好。 Given a picture to scrutinize, there is no way to know what is Important and what is just there...because. 鉴于要仔细检查的图片,没有办法知道什么是重要的,什么是正确的......因为。 I assume every detail is important based on very customized . 我假设每个细节都非常重要,基于very customized Additionally: 另外:

  • Listview/Listbox Pick one: they are very different controls. Listview/Listbox选择一个:它们是非常不同的控件。
  • The little 2 pixel margin between items would be very problematic for a ListView , so I scratched the LV 1 . 项目之间的小2像素边距对于ListView来说是非常有问题的,所以我抓了LV 1
  • Why are all the items gray in the first image? 为什么第一张图片中的所有项目都是灰色的? That typically indicates disabled, so can items be disabled (such as when there is no program data for a given channel)? 这通常表示已禁用,因此可以禁用项目(例如,当给定通道没有程序数据时)?
  • What does the gray box on "FOX" represent? “FOX”上的灰色框代表什么? Is it the selected item? 是选中的项目吗? Is it the HotLight/MouseHover item? 它是HotLight / MouseHover项目吗? ListBox / ListView items do not normally light up when the mouse passes over (the LV can when HotTracking is true, but thats only when the mouse is over the Item not subitem(s)). 当鼠标经过时, ListBox / ListView项目通常不亮(当HotTracking为真时,LV可以HotTracking ,但只有当鼠标在Item而不是子项目时HotTracking )。
  • Is the darker gray for that box rectangle an absolute color? 该框矩形的深灰色是绝对颜色吗? The HotLight and Selected coloring is usually handled by the OS (==Operating System) respecting the theme and color choices of the user. HotLightSelected着色通常由操作系统(==操作系统)处理,尊重用户的主题和颜色选择。 Is this gadget supposed to ignore those, or is your theme using some shade of gray for Highlight ? 这个小工具应该忽略这些,或者你的主题使用一些灰色的高光?

I measured the elements in the first image carefully to get some metrics, then made guesses on the answers to the above. 我仔细测量了第一张图片中的元素以获得一些指标,然后猜测了上面的答案。

No Code Solution 无代码解决方案

Use a Button. 使用按钮。 Since the user will presumably click one of these to select a desired channel, a Button makes more sense than a ListView (much more). 由于用户可能会点击其中一个来选择所需的频道,因此ButtonListView更有意义(更多)。 You can display an image on a Button along with text, and use the FlatAppearance properties to style it how you want. 您可以在Button上显示图像以及文本,并使用FlatAppearance属性根据需要设置样式。 Use the Tag to track the channel ID or the index of the related channel in the collection. 使用Tag可以跟踪集合中的通道ID或相关通道的索引。

Finally, use the MouseHover and MouseLeave events to manipulate FlatAppearance.BorderSize and FlatAppearance.BorderColor to get very close to the first image in the question: 最后,使用MouseHoverMouseLeave事件操作FlatAppearance.BorderSizeFlatAppearance.BorderColor以非常接近问题中的第一个图像:

在此输入图像描述

They reside in an scrolling Panel . 它们位于滚动Panel The panel width is just a bit wider than the control to avoid the horizontal scrollbar. 面板宽度比控件宽一点,以避免水平滚动条。 As for the buttons, the HotLight border (???) is around the entire control rather than just the text. 至于按钮, HotLight边框(???)围绕整个控件而不仅仅是文本。 Not what your picture shows you want, but on the other hand, there is nothing involved beyond some standard properties and a little event handling code (5-6 lines). 不是你想要的图片,但另一方面,除了一些标准属性和一些小事件处理代码(5-6行)之外没有任何其他内容。


Image ListBox 图像列表框

An ownerdraw ListBox will get you a bit closer to what you want, but ultimately this just makes it look like a ListBox that has Buttons in it. ownerdraw ListBox会让你更接近你想要的东西,但最终这只会使它看起来像一个包含ButtonsListBox

Place a ListBox on the form , and set these properties: 在表单上放置一个ListBox ,并设置这些属性:
- DrawMode = OwnerDrawFixed - DrawMode = OwnerDrawFixed
- IntegralHeight = False - IntegralHeight = False
- Itemheight = 64 (this is based on the fact that the images are 60x60 in the first image) - 物品高度= 64(这是基于第一张图像中图像为60x60的事实)
- Set the BackColor to ControlLight or {233,233,233} for that exact shade of gray as desired. - 根据需要将BackColor设置为ControlLight{233,233,233}以获得精确的灰色阴影。

As mentioned, ListBox items do not normally light up when the mouse is over them, so we need some code to track that (like a Button ): 如上所述, ListBox项目通常不会在鼠标悬停时点亮,因此我们需要一些代码来跟踪它(如Button ):

Private mouseItem As Int32 = -1
Private Sub lbChannels_MouseMove(sender As Object,
                                 e As MouseEventArgs) Handles lbChannels.MouseMove

    Dim ndx = lbChannels.IndexFromPoint(e.Location)
    ' test to avoid millions of paints
    If ndx <> mouseItem Then
        If mouseItem <> -1 Then
            ' invalidate/redraw the OLD itemrect 
            lbChannels.Invalidate(lbChannels.GetItemRectangle(mouseItem))
        End If
        mouseItem = ndx
        ' invalidate/redraw the NEW itemrect 
        lbChannels.Invalidate(lbChannels.GetItemRectangle(mouseItem))
    End If

End Sub

Private Sub lbChannels_MouseLeave(sender As Object,
                                  e As EventArgs) Handles lbChannels.MouseLeave
    If mouseItem <> -1 Then
        ' get rect for what wont be the hot item in a tick
        Dim rect = lbChannels.GetItemRectangle(mouseItem)
        'lbChannels.Invalidate()
        mouseItem = -1              ' no longer Hot
        lbChannels.Invalidate(rect)
    End If
End Sub

Updated to minimize flickering by redrawing just the items which changed. 更新以通过重新绘制更改的项目来最小化闪烁。

I did not use the MouseHover event because that fires later, resulting the small (but no where near a second) delay mentioned. 我没有使用MouseHover事件,因为它会在稍后触发,从而导致提到的小(但没有接近一秒)延迟。 MouseMove also makes it easier to get the mouse location. MouseMove还可以更轻松地获取鼠标位置。

IImageItem IImageItem

Based on comments, you want to auto-generate items based on some other data in the app. 根据评论,您希望根据应用中的其他一些数据自动生成项目。 Rather than assume what that class looks like and to make this easy to implement and applicable to any class, this will use an interface: 这将使用一个接口,而不是假设该类看起来像并使这个易于实现并适用于任何类。

Public Interface IImageItem
    Property ID As String      ' ???
    Property ItemImage As Image
    Property Text As String
End Interface

The draw code will require that interface be implemented so it can use those properties to draw your items. 绘制代码将要求实现接口,以便它可以使用这些属性来绘制您的项目。 Id is an extra one to allow a way to link which ListBox item is selected or clicked from the collection (only needed if you add items to the ListBox -- not recommended). Id是一个额外的一个允许链接从集合中选择或单击哪个ListBox项目的方法(仅在向ListBox添加项目时才需要 - 不推荐)。 It could be extended to include an Enabled property to draw those differently, if needed. 它可以扩展为包含Enabled属性,以便在需要时以不同方式绘制它们。 You'd implement this interface on your collection items class thusly: 您可以在集合项类上实现此接口:

Public Class ChannelItem
    Implements IImageItem

    Public Property ItemImage As Image Implements IImageItem.ItemImage
    Public Property Text As String Implements IImageItem.Text
    Public Property ID As String Implements IImageItem.ID
    ' + your existing properties

    Public Sub New(txt As String, img As Image, key As String)
        Text = txt
        ItemImage = img
        ID = key
    End Sub

    Public Overrides Function ToString() As String
        Return Text
    End Function
End Class

Note: ChannelItem is my demo version of whatever class you are using to track the channels. 注意: ChannelItem用于跟踪频道的任何类的演示版本。 Just type Implements IImageItem on your class, press enter and the IDE will add the properties, set them before adding items to your collection. 只需在类上键入Implements IImageItem ,按Enter键,IDE将添加属性,在将项添加到集合之前设置它们。

DrawItem Code DrawItem代码

Private Sub lbChannels_DrawItem(sender As Object,
                                e As DrawItemEventArgs) Handles lbChannels.DrawItem
    Dim lb As ListBox = lbChannels
    If e.Index < 0 Then
        TextRenderer.DrawText(e.Graphics, "", lb.Font, e.Bounds, lb.ForeColor)
        Return
    End If

    Dim iItem As IImageItem
    If TypeOf (lb.Items(e.Index)) Is IImageItem Then
        iItem = DirectCast(lb.Items(e.Index), IImageItem)
    Else
        TextRenderer.DrawText(e.Graphics, lb.Items(e.Index).ToString,
                              lb.Font, e.Bounds, lb.ForeColor)
        Return
    End If

    Dim imgRect As Rectangle = Rectangle.Empty
    Dim txtRect As Rectangle

    ' calc 
    If iItem.ItemImage IsNot Nothing Then
        imgRect = New Rectangle(e.Bounds.X + 1, e.Bounds.Y + 1,
                                 iItem.ItemImage.Width + 2, iItem.ItemImage.Height + 2)
    End If

    ' GetTextExtent
    Dim sz = TextRenderer.MeasureText("   " & iItem.Text, lb.Font)
    txtRect = New Rectangle(iItem.ItemImage.Width + 4, e.Bounds.Y + 1,
                         (e.Bounds.Width - iItem.ItemImage.Width) - 8,
                          e.Bounds.Height - 2)

    ' Draw Big Box around the text portion
    If e.Index = mouseItem Then
        Using pR As New Pen(SystemColors.ControlDark, 2), 
                brB As New SolidBrush(SystemColors.Window)
            e.Graphics.DrawRectangle(pR, txtRect)
            txtRect.Inflate(-1, -1)
            e.Graphics.FillRectangle(brB, txtRect)
        End Using
    ElseIf (e.State.HasFlag(DrawItemState.Selected)) Then
        ' ToDo: modify for whatever is desired for the selected item
        '        this is a guess/example
        Using pR As New Pen(SystemColors.Highlight, 2), 
                       brB As New SolidBrush(SystemColors.Window)
            e.Graphics.DrawRectangle(pR, txtRect)
            txtRect.Inflate(-1, -1)
            e.Graphics.FillRectangle(brB, txtRect)
        End Using
    Else
       ' could use a channel specific color for each BG
       ' just extend IImageItem
        e.DrawBackground()
    End If

    If iItem.ItemImage IsNot Nothing Then
        e.Graphics.DrawImage(iItem.ItemImage, imgRect)
    End If

    ' recalc TR for where the text really goes
    txtRect = New Rectangle(iItem.ItemImage.Width + 4, e.Bounds.Y + 1,
                         sz.Width + 2, e.Bounds.Height - 0)
    TextRenderer.DrawText(e.Graphics, iItem.Text, lb.Font, txtRect, lb.ForeColor)
End Sub

Again, it is not clear whether the darker gray box for "FOX" represents the SelectedItem , HotLight`/Hover item or even disabled. 同样,不清楚“FOX”的深灰色框是否代表SelectedItem , HotLight` / Hover项目甚至是否已禁用。 The code shows how/where to box the text for the first 2 cases. 该代码显示了前两种情况下如何/在哪里打包文本。 Modify as needed. 根据需要修改。

Usage 用法

' a collection of ChannelItem objects (which implement IImageItem)
Dim channels As New List(Of ChannelItem)

' my fake data
channels.Add(New ChannelItem("More 4", My.Resources.SO_LVImg01, "M4"))
channels.Add(New ChannelItem("Channel 4", My.Resources.SO_LVImg02, "4"))
channels.Add(New ChannelItem("RTE One", My.Resources.SO_LVImg03, "RET1"))
channels.Add(New ChannelItem("FOX", My.Resources.SO_LVImg04, "Fox"))
...

It is all pretty straightforward, just substitute your class for ChannelItem (it must implement IImageItem ). 这一切都非常简单,只需将您的类替换为ChannelItem (它必须实现IImageItem )。 Rather than copy items into the items collection, you can use that collection as the DataSource : 您可以将该集合用作DataSource ,而不是将项目复制到项目集合中:

lbChannels.DataSource = channels

SelectedValue will be a ChannelItem object boxed as System.Object , cast it back to get at all the related data such as a URL or Now Playing text. SelectedValue将是一个ChannelItemSystem.ObjectChannelItem对象,将其System.Object为获取所有相关数据,例如URL或正在播放文本。

The result: 结果:

在此输入图像描述 在此输入图像描述

In this case, the selected item has a SystemColor.Hightlight box (blue in my case), and the item the mouse is over has a gray box. 在这种情况下,所选项目有一个SystemColor.Hightlight框(在我的情况下为蓝色),鼠标所在的项目有一个灰色框。 There even appears to be the little 2px gap between each item as in the original image. 在原始图像中,每个项目之间甚至会出现小的2px间隙。 This is the result of the image height being a bit less than the ItemHeight used. 这是图像高度ItemHeight使用的结果。

Putting a border on the things would elide the need to do mouse over highlighting. 在这些东西上设置边框会使得需要将鼠标悬停在突出显示上。


UserControl 用户控件

If you want (nearly) total control over how these pseudo buttons appear, you should probably build a UserControl . 如果你想(几乎)完全控制这些伪按钮的显示方式,你应该建立一个UserControl Using a Label and PictureBox and just the normal events you could get it to do almost anything you want. 使用LabelPictureBox以及正常事件,您可以使用它来完成您想要的任何操作。 You can slap one together in about 20 mins: 你可以在大约20分钟内拍一张:

在此输入图像描述

The Channel UserControls are contained in an autoscrolling Panel . Channel UserControls包含在自动滚动Panel They are basically a custom button, but allows you a great deal of control over the layout, behavior and appearance. 它们基本上是一个自定义按钮,但允许您对布局,行为和外观进行大量控制。 Best of all, each would have its own distinct Click event. 最重要的是,每个人都有自己独特的Click事件。


1 The reason there are no gaps between items in the LV or LB is to make it easier for users to select an item. 1 LV或LB中的项目之间没有间隙的原因是使用户更容易选择项目。 A gutter or gap opens the chance that the user could click there and get no result. 沟槽或间隙打开了用户可以点击那里并且没有结果的机会。 Or depending on the implementation, your code would crash using a bad index. 或者根据实现,您的代码将使用错误的索引崩溃。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM