用户控件中的 WPF 扩展器在 ListBox 项中呈现透明

发布于 2024-10-01 15:36:30 字数 7793 浏览 1 评论 0原文

我遇到了在列表框中呈现的用户控件中的 WPF 扩展器的问题。本质上,我试图在列表框中的每个列表项上获取 PopUpButton 行为。当我打开 Expander 时,内容会呈现在其他所有内容后面,就好像它是透明的,或者在 z 顺序中较低一样。我也尝试过使用 WPF 弹出窗口和切换按钮(使用 Karle Shivllet 的博客 – Expander Control with Popup Content 中描述的技术),但没有成功。

首先让我描述一下我正在尝试做什么。我有两个控件,它们显示我需要为应用程序配置的输入列表。为了简单起见,一个用户控件用于配置图形的输入,另一个控件用于控制简单 Excel 网格的输入。图形和网格的输入各自具有需要配置的属性。我开发了一个名为 InputSelectControl 的简单用户控件,它将呈现一个 ListBox,其中包含要为图形或网格配置的输入列表。 ListBox 中的每个ListItem 都包含一个用于输入名称(例如压力、ECG 等)的TextBlock 和一个WPF 扩展器(单击时会显示该输入的属性编辑器)。由于属性编辑器的呈现方式将根据我是否处理图形输入与网格输入而有所不同,因此我在我的 InputSelectControl 上使用了 ControlTemplate 类型的 DependencyProperty。这允许我的表格和图表分别提供编辑其输入属性所需的演示文稿。另请注意,我将不仅仅是需要此行为的图形和网格,因此希望使其成为可以动态接收演示行为的用户控件。

我尝试将 Expander 放入属性编辑器模板中,还尝试在各个位置尝试使用 ZIndex,但总是以相同的行为结束,Expander 弹出窗口显示在列表中的 ListItems 后面。

下面是一些进一步描述我的方法的代码。希望有人能帮助我摆脱这个困境。

代表我的网格(可以是图形或其他东西)控件的 XAML 包含我的 InputSelectControl:

<UserControl x:Class="MyApp.GridView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:props="clr-namespace:PopupButtonDependencyProp" mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <!-- Specify the control tempalte we want loaded into the 
             properies popup for a grid-->
        <ControlTemplate x:Key="GridPropertyEditorTemplate" TargetType="ContentControl">
            <props:GridInputPropertyEditor />
        </ControlTemplate>
    </UserControl.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock Text="Hello Grid" Margin="5" />

        <!-- Tell the InputSelectControl what template to load into Property 
             Window for each Grid Input item -->
        <props:InputSelectControl Grid.Row="1" 
                 DataContext="{Binding VmUsedInputs, Mode=OneWay}" 
                 PropertyEditorTemplate="{StaticResource GridPropertyEditorTemplate}" />
    </Grid>
</UserControl>

XAML 代表我的 InputSelectControl ,它显示我的输入列表和每个 ListItem 的 ContentControl 占位符,其中我想要我的“Popup”行为”用于编辑属性:

<UserControl x:Class="MyApp.InputSelectControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:props="clr-namespace:PopupButtonDependencyProp" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <!-- Listbox holding our inputs. Assuming whatever we're contained in has 
         set our DataContext to a valid Input collection-->
    <ListBox x:Name="inputsUsed" Grid.Row="1" ItemsSource="{Binding}"         
             ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
             ScrollViewer.VerticalScrollBarVisibility="Auto" 
             SelectionMode="Multiple" ClipToBounds="True">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Border x:Name="border" CornerRadius="7">
            <StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
               <!-- Input label-->
               <TextBlock Text="{Binding Path=Label}" FontWeight ="Bold"
                          FontSize ="12" FontStyle = "Normal" 
                          HorizontalAlignment="Stretch"
                          VerticalAlignment="Stretch" Margin="5,0,5,0" />
               <Expander x:Name="GridPropEditor" Header="Properties" 
                         Height="Auto" Margin="5,0,0,0" 
                         ToolTip="Open trace property dialog">
                  <!-- Properties button - The ContentControl below is rendering 
                       the PropertyEditorTemplate that was set by whoever contains us -->
                  <ContentControl  Template="{Binding PropertyEditorTemplate, 
                                   RelativeSource={RelativeSource AncestorType=props:InputSelectControl}}" />
                     </Expander>
            </StackPanel>
          </Border>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </UserControl>

