【问题标题】:Gridsplitter in a Grid with an ItemsControl带有 ItemsControl 的 Grid 中的 Gridsplitter
【发布时间】:2017-02-20 06:00:47
【问题描述】:

我正在尝试制作一个 PropertyGrid 自定义控件。 PropertyGrid 与 Visual Studio 中使用的 PropertyGrid 非常相似。我尝试使用扩展 WPF 工具包的 PropertyGrid,但是您必须使用属性指定属性的类别,并且我们需要更改类别运行时。据我所知,这是不可能的属性。

所以我自己制作了 PropertyGrid。到目前为止,这是我的代码:

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:HomeMadePropertyGrid"
                    xmlns:System="clr-namespace:System;assembly=mscorlib">

    <BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
    <SolidColorBrush x:Key="GlyphBrush" Color="#444" />
    <ControlTemplate x:Key="toggleButtonTemplate" TargetType="ToggleButton">
        <Grid Width="15" Height="13" Background="Transparent">
            <Path x:Name="ExpandPath" Fill="{StaticResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,1,1,1" />
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger Property="IsChecked" Value="True">
                <Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    <Style x:Key="toggleButtonStyle" TargetType="ToggleButton">
        <Setter Property="Template" Value="{StaticResource toggleButtonTemplate}" />
    </Style>

    <Style TargetType="{x:Type local:PropertyGrid}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:PropertyGrid}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <ItemsControl ItemsSource="{TemplateBinding ItemsSource}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <VirtualizingStackPanel/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
                                        <StackPanel Orientation="Horizontal">
                                            <ToggleButton x:Name="toggleButton" Height="20" Width="20" Style="{StaticResource toggleButtonStyle}"/>
                                            <TextBlock Text="{Binding Name}" FontWeight="Bold"></TextBlock>
                                        </StackPanel>
                                        <ItemsControl ItemsSource="{Binding Items}">
                                            <ItemsControl.ItemTemplate>
                                                <DataTemplate>
                                                    <Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}" 
                                                          Grid.IsSharedSizeScope="True">

                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="*"/>
                                                            <ColumnDefinition Width="Auto" />
                                                            <ColumnDefinition Width="*"/>
                                                        </Grid.ColumnDefinitions>

                                                        <Border BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
                                                            <TextBlock Background="White" Text="{Binding Path=Name}"/>
                                                        </Border>
                                                        <GridSplitter Width="1" 
                                                                      Grid.RowSpan="4" Grid.Column="1" 
                                                                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                                                                      Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}"/>
                                                        <Border Grid.Column="2" BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
                                                            <ContentPresenter Grid.Column="2" Content="{Binding Value}">
                                                                <ContentPresenter.Resources>
                                                                    <DataTemplate DataType="{x:Type System:String}">
                                                                        <TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" 
                                                                             BorderThickness="0"/>
                                                                    </DataTemplate>
                                                                    <DataTemplate DataType="{x:Type System:Int32}">
                                                                        <TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" 
                                                                             TextAlignment="Right"
                                                                             BorderThickness="0"/>
                                                                    </DataTemplate>
                                                                    <DataTemplate DataType="{x:Type System:Double}">
                                                                        <TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" 
                                                                             TextAlignment="Right"
                                                                             BorderThickness="0"/>
                                                                    </DataTemplate>
                                                                    <DataTemplate DataType="{x:Type System:Boolean}">
                                                                        <CheckBox IsChecked="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
                                                                                  HorizontalAlignment="Center"/>
                                                                    </DataTemplate>
                                                                </ContentPresenter.Resources>
                                                            </ContentPresenter>
                                                        </Border>
                                                    </Grid>
                                                </DataTemplate>
                                            </ItemsControl.ItemTemplate>
                                        </ItemsControl>
                                    </StackPanel>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

PropertyGrid.cs

public class PropertyGrid : ItemsControl
{
    public Brush GridColor
    {
        get { return (Brush)GetValue(GridColorProperty); }
        set { SetValue(GridColorProperty, value); }
    }

    public static readonly DependencyProperty GridColorProperty =
        DependencyProperty.Register("GridColor", typeof(Brush), typeof(PropertyGrid), new UIPropertyMetadata(new SolidColorBrush(Colors.Transparent)));

    static PropertyGrid()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
    }
}

属性组

public class PropertyGroup
{
    public string Name { get; set; }
    public List<PropertyGridItem> Items { get; set; }

    public PropertyGroup()
    {
        Items = new List<PropertyGridItem>();
        Name = "";
    }
}

PropertyGridItem

public class PropertyGridItem
{
    public string Name { get; set; }
    public object Value { get; set; }

    public PropertyGridItem(string propertyName, object propertyValue)
    {
        Name = propertyName;
        Value = propertyValue;
    }
}

我的 MainWindow.xaml 中的这段代码:

<local:PropertyGrid ItemsSource="{Binding Path=Groups}" GridColor="#f0f0f0"/>

