2008-10-10 3 views
22

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

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

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

ответ

19

Есть два основных подзадачи необходимо решить для того, чтобы решить эту проблему:

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

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 и событие парения будет стрелять снова, даже когда курсор находится в пределах контрольного границы.

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

+0

Спасибо за предложение ревизии, @reformed. Рецензенты отклонили его, но я внесла коррективы в язык, который вы считали запутанным. – 2014-08-06 14:09:13

6

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

<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 связывания с тем, что ваш обязательным источником, и связывающие части пути с какой-либо публичной собственностью объектов в списке, вы на самом деле хотите дисплей. Более подробная информация доступна по адресу msdn

+2

Вопрос был нацелен на WinForms, а не на WPF. – 2014-03-20 14:11:05

0

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

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

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

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

+0

Это показывает всплывающую подсказку только при открытии списка выбора. По иронии судьбы, это отлично работает для меня, и я использовал это решение, поэтому очень спасибо. – 2015-05-06 15:28:34

-1

Вот стиль, который создает группу 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> 
+1

Вопрос был нацелен на WinForms, а не на WPF. – 2014-03-20 14:11:37

+4

Когда я разместил этот вопрос, StackOverflow описывал себя как вики, организованную вокруг вопросов. На самом деле у меня есть вопрос: «Мне было бы интересно узнать, есть ли какие-либо другие элегантные решения этой проблемы или как это можно сделать в WPF.«Я не могу оценить правильность этого решения WPF, но не нужно просто понижать его, потому что его не WinForms – 2014-04-01 18:40:42

13

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

Использования onmouseover вы можете перебирать каждый элемент списка и показывать t он 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

Вы можете использовать этот простой код, который использует 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 
}; 

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