Wie ein WPF-Popup zu öffnen, wenn ein anderes Steuerelement angeklickt wird, nur XAML Markup?

StackOverflow https://stackoverflow.com/questions/361209

  •  21-08-2019
  •  | 
  •  

Frage

Ich habe zwei Kontrollen bekommt, einen Textblock und ein PopUp. Wenn der Benutzer klickt (MouseDown-) auf dem Textblock, möchte ich das Popup angezeigt werden soll. Ich würde denken, dass ich dies mit einem Eventtrigger auf dem Popup tun könnte, aber ich kann nicht Setter in einem Eventtrigger verwenden, kann ich nur Storyboards starten. Ich möchte dies in XAML streng tun, denn die beiden Kontrollen in einer Vorlage sind, und ich weiß nicht, wie ich das Popup in Code finden würde.

Dies ist, was konzeptionell ich tun möchte, kann aber nicht, weil Sie nicht einen Setter in einem Eventtrigger setzen können (wie kann man mit einem Datatrigger):

<TextBlock x:Name="CCD">Some text</TextBlock>

<Popup>
    <Popup.Style>
        <Style>
            <Style.Triggers>
                <EventTrigger SourceName="CCD" RoutedEvent="MouseDown">
                    <Setter Property="Popup.IsOpen" Value="True" />
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Popup.Style>
...

Was ist der beste Weg, um ein Pop-up streng in XAML zu zeigen, wenn ein Ereignis auf einer anderen Steuerung geschieht?

War es hilfreich?

Lösung

Ich habe etwas einfach, aber es funktioniert.

Ich habe eine typische ToggleButton, die ich als Textblock durch Änderung seiner Steuervorlage neu gestaltet. Dann habe ich gebunden nur die IsChecked Eigenschaft auf der ToggleButton zum IsOpen Eigenschaft im Popup. Popup hat einige Eigenschaften wie Staysopen mit denen Sie die Schließverhalten ändern.

Die folgenden Arbeiten in XamlPad.

 <StackPanel>
  <ToggleButton Name="button"> 
    <ToggleButton.Template>
      <ControlTemplate TargetType="ToggleButton">
        <TextBlock>Click Me Here!!</TextBlock>
      </ControlTemplate>      
    </ToggleButton.Template>
  </ToggleButton>
  <Popup IsOpen="{Binding IsChecked, ElementName=button}" StaysOpen="False">
    <Border Background="LightYellow">
      <TextBlock>I'm the popup</TextBlock>
    </Border>
  </Popup> 
 </StackPanel>

Andere Tipps

Die folgende Vorgehensweise ist die gleiche wie Helge Klein, mit der Ausnahme, dass das Popup automatisch schließt, wenn Sie irgendwo außerhalb des Popup klicken (einschließlich der ToggleButton selbst):

<ToggleButton x:Name="Btn" IsHitTestVisible="{Binding ElementName=Popup, Path=IsOpen, Mode=OneWay, Converter={local:BoolInverter}}">
    <TextBlock Text="Click here for popup!"/>
</ToggleButton>

<Popup IsOpen="{Binding IsChecked, ElementName=Btn}" x:Name="Popup" StaysOpen="False">
    <Border BorderBrush="Black" BorderThickness="1" Background="LightYellow">
        <CheckBox Content="This is a popup"/>
    </Border>
</Popup>

"BoolInverter" in der IsHitTestVisible verwendet Bindung, so dass, wenn Sie die ToggleButton erneut klicken, wird das Popup schließt:

public class BoolInverter : MarkupExtension, IValueConverter
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this;
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool)
            return !(bool)value;
        return value;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Convert(value, targetType, parameter, culture);
    }
}

... das zeigt die praktische Technik des Kombination IValueConverter und Markupextension in einem.