C# 代表我的 DependencyProperty,用于注入属性编辑器模板以在弹出窗口中显示。

/// <summary>
/// Interaction logic for InputSelectControl.xaml
/// </summary>
public partial class InputSelectControl : UserControl
{
    #region Dependency Property stuff

    /// <summary>
    /// Dependency Property for control template to be rendered. This 
    /// lets us adorn the InputSelectControl with content in the Xaml. 
    /// The content can be different fore each instance of InputSelectControl.
    /// </summary>
    public static DependencyProperty PropertyEditorTemplateProperty =
        DependencyProperty.Register("PropertyEditorTemplate", 
                typeof(ControlTemplate), typeof(InputSelectControl));

    /// <summary>
    /// PropertyEditorTemplate. This is how the property is set and get by WPF
    /// </summary>
    public ControlTemplate PropertyEditorTemplate
    {
        get { return GetValue(PropertyEditorTemplateProperty) as ControlTemplate; }
        set { SetValue(PropertyEditorTemplateProperty, value); }
    }

    #endregion

    /// <summary>
    /// Constructor
    /// </summary>
    public InputSelectControl()
    {
        InitializeComponent();
    }
}

代表我的 GridInputPropertyEditor 的 XAML,它是描述编辑网格属性的演示文稿的模板。这对于图表来说是不同的:

<UserControl x:Class="MyApp.GridInputPropertyEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

<Canvas Panel.ZIndex=”99”>
    <!-- Property Editor Control - Assumes DataContext holds to the properties 
         that need to be edited-->
    <StackPanel Orientation="Vertical" Background="WhiteSmoke">

        <!-- Lists the properties for a Grid to be edited. We could use 
             any layout we need here. -->
        <ListBox ItemsSource="{Binding Properties}" Background="WhiteSmoke" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="5,5">
                        <TextBlock Text="{Binding Label}" FontWeight="Bold"/>
                        <TextBlock Text=":" />
                        <TextBox Text="{Binding Value}" Margin="10,0" Width="20" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Canvas>
</UserControl>

使用 Snoop 我能够发现,如果我将 ListBox 项目的 Z-Index 设置为一个较高的数字,我的属性编辑器就会进入前台。如果有人看到更好的解决方法,请告诉我。否则,我可以使用一些帮助来提供触发器,以根据所选项目来提高和降低 zindex。

I’m having issues with a WPF Expander that I have in a user control that gets rendered in a ListBox. Essentially I’m trying to get PopUpButton behavior on each ListItem in my ListBox. When I open the Expander the content is rendering behind everything else as if it were transparent, or lower in the z-order. I’ve tried this with a WPF PopUp and Toggle Button as well (using techniques described int Karle Shivllet’s blog – Expander Control with Popup Content) to no avail.

Let me first describe what it is I’m trying to do. I have two controls that display a list of inputs that I need to configure for my application. For simplicity sake, one user control is used to configure inputs to a graph, and another control is used to control inputs to a simple excel grid. The inputs for the graph and grid each have properties that need to be configured on them. I’ve developed a simple user control called InputSelectControl that will render a ListBox containing the list of inputs to be configured for the graph or grid. Each ListItem in the ListBox consist of a TextBlock for the input’s name (e.g. Pressure, ECG, etc.) and a WPF Expander that , when clicke, displays a property editor for that input. Since the property editor presentation will be different depending on whether I’m dealing with graph inputs versus grid inputs, I’ve used a DependencyProperty on my InputSelectControl that is of type ControlTemplate. This allows my grid and graph to each supply the presentation they need for editing their input properties. Also note that I will have more than just a graph and a grid that need this behavior, thus the desire to make this a user control that can dynamically receive presentation behavior.

