Ответ 1
Просто поместите этот код в раздел инициализации.
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
У меня есть всплывающая подсказка для метки, и я хочу, чтобы она оставалась открытой, пока пользователь не переместит мышь к другому элементу управления.
Я пробовал следующие свойства в подсказке:
StaysOpen="True"
а также
ToolTipService.ShowDuration = "60000"
Но в обоих случаях всплывающая подсказка отображается только в течение 5 секунд.
Почему эти значения игнорируются?
Просто поместите этот код в раздел инициализации.
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
TooltipService.ShowDuration
работает, но вы должны установить его для объекта, имеющего всплывающую подсказку, например:
<Label ToolTipService.ShowDuration="12000" Name="lblShowTooltip" Content="Shows tooltip">
<Label.ToolTip>
<ToolTip>
<TextBlock>Hello world!</TextBlock>
</ToolTip>
</Label.ToolTip>
</Label>
Я бы сказал, что этот дизайн был выбран, потому что он позволяет одну и ту же подсказку с разными таймаутами на разных элементах управления.
Это тоже сводило меня с ума сегодня вечером. Я создал подкласс ToolTip
для решения проблемы. Для меня, на .NET 4.0, свойство ToolTip.StaysOpen
не "действительно" остается открытым.
В следующем классе используйте новое свойство ToolTipEx.IsReallyOpen
вместо свойства ToolTip.IsOpen
. Вы получите управление, которое вы хотите. С помощью вызова Debug.Print()
вы можете смотреть в окне вывода отладчика сколько раз вызывается this.IsOpen = false
! Так много для StaysOpen
, или я должен сказать "StaysOpen"
? Наслаждайтесь.
public class ToolTipEx : ToolTip
{
static ToolTipEx()
{
IsReallyOpenProperty =
DependencyProperty.Register(
"IsReallyOpen",
typeof(bool),
typeof(ToolTipEx),
new FrameworkPropertyMetadata(
defaultValue: false,
flags: FrameworkPropertyMetadataOptions.None,
propertyChangedCallback: StaticOnIsReallyOpenedChanged));
}
public static readonly DependencyProperty IsReallyOpenProperty;
protected static void StaticOnIsReallyOpenedChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
ToolTipEx self = (ToolTipEx)o;
self.OnIsReallyOpenedChanged((bool)e.OldValue, (bool)e.NewValue);
}
protected void OnIsReallyOpenedChanged(bool oldValue, bool newValue)
{
this.IsOpen = newValue;
}
public bool IsReallyOpen
{
get
{
bool b = (bool)this.GetValue(IsReallyOpenProperty);
return b;
}
set { this.SetValue(IsReallyOpenProperty, value); }
}
protected override void OnClosed(RoutedEventArgs e)
{
System.Diagnostics.Debug.Print(String.Format(
"OnClosed: IsReallyOpen: {0}, StaysOpen: {1}", this.IsReallyOpen, this.StaysOpen));
if (this.IsReallyOpen && this.StaysOpen)
{
e.Handled = true;
// We cannot set this.IsOpen directly here. Instead, send an event asynchronously.
// DispatcherPriority.Send is the highest priority possible.
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action)(() => this.IsOpen = true),
DispatcherPriority.Send);
}
else
{
base.OnClosed(e);
}
}
}
Небольшая реклама: Почему Microsoft не создала свойства DependencyProperty
(getters/seters) виртуальными, чтобы мы могли принимать/отклонять/корректировать изменения в подклассах? Или сделать virtual OnXYZPropertyChanged
для каждого DependencyProperty
? Тьфу.
--- Edit ---
Мое решение выше выглядит странно в редакторе XAML - всплывающая подсказка всегда отображается, блокируя некоторый текст в Visual Studio!
Вот лучший способ решить эту проблему:
Некоторые XAML:
<!-- Need to add this at top of your XAML file:
xmlns:System="clr-namespace:System;assembly=mscorlib"
-->
<ToolTip StaysOpen="True" Placement="Bottom" HorizontalOffset="10"
ToolTipService.InitialShowDelay="0" ToolTipService.BetweenShowDelay="0"
ToolTipService.ShowDuration="{x:Static Member=System:Int32.MaxValue}"
>This is my tooltip text.</ToolTip>
Некоторые коды:
// Alternatively, you can attach an event listener to FrameworkElement.Loaded
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Be gentle here: If someone creates a (future) subclass or changes your control template,
// you might not have tooltip anymore.
ToolTip toolTip = this.ToolTip as ToolTip;
if (null != toolTip)
{
// If I don't set this explicitly, placement is strange.
toolTip.PlacementTarget = this;
toolTip.Closed += new RoutedEventHandler(OnToolTipClosed);
}
}
protected void OnToolTipClosed(object sender, RoutedEventArgs e)
{
// You may want to add additional focus-related tests here.
if (this.IsKeyboardFocusWithin)
{
// We cannot set this.IsOpen directly here. Instead, send an event asynchronously.
// DispatcherPriority.Send is the highest priority possible.
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action)delegate
{
// Again: Be gentle when using this.ToolTip.
ToolTip toolTip = this.ToolTip as ToolTip;
if (null != toolTip)
{
toolTip.IsOpen = true;
}
},
DispatcherPriority.Send);
}
}
Заключение: что-то отличается от классов ToolTip
и ContextMenu
. Оба имеют "служебные" классы, такие как ToolTipService
и ContextMenuService
, которые управляют определенными свойствами, и оба используют Popup
как "секретный" родительский элемент управления во время отображения. Наконец, я заметил, что ВСЕ примеры XAML ToolTip в Интернете не используют класс ToolTip
напрямую. Вместо этого они вставляют StackPanel
с TextBlock
s. Вещи, которые заставляют вас сказать: "hmmm..."
Вероятно, вы хотите использовать Popup вместо Tooltip, так как Tooltip предполагает, что вы используете его в предопределенных стандартах пользовательских интерфейсов.
Я не уверен, почему StaysOpen не работает, но ShowDuration работает как задокументировано в MSDN - это время, в течение которого отображается всплывающая подсказка, КОГДА она отображается. Установите его на небольшое количество (например, 500 мс), чтобы увидеть разницу.
В вашем случае трюк заключается в поддержании состояния "последнего зависания", но как только у вас будет достаточно триггера, чтобы изменить цель размещения и динамический контент (вручную или через привязку), если вы используете один всплывающий экран или скрытие последнего видимого всплывающего окна, если вы используете несколько.
Есть некоторые gotchas с всплывающими окнами относительно изменения размера и перемещения окна (всплывающие окна не перемещаются с контейнерами), поэтому вы можете также иметь это в виду, когда вы настраиваете поведение. Подробнее см. эту ссылку.
НТН.
Если вы хотите указать, что только некоторые элементы в вашем Window
имеют
эффективно неопределенное время ToolTip
вы можете определить Style
в Window.Resources
для этих элементов. Здесь Style
для Button
, который имеет такой ToolTip
:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...>
...
<Window.Resources>
<Style x:Key="ButtonToolTipIndefinate" TargetType="{x:Type Button}">
<Setter Property="ToolTipService.ShowDuration"
Value="{x:Static Member=sys:Int32.MaxValue}"/>
</Style>
...
</Window.Resources>
...
<Button Style="{DynamicResource ButtonToolTipIndefinate}"
ToolTip="This should stay open"/>
<Button ToolTip="This Should disappear after the default time.">
...
Можно также добавить Style.Resources
в Style
, чтобы изменить внешний вид ToolTip
, который он показывает, например:
<Style x:Key="ButtonToolTipTransparentIndefinate" TargetType="{x:Type Button}">
<Style.Resources>
<Style x:Key="{x:Type ToolTip}" TargetType="{x:Type ToolTip}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="HasDropShadow" Value="False"/>
</Style>
</Style.Resources>
<Setter Property="ToolTipService.ShowDuration"
Value="{x:Static Member=sys:Int32.MaxValue}"/>
</Style>
Примечание. Когда я это сделал, я также использовал BasedOn
в Style
, поэтому все остальные, определенные для версии моего настраиваемого элемента управления с нормальным ToolTip
, будут применены.
Я боролся с WPF Tooltip только на днях. Кажется, что невозможно остановить его появление и исчезновение само по себе, поэтому в конце я прибег к обработке события Opened
. Например, я хотел остановить его от открытия, если у него не было контента, поэтому я обработал событие Opened
, а затем сделал следующее:
tooltip.IsOpen = (tooltip.Content != null);
Это взломать, но он сработал.
Предположительно, вы могли бы аналогичным образом обработать событие Closed
и сказать ему, чтобы он снова открывался, тем самым сохраняя его видимым.
Только для полноты: В коде это выглядит так:
ToolTipService.SetShowDuration(element, 60000);
Также, если вы когда-либо захотите добавить какой-либо другой элемент управления в свою подсказку, он не будет сфокусирован, так как сама подсказка может получить фокус. Так что, как сказал майхатан, ваш лучший снимок - всплывающее окно.
Получил мою проблему с тем же кодом.
ToolTipService.ShowDurationProperty.OverrideMetadata(typeof (DependencyObject), new FrameworkPropertyMetadata (Int32.MaxValue));
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
Он работает для меня. Скопируйте эту строку в свой конструктор класса.