บังคับให้คำแนะนำเครื่องมือ WPF อยู่บนหน้าจอ


119

ฉันมีคำแนะนำเครื่องมือสำหรับป้ายกำกับและต้องการให้ยังคงเปิดอยู่จนกว่าผู้ใช้จะเลื่อนเมาส์ไปยังส่วนควบคุมอื่น

ฉันได้ลองใช้คุณสมบัติต่อไปนี้ในคำแนะนำเครื่องมือ:

StaysOpen="True"

และ

ToolTipService.ShowDuration = "60000"

แต่ในทั้งสองกรณีคำแนะนำเครื่องมือจะแสดงเป็นเวลา 5 วินาทีเท่านั้น

เหตุใดค่าเหล่านี้จึงถูกละเลย


มีค่าสูงสุดบังคับใช้ที่ใดที่หนึ่งสำหรับคุณสมบัติคิดว่ามันเป็นสิ่งที่ต้องการShowDuration 30,000อะไรที่มากกว่านั้นและค่าเริ่มต้นจะกลับไป5000เป็น
Dennis

2
@ เดนนิส: ฉันทดสอบสิ่งนี้กับ WPF 3.5 และใช้ToolTipService.ShowDuration="60000"งานได้ 5000มันไม่ได้เริ่มต้นที่จะกลับไป
M. Dudley

@emddudley: ToolTip เปิดอยู่ที่ 60000ms จริงหรือ? คุณสามารถตั้งค่าToolTipService.ShowDurationคุณสมบัติเป็นค่าใดก็ได้ > = 0 (ถึง Int32.MaxValue) อย่างไรก็ตามคำแนะนำเครื่องมือจะไม่เปิดอยู่สำหรับความยาวนั้น
Dennis

2
@ เดนนิส: ใช่มันเปิดค้างไว้ 60 วินาที นี่คือบน Windows 7
M. Dudley

@emddudley: นั่นอาจเป็นความแตกต่าง นี่เป็นความรู้ตั้งแต่ตอนที่ฉันพัฒนากับ Windows XP
Dennis

คำตอบ:


113

เพียงใส่รหัสนี้ในส่วนการเริ่มต้น

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

นี่เป็นทางออกเดียวที่ใช้ได้ผลสำหรับฉัน คุณจะปรับรหัสนี้เพื่อตั้งค่าคุณสมบัติตำแหน่งเป็นด้านบนได้อย่างไร new FrameworkPropertyMetadata("Top")ไม่ทำงาน
InvalidBrainException

6
ฉันได้ทำเครื่องหมายสิ่งนี้ (หลังจากผ่านไปเกือบ 6 ปีขออภัย) เป็นคำตอบที่ถูกต้องเพราะสิ่งนี้ใช้ได้กับ Windows ทุกรุ่นที่รองรับและเปิดไว้เป็นเวลา 49 วันซึ่งน่าจะนานพอ: p
TimothyP

1
ฉันใส่สิ่งนี้ไว้ในเหตุการณ์ Window_Loaded ของฉันด้วยและมันก็ใช้งานได้ดี สิ่งเดียวที่คุณต้องทำคือตรวจสอบให้แน่ใจว่าคุณได้กำจัด "ToolTipService.ShowDuration" ใด ๆ ที่คุณตั้งไว้ใน XAML ของคุณระยะเวลาที่คุณตั้งไว้ใน XAML จะแทนที่ลักษณะการทำงานที่รหัสนี้พยายามจะบรรลุ ขอบคุณ John Whiter ที่ให้การแก้ปัญหา
nicko

1
FWIW ฉันชอบอันนี้เพราะมันเป็นสากล - ฉันต้องการให้คำแนะนำเครื่องมือทั้งหมดในแอพของฉันคงอยู่ได้นานขึ้นโดยไม่ต้องประโคมอีกต่อไป การทำเช่นนี้ยังคงช่วยให้คุณสามารถเลือกใช้ค่าที่น้อยกว่าในตำแหน่งที่เจาะจงตามบริบทได้เช่นเดียวกับในคำตอบอื่น ๆ (แต่เช่นเคยสิ่งนี้ใช้ได้เฉพาะในกรณีที่คุณเป็นแอปพลิเคชัน - หากคุณกำลังเขียนไลบรารีควบคุมหรืออย่างอื่นคุณต้องใช้โซลูชันเฉพาะบริบทเท่านั้นรัฐระดับโลกไม่ใช่ของคุณที่จะเล่นด้วย)
Miral