I’ve tried placing my Expander inside my property editor template, had have also tried experimenting with the ZIndex in various places, but always end up with the same behavior, the Expander popup displays behind the ListItems in my list.

Below is some code further describing my approach. Hopefully someone can help me out of this pickle.

XAML representing my Grid (could be graph, or something else) control that hold my InputSelectControl:

<UserControl x:Class="MyApp.GridView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:props="clr-namespace:PopupButtonDependencyProp" mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <!-- Specify the control tempalte we want loaded into the 
             properies popup for a grid-->
        <ControlTemplate x:Key="GridPropertyEditorTemplate" TargetType="ContentControl">
            <props:GridInputPropertyEditor />
        </ControlTemplate>
    </UserControl.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock Text="Hello Grid" Margin="5" />

        <!-- Tell the InputSelectControl what template to load into Property 
             Window for each Grid Input item -->
        <props:InputSelectControl Grid.Row="1" 
                 DataContext="{Binding VmUsedInputs, Mode=OneWay}" 
                 PropertyEditorTemplate="{StaticResource GridPropertyEditorTemplate}" />
    </Grid>
</UserControl>

XAML representing my InputSelectControl that displays my list of inputs and a ContentControl place holder for each ListItem where I want my "Popup behavior" for editing properties:

<UserControl x:Class="MyApp.InputSelectControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:props="clr-namespace:PopupButtonDependencyProp" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <!-- Listbox holding our inputs. Assuming whatever we're contained in has 
         set our DataContext to a valid Input collection-->
    <ListBox x:Name="inputsUsed" Grid.Row="1" ItemsSource="{Binding}"         
             ScrollViewer.HorizontalScrollBarVisibility="Disabled" 
             ScrollViewer.VerticalScrollBarVisibility="Auto" 
             SelectionMode="Multiple" ClipToBounds="True">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Border x:Name="border" CornerRadius="7">
            <StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
               <!-- Input label-->
               <TextBlock Text="{Binding Path=Label}" FontWeight ="Bold"
                          FontSize ="12" FontStyle = "Normal" 
                          HorizontalAlignment="Stretch"
                          VerticalAlignment="Stretch" Margin="5,0,5,0" />
               <Expander x:Name="GridPropEditor" Header="Properties" 
                         Height="Auto" Margin="5,0,0,0" 
                         ToolTip="Open trace property dialog">
                  <!-- Properties button - The ContentControl below is rendering 
                       the PropertyEditorTemplate that was set by whoever contains us -->
                  <ContentControl  Template="{Binding PropertyEditorTemplate, 
                                   RelativeSource={RelativeSource AncestorType=props:InputSelectControl}}" />
                     </Expander>
            </StackPanel>
          </Border>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </UserControl>

C# representing my DependencyProperty for injection the property editor template to present on popup.

/// <summary>
/// Interaction logic for InputSelectControl.xaml
/// </summary>
public partial class InputSelectControl : UserControl
{
    #region Dependency Property stuff

    /// <summary>
    /// Dependency Property for control template to be rendered. This 
    /// lets us adorn the InputSelectControl with content in the Xaml. 
    /// The content can be different fore each instance of InputSelectControl.
    /// </summary>
    public static DependencyProperty PropertyEditorTemplateProperty =
        DependencyProperty.Register("PropertyEditorTemplate", 
                typeof(ControlTemplate), typeof(InputSelectControl));

    /// <summary>
    /// PropertyEditorTemplate. This is how the property is set and get by WPF
    /// </summary>
    public ControlTemplate PropertyEditorTemplate
    {
        get { return GetValue(PropertyEditorTemplateProperty) as ControlTemplate; }
        set { SetValue(PropertyEditorTemplateProperty, value); }
    }

    #endregion

    /// <summary>
    /// Constructor
    /// </summary>
    public InputSelectControl()
    {
        InitializeComponent();
    }
}

XAML representing my GridInputPropertyEditor which is the template describing the presentation for editing Grid properties. This will be different for a Graph:

<UserControl x:Class="MyApp.GridInputPropertyEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

