所有按钮的一个弹出窗口

One popup for all button

作为主题 mentioned.I 我想为我的所有按钮只使用一个弹出窗口 application.I 不知道如何获得我想要的内容。 这是我的 window 的样子:

信息 1:

信息 2:

您可以看到弹出窗口出现错误 position.I 知道我可以通过设置 PlacementTarget.But 来定位弹出窗口 PlacementTarget.But 每个弹出窗口都有不同的位置值 property.That 是 problem.I正在寻找另一种方法。

这是选项 1 的弹出窗口:

<StackPanel Orientation="Horizontal">
 <!--Option 1: text and button-->
 <TextBlock Text="Option 1"
  Margin="10"
  VerticalAlignment="Center" />
 <Popup x:Name="popInfo"
   PlacementTarget="{Binding ElementName=btnInfoOption1}"
   IsOpen="{Binding IsShowInfo1}">
    <ContentControl Style="{StaticResource ContentInfoStyle}">
     <TextBlock Text="{Binding InfoContent}"
      TextWrapping="Wrap"
      Foreground="White"
      Width="340"
      Padding="10"
      Margin="30,0,30,5"
      FontSize="15" />
    </ContentControl>
 </Popup>

 <Button x:Name="btnInfoOption1"
   Style="{StaticResource btnIcons}"
   Background="#0063b1"
   Width="30"
   Height="30"
   Margin="10,10,20,10"
   Command="{Binding CmdShowInfo, Delay=1500}"
   Tag="{StaticResource ic_ginfo}" />
</StackPanel>

选项 2 的弹出窗口:

<StackPanel Orientation="Horizontal">
 <!--Option 2: text and button-->
 <TextBlock Text="Option 2"
  Margin="10"
  VerticalAlignment="Center" />
  <Button x:Name="btnOption2"
   Style="{StaticResource btnIcons}"
   Background="#0063b1"
   Width="30"
   Height="30"
   Margin="10,10,20,10"
   Command="{Binding CmdShowInfo, Delay=1500}"
   Tag="{StaticResource ic_ginfo}" />
</StackPanel>

内容控件样式:

<Style TargetType="{x:Type ContentControl}"
 x:Key="ContentInfoStyle">
 <Setter Property="ContentTemplate">
  <Setter.Value>
   <DataTemplate>
     <Border Background="Green"
      CornerRadius="3"
      Padding="10,0,12,10">
     <StackPanel>
      <Button HorizontalAlignment="Right"
       Tag="{StaticResource ic_gclear}"
       Style="{StaticResource btnIcons}"
       Background="White"
       Margin="10,5,12,5"
       Command="{Binding DataContext.CmdCloseInfo}"
       Height="24" />
        <ContentPresenter x:Name="content"
          TextBlock.FontSize="14"
          TextBlock.Foreground="White"
          TextBlock.FontFamily="Arial"
          Content="{TemplateBinding ContentControl.Content}" />
    </StackPanel>
    </Border>
   </DataTemplate>
   </Setter.Value>
  </Setter>
</Style>

按钮图标样式:

<Style TargetType="Button"
  x:Key="btnIcons">        
  <Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type Button}">
      <Border x:Name="brd" Background="Transparent"
       SnapsToDevicePixels="True">
       <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
         <VisualState x:Name="Normal" />
         <VisualState x:Name="MouseOver" />
         <VisualState x:Name="Pressed" />
        </VisualStateGroup>
       </VisualStateManager.VisualStateGroups>
       <Path Stretch="Uniform" VerticalAlignment="Center"
        Fill="{TemplateBinding Background}"
        Data="{TemplateBinding Tag}" />
      </Border>
    </ControlTemplate>
  </Setter.Value>
  </Setter>
</Style>

ViewModel.cs: 弹窗内容:

private string _InfoContent;

public string InfoContent
{
  get { return _InfoContent; }
  set
  {
    if (value != _InfoContent)
    {
      _InfoContent = value;
      OnRaise("InfoContent");
    }
  }
}

显示 option2option1 的弹出窗口:

private bool _IsShowInfo2;

public bool IsShowInfo2
{
  get { return _IsShowInfo2; }
  set
  {
    if (value != _IsShowInfo2)
    {
      _IsShowInfo2 = value;
      OnRaise("IsShowInfo2");
    }
  }
}
//show the popup for option1
private bool _IsShowInfo1;

public bool IsShowInfo1
{
  get { return _IsShowInfo1; }
  set
  {
    if (value != _IsShowInfo1)
    {
      _IsShowInfo1 = value;
      OnRaise("IsShowInfo1");
    }
  }
}

按钮命令:

private ICommand _CmdShowInfo;

public ICommand CmdShowInfo
{
  get
  {
    _CmdShowInfo = _CmdShowInfo ?? new RelayCommand(x => this.ShowInfo(true, 1), () => true);
    return _CmdShowInfo;
  }
}

private ICommand _CmdShowInfo2;

