Подтвердить что ты не робот

Как я могу открыть всплывающее окно WPF с задержкой?

Я просто хочу открыть WPF Popup с задержкой, вроде ToolTip.

Как я могу это достичь?

И, кстати, Popup.PopupAnimation = PopupAnimation.Fade... затухает слишком быстро. Я хочу, по крайней мере, на полсекунды.

4b9b3361

Ответ 1

Сначала... кредит для этого ответа идет на Эрик Берк. Он ответил на этот вопрос, размещенный в группе WPF Disciples. Я подумал, что было бы полезно поставить этот ответ на StackOverflow тоже.

В принципе, вам нужно оживить свойство IsOpen всплывающего окна с помощью DiscreteBooleanKeyFrame.

Обратите внимание на следующий xaml (который можно легко вставить в Kaxaml или другую свободную утилиту редактирования xaml):

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
    <ContentPresenter>
        <ContentPresenter.ContentTemplate>
            <DataTemplate>
                <Grid>
                    <CheckBox
                        x:Name="cb"
                        Width="100"
                        Height="40"
                        Content="Hover Over Me"
                    />
                    <Popup
                        x:Name="popup"
                        Placement="Bottom"
                        PlacementTarget="{Binding ElementName=cb}"
                    >
                        <Border Width="400" Height="400" Background="Red"/>
                    </Popup>
                </Grid>
                <DataTemplate.Triggers>
                    <Trigger SourceName="cb" Property="IsMouseOver" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard x:Name="bsb">
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames
                                        Storyboard.TargetName="popup"
                                        Storyboard.TargetProperty="IsOpen"
                                        FillBehavior="HoldEnd"
                                    >
                                        <DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="True"/>
                                     </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <StopStoryboard BeginStoryboardName="bsb"/>
                        </Trigger.ExitActions>
                    </Trigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ContentPresenter.ContentTemplate>
    </ContentPresenter>
</Page>

Обратите внимание, что я немного изменил свое первоначальное решение... чтобы вызвать IsOpen на мыши, вместо проверки CheckBox, как и у него. Все в попытке заставить Popup вести себя немного как ToolTip.

Ответ 2

Вы можете создать стиль, который будет применен к Popup следующим образом:

<Style x:Key="TooltipPopupStyle" TargetType="Popup">
    <Style.Triggers>
        <DataTrigger Binding="{Binding PlacementTarget.IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
            <DataTrigger.EnterActions>
                <BeginStoryboard x:Name="OpenPopupStoryBoard" >
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
                            <DiscreteBooleanKeyFrame KeyTime="0:0:0.25" Value="True"/>
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
                <BeginStoryboard x:Name="ClosePopupStoryBoard">
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
                            <DiscreteBooleanKeyFrame KeyTime="0:0:1" Value="False"/>
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.ExitActions>
        </DataTrigger>

        <Trigger Property="IsMouseOver" Value="True">
            <Trigger.EnterActions>
                <PauseStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
                <ResumeStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
            </Trigger.ExitActions>
        </Trigger>
    </Style.Triggers>
</Style>

Затем, всякий раз, когда вы хотите использовать его, вы будете писать разметку, подобную этой (обратите внимание на привязку для PlacementTarget):

<TextBlock x:Name="TargetControl" Text="Hover over me!" />
<Popup PlacementTarget="{Binding ElementName=TargetControl}" Style="{StaticResource TooltipPopupStyle}">
    <Border BorderBrush="Red" BorderThickness="1" Background="White">
        <TextBlock Text="This is a Popup behaving somewhat like the tooltip!" Margin="10" />
    </Border>
</Popup>

Ответ 3

Вложенный ответ cplotts хорош, но может не применяться в вашем случае, потому что он оставляет анимацию, прикрепленную к свойству IsOpen, эффективно блокируя ее на месте и предотвращая ее изменение с помощью настройки прямого свойства, привязки и других способов. Это может затруднить использование вашего кода в зависимости от того, как вы его используете.

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

_popupTimer = new DispatcherTimer(DispatcherPriority.Normal);
_popupTimer.Interval = TimeSpan.FromMilliseconds(100);
_popupTimer.Tick += (obj, e) =>
{
  _popup.IsOpen = true;
};
_popupTimer.Start();

Для поведения типа ToolTip это можно сделать на MouseEnter. Если вы хотите отменить всплывающее окно по какой-либо причине (например, если мышь покидает элемент управления до появления всплывающего окна), просто:

_popupTimer.Stop();

Обновление

