2

この投稿を読むために時間を割いていただきありがとうございます。

SQL データベースからデータを取得するときに、階層オブジェクトを構築しようとすると問題が発生します。私は少し初心者のプログラマーであることに注意してください。

未知のレベルを持つ階層オブジェクトをどのように構築しますか? 不明なレベルとは、各ノードにさまざまな数の子ノードがあり、さらにさまざまな数の独自の子ノードがあるということです。

アイデアは、SQL データを使用して階層オブジェクトを作成し、WPF TreeView コントロールにバインドする必要があるということです。

以下に、これまでのコードを含めます。コードの最初のビットは、プロパティで構成されるクラスです。「Products」クラスには、それ自体を参照する ObservableCollection があることに注意してください。これが、ネストされたノードを構築する方法だと思います。つまり、リスト内のリストです。

2 番目のコードは、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 クエリ データ

*********編集 2012/04/26 14:37 ******************

私のプロジェクト コードをダウンロードするには、以下のリンクを見つけてください (これにはツリービュー コードのみが含まれます)。2 レベルを超えてノードを作成できない理由を確認するために、誰かがそれを見てもらえますか?

コードはユーザー HB MAAM から提供されました。「HB MAAM」今までお世話になりました!

このリンクをクリックしてコードをダウンロードします

4

3 に答える 3

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

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

すべてのオブジェクトの子リストを埋めるには、再帰を使用する必要があります。これは、WPPHierarchicalDataTemplateが機能するために必要です。それ以外の場合は、最初のレベルのみを取得します。 LinqメソッドForEach()を使用し、アクション引数を渡す別の方法があります。次の解決策は非常に単純で理解しやすいものです。

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 に答える