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

时间:2008-10-10 18:38:00

标签: c# .net winforms winapi listbox

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

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

我有兴趣看看是否有任何其他优雅的解决方案可以解决这个问题,或者在WPF中如何做到这一点。

7 个答案:

答案 0 :(得分:19)

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

  1. 确定正在悬停的项目
  2. 当用户悬停在一个项目上时触发MouseHover事件,然后将光标移动到列表框中并悬停在另一个项目上。
  3. 第一个问题很难解决。通过在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;
        }
    }
    

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

    第二个问题是,在光标离开控件的客户区域然后返回之前,通常不会再次触发MouseHover事件。

    您可以通过打包TrackMouseEvent Win32API来解决此问题 在以下代码中,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);
        }
    }
    

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

    我确信这种方法,将所有代码都放在MouseHover处理程序中必须导致更多的MouseHover事件触发而不是真正必要的,但它将完成工作。任何改进都非常受欢迎。

答案 1 :(得分:13)

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

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

汽车类的定义:

    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); } }
    }

表单加载事件:

    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";
    }

工具提示代码(包括创建名为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;
                }
            }
        }

答案 2 :(得分:6)

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

<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>

当然,您将使用任何绑定源替换ItemsSource绑定,并使用您实际要显示的列表中的对象的任何公共属性来绑定Path部分。 有关详细信息,请参阅msdn

答案 3 :(得分:0)

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

为列表框中的所有项目循环播放。

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

希望这有帮助。

答案 4 :(得分:0)

这是一个使用ListBox创建一组RadioButton的Style。一切都受MVVM约束。 MyClass包含两个String属性:MyName和MyToolTip。这将显示RadioButtons列表,包括正确的ToolTip-ing。这个线程感兴趣的是靠近底部的Setter for ToolTip,这使得它成为一个全Xaml解决方案。

使用示例:

ListBox Style =“{StaticResource radioListBox}”ItemsSource =“{Binding MyClass}”SelectedValue =“{Binding SelectedMyClass}”/&gt;

风格:

    <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>

答案 5 :(得分:0)

使用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);
                }
        }   
    }   
}

答案 6 :(得分:0)

您可以使用这个在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);
        }
}

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

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

享受!