public ICommand CmdShowInfo2
{
  get
  {
    _CmdShowInfo2 = _CmdShowInfo2 ?? new RelayCommand(x => this.ShowInfo(true, 0), () => true);
    return _CmdShowInfo2;
  }
}

private void ShowInfo(bool show = true, byte option = 0)
{
  if (option == 0)
  {
    this.InfoContent = "Option 1...";
  }
  else if (option == 1)
  {
    this.InfoContent = "Option 2...";
  }
  this.IsShowInfo1 = show;
}

我最初的想法是使用样式 HeaderedContentControl 来完成此操作,但是您已经获得了图标填充颜色和图标数据,我不得不为它们添加附加属性。一旦你去了那里,你还不如写一个自定义控件。

IconPopupButton 的依赖属性可以像任何依赖一样绑定 属性:

<hec:IconPopupButton
    IsOpen="{Binding IsShowInfo1}"
    IconFill="YellowGreen"
    Content="Another Test Popup"
    IconData="M -10,-10 M 0,3 L 17,20 L 20,17 L 3,0 Z M 0,0 L 0,20 L 20,20 L 20,0 Z"
    />

如果要参数化 Style 应用于 Popup 中的 ContentControl,请添加另一个依赖项 属性。不过,您需要考虑一下,因为您需要将 ToggleButton 以某种方式绑定到模板化父级上的 IsOpen。或许您可以将它绑定到视图模型 属性,后者绑定到弹出按钮的 IsOpen。总有办法。

就是这样。使用片段创建依赖属性,这几乎只是一个填空练习。远比看起来要少得多。

IconPopupButton.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace HollowEarth.Controls
{
    public class IconPopupButton : ContentControl
    {
        static IconPopupButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(IconPopupButton), new FrameworkPropertyMetadata(typeof(IconPopupButton)));
        }

        #region IconData Property
        public Geometry IconData
        {
            get { return (Geometry)GetValue(IconDataProperty); }
            set { SetValue(IconDataProperty, value); }
        }

        public static readonly DependencyProperty IconDataProperty =
            DependencyProperty.Register("IconData", typeof(Geometry), typeof(IconPopupButton),
                new PropertyMetadata(null));
        #endregion IconData Property

        #region IconFill Property
        public Brush IconFill
        {
            get { return (Brush)GetValue(IconFillProperty); }
            set { SetValue(IconFillProperty, value); }
        }

        public static readonly DependencyProperty IconFillProperty =
            DependencyProperty.Register("IconFill", typeof(Brush), typeof(IconPopupButton),
                new PropertyMetadata(SystemColors.ControlTextBrush));
        #endregion IconFill Property

        #region IsOpen Property
        public bool IsOpen
        {
            get { return (bool)GetValue(IsOpenProperty); }
            set { SetValue(IsOpenProperty, value); }
        }

        public static readonly DependencyProperty IsOpenProperty =
            DependencyProperty.Register("IsOpen", typeof(bool), typeof(IconPopupButton),
                new PropertyMetadata(false));
        #endregion IsOpen Property

        #region StaysOpen Property
        public bool StaysOpen
        {
            get { return (bool)GetValue(StaysOpenProperty); }
            set { SetValue(StaysOpenProperty, value); }
        }

        public static readonly DependencyProperty StaysOpenProperty =
            DependencyProperty.Register("StaysOpen", typeof(bool), typeof(IconPopupButton),
                new PropertyMetadata(false));
        #endregion StaysOpen Property

        #region Placement Property
        public PlacementMode Placement
        {
            get { return (PlacementMode)GetValue(PlacementProperty); }
            set { SetValue(PlacementProperty, value); }
        }

        public static readonly DependencyProperty PlacementProperty =
            DependencyProperty.Register("Placement", typeof(PlacementMode), typeof(IconPopupButton),
                new PropertyMetadata(PlacementMode.Right));
        #endregion Placement Property
    }
}

