感谢您抽出时间阅读这篇文章。
从SQL数据库获取数据时,我无法尝试构建层次结构对象。 请注意,我是一个新手程序员。
如何构建具有未知级别的层次结构对象?当我说未知级别时,我的意思是,每个节点可能有不同数量的子节点,而这些子节点可能拥有不同数量的子节点,依此类推。
我的想法是我需要使用我的SQL数据创建一个层次结构对象来绑定到WPF TreeView控件。
下面我已经包含了我到目前为止的代码。 代码的第一位是我的属性组成的类。请注意,“Products”类具有引用自身的ObservableCollection。我认为这是构建嵌套节点的方式。即列表中的列表。
第二段代码是我的Get方法,用于从SQL数据库下载数据。以下是我需要将下载的数据排序为层次结构的方法。
产品类别(属性)
public class Products : INotifyPropertyChanged, IDataErrorInfo
{
private Int64 m_ID;
private SqlHierarchyId m_Hierarchy;
private string m_Name;
private ObservableCollection<Products> m_ChildProducts;
// Default Constructor
public Products()
{
ChildProducts = new ObservableCollection<Products>();
}
//Properties
public Int64 ID
{
get
{
return m_ID;
}
set
{
m_ID = value;
OnPropertyChanged(new PropertyChangedEventArgs("ID"));
}
}
public SqlHierarchyId Hierarchy
{
get
{
return m_Hierarchy;
}
set
{
m_Hierarchy = value;
OnPropertyChanged(new PropertyChangedEventArgs("Hierarchy"));
}
}
public String Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
public Int16 Level
{
get
{
return m_Level;
}
set
{
m_Level = value;
OnPropertyChanged(new PropertyChangedEventArgs("Level"));
}
}
public Int64 ParentID
{
get
{
return m_ParentID;
}
set
{
m_ParentID = value;
OnPropertyChanged(new PropertyChangedEventArgs("ParentID"));
}
}
public ObservableCollection<Products> ChildProducts
{
get
{
return m_ChildProducts;
}
set
{
m_ChildProducts = value;
OnPropertyChanged(new PropertyChangedEventArgs("ChildProducts"));
}
}
//INotifyPropertyChanged Event
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
从SQL DB获取数据的方法:
public static ObservableCollection<Products> GetProductsHierarchy()
{
ObservableCollection<Products> productsHierarchy = new ObservableCollection<Products>();
SqlConnection connection = new SqlConnection(DBConnection.GetConnection().ConnectionString);
string selectStatement = "SELECT ID, Hierarchy, Name, Hierarchy.GetLevel() AS Level, Hierarchy.GetAncestor(1) AS ParentHierarchy, " +
"(SELECT ID " +
"FROM SpecProducts " +
"WHERE (Hierarchy = SpecProducts_1.Hierarchy.GetAncestor(1))) AS ParentID " +
"FROM SpecProducts AS SpecProducts_1 " +
"WHERE (EnableDisable IS NULL) " +
"ORDER BY Hierarchy";
SqlCommand selectCommand = new SqlCommand(selectStatement, connection);
try
{
connection.Open();
SqlDataReader reader = selectCommand.ExecuteReader();
while (reader.Read())
{
Products product = new Products();
product.ID = (Int64)reader["ID"];
product.Name = reader["Name"].ToString();
product.Hierarchy = (SqlHierarchyId)reader["Hierarchy"];
product.Level = (Int16)reader["Level"];
if (reader["ParentID"] != DBNull.Value)
{
product.ParentID = (Int64)reader["ParentID"];
}
else
{
product.ParentID = 0;
}
productsHierarchy.Add(product);
// *** HOW TO BUILD HIERARCHY OBJECT WITH UNKNOWN & VARYING LEVELS?
// *** ADD PRODUCT TO CHILDPRODUCT
}
return productsHierarchy;
}
catch (SqlException ex)
{
throw ex;
}
finally
{
connection.Close();
}
}
下面我附上了一张显示我的SQL查询数据结构的图片。 请注意,将来添加更多产品时,层次结构级别可能会更深。无论节点级别是多少,我需要创建的Hierarchy对象应该足够灵活,可以扩展。
非常感谢你的时间,非常感谢所有的帮助。
********* EDIT 26/04/2012 14:37 *******************
请在下面找到下载我的项目代码的链接(这只包含树视图代码)。 有人可以看看它,看看为什么我不能创建2级以上的节点?
代码由用户HB MAAM提供给我。感谢“HB MAAM”为您提供帮助!
答案 0 :(得分:3)
我会为你创造一个例子,
1-首先我将创建一个包含来自DB
public class SqlDataDto
{
public int? Id { get; set; }
public int? ParentId { get; set; }
public String Name { get; set; }
public String OtherDataRelatedToTheNode { get; set; }
}
2-数据将转换为分层数据,我们将使用此类来保存数据:
public class LocalData : INotifyPropertyChanged
{
private int? _id;
public int? Id
{
get { return _id; }
set { _id = value; OnPropertyChanged("Id"); }
}
private int? _parentId;
public int? ParentId
{
get { return _parentId; }
set { _parentId = value; OnPropertyChanged("ParentId"); }
}
private string _name;
public String Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
private string _otherDataRelatedToTheNode;
public String OtherDataRelatedToTheNode
{
get { return _otherDataRelatedToTheNode; }
set { _otherDataRelatedToTheNode = value; OnPropertyChanged("OtherDataRelatedToTheNode"); }
}
private LocalData _parent;
public LocalData Parent
{
get { return _parent; }
set { _parent = value; OnPropertyChanged("Parent"); }
}
private ObservableCollection<LocalData> _children;
public ObservableCollection<LocalData> Children
{
get { return _children; }
set { _children = value; OnPropertyChanged("Children"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
}
3-最后我们需要将sql数据更改为分层数据:
public List<LocalData> GetHerachy(List<SqlDataDto> sqlData)
{
var sqlParents = sqlData.Where(q => q.ParentId == null).ToList();
var parents = sqlParents.Select(q => new LocalData {Id = q.Id, Name = q.Name}).ToList();
foreach (var parent in parents)
{
var childs = sqlData.Where(q => q.ParentId == parent.Id).Select(q => new LocalData { Id = q.Id, Name = q.Name , Parent = parent});
parent.Children = new ObservableCollection<LocalData>(childs);
}
return parents;
}
然后你可以创建一个虚拟数据并将其转换并在树中显示:
var sqlData = new List<SqlDataDto>
{
new SqlDataDto {Id = 1, ParentId = null, Name = "F1"}
, new SqlDataDto {Id = 2, ParentId = null, Name = "F2"}
, new SqlDataDto {Id = 3, ParentId = 1, Name = "S1"}
, new SqlDataDto {Id = 4, ParentId = 2, Name = "S21"}
, new SqlDataDto {Id = 5, ParentId = 2, Name = "S22"}
};
treeView.ItemsSource = GetHerachy(sqlData);
5-树应该像:
<TreeView Name="treeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
答案 1 :(得分:1)
您需要使用递归来填充每个对象的Child-List。这是WPF HierarchicalDataTemplate工作所必需的。否则你只能得到第一级。 There是使用Linq方法ForEach()并传递Action Argument的替代方法。以下解决方案非常直接且易于理解:
public List<Product> Products { get; set; }
public MainViewModel()
{
Products = new List<Product>();
Products.Add(new Product() { Id = 1, Name = "Main Product 1", ParentId = 0 });
Products.Add(new Product() { Id = 3, Name = "Sub Product 1", ParentId = 1 });
Products.Add(new Product() { Id = 4, Name = "Sub Product 2", ParentId = 1 });
Products.Add(new Product() { Id = 5, Name = "Sub Product 3", ParentId = 1 });
Products.Add(new Product() { Id = 6, Name = "Sub Product 3.1", ParentId = 5 });
this.ProcessRootNodes();
}
private void ProcessRootNodes()
{
var rootNodes = Products.Where(x => x.ParentId == 0).ToList();
for (int i = 0; i < rootNodes.Count; i++)
{
rootNodes[i].Children = this.AddChildren(rootNodes[i]);
}
}
private List<Product> AddChildren(Product entry)
{
var children = Products.Where(x => x.ParentId == entry.Id).ToList();
for(int i=0;i<children.Count;i++)
{
children[i].Children = this.AddChildren(children[i]);
}
return children;
}
答案 2 :(得分:0)
// *** HOW TO BUILD HIERARCHY OBJECT WITH UNKNOWN & VARYING LEVELS?
ObservableCollection<Products> productsHierarchy = new ObservableCollection<Products>();
Dictionary<Int64, Products> IdToProduct = new ...
当你循环你的产品;做一个IdToProduct[product.ID] = product;
然后,循环完成的IdToProduct
集合并执行;
if(product.ParentID != 0)
{
IdToProduct[product.ParentID].ChildProducts.Add(product);
}
现在,您的产品 - &gt; ChildProducts关系已映射出来。
(可选)向Products class
添加属性:
public bool IsCategory { get { return (ChildProducts.Count >= 1); } } // e.g. Oven
public bool IsProduct { get { return !(IsCategory); } } // e.g. Electric (Oven)
现在,您已经定义了大部分视图模型。
这个article是使用WPF TreeView的事实上的起点。
提示:HierarchicalDataTemplate
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Products}"
ItemsSource="{Binding ChildProducts}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
您应该创建一个MainViewModel
类,其中包含:
public Products RootProduct { get; set; }
(通知属性已更改属性)
什么不是;这样做:
RootProduct = IdToProduct.FirstOrDefault(product => (product.Level == 0));
<TreeView ItemsSource="{Binding RootProduct.ChildProducts}">