<Canvas Panel.ZIndex=”99”>
    <!-- Property Editor Control - Assumes DataContext holds to the properties 
         that need to be edited-->
    <StackPanel Orientation="Vertical" Background="WhiteSmoke">

        <!-- Lists the properties for a Grid to be edited. We could use 
             any layout we need here. -->
        <ListBox ItemsSource="{Binding Properties}" Background="WhiteSmoke" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="5,5">
                        <TextBlock Text="{Binding Label}" FontWeight="Bold"/>
                        <TextBlock Text=":" />
                        <TextBox Text="{Binding Value}" Margin="10,0" Width="20" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Canvas>
</UserControl>

Using Snoop I was able to figure out that if I set the Z-Index of the ListBox item to a high number my property editor comes to the foreground. If someone sees a better way around this let me know. Otherwise I could use some help coming up with a trigger to raise and lower the zindex based on the item selected.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

街角卖回忆 2024-10-08 15:36:30

好吧,经过几次尝试和磨难,我能够使用后台代码提出解决方案。我有兴趣找到一种使用触发器来做到这一点的方法,但我不确定这种方法是否可行。

这是更新扩展器 XAML:

<Expander x:Name="GridPropEditor" Header="Properties" Height="Auto" Margin="5,0,0,0" 
          ToolTip="Open trace property dialog"
          Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                                         AncestorType={x:Type ListBoxItem}}}"
          PreviewMouseDown="GridPropEditor_PreviewMouseDown"  
          Expanded="GridPropEditor_Expanded">

这是我添加到 xaml.cs 文件中的代码

//When an expander is expanded, collapse all the other expanders
private void GridPropEditor_Expanded(object sender, RoutedEventArgs e)
{
    if (ExpandersSelected == null)
        ExpandersSelected = new List<Expander>();

    var expander = (sender as Expander);
    var listbox = expander.Tag as ListBoxItem;

    if (!ExpandersSelected.Contains(expander))
        ExpandersSelected.Add(expander);

    if (ExpandersSelected != null)
    {
        foreach(var x in ExpandersSelected)
        {
            if (x.Equals(expander))
            {
                listbox.SetValue(Panel.ZIndexProperty, 99);
                continue;
            }   
            var l = x.Tag as ListBoxItem;
            x.IsExpanded = false;
            l.SetValue(Panel.ZIndexProperty, 0);
        }
    }
}

解决方案背后的代码会关闭可能已打开的所有扩展器,并通过将 zindex 设置为 99 将当前扩展的扩展器容器带到前台。

同样,如果任何人都有更好的解决方案,我洗耳恭听。

Okay, after a few trials and tribulations I was able to come up with solution using code behind. I'd be interested in finding a way to do this with triggers, but I'm not sure it's possible with this approach.

Here's the update expander XAML:

<Expander x:Name="GridPropEditor" Header="Properties" Height="Auto" Margin="5,0,0,0" 
          ToolTip="Open trace property dialog"
          Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                                         AncestorType={x:Type ListBoxItem}}}"
          PreviewMouseDown="GridPropEditor_PreviewMouseDown"  
          Expanded="GridPropEditor_Expanded">

Here's the code behind I added to my xaml.cs file

//When an expander is expanded, collapse all the other expanders
private void GridPropEditor_Expanded(object sender, RoutedEventArgs e)
{
    if (ExpandersSelected == null)
        ExpandersSelected = new List<Expander>();

    var expander = (sender as Expander);
    var listbox = expander.Tag as ListBoxItem;

    if (!ExpandersSelected.Contains(expander))
        ExpandersSelected.Add(expander);

    if (ExpandersSelected != null)
    {
        foreach(var x in ExpandersSelected)
        {
            if (x.Equals(expander))
            {
                listbox.SetValue(Panel.ZIndexProperty, 99);
                continue;
            }   
            var l = x.Tag as ListBoxItem;
            x.IsExpanded = false;
            l.SetValue(Panel.ZIndexProperty, 0);
        }
    }
}

The code behind solution closes any expanders that might already be opened and brings the currently expanding Expanders container to the foreground by setting the zindex to 99.

Again, if anyone has a better solution I'm all ears.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文