最近一个Silverlight项目中,需要多处表达展示内容的“父子”关系。自然用“树”来表达自然是再好不过。Ms Toolkit中包含了TreeView控件,很好的满足了需求。
要表述树状关系,数据库的设计一定要合理,不然这层关系在加载的时候就会很痛苦。另外,根据需求,树状节点的展示还要有一定的顺序,所以一棵树在保存的时候,每个节点在树中的位置也要有所记录。
数据库设计
Code
CREATE TABLE BASIC_INFO
(
ID NUMBER(22,3),
NAME VARCHAR2(100 BYTE),
PARENTID NUMBER(22,3),
GRADE NUMBER(1),
MEMO VARCHAR2(100 BYTE),
IDX NUMBER(5)
)
这样,加载的时候就可以根据ID,ParentID找到父子关系,并根据IDX来加载ID在树中的索引。这个索引要从树的第一个节点开始深度遍历,根据遍历到的节点的顺序记录其索引。
-树的加载
string sqlTv = "SELECT * FROM PDM_BASIC_INFO ORDER BY IDX ASC";
Code
void client_getProjectInfoCompleted(object sender, getProjectInfoCompletedEventArgs e)
{
if (e.Error == null)
{
tvshow.Items.Clear();
ObservableCollection<ProjectInfo> listsForProject = new ObservableCollection<ProjectInfo>();
listsForProject = e.Result;
//添加Title Node
TreeViewItem titleNode = new TreeViewItem();
titleNode.FontSize = 15;
//为树加载标题节点
//titleNode.Background = new SolidColorBrush(Colors.Cyan);
Grid gridTitle = getTitleGridTemplate();
setTitleData(gridTitle);
titleNode.Header = gridTitle;
// titleNode.Background = null;
tvshow.Items.Add(titleNode);
///加载添加Node
AddTreeNodeForProject(0, null);
}
else
{
MessageBox.Show("操作异常,请重新作业" + e.Error.Message, "注意", MessageBoxButton.OK);
}
}
Code
private void AddTreeNodeForProject(int parentID, TreeViewItem treeViewItem)
{
List<ProjectInfo> result = (from Info in listsForProject
where Info.ParentID == parentID
select Info).ToList<ProjectInfo>();
if (result.Count > 0)
{
foreach (ProjectInfo info in result)
{
TreeViewItem objTreeNode = new TreeViewItem();
objTreeNode.IsExpanded = true;
//如果是叶子节点,为其加载模板控制项
if (info.Grade == 3) //叶子节点绑定 时间控制项
{
Grid grid = getGridTemplate();
setGridData(grid, info.Name, info.ID);
objTreeNode.Header = grid;
}
else
{
objTreeNode.Header = "[" + info.ID + "]" + info.Name;
}
//添加根节点
if (treeViewItem == null)
{
tvshow.Items.Add(objTreeNode);
}
else
{
treeViewItem.Items.Add(objTreeNode);
}
//递归加载
AddTreeNodeForProject(info.ID, objTreeNode);
}
}
}
其中,节点的header属性为object类型,这样我们可以根据需求,为其赋予需要的控制项。
设置定制节点
Code
private void setTitleData(Grid grid)
{
TextBlock Phase = new TextBlock();
Phase.Text = "Phase";
Grid.SetColumn(Phase, 0);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "Task";
Grid.SetColumn(Phase, 1);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "Item";
Grid.SetColumn(Phase, 2);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "工期";
Grid.SetColumn(Phase, 3);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "开始时间";
Grid.SetColumn(Phase, 4);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "实际开始时间";
Grid.SetColumn(Phase, 5);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "完成时间";
Grid.SetColumn(Phase, 6);
grid.Children.Add(Phase);
Phase = new TextBlock();
Phase.Text = "实际完成时间";
Grid.SetColumn(Phase, 7);
grid.Children.Add(Phase);
}
private void setGridData(Grid grid, string str, int id)
{
//序号
TextBlock flag = new TextBlock();
flag.Tag = id;
Grid.SetColumn(flag, 0);
grid.Children.Add(flag);
//名称
TextBlock lbl = new TextBlock();
lbl.Text = "[" + id.ToString() + "]" + str;
//lbl.Text = str;
Grid.SetColumn(lbl, 1);
grid.Children.Add(lbl);
//工期
TextBox txt = new TextBox();
txt.IsEnabled = false;
Grid.SetColumn(txt, 2);
grid.Children.Add(txt);
//开始时间
DatePicker dp = new DatePicker();
dp.IsEnabled = false;
Grid.SetColumn(dp, 3);
grid.Children.Add(dp);
//实际开始时间
dp = new DatePicker();
Grid.SetColumn(dp, 4);
grid.Children.Add(dp);
//完成时间
dp = new DatePicker();
dp.IsEnabled = false;
Grid.SetColumn(dp, 5);
grid.Children.Add(dp);
//实际完成时间
dp = new DatePicker();
Grid.SetColumn(dp, 6);
grid.Children.Add(dp);
}
private Grid getGridTemplate()
{
Grid grid = new Grid();
RowDefinition row1 = new RowDefinition();
grid.RowDefinitions.Add(row1);
//id
ColumnDefinition col = new ColumnDefinition();
col.Width = new GridLength(1);
grid.ColumnDefinitions.Add(col);
//ITem Name
col = new ColumnDefinition();
col.Width = new GridLength(250);
grid.ColumnDefinitions.Add(col);
//工期
col = new ColumnDefinition();
col.Width = new GridLength(50);
grid.ColumnDefinitions.Add(col);
//计划开始时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
//实际开始时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
//完成时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
//实际完成时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
return grid;
}
private Grid getTitleGridTemplate()
{
Grid grid = new Grid();
RowDefinition row1 = new RowDefinition();
grid.RowDefinitions.Add(row1);
//Phase
ColumnDefinition col = new ColumnDefinition();
col.Width = new GridLength(40);
grid.ColumnDefinitions.Add(col);
//task
col = new ColumnDefinition();
col.Width = new GridLength(50);
grid.ColumnDefinitions.Add(col);
//item
col = new ColumnDefinition();
col.Width = new GridLength(210);
grid.ColumnDefinitions.Add(col);
//工期
col = new ColumnDefinition();
col.Width = new GridLength(50);
grid.ColumnDefinitions.Add(col);
//计划开始时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
//实际开始时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
//计划完成时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
//实际完成时间
col = new ColumnDefinition();
col.Width = new GridLength(150);
grid.ColumnDefinitions.Add(col);
return grid;
}
Demo:
读取节点信息
Code
for (int n = 0; n < items.Items.Count; n++) //遍历ITems
{
TreeViewItem item = items.Items[n] as TreeViewItem;
Grid grid = item.Header as Grid;
TextBlock txtID = grid.Children[0] as TextBlock;
DatePicker txtStart = grid.Children[4] as DatePicker;
DatePicker txtEnd = grid.Children[6] as DatePicker;
//code here
}
--=============
另外,用户提出一些特定的需求:树状节点的顺序他们要自己调整。Liquid.Treeview控件,可以很好的实现节点的拖拽和移动,我们只需要在用户移动后深度遍历每个节点,并保存其位置索引信息即可。
Liquid.Treeview节点的移动:
Code
Node node = tvshow.Selected;
if (node != null)
{
node.SwapPrevious();
// node.SwapNext();
}
遍历保存节点索引
Code
ObservableCollection<PDMInfo> PDMInfos = new ObservableCollection<PDMInfo>();
int m = 0;
PDMInfo info = new PDMInfo();
for (int i = 0; i < tvshow.Nodes.Count; i++) //phase
{
Node phase = tvshow.Nodes[i] as Node;
m = m + 1;
int PhaseID = Convert.ToInt32(phase.Tag);
info = new PDMInfo()
{
ID = PhaseID,
IDX = m
};
PDMInfos.Add(info);
for (int j = 0; j < phase.Nodes.Count; j++)
{
Node task = phase.Nodes[j] as Node;
m = m + 1;
int TaskID = Convert.ToInt32(task.Tag);
info = new PDMInfo()
{
ID = TaskID,
IDX = m
};
PDMInfos.Add(info);
for (int n = 0; n < task.Nodes.Count; n++)
{
Node item = task.Nodes[n] as Node;
m = m + 1;
int ItemID = Convert.ToInt32(item.Tag);
info = new PDMInfo()
{
ID = ItemID,
IDX = m
};
PDMInfos.Add(info);
}
}
}
client.UpdatePDMInfoToDBCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_UpdatePDMInfoToDBCompleted);
client.UpdatePDMInfoToDBAsync(PDMInfos);