Ich habe ein Problem mit dieser Technik entdecken: WPF Buggy ist, wenn zwei Pop-ups auf dem Bildschirm zur gleichen Zeit sind. Insbesondere dann, wenn Ihre Schalter auf dem „Überlauf Popup“ in einer Symbolleiste ist, dann wird es zwei Popups geöffnet sein, nachdem Sie es klicken. Sie können dann feststellen, dass das zweite Popup (Ihr Popup) offen bleiben, wenn Sie irgendwo anders auf Ihrem Fenster klicken. An diesem Punkt ist das Popup zu schließen schwierig. Der Benutzer kann die ToggleButton klicken Sie erneut auf das Popup zu schließen, weil IsHitTestVisible falsch ist, weil das Popup geöffnet ist! In meiner app hatte ich ein paar Hacks zu verwenden, um dieses Problem, wie der folgenden Test auf dem Hauptfenster zu mildern, die sagen (in der Stimme von Louis Schwarz) „, wenn das Popup geöffnet ist und der Benutzer klickt irgendwo außerhalb des Popup, schließen Sie die friggin Popup ".

PreviewMouseDown += (s, e) =>
{
    if (Popup.IsOpen)
    {
        Point p = e.GetPosition(Popup.Child);
        if (!IsInRange(p.X, 0, ((FrameworkElement)Popup.Child).ActualWidth) ||
            !IsInRange(p.Y, 0, ((FrameworkElement)Popup.Child).ActualHeight))
            Popup.IsOpen = false;
    }
};
// Elsewhere...
public static bool IsInRange(int num, int lo, int hi) => 
    num >= lo && num <= hi;

Wie wäre:

<Button x:Name="OpenPopup">Popup
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames 
                                 Storyboard.TargetName="ContextPopup" 
                                 Storyboard.TargetProperty="IsOpen">
                            <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>
</Button>
<Popup x:Name="ContextPopup"
       PlacementTarget="{Binding ElementName=OpenPopup}"
       StaysOpen="False">
    <Label>Popupcontent...</Label>
</Popup>

Bitte beachten Sie, dass die Popup die Button nach Namen wird referecing und umgekehrt. So x:Name="..." auf beiden erforderlich, die Popup und die Button.

Es kann tatsächlich weiter durch Ersetzen des Storyboard Materials in dieser SO Antwort SetProperty Eventtrigger Aktion beschrieben vereinfacht werden / p>

hatte ich einige Probleme mit dem MouseDown- Teil davon, aber hier ist ein Code, die Ihren Einstieg könnten.

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Control VerticalAlignment="Top">
            <Control.Template>
                <ControlTemplate>
                    <StackPanel>
                    <TextBox x:Name="MyText"></TextBox>
                    <Popup x:Name="Popup" PopupAnimation="Fade" VerticalAlignment="Top">
                        <Border Background="Red">
                            <TextBlock>Test Popup Content</TextBlock>
                        </Border>
                    </Popup>
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <EventTrigger RoutedEvent="UIElement.MouseEnter" SourceName="MyText">
                            <BeginStoryboard>
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup" Storyboard.TargetProperty="(Popup.IsOpen)">
                                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/>
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="UIElement.MouseLeave" SourceName="MyText">
                            <BeginStoryboard>
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup" Storyboard.TargetProperty="(Popup.IsOpen)">
                                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False"/>
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Control.Template>
        </Control>
    </Grid>
</Window>

eine andere Art und Weise, es zu tun:

<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                    <StackPanel>
                        <Image Source="{Binding ProductImage,RelativeSource={RelativeSource TemplatedParent}}" Stretch="Fill" Width="65" Height="85"/>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        <Button x:Name="myButton" Width="40" Height="10">
                            <Popup Width="100" Height="70" IsOpen="{Binding ElementName=myButton,Path=IsMouseOver, Mode=OneWay}">
                                <StackPanel Background="Yellow">
                                    <ItemsControl ItemsSource="{Binding Produkt.SubProducts}"/>
                                </StackPanel>
                            </Popup>
                        </Button>
                    </StackPanel>
                </Border>
scroll top