Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何为列表框中的每个项目设置不同的工具提示文本?_C#_.net_Winforms_Winapi_Listbox - Fatal编程技术网

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

C# 如何为列表框中的每个项目设置不同的工具提示文本?,c#,.net,winforms,winapi,listbox,C#,.net,Winforms,Winapi,Listbox,我有一个列表框,它被数据绑定到一组对象。列表框配置为显示每个对象的标识符属性。我想显示一个工具提示,其中包含特定于悬停在上面的列表框中的项目的信息,而不是整个列表框的一个工具提示 我在WinForms中工作,多亏了一些有用的博客文章,我找到了一个非常好的解决方案,我想与大家分享 我很想看看这个问题是否还有其他优雅的解决方案,或者在WPF中如何解决这个问题。要解决这个问题,必须解决两个主要的子问题: 确定悬停在哪个项目上 当用户将鼠标悬停在一个项目上,然后在列表框中移动光标并将光标悬停在另一个项目

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

我在WinForms中工作,多亏了一些有用的博客文章,我找到了一个非常好的解决方案,我想与大家分享


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

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

  • 确定悬停在哪个项目上
  • 当用户将鼠标悬停在一个项目上,然后在列表框中移动光标并将光标悬停在另一个项目上时,获取要触发的MouseHover事件
  • 第一个问题很容易解决。通过在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事件,但它会完成任务。任何改进都是非常受欢迎的。

    我认为最好的选择是使用 数据模板。所以你可以这样做:

    <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绑定,并使用列表中实际要显示的对象的任何公共属性替换绑定路径部分。
    更多详细信息,请访问使用title属性,我们可以为列表框中的每个列表项设置工具提示

    ListItem li = new ListItem("text","key");
    li.Attributes.Add("title","tool tip text");
    
    对列表框中的所有项目循环此操作

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

    希望这能有所帮助。

    这里有一个样式,它使用列表框创建一组单选按钮。所有这些都是为MVVM绑定的。MyClass包含两个字符串属性:MyName和MyToolTip。这将显示单选按钮列表,包括正确的工具提示。该线程感兴趣的是底部附近工具提示的设置器,使其成为一个全Xaml解决方案

    用法示例:

    ListBox Style=“{StaticResource radioListBox}”ItemsSource=“{Binding MyClass}”SelectedValue=“{Binding SelectedCyclass}”/>

    风格:

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

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

    下面是一个示例,其中汽车类的单个属性显示在列表框中,但当鼠标悬停在任意一行上时,将显示完整信息。要使此示例正常工作,您只需要一个名为lstCars的列表框,其中包含一个MouseMove事件和WinForm上名为tt1的工具提示文本组件

    汽车类别的定义:

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

    使用MouseOver上的
    onmouseover
    可以遍历列表中的每个项目,并显示
    工具提示

    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);
                    }
            }   
        }   
    }
    
    onmouseover=“dotooltiprod(事件,”);
    函数dotooltiprod(e,tipObj)
    {
    Tooltip.init();
    如果(工具提示的类型==“未定义”| |!Tooltip.ready){
    返回;
    }
    mCounter=1;
    
    对于(m=1;m,您可以使用以下简单代码在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);
            }
    }
    

    享受!

    该问题针对的是WinForms,而不是WPF。该问题针对的是WinForms,而不是WPF。当我发布此问题时,StackOverflow将自己描述为围绕问题组织的wiki。我的问题中实际上有“我很想看看是否有任何其他优雅的解决方案来解决这个问题,或者如何在WPF中做到这一点。"“我无法评估此WPF解决方案的正确性,但不需要因为它不是WinFormsThank的修订建议而进行否决,@reformed。审阅者拒绝了它,但我对您认为令人困惑的语言进行了调整。这只在打开“选择列表框”时显示工具提示。讽刺的是,这对我和我来说都很好。”我已经使用了此解决方案,非常感谢。请问“.InsuranceGroup”是什么意思在你的代码中?我在互联网上找不到任何关于这方面的信息,我的Visual studio也不知道这个词。保险集团只是我想展示的汽车类别的一个属性。在英国,保险集团是保险成本是便宜还是贵的一个指标。保险的汽车越便宜,数量越少,保险费用越高,数字就越大。我已将上面的解释扩展为一个完整的示例。它对我来说效果非常好,但代码中可能缺少“ToolTip tt1=new ToolTip();”作为类字段?我不得不添加它
    _toolTip = new ToolTip
    {
                AutoPopDelay = 5000,
                InitialDelay = 1000,
                ReshowDelay = 500,
                ShowAlways = true
    };