Bir WPF ipucunu ekranda kalmaya zorlama


119

Etiket için bir araç ipucum var ve kullanıcı fareyi farklı bir kontrole hareket ettirene kadar açık kalmasını istiyorum.

Araç ipucunda aşağıdaki özellikleri denedim:

StaysOpen="True"

ve

ToolTipService.ShowDuration = "60000"

Ancak her iki durumda da araç ipucu yalnızca tam olarak 5 saniye görüntülenir.

Bu değerler neden göz ardı ediliyor?


Maksimum değer zorlanan yoktur yerde için ShowDurationmülkiyet, bu gibi bir şey olduğunu düşünüyorum 30,000. Bundan daha büyük herhangi bir şey ve varsayılan olarak geri dönecektir 5000.
Dennis

2
@Dennis: Bunu WPF 3.5 ile test ettim ve ToolTipService.ShowDuration="60000"çalıştım. Varsayılan olarak geri dönülmedi 5000.
M. Dudley

@emddudley: Araç İpucu gerçekten 60000 ms için açık kalıyor mu? Sen ayarlayabilirsiniz ToolTipService.ShowDurationmülkü herhangi değere (Int32.MaxValue kadar)> = 0 ancak ipucu Bu boy açık kalmayacak.
Dennis

2
@Dennis: Evet, tam olarak 60 saniye açık kaldı. Bu Windows 7'de.
M. Dudley

@emddudley: Fark bu olabilir. Bu, Windows XP'ye karşı geliştirme yaptığım zamanlardaki bilgiydi.
Dennis

Yanıtlar:


113

Sadece bu kodu başlatma bölümüne koyun.

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

Benim için işe yarayan tek çözüm buydu. Yerleşim özelliğini Üst olarak ayarlamak için bu kodu nasıl uyarlayabilirsiniz? new FrameworkPropertyMetadata("Top")çalışmıyor.
InvalidBrainException

6
Bunu doğru yanıt olarak işaretledim (yaklaşık 6 yıl sonra, üzgünüm) çünkü bu aslında Windows'un tüm desteklenen sürümlerinde çalışıyor ve yeterince uzun olması gereken 49 gün boyunca açık tutuyor: p
TimothyP

1
Bunu Window_Loaded etkinliğime de koydum ve harika çalışıyor. Yapmanız gereken tek şey, XAML'nizde ayarladığınız herhangi bir "ToolTipService.ShowDuration" dan kurtulduğunuzdan emin olmaktır, XAML'de ayarladığınız süreler bu kodun başarmaya çalıştığı davranışı geçersiz kılacaktır. Çözümü sağladığı için John Whiter'a teşekkürler.
nicko

1
FWIW, bunu tam olarak küresel olduğu için tercih ediyorum - uygulamamdaki tüm araç ipuçlarının daha fazla tantana olmadan daha uzun süre kalmasını istiyorum. Bunu yapmak, yine de diğer cevapta olduğu gibi bağlama özgü yerlere seçici olarak daha küçük bir değer uygulamanıza izin verir. (Ancak her zaman olduğu gibi, bu yalnızca uygulama sizseniz geçerlidir - bir kontrol kitaplığı veya başka bir şey yazıyorsanız, o zaman yalnızca bağlama özgü çözümler kullanmanız gerekir ; küresel durumla oynamak sizin değildir.)
Miral

1
Bu tehlikeli olabilir! Wpf, çift hesaplama yapan zamanlayıcı aralığını ayarlarken dahili olarak TimeSpan.FromMilliseconds () kullanır. Bu, değer Interval özelliğini kullanarak zamanlayıcıya uygulandığında ArgumentOutOfRangeException alabileceğiniz anlamına gelir.
Ocak

191

TooltipService.ShowDuration çalışır, ancak bunu Araç İpucuna sahip nesne üzerinde şu şekilde ayarlamalısınız:

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

Bu tasarımın, farklı kontrollerde farklı zaman aşımlarına sahip aynı araç ipucuna izin verdiği için seçildiğini söyleyebilirim.


4
Ayrıca, ToolTipdoğrudan içeriğin içeriğini açıkça belirtmenize olanak tanır <ToolTip>, bu da bağlamayı kolaylaştırabilir.
svick

15
Bu, içeriğe özgü olduğu ve genel olmadığı için seçilen cevap olmalıdır.
Vlad

8
Süre milisaniye cinsindendir. Varsayılan değer 5000'dir. Yukarıdaki kod 12 saniyeyi belirtir.
Contango

1
Aynı araç ipucu örneğini birden fazla denetimle kullanırsanız, er ya da geç "farklı bir ebeveynin zaten görsel alt öğesi" istisnası alırsınız.
springy76

1
Bunun doğru cevap olmasının ana nedeni, gerçek yüksek seviyeli programlama ruhuna, doğrudan XAML koduna ve fark edilmesinin kolay olmasıdır. Diğer çözüm, küresel olduğu noktanın yanı sıra biraz hilekar ve ayrıntılı. Bahse girerim bunu kullanan çoğu insan bir haftada nasıl yaptıklarını unutmuştur.
j riv

15

Bu da bu gece beni deli ediyordu. ToolTipSorunu çözmek için bir alt sınıf oluşturdum . Benim için .NET 4.0'da, ToolTip.StaysOpenözellik "gerçekten" değil, açık kalıyor.

Aşağıdaki sınıfta yeni mülkü kullanın ToolTipEx.IsReallyOpen yerine kullanın ToolTip.IsOpen. İstediğiniz kontrolü alacaksınız. Via Debug.Print()çağrı, birçok kez sadece nasıl ayıklayıcı Çıktı penceresinde izleyebilirsiniz this.IsOpen = falsedenir! Çok mu StaysOpen, yoksa söylemeliyim "StaysOpen"? Zevk almak.

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

