2012-04-24 2 views
2

이 게시물을 읽는 시간을내어 주셔서 감사합니다.SQL 쿼리에서 개체 계층 구조를 작성하는 방법은 무엇입니까? (for WPF TreeView)

SQL 데이터베이스에서 데이터를 가져올 때 계층 적 개체를 만드는 데 문제가 있습니다. 저는 초보자 프로그래머입니다.

레벨을 알 수없는 계층 구조 개체는 어떻게 작성합니까? 내가 알지 못하는 레벨을 말하면, 각 노드는 다양한 수의 자식 노드를 가질 수 있으며, 차례대로 자식 노드의 수가 달라질 수 있습니다.

아이디어는 WPF TreeView 컨트롤에 바인딩하기 위해 SQL 데이터를 사용하여 계층 적 개체를 만들어야한다는 것입니다.

아래에는 지금까지 가지고있는 코드가 포함되었습니다. 코드의 첫 번째 비트는 속성으로 구성된 클래스입니다. "Products"클래스에는 자체를 참조하는 ObservableCollection이 있습니다. 이것이 중첩 된 노드를 만드는 방법이라고 생각합니다. 즉 목록 내의 목록.

두 번째 코드는 SQL 데이터베이스에서 데이터를 다운로드하는 Get 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분 *******************

내 프로젝트 코드를 다운로드하는 링크를 찾으십시오 (여기에는 treeview 코드 만 포함). 누군가 2 레벨 이상으로 노드를 만들 수없는 이유를 확인해 주실 수 있습니까?

코드는 HB MAAM이라는 사용자가 나에 게주었습니다. 지금까지 도움을 주신 "HB MAAM"에게 감사드립니다!

Click this link to download code

내가 당신을 위해 예제를 만들 것입니다
+0

계층의 기본 시각을 표시 할 수 있습니까? 의사 코드를 통해 .. –

+0

안녕하세요. 귀하의 회신에 감사드립니다. 이제이 포럼에 이미지를 업로드 할 수 있습니다. 주어진 쿼리에서 내 SQL 데이터가 어떻게 생겼는지를 내 게시물로 수정하십시오. 원래 SQL 쿼리를 수정했습니다. 위의 코드에서 모든 업데이트가 수정되었습니다. – RobHurd

+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 데이터를 hi로 변경해야합니다. 계층 형 하나

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 항목을 추가하면 트리는 2 단계가 아니라 3 단계 만 표시합니다. 즉, parentID가 5 인 새 항목은 표시되지 않습니다. 왜 이런 일이 일어날 지 아십니까? 다시 한 번, 지금까지 당신의 도움에 대해 대단히 감사드립니다. – RobHurd

+0

처음에 환영합니다. 두 번째로 문제가 해결되면 대답을 받아 들여야합니다. 마지막으로 다음 노드를 SQL 데이터 인스턴스에 추가하려고합니다. 다른 노드는 다음과 같이 추가 할 것입니다. 새 SqlDataDto {Id = 새로운 SqlDataDto {ID = 7, ParentId = 6, Name = "부모 6의 자식입니다."} , 새로운 SqlDataDto { Id = 8, ParentId = 6, Name = "이것은 부모님 6의 다른 자녀입니다."} –

+0

안녕하세요, 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 IdToProduct[product.ID] = product;

그런 다음 완성 된 IdToProduct 컬렉션을 반복 수행하십시오.

if(product.ParentID != 0) 
{ 
    IdToProduct[product.ParentID].ChildProducts.Add(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()를 사용하고 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; 
}