WPF:处理编辑分层数据/的TreeView〜DataGrid的混合
我要寻找一个WPF控件是TreeView和DataGrid中,像Visual Studio调试器或QuickBooks的联系人列表等WPF:处理编辑分层数据/的TreeView〜DataGrid的混合
上的任何其他解决方案的混合体如何处理WPF中的可编辑分层数据也将非常流行。
这在我看来就像一个相当简单的事情,如果你正确地设计您的视图模型来实现。
基本上,设计项目的方式与在正常数据网格中显示它们时的方式相同,即每个项目对每个列都有一个属性。很有可能,您的底层数据模型是分层的,但网格绑定的集合将被展平,即不管父/子关系如何,都将包含层次结构中每个节点的项目。
项目视图模型具有一些额外的属性:Level
,Children
,IsExpanded
,并IsVisible
。 Level
是节点祖先的计数,Children
包含子视图模型节点,在用户界面中使用IsExpanded
,如果节点可见,则IsVisible
为真。它还实现了一个名为VisibleDescendants
属性:
public IEnumerable<NodeViewModel> VisibleDescendants
{
get
{
return Children
.Where(x => x.IsVisible)
.SelectMany(x => (new[] {x}).Concat(x.VisibleDescendants)));
}
}
您使用Level
,HasChildren
和IsExpanded
在样式在控件的第一列的项目:他们所控制的左边空白,什么图标的种类(如果有的话)显示。
您还需要执行ExpandCommand
和CollapseCommand
属性。如果Children.Any()
为真且IsExpanded
为假,则启用ExpandCommand
,并且如果Children.Any()
为真并且IsExpanded
为真,则启用CollapseCommand
。这些命令在执行时会更改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
活动时,子节点成为可见/不可见,但你只想去那里,如果性能是一个问题。
这真棒! – faztp12 2015-12-13 09:29:18
我发现最好的MVVM方法是可能与此控件:http://blogs.msdn.com/b/atc_avalon_team/archive/2006/03/01/541206.aspx
要具有层次视图模型使用它,你可以使用分层数据模板和视图模型指南从这里开始: http://www.codeproject.com/Articles/24973/TreeListView
以下的答案必须从@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);
}
}
}
上面类是什么他解释说。 使用DataGridHierarchialDataModel
中的Data
对象放置您自己的自定义数据,并生成您的等级数据并将其放入DataGridHierarchialData
的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,但相信我,罗伯特Rossney的想法是一个爆炸:) 此外,扩展“ +',' - '样式也包括在内(注释掉) 希望它有帮助:)
但我想混合它与DataGrid(可编辑) – Shimmy 2010-08-11 11:07:27
+1 @Shimmy请参阅com是http://www.codeproject.com/KB/WPF/wpf_treelistview_control.aspx的一部分。它显示了如何使用它,而不是一个列表视图 – k3b 2011-03-23 14:56:45
@ k3b你应该指示这个@Hedge。我认为这不是一个真正的问题@Shimmy关心了......只是说... – 2011-03-24 23:07:57