C# 如何为列表框中的每个项目设置不同的工具提示文本?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/192584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:28:32  来源:igfitidea点击:

How can I set different Tooltip text for each item in a listbox?

c#.netwinformswinapilistbox

提问by Michael Lang

I have a listbox that is databound to a Collection of objects. The listbox is configured to display an identifier property of each object. I would like to show a tooltip with information specific to the item within the listbox that is being hovered over rather than one tooltip for the listbox as a whole.

我有一个数据绑定到对象集合的列表框。列表框被配置为显示每个对象的标识符属性。我想显示一个工具提示,其中包含悬停在​​列表框中的项目的特定信息,而不是整个列表框的一个工具提示。

I am working within WinForms and thanks to some helpful blog posts put together a pretty nice solution, which I wanted to share.

我在 WinForms 中工作,感谢一些有用的博客文章,我想分享一个非常好的解决方案。

I'd be interested in seeing if there's any other elegant solutions to this problem, or how this may be done in WPF.

我很想知道这个问题是否有其他优雅的解决方案,或者如何在 WPF 中完成。

采纳答案by Michael Lang

There are two main sub-problems one must solve in order to solve this problem:

为了解决这个问题,必须解决两个主要的子问题:

  1. Determine which item is being hovered over
  2. Get the MouseHover event to fire when the user has hovered over one item, then moved the cursor within the listbox and hovered over another item.
  1. 确定悬停在哪个项目上
  2. 当用户将鼠标悬停在一个项目上时触发 MouseHover 事件,然后在列表框中移动光标并将鼠标悬停在另一个项目上。

The first problem is rather simple to solve. By calling a method like the following within your handler for MouseHover, you can determine which item is being hovered over:

第一个问题很容易解决。通过在 MouseHover 的处理程序中调用如下所示的方法,您可以确定鼠标悬停在哪个项目上:

private ITypeOfObjectsBoundToListBox DetermineHoveredItem()
{
    Point screenPosition = ListBox.MousePosition;
    Point listBoxClientAreaPosition = listBox.PointToClient(screenPosition);

    int hoveredIndex = listBox.IndexFromPoint(listBoxClientAreaPosition);
    if (hoveredIndex != -1)
    {
        return listBox.Items[hoveredIndex] as ITypeOfObjectsBoundToListBox;
    }
    else
    {
        return null;
    }
}

Then use the returned value to set the tool-tip as needed.

然后根据需要使用返回值设置工具提示。

The second problem is that normally the MouseHover event isn't fired again until the cursor has left the client area of the control and then come back.

第二个问题是,通常直到光标离开控件的客户区然后又回来时,才会再次触发 MouseHover 事件。

You can get around this by wrapping the TrackMouseEventWin32API call.
In the following code, the ResetMouseHovermethod wraps the API call to get the desired effect: reset the underlying timer that controls when the hover event is fired.

您可以通过包装TrackMouseEventWin32API 调用来解决此问题。
在以下代码中,该ResetMouseHover方法包装了 API 调用以获得所需的效果:重置控制悬停事件触发时间的底层计时器。

public static class MouseInput
{
    // TME_HOVER
    // The caller wants hover notification. Notification is delivered as a 
    // WM_MOUSEHOVER message.  If the caller requests hover tracking while 
    // hover tracking is already active, the hover timer will be reset.

    private const int TME_HOVER = 0x1;

    private struct TRACKMOUSEEVENT
    {
        // Size of the structure - calculated in the constructor
        public int cbSize;

        // value that we'll set to specify we want to start over Mouse Hover and get
        // notification when the hover has happened
        public int dwFlags;

        // Handle to what's interested in the event
        public IntPtr hwndTrack;

        // How long it takes for a hover to occur
        public int dwHoverTime;

        // Setting things up specifically for a simple reset
        public TRACKMOUSEEVENT(IntPtr hWnd)
        {
            this.cbSize = Marshal.SizeOf(typeof(TRACKMOUSEEVENT));
            this.hwndTrack = hWnd;
            this.dwHoverTime = SystemInformation.MouseHoverTime;
            this.dwFlags = TME_HOVER;
        }
    }

    // Declaration of the Win32API function
    [DllImport("user32")]
    private static extern bool TrackMouseEvent(ref TRACKMOUSEEVENT lpEventTrack);

