如何仅使用 XAML 标记在单击另一个控件时打开 WPF 弹出窗口?
-
21-08-2019 - |
题
我有两个控件,一个 TextBlock 和一个 PopUp。当用户在文本块上单击(MouseDown)时,我想显示弹出窗口。我认为我可以使用弹出窗口上的 EventTrigger 来完成此操作,但我不能在 EventTrigger 中使用设置器,我只能启动故事板。我想严格在 XAML 中执行此操作,因为这两个控件位于模板中,并且我不知道如何在代码中找到弹出窗口。
从概念上讲,这是我想做的,但不能,因为你不能将 setter 放入 EventTrigger 中(就像使用 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>
...
当事件发生在不同的控件上时,严格在 XAML 中显示弹出窗口的最佳方法是什么?
解决方案
我做了简单的东西,但它的作品。
我使用的典型的切换按钮,这是我重新设计为通过改变其控制模板正文块。然后,我只是绑定在切换按钮的财产器isChecked对弹出的IsOpen属性。弹出窗口有一个像StaysOpen一些属性,让您修改闭幕行为。
在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>
其他提示
以下方法与 Helge Klein 的方法相同,只是当您单击 Popup 外部的任何位置(包括 ToggleButton 本身)时,弹出窗口会自动关闭:
<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>
IsHitTestVisible 绑定中使用了“BoolInverter”,因此当您再次单击 ToggleButton 时,弹出窗口将关闭:
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);
}
}
...这展示了方便的技术 结合 IValueConverter 和 MarkupExtension 合二为一。
我确实发现了这种技术的一个问题:当屏幕上同时出现两个弹出窗口时,WPF 就会出现问题。具体来说,如果您的切换按钮位于工具栏中的“溢出弹出窗口”上,则单击它后将打开两个弹出窗口。然后,您可能会发现,当您单击窗口上的其他任何位置时,第二个弹出窗口(您的弹出窗口)将保持打开状态。那时,关闭弹出窗口很困难。用户无法再次单击 ToggleButton 来关闭弹出窗口,因为 IsHitTestVisible 为 false,因为弹出窗口已打开!在我的应用程序中,我不得不使用一些技巧来缓解这个问题,例如在主窗口上进行以下测试,其中(以路易斯·布莱克的声音)“如果弹出窗口打开并且用户单击弹出窗口之外的某个位置,关闭该死的弹出窗口。”:
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;
如何:
<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>
请注意,Popup
被referecing通过名称Button
,反之亦然。所以x:Name="..."
需要两个,所述Popup
和Button
。
实际上,它可以进一步通过与自定义Storyboard
EventTrigger行动更换SetProperty
东西简化描述在该SO回答
我有一些问题,这鼠标按下的一部分,但这里是一些代码,可能让你的开始。
<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>
另一种方式来做到这一点:
<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>