Как я могу установить различный текст всплывающей подсказки для каждого элемента в списке? - PullRequest
22 голосов
/ 10 октября 2008

У меня есть список, связанный с коллекцией объектов. Список сконфигурирован для отображения свойства идентификатора каждого объекта. Я хотел бы показать всплывающую подсказку с информацией, относящейся к элементу в списке, над которым наведен курсор, а не одну подсказку для списка в целом.

Я работаю в WinForms и, благодаря некоторым полезным постам в блоге, собрал довольно хорошее решение, которым я хотел поделиться.

Мне было бы интересно узнать, есть ли другие элегантные решения этой проблемы или как это можно сделать в WPF.

Ответы [ 7 ]

19 голосов
/ 10 октября 2008

Для решения этой проблемы необходимо решить две основные подзадачи:

  1. Определите, какой предмет находится над
  2. Получите событие 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 для получения желаемого эффекта: сбрасывает базовый таймер, который контролирует, когда происходит событие hover.

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

Имея упаковщик, вы можете просто вызвать ResetMouseHover(listBox.Handle) в конце вашего обработчика MouseHover, и событие hover снова сработает, даже когда курсор останется в пределах элемента управления.

Я уверен, что этот подход, вставляющий весь код в обработчик MouseHover, должен привести к большему количеству событий MouseHover, чем на самом деле необходимо, но он выполнит свою работу. Любые улучшения приветствуются.

13 голосов
/ 27 июля 2011

Используя событие MouseMove, вы можете отслеживать индекс элемента, над которым находится мышь, и сохранять его в переменной, которая сохраняет свое значение между MouseMoves. Каждый раз, когда MouseMove запускается, он проверяет, изменился ли индекс. Если это так, он отключает всплывающую подсказку, изменяет текст всплывающей подсказки для этого элемента управления, а затем повторно активирует ее.

Ниже приведен пример, в котором одно свойство класса Car отображается в ListBox, но затем отображается полная информация при наведении курсора на любую строку. Чтобы этот пример работал, все, что вам нужно, это ListBox с именем lstCars с событием MouseMove и текстовый компонент ToolTip с именем tt1 в WinForm.

Определение класса автомобиля:

    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;
                }
            }
        }
6 голосов
/ 12 мая 2009

Я думаю, что лучшим вариантом, так как ваша база данных привязывает ваш список к объектам, будет использовать табличка с данными. Таким образом, вы можете сделать что-то вроде этого:

<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

0 голосов
/ 19 декабря 2017

Вы можете использовать этот простой код, который использует событие onMouseMove ListBox в WinForms:

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

Конечно, вам придется инициировать объект ToolTip в конструкторе или какую-либо функцию инициализации:

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

Наслаждайтесь!

0 голосов
/ 12 ноября 2014

Используя 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);
                }
        }   
    }   
}
0 голосов
/ 12 июня 2010

Вот стиль, который создает группу RadioButton с помощью ListBox. Все обязательно для MVVM-ов. MyClass содержит два свойства String: MyName и MyToolTip. Это отобразит список RadioButton, включая соответствующие подсказки. Интерес к этой теме представляет сеттер для всплывающей подсказки внизу, что делает это решение полностью Xaml.

Пример использования:

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

Стиль:

    <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>
0 голосов
/ 14 августа 2009

Используя атрибут title, мы можем установить подсказку для каждого элемента списка в поле списка.

Loop это для всех элементов в списке.

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

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...