处理可编辑的分层数据/ TreeView~DataGrid混合

时间:2010-08-11 09:40:09

标签: wpf datagrid controls treeview wpf-controls

我正在寻找一个WPF控件,它是TreeView和DataGrid的混合体,类似于Visual Studio调试器或QuickBooks联系人列表等。

如何在WPF中处理可编辑的分层数据的任何其他解决方案也将非常受欢迎。

enter image description here

5 个答案:

答案 0 :(得分:11)

如果你正确地设计你的视图模型,这对我来说似乎是一个相当简单的事情。

您基本上按照在普通数据网格中显示项目的方式设计项目,即每个项目都有每列的属性。很可能,您的基础数据模型是分层的,但网格绑定的集合将被展平,即将包含层次结构中每个节点的项目,而不管父/子关系。

项目视图模型具有一些其他属性:LevelChildrenIsExpandedIsVisibleLevel是节点祖先的计数,Children包含子视图模型节点,UI中使用IsExpanded,如果节点可见,则IsVisible为真。它还实现了一个名为VisibleDescendants的属性:

public IEnumerable<NodeViewModel> VisibleDescendants
{
   get
   {
      return Children
             .Where(x => x.IsVisible)
             .SelectMany(x => (new[] {x}).Concat(x.VisibleDescendants)));
   }
}

您在控件第一列中的项目样式中使用LevelHasChildrenIsExpanded:它们控制左边距以及哪种图标(如果有)是显示。

您还需要实施ExpandCommandCollapseCommand属性。如果ExpandCommand为true且Children.Any()为false,则启用IsExpanded,如果CollapseCommand为true且Children.Any()为true,则启用IsExpanded 。这些命令在执行时会更改IsExpanded的值。

这就是它变得有趣的地方。实现此目的的简单方法可能适用于您:项目由父视图模型公开,其Items属性不是集合。相反,它是一个枚举器,沿着子视图模型链向下移动,只产生可见节点:

public IEnumerable<NodeViewModel> Items
{
   get
   {
      return _Items
             .Where(x => x.IsVisible)
             .SelectMany(x => (new[] {x}).Concat(x.VisibleDescendants));
   }
}

每当后代的IsVisible属性发生更改时,父视图模型会为PropertyChanged属性引发Items,这会强制数据网格重新填充。

还有一个不那么简单的实现,您可以将Items属性设置为实现INotifyCollectionChanged的类,并在后代节点变得可见/不可见时引发正确的CollectionChanged事件,但是如果表现有问题,你只想去那里。

答案 1 :(得分:5)

以下答案来自@Robert Rossney的回答:

public class DataGridHierarchialDataModel
{

    public DataGridHierarchialDataModel() { Children = new List<DataGridHierarchialDataModel>(); }


    public DataGridHierarchialDataModel Parent { get; set; }
    public DataGridHierarchialData DataManager { get; set; }
    public void AddChild(DataGridHierarchialDataModel t)
    {
        t.Parent = this;
        Children.Add(t);
    }


    #region LEVEL
    private int _level = -1;
    public int Level
    {
        get
        {
            if (_level == -1)
            {                    
                _level = (Parent != null) ? Parent.Level + 1 : 0;
            }
            return _level;
        }
    }

    #endregion
    public bool IsExpanded 
    {
        get { return _expanded; }
        set 
        {
            if (_expanded != value)
            {
                _expanded = value;
                if (_expanded == true)
                    Expand();
                else
                    Collapse();
            }
        } 
    }


    public bool IsVisible 
    {
        get { return _visible; }
        set
        {
            if (_visible != value)
            {
                _visible = value;
                if (_visible)
                    ShowChildren();
                else
                    HideChildren();
            }
        }
    }
    public bool HasChildren { get { return Children.Count > 0; } }
    public List<DataGridHierarchialDataModel> Children { get; set; }



    public object Data { get; set; } // the Data (Specify Binding as such {Binding Data.Field})

    public IEnumerable<DataGridHierarchialDataModel> VisibleDescendants
    {
       get
       {               
            return Children
                .Where(x => x.IsVisible)
                .SelectMany(x => (new[] {x}).Concat(x.VisibleDescendants));            
       }
    }



    // Expand Collapse
    private bool _expanded = false;
    private bool _visible = false;
    private void Collapse()
    {
        DataManager.RemoveChildren(this);
        foreach (DataGridHierarchialDataModel d in Children)
            d.IsVisible = false;
    }

    private void Expand()
    {
        DataManager.AddChildren(this);
        foreach (DataGridHierarchialDataModel d in Children)
            d.IsVisible = true;
    }