1
อาจเป็นอันตรายได้! Wpf ใช้ภายใน TimeSpan.FromMilliseconds () เมื่อตั้งค่าช่วงเวลาซึ่งจะคำนวณสองครั้ง ซึ่งหมายความว่าเมื่อใช้ค่ากับตัวจับเวลาโดยใช้คุณสมบัติ Interval คุณจะได้รับ ArgumentOutOfRangeException
มกราคม

191

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>ซึ่งจะทำให้การผูกง่ายขึ้น
svick

15
นี่ควรเป็นคำตอบที่เลือกเนื่องจากเป็นคำตอบเฉพาะบริบทไม่ใช่ทั่วโลก
Vlad

8
ระยะเวลาเป็นมิลลิวินาที ค่าเริ่มต้นคือ 5000 รหัสด้านบนระบุ 12 วินาที
Contango

1
หากคุณใช้อินสแตนซ์คำแนะนำเครื่องมือเดียวกันกับการควบคุมหลายรายการไม่ช้าก็เร็วคุณจะได้รับข้อยกเว้น "ลูกที่มองเห็นแล้วของผู้ปกครองรายอื่น"
springy76

1
เหตุผลหลักที่ควรเป็นคำตอบที่ถูกต้องก็คือมันอยู่ในจิตวิญญาณของการเขียนโปรแกรมระดับสูงจริงอยู่ในโค้ด XAML และสังเกตเห็นได้ง่าย วิธีแก้ปัญหาอื่น ๆ คือค่อนข้างแฮ็คและมีรายละเอียดนอกเหนือจากประเด็นที่เป็นสากล ฉันพนันได้เลยว่าคนส่วนใหญ่ที่ใช้มันลืมไปแล้วว่าพวกเขาทำได้อย่างไรในหนึ่งสัปดาห์
j riv

15

นี่ยังทำให้ฉันบ้าในคืนนี้ ฉันสร้าง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 / setters) เสมือนเพื่อให้เราสามารถยอมรับ / ปฏิเสธ / ปรับเปลี่ยนการเปลี่ยนแปลงในคลาสย่อยได้ หรือทำ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ในฐานะที่เป็น "ความลับ" การควบคุมผู้ปกครองระหว่างการแสดง สุดท้ายผมสังเกตเห็นทั้งหมดตัวอย่าง XAML คำแนะนำเครื่องมือบนเว็บไม่ได้ใช้ระดับToolTipโดยตรง แต่จะฝัง a StackPanelด้วยTextBlocks สิ่งที่ทำให้คุณพูด: "อืม ... "


1
คุณควรได้รับคะแนนเสียงมากขึ้นสำหรับคำตอบของคุณเพียงเพื่อความละเอียดถี่ถ้วน +1 จากฉัน
Hannish

ToolTipService จำเป็นต้องวางไว้ในองค์ประกอบหลักดูคำตอบของ Martin Konicek ด้านบน
Jeson Martajaya

8

คุณอาจต้องการใช้ Popup แทน Tooltip เนื่องจาก Tooltip จะถือว่าคุณกำลังใช้งานตามมาตรฐาน UI ที่กำหนดไว้ล่วงหน้า

ฉันไม่แน่ใจว่าทำไม StaysOpen ถึงไม่ทำงาน แต่ ShowDuration ทำงานตามที่บันทึกไว้ใน MSDN นั่นคือระยะเวลาที่ Tooltip จะปรากฏขึ้นเมื่อมันปรากฏขึ้น ตั้งค่าเป็นจำนวนเล็กน้อย (เช่น 500 msec) เพื่อดูความแตกต่าง

เคล็ดลับในกรณีของคุณคือการรักษาสถานะ "การควบคุมที่ปรากฏครั้งสุดท้าย" แต่เมื่อคุณมีแล้วควรเปลี่ยนเป้าหมายตำแหน่งและเนื้อหาแบบไดนามิก (ไม่ว่าจะด้วยตนเองหรือผ่านการเชื่อมโยง) หากคุณใช้ป๊อปอัปเดียว หรือซ่อน Popup สุดท้ายที่มองเห็นได้หากคุณใช้หลายตัว

