Заставить всплывающую подсказку WPF оставаться на экране - PullRequest
108 голосов
/ 22 мая 2009

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

Я попробовал следующие свойства в подсказке:

StaysOpen="True"

и

ToolTipService.ShowDuration = "60000"

Но в обоих случаях всплывающая подсказка отображается только в течение 5 секунд.

Почему эти значения игнорируются?

Ответы [ 10 ]

166 голосов
/ 20 июля 2009

TooltipService.ShowDuration работает, но вы должны установить его для объекта, имеющего всплывающую подсказку, например:

<Label ToolTipService.ShowDuration="12000" Name="lblShowTooltip" Content="Shows tooltip">
    <Label.ToolTip>
        <ToolTip>
            <TextBlock>Hello world!</TextBlock>
        </ToolTip>
    </Label.ToolTip>
</Label>

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

95 голосов
/ 29 ноября 2011

Просто поместите этот код в раздел инициализации.

ToolTipService.ShowDurationProperty.OverrideMetadata(
    typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
15 голосов
/ 19 октября 2012

Это также сводило меня с ума сегодня вечером. Я создал подкласс 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);
        }
    }
}

Small rant: Почему Microsoft не сделала свойства DependencyProperty (методы получения / установки) виртуальными, чтобы мы могли принимать / отклонять / корректировать изменения в подклассах? Или сделать virtual OnXYZPropertyChanged для каждого DependencyProperty? Тьфу.

--- Редактировать ---

Мое решение, описанное выше, выглядит странно в редакторе 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 в качестве «секретного» родительского элемента управления во время отображения. Наконец, я заметил, что ALL примеры подсказок XAML в Интернете не используют класс ToolTip напрямую. Вместо этого они вставляют StackPanel с TextBlock с. Вещи, которые заставляют вас говорить: "хммм ..."

8 голосов
/ 22 мая 2009

Возможно, вы захотите использовать Popup вместо Tooltip, поскольку Tooltip предполагает, что вы используете его в соответствии с заранее определенным стандартом интерфейса пользователя.

Я не уверен, почему StaysOpen не работает, но ShowDuration работает, как описано в MSDN, - это время, в течение которого отображается всплывающая подсказка, когда она отображается. Установите небольшую величину (например, 500 мсек), чтобы увидеть разницу.

Хитрость в вашем случае заключается в поддержании состояния «последнего элемента управления», но, как только вы это сделаете, будет довольно тривиально изменить цель размещения и контент динамически (либо вручную, либо через привязку), если вы используете одно всплывающее окно или скрытие последнего видимого всплывающего окна, если вы используете несколько.

Есть некоторые ошибки с всплывающими окнами, касающиеся изменения размера и перемещения окна (всплывающие окна не перемещаются с контейнерами), так что вы можете иметь это в виду и при настройке поведения. См. эту ссылку для более подробной информации.

НТН.

7 голосов
/ 02 мая 2015

Если вы хотите указать, что только определенные элементы в вашем 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, будет применено.

5 голосов
/ 22 мая 2009

Я боролся с подсказкой WPF только на днях. Кажется, невозможно остановить его появление и исчезновение само по себе, поэтому в итоге я прибег к обработке события Opened. Например, я хотел, чтобы он не открывался, если в нем нет некоторого содержимого, поэтому я обработал событие Opened и затем сделал следующее:

tooltip.IsOpen = (tooltip.Content != null);

Это взлом, но это сработало.

Предположительно, вы могли бы аналогичным образом обработать событие Closed и сказать, чтобы оно открывалось снова, сохраняя его видимым.

2 голосов
/ 16 июля 2014

Просто для полноты картины: В коде это выглядит так:

ToolTipService.SetShowDuration(element, 60000);
0 голосов
/ 03 апреля 2018

Исправлена ​​проблема с тем же кодом.

ToolTipService.ShowDurationProperty.OverrideMetadata ( typeof (DependencyObject), новый FrameworkPropertyMetadata (Int32.MaxValue));

0 голосов
/ 02 апреля 2012
ToolTipService.ShowDurationProperty.OverrideMetadata(
    typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));

Это работает для меня. Скопируйте эту строку в ваш конструктор класса.

0 голосов
/ 22 мая 2009

Кроме того, если вы когда-нибудь захотите поместить какой-либо другой элемент управления в свою всплывающую подсказку, он не будет фокусируемым, поскольку сама всплывающая подсказка может получить фокус. Итак, как сказал Микахтан, ваш лучший снимок - это всплывающее окно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...