Так как cplotts, рассмотренные в комментарии, вы также захотите установить _popup.IsOpen = false в некоторых ситуациях в событии MouseLeave, в зависимости от вашей логики для обработки событий ввода/вывода мыши между вашим элементом управления и всплывающим окном. Имейте в виду, что вы обычно не хотите слепо устанавливать IsOpen=false в каждое событие MouseLeave, потому что это может произойти, когда всплывающее окно появляется над ним. В некоторых ситуациях это приведет к появлению мерцающего всплывающего окна. Поэтому вам понадобится какая-то логика.

Ответ 4

System.Windows.Controls.ToolTip tp = new System.Windows.Controls.ToolTip();

System.Windows.Threading.DispatcherTimer tooltipTimer =
    new System.Windows.Threading.DispatcherTimer
    (
        System.Windows.Threading.DispatcherPriority.Normal
    );

private void TooltipInvalidCharacter()
{
    tp.Content =
        "A flie name cannot contain any of the following character :" +
        "\n" + "\t" + "\\  / : *  ?  \"  <  >  |";

    tooltipTimer.Interval = TimeSpan.FromSeconds(5);
    tooltipTimer.Tick += new EventHandler(tooltipTimer_Tick);
    tp.IsOpen = true;
    tooltipTimer.Start();       
}

void tooltipTimer_Tick(object sender, EventArgs e)
{
     tp.IsOpen = false;
     tooltipTimer.Stop();
}

Ответ 5

Вы можете расширить XAML для этого решения, чтобы всплывающее окно оставалось открытым, пока мышь над ним, затем автоматически исчезает.

Я модифицировал образец следующим образом:

  • Создайте анимацию "ClosePopop", которая устанавливает IsOpen в False через 0,5 секунды. Я сделал это ресурсом, потому что он использовался дважды.
  • Для управляющего триггера IsMouseOver добавьте ExitAction, который запустит анимацию ClosePopup. Это дает пользователю возможность переместить указатель мыши над всплывающим окном, прежде чем он закроется. Я назвал эту анимацию "bxb"
  • Добавить триггер для всплывающего свойства IsMouseOver. На mouseover остановите (но не удаляйте) оригинальную анимацию Closexopup "bxb". Это оставляет всплывающее окно видимым; удаление анимации здесь приведет к закрытию всплывающего окна.
  • В выпадающем меню мыши запустите новую анимацию ClosePopup и удалите анимацию "bxb". Последний шаг критический, потому что в противном случае первая, остановленная анимация "bxb" сохранит всплывающее окно.

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

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 <DataTemplate x:Key="TooltipPopup">
  <Grid>
    <CheckBox
        x:Name="cb"
        Width="100"
        Height="40"
        Content="Hover Over Me"/>
    <Popup
        x:Name="popup"
        Placement="Bottom"
        PlacementTarget="{Binding ElementName=cb}">
        <Border x:Name="border" Width="400" Height="400" Background="Red"/>
    </Popup>
  </Grid>
  <DataTemplate.Resources>
    <Storyboard x:Key="ClosePopup">
        <BooleanAnimationUsingKeyFrames
                        Storyboard.TargetName="popup"
                        Storyboard.TargetProperty="IsOpen"
                        FillBehavior="Stop">
            <DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="False"/>
        </BooleanAnimationUsingKeyFrames>
    </Storyboard>
  </DataTemplate.Resources>
  <DataTemplate.Triggers>
    <Trigger SourceName="cb" Property="IsMouseOver" Value="True">
        <Trigger.EnterActions>
            <BeginStoryboard x:Name="bsb" >
                <Storyboard>
                    <BooleanAnimationUsingKeyFrames
                        Storyboard.TargetName="popup"
                        Storyboard.TargetProperty="IsOpen"
                        FillBehavior="HoldEnd">
                        <DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="True"/>
                    </BooleanAnimationUsingKeyFrames>
                </Storyboard>
            </BeginStoryboard>
        </Trigger.EnterActions>
        <Trigger.ExitActions>
            <StopStoryboard BeginStoryboardName="bsb"/>
            <BeginStoryboard x:Name="bxb" Storyboard="{StaticResource ClosePopup}"/>
        </Trigger.ExitActions>
    </Trigger>
    <Trigger SourceName="popup" Property="IsMouseOver" Value="True">
        <Setter TargetName="border" Property="Background" Value="Blue"/>
        <Trigger.EnterActions>
            <StopStoryboard BeginStoryboardName="bxb"/>
        </Trigger.EnterActions>
        <Trigger.ExitActions>
            <BeginStoryboard Storyboard="{StaticResource ClosePopup}"/>
            <RemoveStoryboard BeginStoryboardName="bxb"/>
        </Trigger.ExitActions>
    </Trigger>
  </DataTemplate.Triggers>
 </DataTemplate>
</Page>