WPF中TreeView和Popup控件的问题

4

在WPF的TreeView中使用Popup时,我遇到一个问题,即弹出窗口内的控件无法使用。例如,使用以下代码时,弹出窗口中的ToggleButton只能切换一次,然后就无法再切换回来了。是否有任何方法可以解决此问题?

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <TreeView>
            <TreeViewItem>
                <TreeViewItem.Header>
                    <StackPanel>
                        <ToggleButton>
                            Button outside popup
                        </ToggleButton>
                        <Popup IsOpen="True">
                            <ToggleButton>
                                Button inside popup
                            </ToggleButton>
                        </Popup>
                    </StackPanel>
                </TreeViewItem.Header>
            </TreeViewItem>
        </TreeView>
    </Grid>   
</Page>
4个回答

3
问题在于你将弹出窗口嵌入到了TreeViewItem中,这样会干扰其内置的功能。我认为弹出窗口本来就不应该这样使用。因为可以指定Popup的PlacementTarget,所以你可以在任何地方声明它们,然后在想要的位置打开它们。下面的标记演示了如何在你的示例中实现这一点:
<StackPanel>
  <Popup IsOpen="True" PlacementTarget="{Binding ElementName=buttonPanel}"
         Placement="Bottom">
    <ToggleButton>
      Button inside popup
    </ToggleButton>
  </Popup>
  <TreeView>
    <TreeViewItem>
      <TreeViewItem.Header>
        <StackPanel Name="buttonPanel">
          <ToggleButton>
            Button outside popup
          </ToggleButton>
        </StackPanel>
      </TreeViewItem.Header>
    </TreeViewItem>
  </TreeView>
</StackPanel>

正如您所看到的,弹出窗口可以在任何地方声明。然后,您可以使用PlacementTarget将其放置在所需位置。通过此更改,ToggleButton 将按预期工作。

我同意在这里提出的简单情况下,这个解决方案可以工作,但是对于我的实际场景,我正在使用一个用户控件在HierarchicalDataTemplate中定义TreeViewItem标头,而这个用户控件中有一个弹出窗口,我得到了与这里提出的简单情况相同的行为。因此,对于我来说,使用PlacementTarget并在TreeView之外声明Popup是行不通的。 - dlannoye
你必须把它放在TreeViewItem.Header中吗?你确定不能把它放在UserControl.Resources或其他任何地方吗? - Charlie
1
这可能有效,但远非理想。我想要托管在TreeViewItem内的控件类似于ComboBox,但不能使用ComboBox创建,而我正在使用Popup来像ComboBox的下拉部分一样使用。因此,我希望将Popup与其他TreeViewItem内容保持在单个用户控件中。肯定有某种方法可以解决这个问题,因为从ComboBox控件模板中可以看到它正在使用一个弹出窗口,并且可以在TreeViewItem中使用而没有任何问题。 - dlannoye
也许你可以发布你正在使用的代码,而不是简化的情况。我无法完全理解你试图做什么,使得这个解决方案不可行。 - Charlie

1
感谢Charlie找出了使用TreeView的问题...
将TreeView的Focusable设置为false似乎也可以解决问题,但可能会引起其他问题。
<TreeView Focusable="False">

据我所知,问题在于哪个控件正在获得焦点,因此看起来你的树视图正在获取鼠标单击的焦点,而不是按钮。

1
谢谢您的建议,但将TreeView设置为不可聚焦意味着无法使用键盘进行导航。 - dlannoye

1

与WPF团队讨论后,我发现这只是一个WPF问题。

对于我的情况,我试图创建一个用于每个TreeViewItem的下拉对话框。我尝试使用不同的控件来获得所需的外观,然后我发现ComboBox的Popup部分在TreeView中可以正常工作。

因此,我最终在TreeView中使用了ComboBox,并强制ComboBox只有一个始终被选中的项,该项将成为我的下拉对话框。之后,我只需要将ComboBox样式设置为所需的外观即可。以下代码显示了我所做的事情的一般想法。

