it-swarm.com.de

Wie kann ein WPF-Popup geöffnet werden, wenn ein anderes Steuerelement angeklickt wird, das nur XAML-Markup verwendet

Ich habe zwei Steuerelemente, einen TextBlock und einen PopUp. Wenn der Benutzer auf den Textblock (MouseDown) klickt, möchte ich das Popup anzeigen. Ich würde denken, dass ich dies mit einem EventTrigger im Popup machen könnte, aber ich kann keine Setter in einem EventTrigger verwenden, ich kann nur Storyboards starten. Ich möchte dies streng in XAML tun, da sich die beiden Steuerelemente in einer Vorlage befinden und ich nicht weiß, wie ich das Popup im Code finden würde.

Dies ist, was ich konzeptionell tun möchte, aber ich kann es nicht, weil Sie in einem EventTrigger keinen Setter setzen können (wie Sie es mit einem DataTrigger tun können):

<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 Popup streng in XAML anzuzeigen, wenn ein Ereignis auf einem anderen Steuerelement stattfindet?

55
viggity

Ich habe etwas einfaches gemacht, aber es funktioniert.

Ich habe einen typischen ToggleButton verwendet, den ich als Textblock umgestaltet habe, indem ich seine Steuerungsvorlage änderte. Dann habe ich einfach die IsChecked-Eigenschaft des ToggleButton an die IsOpen-Eigenschaft im Popup gebunden. Popup hat einige Eigenschaften wie StaysOpen, mit denen Sie das Schließverhalten ändern können.

Folgendes funktioniert 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>
80
John Melville

Die folgende Vorgehensweise ist dieselbe wie bei Helge Klein, mit der Ausnahme, dass das Popup-Fenster automatisch geschlossen wird, wenn Sie auf eine beliebige Stelle außerhalb des Popup-Fensters klicken (einschließlich des ToggleButton-Objekts):

<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" wird in der IsHitTestVisible-Bindung verwendet, sodass das Popup beim erneuten Klicken auf den ToggleButton geschlossen wird:

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

... die praktische Technik der Kombination von IValueConverter und MarkupExtension in einem.

Ich habe ein Problem mit dieser Technik entdeckt: WPF ist fehlerhaft, wenn zwei Popups gleichzeitig auf dem Bildschirm angezeigt werden. Wenn sich die Umschaltfläche in der Symbolleiste im "Überlauf-Popup" befindet, werden nach dem Klicken zwei Popup-Fenster geöffnet. Sie können dann feststellen, dass das zweite Popup (Ihr Popup) geöffnet bleibt, wenn Sie auf eine andere Stelle in Ihrem Fenster klicken. An diesem Punkt ist das Schließen des Popup-Fensters schwierig. Der Benutzer kann nicht erneut auf die ToggleButton-Schaltfläche klicken, um das Popup zu schließen, da IsHitTestVisible false ist, da das Popup geöffnet ist! In meiner App musste ich ein paar Hacks verwenden, um dieses Problem zu mildern, beispielsweise den folgenden Test im Hauptfenster, in dem (in der Stimme von Louis Black) "wenn das Popup geöffnet ist und der Benutzer irgendwo außerhalb des Popup klickt Schließen Sie das 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;
    }
};
50
Qwertie

Ich hatte einige Probleme mit dem MouseDown-Teil davon, aber hier ist ein bisschen Code, der Ihnen den Einstieg erleichtern könnte.

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

Wie wäre es mit:

<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 referenziert und umgekehrt. Daher ist x:Name="..." sowohl für die Popup als auch für die Button erforderlich.

Es kann tatsächlich weiter vereinfacht werden, indem das Storyboard-Zeugs durch eine benutzerdefinierte SetProperty-EventTrigger-Aktion ersetzt wird, die in diesem SO beschrieben wird. Antwort

8

eine andere Möglichkeit, 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>
0
Mike