มีป๊อปอัปบางอย่างที่มีป๊อปอัปจนถึงการปรับขนาดและย้ายหน้าต่าง (ป๊อปอัปไม่ย้ายด้วยคอนเทนเนอร์) ดังนั้นคุณอาจต้องการคำนึงถึงสิ่งนั้นในขณะที่คุณกำลังปรับเปลี่ยนพฤติกรรม ดูรายละเอียดเพิ่มเติมที่ลิงค์นี้

HTH


3
นอกจากนี้โปรดระวังว่าป๊อปอัปจะอยู่ด้านบนของวัตถุบนเดสก์ท็อปทั้งหมดเสมอแม้ว่าคุณจะเปลี่ยนไปใช้โปรแกรมอื่นป๊อปอัปจะมองเห็นได้และเป็นส่วนที่คลุมเครือของโปรแกรมอื่น
Jeff B

นั่นเป็นเหตุผลว่าทำไมฉันถึงไม่ชอบใช้ป๊อปอัป .... เพราะมันไม่ได้ย่อขนาดกับโปรแกรมและพวกเขาก็อยู่เหนือโปรแกรมอื่น ๆ ทั้งหมด นอกจากนี้การปรับขนาด / ย้ายแอปพลิเคชันหลักจะไม่ย้ายป๊อปอัปด้วยโดยค่าเริ่มต้น
Rachel

FWIW ประชุม UI นี้ล่ะค่ะขยะ มีบางสิ่งที่น่ารำคาญไปกว่าคำแนะนำเครื่องมือที่หายไปในขณะที่ฉันอ่าน
Roman Starkov

7

หากคุณต้องการระบุว่ามีเพียงองค์ประกอบบางอย่างในของคุณWindowเท่านั้นที่มีToolTipระยะเวลาไม่แน่นอนอย่างมีประสิทธิภาพคุณสามารถกำหนด a 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

ฉันต่อสู้กับ WPF Tooltip เมื่อวันก่อนเท่านั้น ดูเหมือนจะเป็นไปไม่ได้ที่จะหยุดไม่ให้ปรากฏและหายไปเองในท้ายที่สุดฉันจึงใช้วิธีจัดการกับOpenedเหตุการณ์นี้ ตัวอย่างเช่นฉันต้องการหยุดไม่ให้เปิดเว้นแต่ว่าจะมีเนื้อหาบางส่วนดังนั้นฉันจึงจัดการOpenedเหตุการณ์แล้วทำสิ่งนี้:

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

มันเป็นการแฮ็ก แต่ได้ผล

สันนิษฐานว่าคุณสามารถจัดการClosedเหตุการณ์ในทำนองเดียวกันและบอกให้เปิดอีกครั้งเพื่อให้สามารถมองเห็นได้


ToolTip มีคุณสมบัติที่เรียกว่า HasContent ซึ่งคุณสามารถใช้แทนได้
benPearce

2

เพียงเพื่อความสมบูรณ์: ในโค้ดจะมีลักษณะดังนี้:

ToolTipService.SetShowDuration(element, 60000);

0

นอกจากนี้หากคุณต้องการใส่การควบคุมอื่น ๆ ใน ToolTip ของคุณก็จะไม่สามารถโฟกัสได้เนื่องจาก ToolTip เองสามารถโฟกัสได้ เช่นเดียวกับมิคาตันกล่าวว่าช็อตที่ดีที่สุดของคุณคือป๊อปอัป


0

แก้ไขปัญหาของฉันด้วยรหัสเดียวกัน

ToolTipService.ShowDurationProperty.OverrideMetadata (typeof (DependencyObject) ใหม่ FrameworkPropertyMetadata (Int32.MaxValue));


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

มันทำงานให้ฉัน คัดลอกบรรทัดนี้ไปยังตัวสร้างคลาสของคุณ


3
นี่คือการคัดลอกและวางคำตอบที่ได้รับการยอมรับโดยมีผู้
โหวต
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.