    // Only if this is Expanded
    private void HideChildren()
    {
        if (IsExpanded)
        {
            // Following Order is Critical
            DataManager.RemoveChildren(this);
            foreach (DataGridHierarchialDataModel d in Children)
                d.IsVisible = false;
        }
    }
    private void ShowChildren()
    {
        if (IsExpanded)
        {
            // Following Order is Critical
            DataManager.AddChildren(this);
            foreach (DataGridHierarchialDataModel d in Children)
                d.IsVisible = true;
        }
    }
}

public class DataGridHierarchialData : ObservableCollection<DataGridHierarchialDataModel>
{

    public List<DataGridHierarchialDataModel> RawData { get; set; }
    public DataGridHierarchialData() { RawData = new List<DataGridHierarchialDataModel>(); }

    public void Initialize()
    {
        this.Clear();
        foreach (DataGridHierarchialDataModel m in RawData.Where(c => c.IsVisible).SelectMany(x => new[] { x }.Concat(x.VisibleDescendants)))
        {                
            this.Add(m);
        }
    }

    public void AddChildren(DataGridHierarchialDataModel d)
    {
        if (!this.Contains(d))
            return;
        int parentIndex = this.IndexOf(d);
        foreach (DataGridHierarchialDataModel c in d.Children)
        {
            parentIndex += 1;
            this.Insert(parentIndex, c);
        }
    }

    public void RemoveChildren(DataGridHierarchialDataModel d)
    {
        foreach (DataGridHierarchialDataModel c in d.Children)
        {
            if (this.Contains(c))
                this.Remove(c);
        }
    }
}

上述课程是他解释的。 使用Data中的DataGridHierarchialDataModel对象放入您自己的自定义数据,并生成您的层次结构数据并将其放入DataGridHierarchialData s RawData。在完成所有工作时致电Initialize;

DataTable accTable = await DB.getDataTable("SELECT * FROM Fm3('l1')");
        accTable.DefaultView.Sort = "iParent";

        DataGridHierarchialData data = new DataGridHierarchialData();

        Action<DataRowView, DataGridHierarchialDataModel> Sort = null;
        Sort = new Action<DataRowView, DataGridHierarchialDataModel>((row, parent) =>
        {
            DataGridHierarchialDataModel t = new DataGridHierarchialDataModel() { Data = row, DataManager = data };
            if (row["iGroup"].ToString() == "1")
            {                    
                foreach (DataRowView r in accTable.DefaultView.FindRows(row["iSmajId"]))
                    Sort(r, t);
            }
            parent.AddChild(t);
        });

        foreach (DataRowView r in accTable.DefaultView.FindRows(0))
        {
            DataGridHierarchialDataModel t = new DataGridHierarchialDataModel() { Data = r, DataManager = data };
            if (r["iGroup"].ToString() == "1")
            {                    
                foreach (DataRowView rf in accTable.DefaultView.FindRows(r["iSmajId"]))
                    Sort(rf, t);
            }

            t.IsVisible = true; // first layer
            data.RawData.Add(t);
        }
        data.Initialize();
        dg.ItemsSource = data;

^这是我的方案,分组帐户

XAML:

