WPFツールチップを画面に留める


119

ラベルのツールチップがあり、ユーザーがマウスを別のコントロールに移動するまで開いたままにしておきたい。

ツールチップで次のプロパティを試しました:

StaysOpen="True"

そして

ToolTipService.ShowDuration = "60000"

ただし、どちらの場合も、ツールチップは正確に5秒間しか表示されません。

これらの値が無視されるのはなぜですか?


プロパティのどこかに適用される最大値がありShowDurationます。それはのようなものだと考えてください30,000。それ以上の場合、デフォルトでに戻ります5000
Dennis

2
@Dennis:私はこれをWPF 3.5でテストして動作しToolTipService.ShowDuration="60000"ました。デフォルトではに戻りませんでした5000
M.ダドリー

@emddudley:ToolTipは実際には60000ms開いたままですか?ToolTipService.ShowDurationプロパティを0以上の任意の値(Int32.MaxValueに)に設定できますが、ツールチップはその長さの間開いたままにはなりません。
Dennis

2
@Dennis:はい、それはちょうど60秒間開いたままでした。これは、Windows 7上で
M.ダドリー

@emddudley:それは違いかもしれません。これは、私がWindows XPに対して開発していたときの知識でした。
Dennis

回答:


113

このコードを初期化セクションに置くだけです。

ToolTipService.ShowDurationProperty.OverrideMetadata(
    typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));

これが私にとって有効な唯一の解決策でした。このコードをどのように調整して、PlacementプロパティをTopに設定できますか?new FrameworkPropertyMetadata("Top")動作しません。
InvalidBrainException

6
P:これは、実際のWindowsのサポートされているすべてのバージョンで動作し、十分な長さであるべき、それは49日間開いて続けるので、私はこの(ほぼ6年の年後、申し訳ありません)正しい答えなどをマークしている
TimothyP

1
また、これをWindow_Loadedイベントに配置しました。XAMLで設定した "ToolTipService.ShowDuration"を確実に取り除く必要があります。XAMLで設定した期間は、このコードが達成しようとしている動作を上書きします。解決策を提供してくれたJohn Whiterに感謝します。
Nicko 2017年

1
FWIW、私はこれがグローバルだからこそ私はこれを好みます-アプリ内のすべてのツールチップを、それ以上ファンファーレなしで長く存続させたいです。これを行うと、コンテキスト固有の場所でも、他の回答とまったく同じように、より小さい値を選択的に適用できます。(あなたがアプリケーションであればしかし、いつものように、これはのみ有効です-あなたがコントロールライブラリまたは何か他のものを書いているならば、あなたはしなければならない唯一のコンテキスト固有のソリューションを使用し、グローバル状態はと遊ぶのがあなたではありません)
Miral

1
これは危険です。Wpfは、二重の計算を行うタイマー間隔を設定するときに、TimeSpan.FromMilliseconds()を内部的に使用します。つまり、Intervalプロパティを使用して値がタイマーに適用されると、ArgumentOutOfRangeExceptionが発生する可能性があります。
1月

190

TooltipService.ShowDuration 動作しますが、次のように、ツールチップを持つオブジェクトに設定する必要があります。

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

このデザインが選択されたのは、異なるコントロールで異なるタイムアウトを使用して同じツールチップを許可するためです。


4
またToolTip、明示的な<ToolTip>を使用せずに、直接のコンテンツを指定できるため、バインディングが簡単になります。
12

15
これはコンテキスト固有でグローバルではないため、これを選択する必要があります。
ウラッド

8
期間はミリ秒単位です。デフォルトは5000です。上記のコードは12秒を指定しています。
コンタンゴ2015年

1
同じツールチップインスタンスを複数のコントロールで使用すると、遅かれ早かれ「別の親の視覚的な子」例外が発生します。
springy76 2016年

1
これが正しい答えである主な理由は、それが実際の高レベルのプログラミングの精神にあり、XAMLコードに直接含まれ、簡単に気づくからです。他のソリューションは、それがグローバルであるという点に加えて、ちょっとハッキーで冗長です。私はそれを使用したほとんどの人々が彼らが一週間でそれをどのようにしたかについて忘れていたに違いない。
j riv

