2012-04-24 7 views
2

このポストを読む時間を取ってくれてありがとう。SQLクエリからオブジェクト階層を構築する方法は? (for WPF TreeView)

SQLデータベースからデータを取得するときに階層オブジェクトを作成しようとしています。 私は少し初心者のプログラマーです。

レベルが不明な階層オブジェクトを作成するにはどうすればよいですか?私が知らないレベルを言うと、各ノードにはさまざまな数の子ノードがあり、その子ノードにはさまざまな数の子ノードがある可能性があります。

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

以下、これまでのコードを含めました。 コードの最初のビットは、プロパティで構成された私のクラスです。 "Products"クラスにはObservableCollectionが参照されています。私はこれがネストしたノードを構築する方法だと思います。すなわち、リスト内のリスト。

2番目のコードは、SQLデータベースからデータをダウンロードするGetメソッドです。ダウンロードされたデータをどのようにして階層にソートするかについては、ここで私が必要とするところです。

製品クラス(プロパティ)SQL DBからデータを取得

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クエリーデータの構造を示す画像を添付した以下

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

。 今後さらに多くの製品が追加されると、階層レベルがさらに深まる可能性があります。私が作成しなければならないHierarchyオブジェクトは、ノードレベルの数に関係なく展開するのに十分柔軟でなければなりません。

ありがとうございました。ご協力いただきまして誠にありがとうございます。

SQL Query Data

********* EDIT 26/04/2012午前14時37分*******************

私のプロジェクトコードをダウンロードするリンクを見つけてください(これにはツリービューコードしかありません)。 誰かが2レベルを超えてノードを作成できない理由を見てみることができますか?

コードは、ユーザーHB MAAMによって私に与えられました。これまでのあなたの助けを "HB MAAM"ありがとう!

Click this link to download code

+0

あなたは階層の基本的なビジュアルを表示できますか?擬似コードを介して.. –

+0

こんにちは。お返事をありがとうございます。私は今このフォーラムに画像をアップロードすることができます。指定したクエリから私のSQLデータがどのように見えるかを私の投稿に修正してください。私は元々のSQLクエリを少し変更しました。上記のコードですべてのアップデートが修正されました。 – RobHurd

+0

親のためのすべての必要はありません –

答えて

3

私が最初に私は、データが変換されることを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-くるデータを保持するクラスを作成します
1-、あなたのための例を作成します階層的なデータに変換し、このクラスを使用して保持します。

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データをhiに変更する必要がありますerarchical 1:

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> 
+0

こんにちは、ありがとうございます。あなたのソリューションはうまくいきます。しかし、3レベルのノードを追加すると動作しないようです。たとえば、ParentId = 5の別のsqlDataアイテムを追加すると、ツリーには3つではなく2つのレベルしか表示されません。つまり、parentID = 5の新しいアイテムは表示されません。なぜこれがあるのか​​知っていますか?ここまで、ご協力いただきありがとうございます。 – RobHurd

+0

最初に歓迎です、問題を解決した場合には、まずは答えを受け入れるようにしてください。最後に、次のノードをSQLデータインスタンスに追加しようとすると、他のノードは次のようになります。新しいSqlDataDto {Id =新しいSqlDataDto {ID = 7、ParentId = 6、Name = "これは親6の子です"} 、新しいSqlDataDto {6、ParentId = null、Name = "これはルート(ID = 6) Id = 8、ParentId = 6、Name = "これは親6の別の子です"} –

+0

こんにちはHB MAAM。はい、申し訳ありませんが私はあなたの答えをマークしますが、現在完全には機能しません。新しいアイテムを追加すると、新しいSqlDataDto {Id = 6、ParentId = 5、Name = "Child of Id5"}が作成されます。子供の中に子供が生まれるわけではありません。 Id = 5のアイテムをデバッグするときにnullの子がある場合、Id = 6の子を持つはずです。私を助けるためにあなたの時間をもう一度ありがとう。 – RobHurd

0

// *** HOW TO BUILD HIERARCHY OBJECT WITH UNKNOWN & VARYING LEVELS?


の代わりに ObservableCollection<Products> productsHierarchy = new ObservableCollection<Products>();
使用Dictionary<Int64, Products> IdToProduct = new ...

製品をループするときに、 do a 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)

今、あなたが定義されたビューのためのモデルのほとんどを持っています。

この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; }(通知プロパティが変更されたプロパティ)

あなたのSQLの解析とものではありませんが行った後;操作を行います。
RootProduct = IdToProduct.FirstOrDefault(product => (product.Level == 0));

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

1

あなたはすべてのオブジェクトのお子様リストを埋めるために再帰を使用する必要があります。これは、WPF HierarchicalDataTemplateが機能するために必要です。それ以外の場合は、最初のレベルのみを取得します。 Thereは、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; 
} 
関連する問題