我的 ViewModel 背后的代码:

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new ViewModel();
}

视图模型

public class ViewModel
{
    public List<PropertyGroup> Groups { get; set; }

    public ViewModel()
    {
        Groups = new List<PropertyGroup>();

        PropertyGroup group1 = new PropertyGroup();
        group1.Name = "Group1";
        group1.Items.Add(new PropertyGridItem("Item1", "test"));
        group1.Items.Add(new PropertyGridItem("Item2", 300));
        group1.Items.Add(new PropertyGridItem("Item3", true));
        group1.Items.Add(new PropertyGridItem("Item4", 5.2));
        Groups.Add(group1);

        PropertyGroup group2 = new PropertyGroup();
        group2.Name = "Group2";
        group2.Items.Add(new PropertyGridItem("Item1", "test"));
        group2.Items.Add(new PropertyGridItem("Item2", 300));
        group2.Items.Add(new PropertyGridItem("Item3", true));
        group2.Items.Add(new PropertyGridItem("Item4", 5.2));
        Groups.Add(group2);
    }
}

我遇到的问题是每行都应用了 GridSplitter。我希望将 GridSplitter 应用于组的所有行。我知道这是因为我为每个项目制作了一个新的网格。要使附加属性起作用,这些项目必须是 Grid 的直接子级。 DataGrid 也不是一个选项,因为 GridSplitter 仅在列标题之间可用。

长话短说:我如何在 ItemsControl 中使用带有 GridSplitter 的 Grid,该 GridSplitter 适用于理想情况下一组或整个 Grid 的所有行(如果不可能的话)。

【问题讨论】:

  • 这就是我所需要的!你能分享你的代码来源吗?
  • @twister0k 我在问答中分享的内容几乎就是我能分享的全部内容。使用此代码,您应该能够制作一个简单的属性网格。有什么特别的问题可以帮您解决吗?
  • 感谢您的回答。我根据您的代码做了一个简单的项目,但是,我不知道为什么,它不起作用。如果您有时间,请查看code
  • 我更正了您的代码并上传了here。该代码包含一个有效的解决方案。它不起作用的原因是因为您没有对属性网格进行自定义控件。
  • 它有效。谢谢!但是现在,我遇到了与您之前相同的问题,GridSplitter 应用于每一行。

标签: c# wpf custom-controls wpftoolkit itemscontrol


【解决方案1】:

我终于找到了解决这个问题的方法。

要让它发挥作用,我必须:

  • 在父 ItemsControl 上将 Grid.IsSharedSizeScope 设置为 true。
  • 使用任意名称在名称列上设置 SharedSizeGroup 属性。
  • 删除名称列上的星号。由于某种原因,将第一列和第三列都设置为星号会导致 GridSplitter 卡住。
  • 在 GridSplitter 上设置固定宽度,我将 Width 设置为 2。
  • 将 GridSplitter 的 ResizeBehaviour 设置为 PreviousAndNext。

这是生成的代码的相关部分:

 <ItemsControl ItemsSource="{Binding Items}" Grid.IsSharedSizeScope="True">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
                <Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}" >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" SharedSizeGroup="nameColumn"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Border Grid.Column="0"  Style="{StaticResource BodyPropertyGrid_CellBorder}">
                    <TextBlock Text="{Binding Path=Name}"/>
                </Border>
                <GridSplitter Grid.Column="1" Width="2"
                              ResizeBehavior="PreviousAndNext"
                              Style="{StaticResource BodyPropertyGridSplitter}"/>
                <Border Grid.Column="2" Style="{StaticResource BodyPropertyGrid_CellBorder}">
                    <ContentControl Content="{Binding}" 
                                    ContentTemplateSelector="{StaticResource propertyItemTemplateSelector}"/>
                </Border>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

【讨论】:

    【解决方案2】:

    很遗憾,您没有为我们提供一段很好的简单代码来演示您的问题,而且我没有时间让它在我的测试项目中编译和运行,所以我只能给您建议而不是测试的解决方案。下次请花点时间展示一个我们可以复制粘贴到项目中的代码示例。

    如果您使用Grid.IsSharedSizeScope Attached Property 加入Grids 的所有行,您可能会得到“拉伸”GridSplitter

    <Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter=
        {StaticResource BoolToVisConverter}}" Grid.IsSharedSizeScope="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" SharedSizeGroup="FirstColumn" />
            <ColumnDefinition Width="Auto" SharedSizeGroup="GridSplitterColumn" />
            <ColumnDefinition Width="*" SharedSizeGroup="LastColumn" />
        </Grid.ColumnDefinitions>
        ...
    </Grid>
    

    【讨论】:

    • 感谢您的快速回答。不幸的是,它没有用。 GridSplitter 不能再移动,并且列具有自动宽度。很抱歉之前没有添加测试代码。我编辑了我的问题并添加了制作测试项目所需的所有代码。
    猜你喜欢
    • 2011-02-24
    • 2014-05-13
    • 2011-01-20
    • 2012-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多