2008-12-11 7 views
54

Ich habe zwei Steuerelemente, ein TextBlock und ein PopUp. Wenn der Benutzer auf den Textblock klickt (MouseDown), möchte ich das Popup anzeigen. Ich würde denken, dass ich dies mit einem EventTrigger auf dem Popup machen könnte, aber ich kann keine Setter in einem EventTrigger verwenden, ich kann nur Storyboards starten. Ich möchte dies ausschließlich in XAML tun, weil die beiden Steuerelemente in einer Vorlage sind und ich nicht weiß, wie ich das Popup im Code finden würde.Wie öffne ich ein WPF-Popup, wenn ein anderes Steuerelement angeklickt wird, nur mit XAML-Markup?

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

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

Antwort

77

Ich habe etwas Einfaches getan, aber es funktioniert.

Ich verwendete einen typischen ToggleButton, den ich als Textblock neu gestaltete, indem ich seine Kontrollschablone änderte. Dann habe ich nur die IsChecked-Eigenschaft auf dem ToggleButton an die IsOpen-Eigenschaft im Popup gebunden. Popup verfügt über 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> 
+0

interessanter Ansatz – viggity

+4

siehe auch @ Qwertie Ansatz beschrieben Eventtrigger Aktion mit einem benutzerdefinierten Material vereinfacht werden - eine nützlichere Version von dieser inspiriert, dass das Popup automatisch geschlossen wird, wenn Sie alt-Tab oder klicken Sie außerhalb des Popup –

8

Ich hatte einige Probleme mit dem MouseDown-Teil von diesem, aber hier ist ein Code, mit dem Sie beginnen 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> 
48

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" verwendet wird in die IsHitTestVisible Bindung, so dass, wenn Sie die ToggleButton erneut auf, schließt sich das Popup:

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

... welche die handliche Technik von combining IValueConverter and MarkupExtension in einem zeigt.

Ich habe ein Problem mit dieser Technik entdeckt: WPF ist fehlerhaft, wenn zwei Popups gleichzeitig auf dem Bildschirm sind. Genauer gesagt, wenn sich die Umschalttaste im "Überlauf-Popup" in einer Symbolleiste befindet, werden zwei Popups geöffnet, nachdem Sie darauf geklickt haben. Sie werden dann feststellen, dass das zweite Popup (Ihr Popup) geöffnet bleibt, wenn Sie irgendwo anders auf Ihr Fenster klicken. An diesem Punkt ist das Schließen des Popups schwierig. Der Benutzer kann nicht erneut auf den ToggleButton klicken, um das Popup zu schließen, da IsHitTestVisible false ist, weil das Popup geöffnet ist! In meiner App musste ich ein paar Hacks verwenden, um dieses Problem zu mildern, wie den folgenden Test im Hauptfenster, der sagt (in der Stimme von Louis Black): "Wenn das Popup geöffnet ist und der Benutzer irgendwo außerhalb des Popups klickt, 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; 
    } 
}; 
+1

funktioniert dies sehr gut - auch alt-Tab in einer anderen Anwendung oder klicken irgendwo anders im Fenster –

+1

die Staysopen = „false“ ist der Schlüssel für das out-of-Komponente automatisch geschlossen Verhalten. Warum oh warum ist das standardmäßig wahr? –

+1

@ChrisDolan Ich bin so genervt von Dingen wie Standardeinstellungen, die im Grunde gegen jedes UX-Prinzip verstoßen, das ich kenne. Also mache ich einen impliziten Stil für "Popup" und setze die Standardeinstellungen, die ich mag, einschließlich "StaysOpen = False". – erodewald

0

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

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

Es kann tatsächlich weiter durch Ersetzen des StoryboardSetPropertyin this SO Answer

Verwandte Themen