    public static void ResetMouseHover(IntPtr windowTrackingMouseHandle)
    {
        // Set up the parameter collection for the API call so that the appropriate
        // control fires the event
        TRACKMOUSEEVENT parameterBag = new TRACKMOUSEEVENT(windowTrackingMouseHandle);

        // The actual API call
        TrackMouseEvent(ref parameterBag);
    }
}

With the wrapper in place, you can simply call ResetMouseHover(listBox.Handle)at the end of your MouseHover handler and the hover event will fire again even when the cursor stays within the control's bounds.

包装器就位后,您可以简单地ResetMouseHover(listBox.Handle)在 MouseHover 处理程序的末尾调用,即使光标停留在控件的边界内,悬停事件也会再次触发。

I'm sure this approach, sticking all the code in the MouseHover handler must result in more MouseHover events firing than are really necessary, but it'll get the job done. Any improvements are more than welcome.

我确信这种方法,将所有代码粘贴在 MouseHover 处理程序中一定会导致比实际需要更多的 MouseHover 事件触发,但它会完成工作。任何改进都非常受欢迎。

回答by Michael Lang

I think the best option, since your databinding your listbox to objects, would be to use a datatemplate. So you could do something like this:

我认为最好的选择是使用数据模板,因为您将列表框数据绑定到对象。所以你可以做这样的事情:

<ListBox Width="400" Margin="10" 
         ItemsSource="{Binding Source={StaticResource myTodoList}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=TaskName}" 
                       ToolTipService.ToolTip="{Binding Path=TaskName}"/>
        </DataTemplate>
     </ListBox.ItemTemplate>
</ListBox>

Of course you'd replace the ItemsSource binding with whatever your binding source is, and the binding Path parts with whatever public property of the objects in the list you actually want to display. More details available on msdn

当然,您可以将 ItemsSource 绑定替换为您的绑定源,并将绑定 Path 部分替换为您实际想要显示的列表中对象的任何公共属性。可在msdn 上获得更多详细信息

回答by Michael Lang

Using title attribute, we can set tool tip for each list items in a list box.

使用 title 属性,我们可以为列表框中的每个列表项设置工具提示。

Loop this for all the items in a list box.

对列表框中的所有项目循环此操作。

ListItem li = new ListItem("text","key");
li.Attributes.Add("title","tool tip text");

Hope this helps.

希望这可以帮助。

回答by BSalita

Here is a Style that creates a group of RadioButtons by using a ListBox. All is bound for MVVM-ing. MyClass contains two String properties: MyName and MyToolTip. This will display the list of RadioButtons including proper ToolTip-ing. Of interest to this thread is the Setter for ToolTip near bottom making this an all Xaml solution.

这是一个使用 ListBox 创建一组 RadioButtons 的 Style。一切都是为了 MVVM-ing。MyClass 包含两个 String 属性:MyName 和 MyToolTip。这将显示 RadioButtons 列表,包括正确的 ToolTip-ing。该线程感兴趣的是靠近底部的工具提示设置器,使其成为全 Xaml 解决方案。

Example usage:

用法示例:

ListBox Style="{StaticResource radioListBox}" ItemsSource="{Binding MyClass}" SelectedValue="{Binding SelectedMyClass}"/>

ListBox Style="{StaticResource radioListBox}" ItemsSource="{Binding MyClass}" SelectedValue="{Binding SelectedMyClass}"/>

Style:

风格:

    <Style x:Key="radioListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Margin" Value="5" />
    <Setter Property="Background" Value="{x:Null}" />
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListBoxItem">
                            <Grid Background="Transparent">
                                <RadioButton Focusable="False" IsHitTestVisible="False" IsChecked="{TemplateBinding IsSelected}" Content="{Binding MyName}"/>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="ToolTip" Value="{Binding MyToolTip}" />
            </Style>
        </Setter.Value>
    </Setter>
</Style>

回答by Michael

Using the MouseMove event, you can keep track of the index of the item that the mouse is over and store this in a variable that keeps its value between MouseMoves. Every time MouseMove is triggered, it checks to see if the index has changed. If so, it disables the tooltip, changes the tooltip text for this control, then re-activates it.

使用 MouseMove 事件,您可以跟踪鼠标悬停的项目的索引,并将其存储在一个变量中,该变量在 MouseMove 之间保持其值。每次触发 MouseMove 时,它​​都会检查索引是否已更改。如果是这样,它会禁用工具提示,更改此控件的工具提示文本,然后重新激活它。

