즐겁게 개발을...

개발보다 게임이 더 많이 올라오는 것 같은...

개발/C#

Datatable을 Listview에 바인딩해서 사용하기

다물칸 2024. 2. 13. 15:06
728x90

Listview 10년 넘게 쓰다가 DevExpress사용한 이래 거의 사용하지 않다가 다시 사용하려니 엄청나게 불편하군요. 

구글링을 해보니 10년 전에는 바인딩 지원을 했던 것 같은데 그것마저 사라져있었습니다. (.NET 6 기준)

 

데이터테이블을 사용하는 이유는 object를 listview에서는 지원하지 않기 때문입니다. 

그리드에는 몇가지만 넣어두고 Row클릭 시에만 상세보기로 보여주려면 어떻게 해야 할까요? 

 

방안1) 그리드에 모든 컬럼을 넣어두고 보여질 컬럼의 Width만 0보다 크게 한다. 

방안2) 키값을 포함한 컬럼을 넣어두고 클릭할 때마다 DB로부터 다시 가져와 표시한다. 

 

Datatable을 사용하면 디비에서 긁어온 Row객체(Object)를 datarow에 넣어둘 수 있기 때문에 헛짓하지 않고 쉽게 가져와 처리할 수 있습니다. 

 

자 시작해봅시다. 

 

// Listview가 있는 폼
DataTable Dt;

private void InitClass() 
{
    Dt = new DataTable();
    Dt.Columns.Add(GClass.MakeDataColumn("storagekey", typeof(int), "key"));
    Dt.Columns.Add(GClass.MakeDataColumn("storagename", typeof(string), "Name"));
    Dt.Columns.Add(GClass.MakeDataColumn("storagetype", typeof(int), "Type"));
    Dt.Columns.Add(GClass.MakeDataColumn("usageyn", typeof(int), "status"));
    Dt.Columns.Add(GClass.MakeDataColumn("createat", typeof(string), "Create At"));
    Dt.Columns.Add(GClass.MakeDataColumn("object", typeof(object), "object"));
    
    // 디비로부터 데이터 가져오기
    LoadData();
    // Datatable에서 Listview로 바인딩
    GClass.CopyDataTableToListView(Dt, Lv);
    // 첫번째 키는 보이지 않게 하자.
    Lv.Columns[0].Width = 0;
}

private void LoadData() 
{
    IEnumerable<Storage> storages = GClass.storageCtrl.GetAll();
    Dt.Rows.Clear();
    foreach (Storage storage in storages)
    {
    	DataRow dr = Dt.NewRow();
        dr["storagekey"] = storage.StorageKey;
        dr["storagename"] = storage.StorageName;
        dr["storagetype"] = storage.StorageType;
        dr["usageyn"] = storage.UsageYn;
        dr["createat"] = storage.createdAt;
        dr["object"] = storage;	// Storage객체를 그대로 넣기
        Dt.Rows.Add(dr);
	}
}

 

위 소스는 Datatable 초기화하고 DB로부터 가져와 DataRow 객체를 통해 데이터까지 Datatable에 넣는 소스입니다. 

이것을 ListView에 바인딩을 해보죠. (위에 MakeDataColumn함수는 아래에 포함하겠습니다. 일전에도 공유한 함수이기도 합니다.)

 

        // Datatable에 컬럼 넣는 함수
        public static DataColumn MakeDataColumn(string ColumnName, Type ColumnType, string strCaption = "")
        {
            DataColumn col = new DataColumn
            {
                Caption = strCaption,
                ColumnName = ColumnName,
                DataType = ColumnType
            };

            return col;
        }

        // Datatable의 컬럼과 Row데이터를 Listview에 복사해주는 함수
        // object를 listview에서 처리하지 못하기 때문에 object는 listview에 들어가지 않도록 했습니다.
        public static void CopyDataTableToListView(DataTable data, ListView lv)
        {
            lv.BeginUpdate();
            // if column count is different (typically not yet populated)
            if (lv.Columns.Count != data.Columns.Count)
            {
                lv.Columns.Clear();
                // prepare columns
                foreach (DataColumn column in data.Columns)
                {
                    if (column.ColumnName != "object") lv.Columns.Add(column.ColumnName);
                }
            }
            // clear rows
            lv.Items.Clear();
            // load rows
            foreach (DataRow row in data.Rows)
            {
                ListViewItem item = new ListViewItem(row[0].ToString());
                for (int i = 1; i < data.Columns.Count; i++)
                {
                    if (data.Columns[i].ColumnName != "object") item.SubItems.Add(row[i].ToString());
                }
                lv.Items.Add(item);
            }
            lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
            lv.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
            lv.EndUpdate();
        }

 

요기까지 하면 우선은 리스트뷰에 데이터가 잘 들어와 보입니다. 

우리는 클릭해서 상세보기를 해야 합니다. 

 

리스트뷰 속성을 아래와 같이 바꿔줍니다. 디자인에서 직접 해도 되고 복사해서 Designer.cs에서 복붙하시면 됩니다. 

            Lv.FullRowSelect = true;
            Lv.GridLines = true;
            Lv.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
            Lv.HotTracking = true;
            Lv.HoverSelection = true;
            Lv.MultiSelect = false;

그리드라인이라든가 기타 옵션은 상황에 맞춰서 처리하면 됩니다. 

 

아래 코드는 Listview에서 클릭했을 때, 상세보기하는 함수입니다. 

        private void Lv_SelectedIndexChanged(object sender, EventArgs e)
        {
            // 선택한게 없으면 나가자.
            if (Lv.SelectedItems.Count == 0) return;
            // 선택한 Row Index 구하기
            int SelectRow = Lv.SelectedItems[0].Index;
            // 선택한 Row에서 Row 데이터의 키값 가져오기
            string Key = Lv.Items[SelectRow].Text;
            // Int로 바꾸자.
            int.TryParse(Key, out int nStorageKey);
            // DataTable에서 데이터의 키로 Select해서 DataRow 구하기
            DataRow[] dr = Dt.Select($"storagekey = {nStorageKey}");
            if (dr.Length > 0)
            {
                // Object를 Storage객체로 변환하기
                Storage storage = dr[0]["object"] as Storage;

                TxtProfile.Text = storage.StorageName;
                TxtStoragePath.Text = storage.StoragePath;
                TxtBackupPath.Text = storage.BackupPath;
                TxtLogicalSize.Text = ConvertMbToGb((long)ConvertBytesToMegabytes(storage.LogicalSize)).ToString();
                TxtThreshold.Text = storage.ThresholdPercent.ToString();
                CboStorageType.Text = storage.StorageType == (int)DB_STORAGE_TYPE.FTP ? "FTP" : "SMB";
                CboStorageStatus.Text = storage.UsageYn == 1 ? "Y" : "N";
            }
        }

 

이제 끝났네요. 오랜만에 긴 팁글 올려봅니다. 

반응형