2

感谢您抽出时间阅读这篇文章。

从我的 SQL 数据库获取数据时,我在尝试构建分层对象时遇到了麻烦。请注意,我是一个新手程序员。

如何构建具有未知级别的分层对象?当我说未知级别时,我的意思是每个节点可能有不同数量的子节点,而子节点又可能有不同数量的自己的子节点,依此类推。

这个想法是我需要使用我的 SQL 数据创建一个层次对象来绑定到 WPF TreeView 控件。

下面我已经包含了我到目前为止的代码。第一段代码是我的由属性组成的类。请注意,“Products”类有一个 ObservableCollection 引用自身。我认为这就是构建嵌套节点的方式。即列表中的列表。

第二段代码是我从 SQL 数据库下载数据的 Get 方法。这是我需要了解如何将下载的数据分类为层次结构的地方。

产品类(属性)

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 对象都应该足够灵活以进行扩展。

非常感谢您的宝贵时间,非常感谢您提供的所有帮助。

SQL 查询数据

********* 编辑 26/04/2012 14:37 *******************

请在下面找到下载我的项目代码的链接(这仅包含树视图代码)。有人可以看看它,看看为什么我不能创建超过 2 个级别的节点吗?

该代码是由用户 HB MAAM 提供给我的。感谢“HB MAAM”到目前为止的帮助!

点击此链接下载代码

4

3 回答 3

3

我将为您创建一个示例,
1-首先我将创建一个包含来自数据库的数据的类

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;
}

4-然后您可以创建一个虚拟数据并将其转换并在树中显示:

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>
于 2012-04-24T17:08:28.470 回答
1

您需要使用递归来填充每个对象的子列表。这是 WPF HierarchicalDataTemplate 工作所必需的。否则你只能得到第一级。 还有一种使用 Linq 方法 ForEach() 并传递 Action 参数的替代方法。以下解决方案非常简单易懂:

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;
}
于 2012-06-23T19:32:53.493 回答
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);
}

现在,您的 Product --> ChildProducts 关系已被映射出来。

(可选)将属性添加到Products class
public bool IsCategory { get { return (ChildProducts.Count >= 1); } } // e.g. Oven
public bool IsProduct { get { return !(IsCategory); } } // e.g. Electric (Oven)

现在,您已经定义了视图的大部分模型。

本文是使用 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; }通知属性更改属性)的类

在你做你的 SQL 解析之后,什么不是;做:
RootProduct = IdToProduct.FirstOrDefault(product => (product.Level == 0));

<TreeView ItemsSource="{Binding RootProduct.ChildProducts}">

于 2012-04-24T17:10:57.377 回答