オブジェクトのコレクションにデータバインドされたリストボックスがあります。リストボックスは、各オブジェクトの識別子プロパティを表示するように構成されています。リストボックス全体の1つのツールチップではなく、ホバーされているリストボックス内のアイテムに固有の情報を含むツールチップを表示したいと思います。
私はWinForms内で作業しており、いくつかの役立つブログ投稿のおかげで、非常に優れたソリューションをまとめました。これを共有したいと思います。
この問題に対する他のエレガントな解決策があるかどうか、またはこれがWPFでどのように行われるかを確認したいと思います。
この問題を解決するために解決しなければならない2つの主要なサブ問題があります。
最初の問題は簡単に解決できます。 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イベントが再度発生しないことです。
これを回避するには、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);
}
}
_
ラッパーを配置すると、MouseHoverハンドラーの最後でResetMouseHover(listBox.Handle)
を呼び出すだけで、カーソルがコントロールの範囲内にある場合でも、ホバーイベントが再度発生します。
このアプローチでは、MouseHoverハンドラー内のすべてのコードを貼り付けると、実際に必要な数よりも多くのMouseHoverイベントが発生するはずですが、それで仕事は完了します。どんな改善も大歓迎です。
MouseMoveイベントを使用すると、マウスが上にあるアイテムのインデックスを追跡し、これをMouseMoves間で値を保持する変数に格納できます。 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;
}
}
}
リストボックスをオブジェクトにデータバインディングするので、最良のオプションはデータテンプレートを使用することだと思います。だからあなたはこのようなことをすることができます:
<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 をご覧ください。
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
};
楽しい!
タイトル属性を使用して、リストボックス内の各リストアイテムのツールヒントを設定できます。
リストボックス内のすべての項目に対してこれをループします。
ListItem li = new ListItem("text","key");
li.Attributes.Add("title","tool tip text");
お役に立てれば。
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);
}
}
}
}
以下は、ListBoxを使用してRadioButtonのグループを作成するスタイルです。すべてがMVVMに向けられています。 MyClassには、MyNameとMyToolTipの2つのStringプロパティが含まれています。これにより、適切なツールチップを含むラジオボタンのリストが表示されます。このスレッドで興味深いのは、これをすべてXamlソリューションにする下部近くのSetter forToolTipです。
使用例:
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>