treegrid 数据格式懒加载时为什么会绘制两次节点?

官网或者 喜欢微软,喜欢.NET
本人尊重别人劳动成果,感觉写的很好,拿过来分享一下,本文不是原文,而是个人的理解,学习和分享
声明:原文:
一、基本工作
1.新建WPF应用程序 &TreeViewLoadingAsync
2.新建文件夹DB,把准备好的 Access 示例数据库Sample.mdb拷贝到DB文件夹下
& 此时会弹出 数据源配置向导 窗口,点下一步,勾选表和视图,点击完成,此时app.config数据库连接字符串都已经生成好了
& 数据库就一张表
3.用linq获得基本数据
在DB文件夹下新建一个DepartmentHelper类获得基本数据
新建两个静态对象
static SampleDataSet ds = new SampleDataSet();
static DepartmentTableAdapter da = new DepartmentTableAdapter();
第一个 &Sample是你的数据库名称,然后加DataSet,这个对象可直接敲出,你可以理解为Sample这个数据库的临时数据集,就是另一个形式的数据库,充当临时数据库的角色,这个数据库我么直接可以用linq去操作它,不用sql了,目前还是空的一个数据库,下面一行代码是 Sample中的一个表名称+TableAdapter,部门表适配器,这样的话就可以用linq语法直接操作Department这张表了,如果还有其他表以此类推。(讲的不专业,只是方便理解)DepartmentTableAdapter&写完不变色,按一下Shift+Alt+F10导入命名空间,导不进来,请检查一下名称是否拼写错误
继续写代码
static DepartmentHelper() {
da.Fill(ds.Department);
public static IEnumerable GetSubDepartments(int pid) {
var list = ds.Department.Where(c =& c.PID == pid).ToList();
staticDepartmentHelper(),静态构造函数,就是在调用DepartmentHelper里面的方法时首先要向ds里面的Department表中填充数据,这里用static修饰的,所以DepartmentHelper的对象是保存在内存中的,所以不会重复被创建,知道该程序关闭时,这块被占用的内存会被释放(使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员。static 修饰符可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型)如果把这个思想理解了,Entity Framework就好学了这两个方法很简单不讲了
二、新建视图模型1.本例子简单,不细分MVVM了,思想是的新建DepartmentViewModel类,实现INotifyPropertyChanged接口,导入&ponentMSystem.Collections.ObjectM这两个命名空间,惯性,实现该接口
& & & & & & & & & & &先写3个变量,1个构造函数
private DepartmentViewModel(object currentObject)
//临时子节点用,当Expanded时移除此节点,添加子节点
static readonly DepartmentViewModel _temp = new DepartmentViewModel(null);
//选中的子节点
private static ObservableCollection&DepartmentViewModel& _checkedItems = new ObservableCollection&DepartmentViewModel&();
public ObservableCollection&DepartmentViewModel& CheckedItems
return _checkedI
static DepartmentViewModel _rootI
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &
先写4个属性,保存父节点数据,子节点集合,treeview上要显示的文字
private DepartmentViewModel _
public DepartmentViewModel Parent
get { return _ }
set { _parent = }
private List&DepartmentViewModel& _
public List&DepartmentViewModel& Children
get { return _ }
private set { _children = }
private object _
public object Current
get { return _ }
set { _current = }
public string DisplayText
get { return ((SampleDataSet.DepartmentRow)Current)["DName"].ToString(); }
/// &summary&
/// 判断是否有子节点(逻辑是:如果只有一个临时子节点,说明没有真正的子节点)
/// &/summary&
/// &returns&&/returns&
private bool HasChildren() {
return !(Children.Count == 1 && Children[0] == _temp);
添加checkbox处理代码
private bool? _isC
public bool? IsChecked {
get { return _isC }
SetCheckState(value, true, true);
private void SetCheckState(bool? value, bool updateChildren, bool updateParent) {
if (_isChecked != value) {
_isChecked =
//通知选中项的集合
if (_isChecked == true) {
_checkedItems.Add(this);
PropertyChanged(this, new PropertyChangedEventArgs("CheckedItems"));
} else if (_isChecked == false) {
_checkedItems.Remove(this);
PropertyChanged(this, new PropertyChangedEventArgs("CheckedItems"));
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
if (updateChildren) {
if (HasChildren()) {
Children.ForEach(c =& c.SetCheckState(value, true, false));
if (updateParent && _parent != null) {
_parent.VerifyState();
private void VerifyState() {
bool? state = null;
for (int i = 0; i & this.Children.C ++i) {
bool? currentState = this.Children[i].IsC
if (i == 0) {
state = currentS
} else if (state != currentState) {
state = null;
this.SetCheckState(state, false, true);
构造函数,添加一下代码,初始化一些值
private DepartmentViewModel(object currentObject) {
Current = currentO
_isChecked = false;
Children = new List&DepartmentViewModel&();
Children.Add(_temp);
//好让显示有个图标箭头,一个treeview节点下至少一个子节点
展开节点,展开如果没有子节点,把默认的那个节点移除,
private bool _isE
public bool IsExpanded {
get { return _isE }
if (value != _isExpanded) {
_isExpanded =
PropertyChanged(this, new PropertyChangedEventArgs("IsExpanded"));
if (!HasChildren()) {
Children.Remove(_temp);
LoadChildren();
/// &summary&
/// 加载子节点
/// &/summary&
private void LoadChildren() {
if (Current != null) {
int pid = Convert.ToInt32(((SampleDataSet.DepartmentRow)Current)["DID"]);
var list = DepartmentHelper.GetSubDepartments(pid);
foreach (var item in list) {
DepartmentViewModel model = new DepartmentViewModel(item) { _isChecked = this.IsChecked };
if (model.IsChecked == true) {
_checkedItems.Add(model);
PropertyChanged(this, new PropertyChangedEventArgs("CheckedItems"));
Children.Add(model);
public static List&DepartmentViewModel& Create() {
// 获得ID获得部门对象
var list = DepartmentHelper.GetSubDepartments(0);
DepartmentViewModel root = new DepartmentViewModel(null);
_rootItem =
root.Children.Clear();
foreach (var item in list) {
root.Children.Add(new DepartmentViewModel(item));
return root.C
/// &summary&
/// 初始化,用于设置父节点
/// &/summary&
private void Init() {
if (!HasChildren()) return;
foreach (DepartmentViewModel child in Children) {
child.Parent = this;
child.Init();
PropertyChanged(this, new PropertyChangedEventArgs("Children"));
&就一个xaml窗体文件,就前台,后台没有代码
&Window x:Class="DepartmentTreeView.MainWindow"
xmlns="/winfx/2006/xaml/presentation"
xmlns:x="/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewLoadingAsync"
Title="MainWindow" Height="329" Width="212" FontFamily="Arial"&
&Window.Resources&
&ObjectDataProvider x:Key="depProvider" ObjectType="{x:Type local:DepartmentViewModel}" MethodName="Create" /&
&Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}"&
&Setter Property="IsExpanded" Value="{Binding Path=IsExpanded,Mode=TwoWay}" /&
&HierarchicalDataTemplate x:Key="CheckBoxItemTemplate" ItemsSource="{Binding Children}"&
&StackPanel Orientation="Horizontal" Margin="0,2,0,0"&
&CheckBox Focusable="False" IsChecked="{Binding IsChecked,Mode=TwoWay}" VerticalAlignment="Center" /&
&ContentPresenter Content="{Binding DisplayText,Mode=OneWay}" Margin="2,0" /&
&/StackPanel&
&/HierarchicalDataTemplate&
&/Window.Resources&
&Grid Margin="5"&
&Grid.RowDefinitions&
&RowDefinition /&
&RowDefinition Height="Auto" /&
&RowDefinition Height="Auto" /&
&/Grid.RowDefinitions&
&TreeView Name="tvDepartment" Grid.Row="0" ItemContainerStyle="{StaticResource TreeViewItemStyle}" ItemsSource="{Binding Source={StaticResource depProvider}}" ItemTemplate="{StaticResource CheckBoxItemTemplate}" /&
对上分析:
&ObjectDataProvider x:Key="depProvider" ObjectType="{x:Type local:DepartmentViewModel}" MethodName="Create" /&对象类型的数据源提供器:它很常用的,这里ObjectType指定了该对象的类型,是个DepartmentViewModel类型的,该类型下面有个方法叫做Create方法,所以后台不用指定数据了,Create方法返回的是一个List&DepartmentViewModel&类型的
如果你要的数据,多个地方都要用的到,不容易绑定,试着把公用的数据放在资源里,例如:ObjectDataProvider ,还有个XMLDataProvider有兴趣可以看一下整体思路:1.创建数据库的linq类,写个读取Department数据的访问类,这里叫DepartmentHelper,其实也就是数据访问层,根据父节点ID获取对象集合2.创建ViewModel,主要通过这个方法List&DepartmentViewModel& Create()&提供treeview的数据;
①获得父节点ID是0的,即根节点集合
②创建一个虚拟根节点DepartmentViewModel类型的,把得到的真正根节点的DepartmentViewModel类型化后,遍历根节点结合,添加到虚拟根节点的Children集合中3.前台给treeview绑定数据,ObjectDataProvider 4.绑定容器样式ItemContainerStyle,子项目数据源ItemsSource,子项目模板ItemTemplate5.由于DepartmentViewModel实现了INotifyPropertyChanged接口,直接对他里面的值修改,于是不要对前台的控件执行修改,就可以改变了6.treeview的ItemContainerStyle样式修改了IsExpanded属性,设置了Mode属性双向绑定7.在DepartmentViewModel中有个IsExpanded属性,在设置set属性中触发事件,触发LoadChildren方法,读取以此ID为父节点ID的那些部门对象,根据父节点左边的CheckBox状态,初始化其他节点的选中状态,子对象集合全部放入Children集合内,想要立即更新UI的状态,调用PropertyChanged委托就行了8.UI界面绑定了Chekbox的IsChecked属性和DepartmentViewModel中的IsChecked属性,同理双向的,IsChecked属性,SetCheckState方法,VerifyState方法9.Init方法,是遍历Children,设置Children这个集合中的对象的Parent属性,递归把Children中Children等以此类推全部设置一下10.CheckedItems集合存着的是DepartmentViewModel对象,也就是间接的选中的TreeviewItem对象。也方便后台提取选中项的信息,然后继续操作扩展:在treeview外添加一个按钮,添加单击事件
private void Button_Click(object sender, RoutedEventArgs e)
ObjectDataProvider provider = FindResource("depProvider") as ObjectDataP
List&DepartmentViewModel& firstLevelItems = provider.Data as List&DepartmentViewModel&;
ICollectionView view = CollectionViewSource.GetDefaultView(firstLevelItems);
DepartmentViewModel rootItem = view.CurrentItem as DepartmentViewM
StringBuilder builder = new StringBuilder();
foreach (DepartmentViewModel checkItem in rootItem.CheckedItems)
builder.AppendLine(checkItem.DisplayText);
MessageBox.Show("Checked items:\n" + builder.ToString());
本例子靓点:MVVM,Treeview ViewModel的设计,点击读取加载节点信息,checkbox的正确选择,后台能够获得选择的treeview中选择的项;
难点:ViewModel类
巧妙:利用ObjectDataProvider,IsExpanded巧妙双向绑定时,利用属性动态加载数据,后台页面无代码;CheckBox版本treeview选择的问题
待解决:样式,还有在读取节点信息时,友好提示,例如,"信息读取中..."
阅读(...) 评论()
&&|&&&&|&&&&|&&&&|&&&&|&&&&|&&&&|&&&&|&&&&|&&&&|&&&&|C# WPF :从一个Treeview控件中往一个列表添加子节点,当列表中已经存在该子节点时,(见问题补充)_百度知道
C# WPF :从一个Treeview控件中往一个列表添加子节点,当列表中已经存在该子节点时,(见问题补充)
当列表中已经存在该子节点时,在Treeview列表中该子节点要显示为灰色,表示不能浮礌蹿度讷道寸权丹护对其进行操作,该怎么做?请各位大侠指教,谢谢!!!
提问者采纳
采用mvvm设计模式:两个treeview的Itemssource应该绑定各自的如ObservableCollcetion&DataClass&这样的集合。可以在DataClas浮礌蹿度讷道寸权丹护s中新增一个用于表示是否变灰的bool属性。在样式中使用触发器,当bool值为true,就变灰。然后当有个Treeview里面的Item添加到目的地列表中的时候,将该Item种的DataClass的是否变灰属性设置为true即可实现你要的功能。记得DataClass实现接口INotifyPropertyChanged其他人的回答你完全可以忽略了。因为其他人的回答把wpf程序硬是做成了winform。如果你用别人的方法,你的程序和winform做的,asp.net做的没啥区别了。也没有利用到wpf的特点。
提问者评价
非常正确!!!谢谢!!!要的就是WPF的,不是Winform。
其他类似问题
按默认排序
其他2条回答
private void initTree(TreeNodeCollection pNodes)
for (int i = 0; i & pNodes.Coun浮礌蹿度讷道寸权丹护t; i++)
if (&0& == pNodes[i].Tag.ToString())
pNodes[i].ForeColor = Color.S//设置节点tag=0的颜色为灰色,即不可用
initTree(pNodes[i].Nodes);
private void button1_Click(object sender, EventArgs e)
initTree(treeView1.Nodes);//初始化treeview的节点的tag是0的节点为灰色
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
if (&0& == e.Node.Tag.ToString())
treeView1.SelectedNode = e.Node.PrevVisibleN//不能选择灰色的节点
private void treeView1_Click(object sender, EventArgs e)
if(treeView1.SelectedNode!=null)
if (&0& == treeView1.SelectedNode.Tag.ToString())
//选中灰色的节点不响应click事件
你的意思是换图标不知道怎么换,还是怎么设置该节点不能操作啊?设置图标就是把节点的图标进行再次的设置。因为一般都是有默认的。至于设置节点不能操作,你可以在节点的操作逻辑里面判断,如果不能操作,直接退出或者给个提示就行了。
要怎么判断在目标列表中已经有存在Treeview中的某一节点,并把Treeview中的该节点弄成灰色的?因为我的点比较多,添加的时候要避免重复,弄成灰色的点不能添加,其他的点还是可以正常添加,不要直接退出和提示。
你要先把这个treeview的图标指向一个imagelist,然后你在向里面加节点的时候,根据节点的需要,设置其图标索引就可以将节点的图标设置为不同的。至于有的不能添加,那还是要在后面的逻辑里面控制。
treeview控件的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁c# winform开发,treeview动态加载大量节点(100万)时候,如何提高效率?_百度知道
c# winform开发,treeview动态加载大量节点(100万)时候,如何提高效率?
VS2010c# 4.0
提问者采纳
应该能加载并显示<img class="word-replace" src="/api/getdecpic?picenc=0a007a0万节点吧需要显示候加载需要显示数据行(比说屏幕能显示20项数据读取创建二十条行)定需要加载量数据象线程类考虑等待间较象进度条类要做
提问者评价
其他类似问题
按默认排序
其他2条回答
载入数据量想本身错误高效利用treeview控件相应拆叠事件根据前节点载入节点所需要展现项目
看样子 没有什么开发经验 一次加载这么多 有什么意义?
treeview的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁查看: 2484|回复: 9
注册时间最后登录阅读权限10积分409精华0帖子
中级会员, 积分 409, 距离下一级还需 91 积分
本帖最后由
15:46 编辑
1)、mini-treegrid在懒加载的情况下,前面的序号是有问题的,没有按照正常的顺序进行编号,此问题能否解决?如图:
2)、在验证表单数据时,我为“区域类型”这个字段的值验证为不能为空或不能为 -1,在新增或编辑数据的时候,如果
“区域类型”的值为 0,则也会被拦截掉,然后选择了其他值再选择0时,此时又不会被拦截了,如图:
(首次赋值验证不通过)
(重新选择值后验证通过)
3)、懒加载情况下渲染treegrid数据,出现了这样的情况是怎么回事?如图:
(不点击节点则不渲染)
(点击节点之后则渲染)
不知各位是否遇到类似的问题呢?请解答!
附件: 你需要才可以下载或查看附件。没有帐号?
注册时间最后登录阅读权限200积分7289精华0帖子
我们会一一详细验证你说的问题。
注册时间最后登录阅读权限10积分409精华0帖子
中级会员, 积分 409, 距离下一级还需 91 积分
niko 发表于
我们会一一详细验证你说的问题。
恩 好 treegrid作为比较常用的重要组件,请尽快处理反馈的疑问,谢谢`
注册时间最后登录阅读权限200积分7289精华0帖子
1)TreeGrid懒加载序号问题。
答:测试是BUG,已解决。下周版本发布
2)验证表单数据,验证错误。
答:请提供示例文件,我们本地没有测试发现问题。
3)懒加载情况下,单元格渲染问题。
答:请提供示例文件,本地无法发现。
注册时间最后登录阅读权限10积分409精华0帖子
中级会员, 积分 409, 距离下一级还需 91 积分
本帖最后由
11:41 编辑
niko 发表于
1)TreeGrid懒加载序号问题。
答:测试是BUG,已解决。下周版本发布
2)验证表单数据,验证错误。
treegrid懒加载数据都是访问数据库的,且各个访问层又是相互关联的,那应该要上传哪些文件呢?
注册时间最后登录阅读权限200积分7289精华0帖子
你treegrid的渲染drawcell事件代码是怎么绑定的,怎么写的。
注册时间最后登录阅读权限10积分409精华0帖子
中级会员, 积分 409, 距离下一级还需 91 积分
niko 发表于
你treegrid的渲染drawcell事件代码是怎么绑定的,怎么写的。
恩,我这里不好叙述,就截图吧:
(代码一)
(代码二)
有了上面的代码之后,最终呈现的效果就是这样了:
(效果一)
(效果二)
附件: 你需要才可以下载或查看附件。没有帐号?
注册时间最后登录阅读权限200积分7289精华0帖子
这样就清晰很多了。
看起来,是第一次加载的时候,drawcell事件没有激发导致的。
注册时间最后登录阅读权限10积分409精华0帖子
中级会员, 积分 409, 距离下一级还需 91 积分
niko 发表于
这样就清晰很多了。
看起来,是第一次加载的时候,drawcell事件没有激发导致的。 ...
对呀,首次加载就是没有激发drawcell事件才没渲染成功的。但你们提供的demo为什么都可以正常渲染?
注册时间最后登录阅读权限200积分7289精华0帖子
你下载最新版本试试
Powered by

我要回帖

更多关于 treegrid 的文章

 

随机推荐