15

これも今夜私を夢中にさせた。ToolTipこの問題を処理するサブクラスを作成しました。私にとって、.NET 4.0では、ToolTip.StaysOpenプロパティは「本当に」開いたままではありません。

以下のクラスでは、property ToolTipEx.IsReallyOpenではなく、新しいpropertyを使用してください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);
        }
    }
}

小さな怒り:DependencyPropertyサブクラスでの変更を受け入れ/拒否/調整できるように、なぜMicrosoftはプロパティ(ゲッター/セッター)を仮想化しなかったのですか?または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表示中に「秘密」の親コントロールとして使用されます。最後に、Web上のすべてのXAML ToolTipサンプルがクラスをToolTip直接使用していないことに気付きました。代わりに、StackPanelwith TextBlocks を埋め込みます。あなたが言うこと:「うーん...」


1
あなたは、その完全性のためだけにあなたの答えにもっと投票するべきです。私からの+1。
Hannish 2013

ToolTipServiceは親要素に配置する必要があります。上記のMartin Konicekの回答を参照してください。
Jeson Martajaya

8

Tooltipは、事前に定義されたUI標準の方法で使用することを想定しているため、TooltipではなくPopupを使用することをお勧めします。

StaysOpenが機能しない理由はわかりませんが、ShowDurationはMSDNに記載されているように機能します。これは、ツールチップが表示されているときにツールチップが表示される時間です。違いを確認するには、この値を少し(たとえば、500ミリ秒)に設定します。

あなたの場合の秘訣は「最後にホバーされたコントロール」の状態を維持することですが、1つのポップアップを使用している場合、配置ターゲットとコンテンツを動的に(手動で、またはバインディングを介して)変更することはかなり簡単です。複数を使用している場合は、最後に表示されるポップアップを非表示にします。

ウィンドウのサイズ変更と移動(ポップアップはコンテナーと一緒に移動しない)までは、ポップアップに関する問題がいくつかあります。そのため、動作を微調整している間、それを念頭に置いておくとよいでしょう。詳細については、このリンクを参照してください。

HTH。


3
また、ポップアップは常にすべてのデスクトップオブジェクトの上にあることに注意してください。別のプログラムに切り替えても、ポップアップは表示され、他のプログラムの一部を覆い隠します。
ジェフB

私がポップアップを使用したくないのはまさにそのためです。ポップアップはプログラムとともに縮小せず、他のすべてのプログラムの上位に留まるためです。また、メインアプリケーションのサイズ変更/移動を行っても、デフォルトではポップアップは移動しません。
レイチェル

FWIW、このUI規約はとにかくごみです。私がそれを読んでいる間に消えるツールチップよりも、煩わしいものはほとんどありません。
Roman Starkov 14

7

あなたのその特定の要素だけを指定したい場合はWindow持って効果的に無期限のToolTip期間を、あなたが定義することができStyle、あなたの中にWindow.Resourcesそれらの要素について。ここStyleButtonそのようなものがあるためです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

先日だけWPFツールチップと格闘していました。それ自体が現れたり消えたりするのを止めることは不可能に思えるので、結局私はOpenedイベントを処理することに頼りました。たとえば、コンテンツがない限り開かないようにしたかったので、Openedイベントを処理して次のようにしました。

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

それはハックですが、うまくいきました。

おそらく、同様にClosedイベントを処理し、再度開くように指示して、可視に保つことができます。


ToolTipにはHasContentと呼ばれるプロパティがあり、代わりに使用できます
benPearce


0

また、ToolTipに他のコントロールを配置したい場合でも、ToolTip自体がフォーカスを取得できるため、フォーカスできません。ミカタンが言ったように、あなたのベストショットはポップアップです。


0

同じコードで問題を修正しました。

ToolTipService.ShowDurationProperty.OverrideMetadata(typeof(DependencyObject)、new FrameworkPropertyMetadata(Int32.MaxValue));


-4
ToolTipService.ShowDurationProperty.OverrideMetadata(
    typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));

それは私のために働いています。この行をクラスコンストラクターにコピーします。


3
これは、承認された回答のコピー&貼り付けで、2番目に多い投票
CodingYourLife
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.