<DataGrid x:Name="dg" AutoGenerateColumns="False" IsReadOnly="False" CanUserAddRows="False" GridLinesVisibility="All" ColumnWidth="*">

        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Data.sName}">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell" BasedOn="{StaticResource MetroDataGridCell}">
                        <Setter Property="Template">
                            <Setter.Value>

                                <ControlTemplate TargetType="DataGridCell">
                                    <Border BorderBrush="{TemplateBinding BorderBrush}"
                                        BorderThickness="{TemplateBinding BorderThickness}"
                                        Background="{TemplateBinding Background}"
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">

                                        <StackPanel Orientation="Horizontal">
                                            <ToggleButton x:Name="Expander"                                               
                                          Margin="{Binding Level,Converter={StaticResource LevelToIndentConverter}}"
                                          IsChecked="{Binding Path=IsExpanded, UpdateSourceTrigger=PropertyChanged}"
                                          ClickMode="Press" >
                                                <ToggleButton.Style>
                                                    <Style  TargetType="{x:Type ToggleButton}">
                                                        <Setter Property="Focusable" Value="False"/>
                                                        <Setter Property="Width" Value="19"/>
                                                        <Setter Property="Height" Value="13"/>
                                                        <Setter Property="Template">
                                                            <Setter.Value>
                                                                <ControlTemplate TargetType="{x:Type ToggleButton}">
                                                                    <Border Width="19" Height="13" Background="Transparent">
                                                                        <Border Width="9" Height="9"
                                                                              BorderThickness="0"
                                                                              BorderBrush="#FF7898B5"
                                                                              CornerRadius="1"
                                                                              SnapsToDevicePixels="true">
                                                                            <Border.Background>
                                                                                <SolidColorBrush Color="Transparent"/>
                                                                                <!--
                                                                                    <LinearGradientBrush StartPoint="0,0"
                                                                                        EndPoint="1,1">
                                                                                        <LinearGradientBrush.GradientStops>
                                                                                            <GradientStop Color="White"
                                                                                    Offset=".2"/>
                                                                                            <GradientStop Color="#FFC0B7A6"
                                                                                    Offset="1"/>
                                                                                        </LinearGradientBrush.GradientStops>
                                                                                    </LinearGradientBrush>
                                                                                -->
                                                                            </Border.Background>
                                                                            <Path x:Name="ExpandPath"                                      
                                                                            Data="M0,0 L0,6 L6,0 z"
                                                                            Fill="Transparent"
                                                                            Stroke="{DynamicResource BlackBrush}" Margin="1,2,1,1">
                                                                                <Path.RenderTransform>
                                                                                    <RotateTransform Angle="135"
                                                                                     CenterY="3"
                                                                                     CenterX="3" />
                                                                                </Path.RenderTransform>
                                                                            </Path>
                                                                            <!--
                                                                            <Path x:Name="ExpandPath"
                                                                            Margin="1,1,1,1"
                                                                            Fill="Black"
                                                                            Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z"/>
                                                                            -->
                                                                        </Border>
                                                                    </Border>
                                                                    <ControlTemplate.Triggers>
                                                                        <Trigger Property="IsChecked"
                                                                            Value="True">
                                                                            <Setter Property="RenderTransform"
                                                                                TargetName="ExpandPath">
                                                                                <Setter.Value>
                                                                                    <RotateTransform Angle="180"
                                                                                     CenterY="3"
                                                                                     CenterX="3" />
                                                                                </Setter.Value>
                                                                            </Setter>
                                                                            <Setter Property="Fill"
                                                                                TargetName="ExpandPath"
                                                                                Value="{DynamicResource GrayBrush1}" />
                                                                            <Setter Property="Stroke"
                                                                                TargetName="ExpandPath"
                                                                                Value="{DynamicResource BlackBrush}" />

                                                                                <!--
                                                                                    <Setter Property="Data"
                                                                            TargetName="ExpandPath"
                                                                            Value="M 0 2 L 0 3 L 5 3 L 5 2 Z"/>
                                                                            -->
                                                                        </Trigger>
                                                                    </ControlTemplate.Triggers>
                                                                </ControlTemplate>
                                                            </Setter.Value>
                                                        </Setter>
                                                    </Style>
                                                </ToggleButton.Style>
                                            </ToggleButton>

                                            <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}"
                                                        Content="{TemplateBinding Content}"
                                                        ContentStringFormat="{TemplateBinding ContentStringFormat}"
                                                        Margin="{TemplateBinding Padding}"
                                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                                        HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />


                                        </StackPanel>
                                    </Border>
                                    <ControlTemplate.Triggers>
                                        <DataTrigger Binding="{Binding HasChildren}" Value="False">
                                            <Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
                                        </DataTrigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
            <DataGridTextColumn Header="Code" Binding="{Binding Data.sCode}"/>
            <DataGridTextColumn Header="Type" Binding="{Binding Data.c867x1}"/>

        </DataGrid.Columns>
    </DataGrid>

多数人:P但是相信我,罗伯特罗斯尼的想法是一个爆炸:) 此外,扩展器&#39; +&#39;,&#39; - &#39;样式也包括在内(已注释掉) 希望它有所帮助:)

答案 2 :(得分:2)

答案 3 :(得分:0)

我发现使用此控件可以实现最佳的MVVM方法:http://blogs.msdn.com/b/atc_avalon_team/archive/2006/03/01/541206.aspx

要将其与分层视图模型一起使用,您可以从此处使用分层数据模板和视图模型指南: http://www.codeproject.com/Articles/24973/TreeListView

答案 4 :(得分:0)

参加该聚会的路很晚,但SO表示此线程仅在2个月前处于活动状态。我看不到最近的评论是怎么回事-但我还是会提供此评论,因为我刚刚找到它,并希望分享答案,以防其他人也在寻找它。

我在CodeProject上找到了它-它们全部包装在一个漂亮的小包装中。到目前为止,似乎工作正常。 (和ps:这已经不是普通的WPF了吗?我可以在WinForms中使用任意数量的自动执行此操作的控件来完成此操作)

以下是链接-希望对您有所帮助: https://www.codeproject.com/Articles/1213466/WPF-TreeGrid-using-a-DataGrid