Below is an example where a single property of a Car class is shown in a ListBox, but then full information is shown when hovering over any one row. To make this example work, all you need is a ListBox called lstCars with a MouseMove event and a ToolTip text component called tt1 on your WinForm.

下面是一个示例,其中 Car 类的单个属性显示在 ListBox 中,但是当鼠标悬停在任何一行上时会显示完整信息。为了使这个示例工作,您只需要一个名为 lstCars 的 ListBox,它带有一个 MouseMove 事件和一个在 WinForm 上名为 tt1 的 ToolTip 文本组件。

Definition of the car class:

汽车类定义:

    class Car
    {
        // Main properties:
        public string Model { get; set; }
        public string Make { get; set; }
        public int InsuranceGroup { get; set; }
        public string OwnerName { get; set; }
        // Read only property combining all the other informaiton:
        public string Info { get { return string.Format("{0} {1}\nOwner: {2}\nInsurance group: {3}", Make, Model, OwnerName, InsuranceGroup); } }
    }

Form load event:

表单加载事件:

    private void Form1_Load(object sender, System.EventArgs e)
    {
        // Set up a list of cars:
        List<Car> allCars = new List<Car>();
        allCars.Add(new Car { Make = "Toyota", Model = "Yaris", InsuranceGroup = 6, OwnerName = "Joe Bloggs" });
        allCars.Add(new Car { Make = "Mercedes", Model = "AMG", InsuranceGroup = 50, OwnerName = "Mr Rich" });
        allCars.Add(new Car { Make = "Ford", Model = "Escort", InsuranceGroup = 10, OwnerName = "Fred Normal" });

        // Attach the list of cars to the ListBox:
        lstCars.DataSource = allCars;
        lstCars.DisplayMember = "Model";
    }

The tooltip code (including creating the class level variable called hoveredIndex):

工具提示代码(包括创建名为 hoveredIndex 的类级别变量):

        // Class variable to keep track of which row is currently selected:
        int hoveredIndex = -1;

        private void lstCars_MouseMove(object sender, MouseEventArgs e)
        {
            // See which row is currently under the mouse:
            int newHoveredIndex = lstCars.IndexFromPoint(e.Location);

            // If the row has changed since last moving the mouse:
            if (hoveredIndex != newHoveredIndex)
            {
                // Change the variable for the next time we move the mouse:
                hoveredIndex = newHoveredIndex;

                // If over a row showing data (rather than blank space):
                if (hoveredIndex > -1)
                {
                    //Set tooltip text for the row now under the mouse:
                    tt1.Active = false;
                    tt1.SetToolTip(lstCars, ((Car)lstCars.Items[hoveredIndex]).Info);
                    tt1.Active = true;
                }
            }
        }

回答by Satheesh ponugoti

Using onmouseoveryou can iterate through each item of the list and can show the ToolTip

使用onmouseover您可以遍历列表的每个项目,并可以显示ToolTip

onmouseover="doTooltipProd(event,'');

function doTooltipProd(e,tipObj)
{

    Tooltip.init();
      if ( typeof Tooltip == "undefined" || !Tooltip.ready ) {
      return;
      }
      mCounter = 1;
   for (m=1;m<=document.getElementById('lobProductId').length;m++) {

    var mCurrent = document.getElementById('lobProductId').options[m];
        if(mCurrent != null && mCurrent != "null") {
            if (mCurrent.selected) {
                mText = mCurrent.text;
                Tooltip.show(e, mText);
                }
        }   
    }   
}

回答by Shalom

You can use this simple code that uses the onMouseMove event of ListBox in WinForms:

您可以使用这个简单的代码,它在 WinForms 中使用 ListBox 的 onMouseMove 事件:

private void ListBoxOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
        var listbox = sender as ListBox;
        if (listbox == null) return;

        // set tool tip for listbox
        var strTip = string.Empty;
        var index = listbox.IndexFromPoint(mouseEventArgs.Location);

        if ((index >= 0) && (index < listbox.Items.Count))
            strTip = listbox.Items[index].ToString();

        if (_toolTip.GetToolTip(listbox) != strTip)
        {
            _toolTip.SetToolTip(listbox, strTip);
        }
}

Of course you will have to init the ToolTip object in the constructor or some init function:

当然,您必须在构造函数或某些 init 函数中初始化 ToolTip 对象:

_toolTip = new ToolTip
{
            AutoPopDelay = 5000,
            InitialDelay = 1000,
            ReshowDelay = 500,
            ShowAlways = true
};

Enjoy!

享受!