Подтвердить что ты не робот

Как я могу установить разные тексты подсказок для каждого элемента в списке?

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

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

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

4b9b3361

Ответ 1

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

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

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 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, чем это действительно необходимо, но он выполнит эту работу. Любые улучшения более чем приветствуются.

Ответ 2

Используя событие 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 timw 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;
                }
            }
        }

Ответ 3

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

<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

Ответ 4

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

Завершите это для всех элементов в списке.

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

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

Ответ 5

Вот стиль, который создает группу RadioButtons с помощью ListBox. Все связано с MVVM-ing. MyClass содержит два свойства String: MyName и MyToolTip. Это отобразит список RadioButtons, включая правильный всплывающий подсказку. Интерес к этой теме представляет собой Setter для ToolTip рядом с нижним, что делает это всем решением 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>

Ответ 6

Используя 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);
                }
        }   
    }   
}

Ответ 7

Вы можете использовать этот простой код, который использует событие 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 в конструкторе или некоторой функции init:

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

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