25

オブジェクトのコレクションにデータバインドされたリストボックスがあります。リストボックスは、各オブジェクトの識別子プロパティを表示するように構成されています。リストボックス全体の1つのツールチップではなく、ホバーされているリストボックス内のアイテムに固有の情報を含むツールチップを表示したいと思います。

私はWinForms内で作業しており、いくつかの役立つブログ投稿のおかげで、非常に優れたソリューションをまとめました。これを共有したいと思います。

この問題に対する他の洗練された解決策があるかどうか、またはこれがWPFでどのように行われるかを確認したいと思います。

4

7 に答える 7

20

この問題を解決するために解決しなければならない 2 つの主なサブ問題があります。

  1. ホバーされているアイテムを特定する
  2. ユーザーが 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;
    }
}

次に、返された値を使用して、必要に応じてツールヒントを設定します。

2 つ目の問題は、通常、カーソルがコントロールのクライアント領域を離れてから戻るまで、MouseHover イベントが再び発生しないことです。

TrackMouseEventWin32API 呼び出しをラップすることで、これを回避できます。
次のコードでは、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 イベントが発生することは間違いありませんが、それで問題は解決します。どんな改善も大歓迎です。

于 2008-10-10T18:58:20.887 に答える
15

MouseMove イベントを使用すると、マウスが置かれている項目のインデックスを追跡し、MouseMove 間で値を保持する変数にこれを格納できます。MouseMove がトリガーされるたびに、インデックスが変更されたかどうかを確認します。その場合、ツールチップを無効にし、このコントロールのツールチップ テキストを変更してから、再度アクティブにします。

次の例では、Car クラスの単一のプロパティが ListBox に表示されますが、いずれかの行にカーソルを合わせると完全な情報が表示されます。この例を機能させるために必要なのは、MouseMove イベントを持つ lstCars という ListBox と、WinForm 上の tt1 という ToolTip テキスト コンポーネントだけです。

車のクラスの定義:

    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;
                }
            }
        }
于 2011-07-27T17:14:04.147 に答える
7

リストボックスをオブジェクトにデータバインドするため、データテンプレートを使用するのが最善の選択肢だと思います。したがって、次のようなことができます。

<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で入手できます

于 2009-05-12T15:29:06.300 に答える
3

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

もちろん、コンストラクターまたはいくつかの init 関数で ToolTip オブジェクトを初期化する必要があります。

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

楽しみ!

于 2017-12-19T11:58:17.377 に答える
0

Using title attribute, we can set tool tip for each list items in a list box.

Loop this for all the items in a list box.

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

Hope this helps.

于 2009-08-14T04:00:43.170 に答える
0

ListBox を使用して RadioButton のグループを作成する Style を次に示します。すべてがMVVM-ingにバインドされています。MyClass には、MyName と MyToolTip の 2 つの String プロパティが含まれています。これにより、適切なツールチップを含む RadioButton のリストが表示されます。このスレッドで興味深いのは、下の方にある 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>
于 2010-06-12T15:13:25.410 に答える