Themes\Shared.xaml

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:HeaderedPopupTest.Themes"
    >
    <Geometry x:Key="ic_gclear">M56,4 52,0 28,24 4,0 0,4 24,28 0,52 4,56 28,32 52,56 56,52 32,28Z</Geometry>
    <Geometry x:Key="ic_ginfo">M31,0C13.879,0,0,13.879,0,31s13.879,31,31,31s31-13.879,31-31S48.121,0,31,0z M34,46h-6V27.969h6V46z M34,21.969h-6V16h6V21.969z</Geometry>

    <Style TargetType="ButtonBase" x:Key="btnIcons">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ButtonBase}">
                    <Border x:Name="brd" Background="Transparent" SnapsToDevicePixels="True">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="MouseOver" />
                                <VisualState x:Name="Pressed" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid>
                            <Path 
                                x:Name="Path"
                                Stretch="Uniform" 
                                VerticalAlignment="Center" 
                                Fill="{TemplateBinding Background}"
                                Data="{TemplateBinding Tag}" 
                                />
                            <TextBlock 
                                x:Name="MissingIconData"
                                Visibility="Collapsed" 
                                Text="?" 
                                FontWeight="Bold" 
                                FontSize="30" 
                                ToolTip="IconData (Tag) not set"
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                />
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Tag" Value="{x:Null}">
                            <Setter TargetName="MissingIconData" Property="Visibility" Value="Visible" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Themes\Generic.xaml

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:HeaderedPopupTest.Themes"
    xmlns:hec="clr-namespace:HollowEarth.Controls"
    >

    <ResourceDictionary.MergedDictionaries>
        <!-- Change HeaderedPopupTest to the name of your own assembly -->
        <ResourceDictionary Source="/HeaderedPopupTest;component/Themes/Shared.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="hec:IconPopupButton">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="hec:IconPopupButton">
                    <Grid x:Name="Grid">
                        <ToggleButton
                            x:Name="OpenButton"
                            Style="{StaticResource btnIcons}"
                            Background="{TemplateBinding IconFill}"
                            Tag="{TemplateBinding IconData}"
                            IsChecked="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                            ToolTip="{TemplateBinding ToolTip}"
                            />
                        <Popup
                            x:Name="Popup"
                            StaysOpen="{Binding StaysOpen, RelativeSource={RelativeSource TemplatedParent}}"
                            IsOpen="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                            PlacementTarget="{Binding ElementName=ToggleButton}"
                            Placement="{TemplateBinding Placement}"
                            >
                            <Border 
                                Background="Green"
                                CornerRadius="3"
                                Padding="10,0,12,10">
                                <StackPanel>
                                    <ToggleButton 
                                        HorizontalAlignment="Right" 
                                        Tag="{StaticResource ic_gclear}"
                                        Style="{StaticResource btnIcons}"
                                        Background="White"
                                        Margin="10,5,12,5"
                                        IsChecked="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                                        Height="24" 
                                        />
                                    <ContentPresenter 
                                        x:Name="content"
                                        TextBlock.FontSize="14"
                                        TextBlock.Foreground="White"
                                        TextBlock.FontFamily="Arial"
                                        Content="{TemplateBinding Content}"
                                        />
                                </StackPanel>
                            </Border>
                        </Popup>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <!-- 
                                I don't understand this: If I use the templated parent's IsOpen, 
                                the effect is as if it were never true. 
                                -->
                                <Condition SourceName="Popup" Property="IsOpen" Value="True" />
                                <Condition Property="StaysOpen" Value="False" />
                            </MultiTrigger.Conditions>
                            <!-- 
                            If StaysOpen is false and the button is enabled while the popup is open, 
                            then clicking on it will cause the popup to flicker rather than close. 
                            -->
                            <Setter TargetName="OpenButton" Property="IsEnabled" Value="False" />
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

MainWindow.xaml 用法示例:

<Window 
    x:Class="HeaderedPopupTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:HeaderedPopupTest"
    xmlns:hec="clr-namespace:HollowEarth.Controls"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525"
    >
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Themes\Shared.xaml" />
            </ResourceDictionary.MergedDictionaries>

            <Style 
                x:Key="InfoPopupButton" 
                TargetType="hec:IconPopupButton" 
                BasedOn="{StaticResource {x:Type hec:IconPopupButton}}"
                >
                <Setter Property="IconFill" Value="DeepSkyBlue" />
                <Setter Property="IconData" Value="{StaticResource ic_ginfo}" />
            </Style>
        </ResourceDictionary>
    </Window.Resources>

    <Grid>
        <StackPanel 
            Orientation="Vertical"
            HorizontalAlignment="Left"
            >
            <hec:IconPopupButton
                Style="{StaticResource InfoPopupButton}"
                Content="This is a test popup"
                ToolTip="Test Popup Tooltip"
                />
            <hec:IconPopupButton
                IconFill="YellowGreen"
                Content="Another Test Popup"
                IconData="M -10,-10 M 0,3 L 17,20 L 20,17 L 3,0 Z M 0,0 L 0,20 L 20,20 L 20,0 Z"
                />
            <hec:IconPopupButton
                IconFill="DarkRed"
                Content="Missing IconData behavior example"
                />
        </StackPanel>
    </Grid>
</Window>

您会注意到我将您的按钮更改为 ToggleButton。这是为了方便将它们连接到 IsOpen 属性:使用 ToggleButton,我只需绑定 IsChecked 即可。不需要命令。这样做的一个副作用是,如果 StaysOpenfalse,则当用户单击 Popup 的打开按钮时,焦点更改会关闭 Popup,从而取消选中按钮,然后然后按钮获取鼠标消息。所以按钮再次打开弹出窗口。从用户的角度来看,这是一种奇怪的行为,因此您添加了一个触发器以在弹出窗口打开且 StaysOpen 为 false 时禁用该按钮。当 StaysOpen 为真时,焦点更改不会关闭 Popup,因此您希望在这种情况下启用该按钮。

我将 btnIcons 样式更改为目标 ButtonBase,因此它与 ButtonToggleButton 的工作方式相同。