it-swarm.com.de

Wie kann ich für jedes Element in einer Listbox einen anderen QuickInfo-Text festlegen?

Ich habe eine Listbox, die an eine Sammlung von Objekten gebunden ist. Das Listenfeld ist so konfiguriert, dass eine Kennungseigenschaft für jedes Objekt angezeigt wird. Ich möchte einen Tooltip mit Informationen zu dem Element in der Listbox anzeigen, über das der Mauszeiger bewegt wird, anstatt einen Tooltip für die gesamte Listbox.

Ich arbeite in WinForms und habe dank einiger hilfreicher Blog-Beiträge eine ziemlich nette Lösung zusammengestellt, die ich teilen wollte.

Ich würde gerne sehen, ob es andere elegante Lösungen für dieses Problem gibt oder wie dies in WPF gemacht werden kann.

22
Michael Lang

Es gibt zwei Haupt-Unterprobleme, die man lösen muss, um dieses Problem zu lösen:

  1. Bestimmen Sie, über welches Element der Mauszeiger bewegt wird
  2. Das MouseHover-Ereignis wird ausgelöst, wenn der Benutzer den Mauszeiger über ein Element bewegt und dann den Cursor innerhalb des Listenfelds über ein anderes Element bewegt hat.

Das erste Problem ist ziemlich einfach zu lösen. Durch Aufrufen einer Methode wie der folgenden in Ihrem Handler für MouseHover können Sie bestimmen, über welches Element der Mauszeiger bewegt wird:

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

Verwenden Sie dann den zurückgegebenen Wert, um den Tooltip nach Bedarf festzulegen.

Das zweite Problem besteht darin, dass das MouseHover-Ereignis normalerweise erst dann erneut ausgelöst wird, wenn der Cursor den Clientbereich des Steuerelements verlassen hat und dann zurückkehrt.

Sie können dies umgehen, indem Sie den Win32API-Aufruf TrackMouseEvent einschließen.
Im folgenden Code umschließt die Methode ResetMouseHover den API-Aufruf, um den gewünschten Effekt zu erzielen: Setzen Sie den zugrunde liegenden Zeitgeber zurück, der steuert, wann das Hover-Ereignis ausgelöst wird.

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

Mit dem Wrapper können Sie einfach ResetMouseHover(listBox.Handle) am Ende Ihres MouseHover-Handlers aufrufen, und das Hover-Ereignis wird erneut ausgelöst, auch wenn der Cursor innerhalb der Grenzen des Steuerelements bleibt.

Ich bin mir sicher, dass dieser Ansatz, bei dem der gesamte Code im MouseHover-Handler festgehalten wird, dazu führen muss, dass mehr MouseHover-Ereignisse ausgelöst werden, als wirklich notwendig sind, aber der Job wird erledigt. Verbesserungen sind jederzeit willkommen.

19
Michael Lang

Mit dem MouseMove-Ereignis können Sie den Index des Elements verfolgen, über dem sich die Maus befindet, und diesen in einer Variablen speichern, die ihren Wert zwischen MouseMoves beibehält. Bei jedem Auslösen von MouseMove wird überprüft, ob sich der Index geändert hat. In diesem Fall wird die QuickInfo deaktiviert, der QuickInfo-Text für dieses Steuerelement geändert und dann erneut aktiviert.

Im Folgenden finden Sie ein Beispiel, in dem eine einzelne Eigenschaft einer Car-Klasse in einer ListBox angezeigt wird, die vollständigen Informationen jedoch angezeigt werden, wenn Sie den Mauszeiger über eine Zeile bewegen. Damit dieses Beispiel funktioniert, benötigen Sie lediglich eine ListBox namens lstCars mit einem MouseMove-Ereignis und einer QuickInfo-Textkomponente namens tt1 in Ihrem WinForm-Dokument.

Definition der Fahrzeugklasse:

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

Formular Ladeereignis:

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

Der Tooltip-Code (einschließlich der Erstellung der Klassenvariablen 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;
                }
            }
        }
13
Michael

Ich denke, die beste Option, da Ihre Daten Ihre Listbox an Objekte binden, wäre die Verwendung einer Datenvorlage. Sie könnten also so etwas tun:

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

Natürlich würden Sie die ItemsSource-Bindung durch eine beliebige Bindungsquelle ersetzen und den Bindungspfad durch eine beliebige öffentliche Eigenschaft der Objekte in der Liste, die Sie tatsächlich anzeigen möchten. Weitere Details finden Sie auf msdn

6
Alex

Mithilfe des title-Attributs können wir für jedes Listenelement in einem Listenfeld einen Tooltip festlegen.

Führen Sie eine Schleife für alle Elemente in einem Listenfeld aus.

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

Hoffe das hilft.

0
Bhaskar

Mit onmouseover können Sie jedes Element der Liste durchlaufen und die ToolTip anzeigen.

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

Hier ist ein Style, der mithilfe einer ListBox eine Gruppe von RadioButtons erstellt. Alles ist für MVVM-ing gebunden. MyClass enthält zwei String-Eigenschaften: MyName und MyToolTip. Dadurch wird die Liste der RadioButtons einschließlich der richtigen QuickInfo angezeigt. Von Interesse für diesen Thread ist der Setter for ToolTip in der Nähe der Unterseite, der dies zu einer All-Xaml-Lösung macht.

Beispielverwendung:

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

Stil:

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

Sie können diesen einfachen Code verwenden, der das Ereignis onMouseMove von ListBox in WinForms verwendet:

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

Natürlich müssen Sie das ToolTip-Objekt im Konstruktor oder in einer Init-Funktion initialisieren:

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

Genießen!

0
Shalom