Küçük rant: Microsoft neden DependencyPropertyözellikleri (alıcılar / ayarlayıcılar) sanal yapmadı, böylece alt sınıflardaki değişiklikleri kabul edebilir / reddedebilir / ayarlayabiliriz? Veya virtual OnXYZPropertyChangedher biri için bir tane DependencyPropertymi hazırlıyorsunuz ? Ugh.

---Düzenle---

Yukarıdaki çözümüm XAML düzenleyicide garip görünüyor - araç ipucu her zaman gösteriliyor, Visual Studio'daki bazı metinleri engelliyor!

İşte bu sorunu çözmenin daha iyi bir yolu:

Bazı 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>

Bazı kodlar:

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

Sonuç: Sınıflar hakkında farklı bir şey var ToolTipve ContextMenu. Her ikisinin de belirli özellikleri yöneten ToolTipServiceve gibi "hizmet" sınıfları vardır ContextMenuServiceve her ikisi de Popupgörüntüleme sırasında "gizli" üst denetim olarak kullanılır. Son olarak, Web'deki TÜM XAML ToolTip örneklerinin ToolTipdoğrudan sınıfı kullanmadığını fark ettim . Bunun yerine, a StackPanelile TextBlocks yerleştirirler . Size şunu söyleten şeyler: "hmmm ..."


1
Cevabınızın eksiksizliği için daha fazla oy almalısınız. Benden +1.
Hannish

ToolTipService ana öğeye yerleştirilmelidir, Martin Konicek'in yukarıdaki cevabına bakın.
Jeson Martajaya

8

Araç İpucu onu önceden tanımlanmış kullanıcı arabirimi standartlarında kullandığınızı varsaydığından, muhtemelen Araç İpucu yerine Açılır Pencere kullanmak isteyeceksiniz.

StaysOpen'ın neden çalışmadığından emin değilim, ancak ShowDuration, MSDN'de belgelendiği gibi çalışıyor - görüntülendiğinde Araç İpucunun görüntülendiği süredir. Farkı görmek için küçük bir miktara (örneğin 500 milisaniye) ayarlayın.

Sizin durumunuzdaki püf noktası, "son fareyle üzerine gelinen kontrol" durumunu korumaktır, ancak buna sahip olduğunuzda, bir Popup kullanıyorsanız, yerleşim hedefini ve içeriği dinamik olarak (manuel olarak veya bağlama yoluyla) değiştirmek oldukça önemsiz olmalıdır. veya birden çok kullanıyorsanız görünen son Popup'ı gizleme.

Pencereyi yeniden boyutlandırma ve taşıma (Pop-up'lar kaplar ile hareket etmez) kadar Popup'larla ilgili bazı sorunlar vardır, bu nedenle davranışı değiştirirken bunu da aklınızda bulundurmak isteyebilirsiniz. Bu bağlantıya bakın fazla ayrıntı için bakın.

HTH.


3
Ayrıca, Popup'ların her zaman tüm masaüstü nesnelerinin üstünde olduğuna dikkat edin - başka bir programa geçseniz bile, açılır pencere görünür olacak ve diğer programın bir kısmını belirsiz hale getirecektir.
Jeff B

İşte bu yüzden pop-up'ları kullanmayı sevmiyorum ... çünkü programla küçülmüyorlar ve diğer tüm programların üstünde kalıyorlar. Ayrıca, ana uygulamayı yeniden boyutlandırmak / taşımak, açılır pencereyi varsayılan olarak onunla birlikte taşımaz.
Rachel

FWIW, bu kullanıcı arayüzü kuralı zaten bir çöp . Okurken kaybolan bir araç ipucundan daha sinir bozucu çok az şey var.
Roman Starkov

7

Sizdeki yalnızca belirli öğelerin Windowetkin bir şekilde belirsiz ToolTipsüreye sahip olduğunu belirtmek istiyorsanız, bu öğeler için kendi Styleiçinde tanımlayabilirsiniz Window.Resources. İşte bunun Styleiçin Buttonböyle bir 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.">
    ...

Bir de ekleyebilir Style.Resourcesiçin Stylegörünümünü değiştirmek için ToolTipörneğin, gösterileri:

<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>

Not: Bunu yaptığımda ben de kullandım BasedOn, Styleböylece özel kontrolümün sürümü için normal olan diğer her şey ToolTipuygulanacaktı.


5

Sadece geçen gün WPF Tooltip ile güreşiyordum. Kendi kendine ortaya çıkmasını ve kaybolmasını durdurmak mümkün görünmüyor, bu yüzden sonunda Openedolayı ele almaya başladım . Örneğin, içeriği olmadığı sürece açılmasını engellemek istedim, bu yüzden Openedolayı ele aldım ve sonra şunu yaptım:

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

Bu bir hack, ama işe yaradı.

Muhtemelen Closedolayı benzer şekilde idare edebilir ve tekrar açmasını söyleyerek görünür kılarsınız.


ToolTip, bunun yerine kullanabileceğiniz HasContent adlı bir özelliğe sahiptir
benPearce

2

Tamlık adına: Kodda şöyle görünür:

ToolTipService.SetShowDuration(element, 60000);

0

Ayrıca, Araç İpucunuza başka bir kontrol eklemek isterseniz, bir Araç İpucunun kendisi odaklanabileceğinden odaklanma yapılamayacaktır. Micahtan'ın dediği gibi, en iyi atışınız bir Popup.


0

Sorunum aynı kodla düzeltildi.

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


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

Benim için çalışıyor. Bu satırı sınıf kurucunuza kopyalayın.


3
bu kabul edilen cevabın ikinci en çok olumlu oyla kopyalanıp yapıştırılmasıdır
CodingYourLife
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.