在树形视图中显示自引用分层数据
如何在 TreeView 中显示自引用分层数据库表。
引言
这是我一直想写一篇小文章的东西。 我终于找到了必要的时间/精力,敲击键盘,让自己看起来有点博学,因此我带来了
“WPF – 表格自引用父子绑定 TreeView 的巧妙之处”
或者 – 如何让 TreeView 正确显示行具有父子关系的自引用表。
背景
有时你需要在数据库表中描述分层信息,其中条目引用其他条目作为父条目,有时你想在 TreeView 控件中显示数据。 本文向您展示如何在 WPF 中以非常简单的方式做到这一点。
请注意,为了保持代码的简单性,我没有添加异常处理、错误捕获等。
我们的数据
首先,让我们拥有我们的数据。
如你所见,我为一家假想的公司创建了一个虚假的组织结构。 每个元素都有一个 ID。 它也可能(或可能不)有一个父元素。 该父元素由 parent_element_id
标识(即父元素的 ID)。 应该很明显我们在描述一个汇报结构。 例如,我们可以看到,茶水工 Chris 向卑微的 Maunder 程序员汇报,而 Maunder 程序员又向经理 3 汇报。
太棒了,这看起来很像一棵树,我可以告诉你你很兴奋。 除非你是个茶水工。
现在是 XAML 时间
启动你信任的 Visual Studio – 我正在使用 Visual Studio 2010。打开随本文一起提供的 WPF 解决方案的下载 – 我称它为 WpfTreeviewStuff,因为我没有特别的想象力。
现在,看看 MainWindow.xaml 文件。
包含嵌入式 TreeView 的窗口的 XAML 如下;
WTF? WPF
XAML 告诉 WPF 我们有一个 TreeView,它将绑定(附加)一些数据。 这一段
ItemsSource="{Binding}"
创建一个默认绑定对象供以后使用。
换句话说 - TreeView 显示的项目列表(即 itemsource)将在未来某个时候连接到 TreeView。
分层数据显示
这部分下一个有趣的部分不仅仅是漂亮的颜色,还有 HierarchicalDataTemplate
标签。 通常,对于 TreeView,我们想要显示具有某种逻辑层次结构的数据集合。 为了在 XAML 中做到这一点,我们使用 HierarchicalDataTemplate
标签。
在我们的例子中,数据集合由 "rsParentChild
" 描述 – 在这种情况下(您将看到)它是一个数据关系对象。 TreeView 可以使用绑定到名为 rsParentChild
的对象的数据来填充自身。
要显示的内容
我们想要显示的实际文本项称为 element_name
,是的,它与数据库中的列名称相同。 TextBlock
标签告诉 TreeView 实际在屏幕上显示什么,即 TextBlock
。 你可以从中获得很多乐趣,因为你可以随心所欲地做任何事情。
代码时间
好吧,如果你对 WPF 还不太熟悉,那么这些都没什么意义,但也可能有一些意义。 现在,转到代码隐藏(为了保持简单,今天我们是一个无 MVVM 区)。
在构造函数中,您将看到以下内容
public partial class MainWindow : Window
{
string connString = @"Data Source=.\SQLEXPRESS;AttachDbFilename={0}\" +
@"treeviewdata.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
SqlConnection conn = new SqlConnection();
public MainWindow()
{
InitializeComponent();
//get the folder where i put the mdf file.
string baseDir = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string whereTheWildFileLives = Directory.GetParent(baseDir).ToString() ;
whereTheWildFileLives = Directory.GetParent(whereTheWildFileLives).ToString();
//build out connection string
connString = string.Format(connString, whereTheWildFileLives);
conn.ConnectionString = connString;
conn.Open();
// select the data from our table and fill our dataset
String sql = "select element_name,element_id,parent_element_id from sexytimetable";
DataSet myGroovySet = new DataSet();
SqlDataAdapter adpt = new SqlDataAdapter(sql, conn);
adpt.Fill(myGroovySet);
myGroovySet.Relations.Add("rsParentChild",
myGroovySet.Tables[0].Columns["element_id"],
myGroovySet.Tables[0].Columns["parent_element_id"]);
//get the default view and then filter based on the parent element id being null
DataView vw = myGroovySet.Tables[0].DefaultView;
vw.RowFilter = "parent_element_id IS NULL"; //try commenting me out later
treeView1.DataContext = vw;
conn.Close();
}
}
第一个有趣的代码片段如下
String sql = "select element_name, element_id, parent_element_id from sexytimetable"; DataSet myGroovySet = new DataSet(); SqlDataAdapter adpt = new SqlDataAdapter(sql, conn); adpt.Fill(myGroovySet);
现在我们可以聪明一点并创建一个实体模型,但本文不是关于这种技术的。 而且我花了很多时间来设置我的 Visual Studio,以便使用大型 Oracle 数据库的实体模型。 所以我不喜欢它们(与 Oracle 数据库一起使用),为了简单起见,我坚持使用你看到的代码。
下一段代码是发生有趣事情的地方
myGroovySet.Relations.Add("rsParentChild",
myGroovySet.Tables[0].Columns["element_id"],
myGroovySet.Tables[0].Columns["parent_element_id"]);
该代码显示正在创建一个关系(称为 "rsParentChild
")并将其添加到数据集中,该关系位于两列 element_id 和 parent_element_id 之间。 这种关系将显示在 TreeView 中。
在显示数据之前,我们需要过滤数据集以首先关注“最顶层”的元素,即没有任何父元素的元素。 我们可以通过创建一个 dataview 对象并根据具有空 parent_element_id
的行进行过滤来实现。
DataView vw = myGroovySet.Tables[0].DefaultView;
vw.RowFilter = "parent_element_id IS NULL";
treeView1.DataContext = vw;
当我们运行代码时,我们会得到一个组织良好的 TreeView,看起来像,我们可以看到“员工”正在正确地向他们的“经理”汇报。
展开所有节点后,它看起来像以下内容(是的,即使是茶水工 Chris 也在正确汇报)
那么,如果我们不添加行过滤器 "parent_element_id IS NULL
" 会发生什么?
这就是发生的事情,这不是我们想要的(原因很明显)
差不多就是这样了,我们像喝了一品脱的 Maunder 一样结束了。 感谢你的阅读,我希望你喜欢这个节目。
其他参考
最后但同样重要的是,非常酷的 Josh Smith