<TreeView>
    <TreeViewItem>
        <ComboBox HorizontalAlignment="Left" VerticalAlignment="Top">
            <ComboBox.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
            </ComboBox.Resources>
            <ComboBoxItem IsSelected="True">
                <StackPanel Orientation="Vertical">
                    <Label>Some stuff on the combobox popup</Label>
                    <RadioButton>Radio Button 1</RadioButton>
                    <RadioButton>Radio Button 2</RadioButton>
                    <CheckBox>Check Box</CheckBox>
                    <Button HorizontalAlignment="Right">Ok</Button>
                </StackPanel>
            </ComboBoxItem>
        </ComboBox>
    </TreeViewItem>
</TreeView>

1

我曾经遇到了同样的问题,其中一个TreeView使用数据模板和多个用户控件显示层次结构项,其中一个用户控件是带有弹出窗口的切换按钮。

这会完全冻结并且完全没有响应。该问题与Focus属性有关,但设置Focusable为false并不总是有效。

通过查看以上提供的解决方案,我喜欢最后一个使用ComboBox在TreeViewItem内部的解决方案。但它导致了另一个问题。每当用户选择组合框的(单个)项目时,它将显示在组合框中作为selectedValue,而我只想像切换功能一样运行。

此外,我想将组合框上的三角形更改为省略号。因此,我尝试通过更改控件模板来解决此问题。

这是我的解决方案:

...

    <Style x:Key="ComboBoxStyle1" TargetType="{x:Type ComboBox}">
        <Setter Property="FocusVisualStyle" Value="{StaticResource ComboBoxFocusVisual}"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
        <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
        <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
        <Setter Property="Padding" Value="4,3"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBox}">
                    <Grid x:Name="MainGrid" SnapsToDevicePixels="true">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Popup x:Name="PART_Popup" Margin="1" 
                               AllowsTransparency="true" 
                               IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" 
                               Placement="Bottom" 
                               PopupAnimation="Fade">
                            <Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}" Color="Transparent">
                                <Border x:Name="DropDownBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="2" CornerRadius="0,4,4,4">
                                    <ScrollViewer CanContentScroll="true">
                                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.DirectionalNavigation="Contained"/>
                                    </ScrollViewer>
                                </Border>
                            </Microsoft_Windows_Themes:SystemDropShadowChrome>
                        </Popup>
                        <ToggleButton Style="{StaticResource ComboBoxReadonlyToggleButton}" 
                                      Background="{TemplateBinding Background}" 
                                      BorderBrush="{TemplateBinding BorderBrush}" 
                                      IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
                            <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
                            <Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
                        </Trigger>
                        <Trigger Property="HasItems" Value="false">
                            <Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            <Setter Property="Background" Value="#FFF4F4F4"/>
                        </Trigger>
                        <Trigger Property="IsGrouping" Value="true">
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsEditable" Value="true">
                <Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
                <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
                <Setter Property="IsTabStop" Value="false"/>
                <Setter Property="Padding" Value="3"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="ComboBoxItemStyle1" TargetType="{x:Type ComboBoxItem}">
        <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="Padding" Value="3,0,3,0"/>
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBoxItem}">
                    <Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsHighlighted" Value="true">
                            <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<TreeView >
    <TreeViewItem IsExpanded="True">
        <ComboBox HorizontalAlignment="Left" VerticalAlignment="Top"
                  IsEditable="False"
                  Style="{StaticResource ComboBoxStyle1}">
            <ComboBox.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
            </ComboBox.Resources>
            <ComboBoxItem IsSelected="False">
                <StackPanel Orientation="Vertical">
                    <Label>Some stuff on the combobox popup</Label>
                    <RadioButton>Radio Button 1</RadioButton>
                    <RadioButton>Radio Button 2</RadioButton>
                    <CheckBox>Check Box</CheckBox>
                    <Button HorizontalAlignment="Right">Ok</Button>
                </StackPanel>
            </ComboBoxItem>
        </ComboBox>
    </TreeViewItem>
</TreeView>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接