작업 os: WinXp, VC2008 .Net(C#)

본 자료는 SourceGrid 사용방법을 기술한 것으로.

하기 www.devage.com에서도 같은 내용을 무료다운가능합니다.

 

1. Designer쪽 UI Form에 sourceGrid를 넣는다.

=. toolbox에서 마우스오른쪽을 눌러 "Choose Items..."을 선택한 후,

첨부파일중에 User Control로 제작되어져 있는 SourceGrid2.dll을

loading하면 해당 아이콘이 생성된다.

 

2. grid에 이름을 넣고, 하기와 같은 설정으로 하였다.

 

3. Form이 Load되는 시점에, Row 4, Column 9개의 Grid를 만들었다.

/// function history
/// Makeby : HJKIM@FROM30 20090824
/// FuncName: MainUI_Load
/// review : While this form is loading, create Grid of the TestInfo in it
/// apply : SourceGrid2.dll UserControl method.
private void MainUI_Load(object sender, EventArgs e)
{

// 2차원 배열 선언.
string[,] strTestInfo

= new string[,]{{"TestID","LotNo","FabCode", "Device Code",

"Grade", "Process", "Customer", "OperatorID", "SystemNo"},
{"PGM P-Code", "PGM Name", "PGM Temp", "H-Set Temp",

"Meas Temp", "Test Type", "Input QTY", "Start Date", "End Date"} };

gridTestInfo.BorderStyle = BorderStyle.FixedSingle;
gridTestInfo.Redim(4, 9);

for (int row = 0; row < 4; row ++){
for (int col = 0; col < 9; col++){
if (row == 0)
gridTestInfo[row, col]

= new SourceGrid2.Cells.Real.ColumnHeader(strTestInfo[0, col]);
else if(row == 2)
gridTestInfo[row, col]

= new SourceGrid2.Cells.Real.ColumnHeader(strTestInfo[1, col]);
else
gridTestInfo[row, col] = new SourceGrid2.Cells.Real.Cell("");
}// for int col
}// for int row

gridTestInfo.AutoSizeAll();
}

 

==> 여기까지 실행시키면, Form에 4,9 Grid가 만들어져있을것이다~~

 

 

우선,

1. Resource에 원하는 이미지를 등록한다.

2. Button에 원하는 이미지를 올린다.

3. 마우스 이벤트를 사용한다.

3.1 picLotIdScan_MouseHover => Mouse가 button위로 올라갔을때.

picLotIdScan_MouseLeave => Mouse가 button위에 없을때.

 

각 생성 함수부는 하기와 같다.

 

// 마우스 Over

private void picLotIdScan_MouseHover(object sender, EventArgs e)
{

// button or picture ctrol명.Image에 리소스 이미지를 바로 넣으면 된다.
this.picLotIdScan.Image = Properties.Resources.Over_LotIdScan;
}

// 마우스 Leave

private void picLotIdScan_MouseLeave(object sender, EventArgs e)
{
this.picLotIdScan.Image = Properties.Resources.LotIdScan;
}

 

* 추가로 Form1에서 Form2 호출하는 방법.

private void picLotIdScan_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();

f2.ShowDialog();
}

 

 

Part 1

 

1.윈도우에서 핸들값이란 무엇인가??

응용프로그램을 제어하려면 바로 핸들 이라는 배경지식을 필수로 알고 있어야한다.
자..그럼 핸들에 관한 지식을 예를 들어 설명해 보도록 하겠다.
자기 컴퓨터의 윈도우가 실행되고 msn메신저와
익스플로러등등의 프로그램이 응용 프로그램이 실행되어 있다고 가정하자
그렇다면 전체 핸들의 구조는 먼저 부팅했던 된 윈도우에 핸들 구조를 보면
[메신저,익스플로러,등등]의 핸들들을 자식으로 가지게 된다(트리구조와 유사하다.)

윈도우-익스플로러
-메신저-로그인버튼
-친구추가 버튼
-로아웃버튼
-메모장

깊이 들어 가서 메신저의 구조를 본다면..
메신저 안의 [로그인버튼,친구추가버튼,리스트뷰,리스트박스] 이런 컨트롤들이 각각의
핸들값들을 가지고 있는것이다
즉 이러한 버튼 하나하나의 것들도 다 핸들값을 가지고 있다는 개념이고,
핸들의 구조역시 트리구조로 되어져 있다는 것을 예측하실수 있을 것이다.

<그림> 나중에 설명할 SPY++에 나와있는 핸들의 구조를 캡쳐한것이다


3. 핸들값을 왜 알아야 하는것인가.. ??

만약 메신저에 있는 친구추가 버튼을 자동으로 누르게 하고싶다면
"버튼을 클릭했다"와 같은 이벤트를 바로 핸들값을 찾아서 주어야 하기 때문이다.
이핸들값에 클릭 메세지를 보내라 라는 프로그램 명령을 하게 되기 때문이다.
우리가 어떤 메크로 프로그램을 하기 위해서는 핸들값을 몰라서는 프로그램을 만들수 없다.
앞으로 다룰 내용이 핸들값을 가져오는 부분이 될 것이고 그후에 메세지를 보내고,,
등등!!의 프로그램을 만들어 보게 될것이다.

 

Part 2

 

1. API함수를 이용한 핸들찾기

핸들을 찾기위해 알아야 할 기초적 API함수들을 알아 보도록 하겠다.

1_1.FindWindow()

가장 최상위의 핸들을 찾는 함수이며 인자값으로 클래스이름과 캡션값을 넘겨준다.

C#에서의 선언하기

[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName,string lpWindowName);

첫번째 인자값은 찾고자하는 클래스이름,두번째는 캡션값을 넘겨준다.
둘중 하나의 값만 주어도 찾을수 있지만 캡션명이 똑같은 프로그램이 실행중이라면,
클래스명이 필요할것이다.리턴값은 그 캡션명과 클래스 이름을 가지고 있는 핸들값이다.
이런 캡션명과 클래스명은 spy++프로그램을 통해서 알수 있다.
하지만 이함수는 단지 한프로그램의 최상위 핸들 값만 리턴해줄 뿐이고,
자식들의 핸들값들은 FindWindowEX()라는 함수를 통해 알 수가 있다.

참고자료:ms-help://MS.VSCC/MS.MSDNVS.1042/winui/windows_03sn.htm

1_2.FindWindowEX()

인자값으로 받은 핸들의 자식 핸들을 찾는 클래스이다.

[C#에서의 선언]

[DllImport("user32.dll")]
public static extern int FindWindowEx(int hWnd1,int hWnd2,string lpsz1,string lpsz2);

첫번째 인자값은 바로 위의 부모값을 주고, 두번째 인자값은 0이나 null을 주자.
세번째와 네번째 인자값에는 FindWindow와 마찬가지로 클래스명과 캡션명을 넘겨주면
첫번째 인자로 넘겨주었던 핸들자식중에 세번째 네번째 주었던 인자값의 해당하는 핸들을 찾는것이다.
만약 핸들을 찾으면 핸들값을 리턴해주고 그렇지 않으면 0을 리턴한다.

참고자료:ms-help://MS.VSCC/MS.MSDNVS.1042/winui/windows_1zjs.htm



2. SPY++따라하기

이 두개의 함수만으로도 일단 왠만한 핸들은 다 접근할수 있을거라 생각한다.
그렇다면 이제 SPY++을 통해 캡션명과 클래스이름을 알아 보는 방법을 보자.



[그림1]SPY++의 실행

 

[그림2] 기본 메뉴의 설명

 



[그림3] SPY++캡션명과 클래스명

 

[그림4] SPY++의 핸들 속성

 

 

이 SPY++을 주물럭 주물럭 하다보면 쉽게 익숙해 질것이다.
그렇다면 이제 한번 예제를 통해서 핸들값을 가져오는 연습을 한번 해보자
아래의 예제는 위에 [그림4] 에 나오는 필자의 msn메신저5.0버전을
FindWindow와 FindWindowEX를 통해서 한번 접근해 본것이다.

3. 핸들값 찾기 예제(ConSole Program)

using System;
using System.Runtime.InteropServices; //추가.. namespace _1_1Con
{
class Class1
{
//API함수를 선언한다.
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName,string lpWindowName);

 

[DllImport("user32.dll")]
public static extern int FindWindowEx(int hWnd1,int hWnd2,string lpsz1,string lpsz2);

[STAThread]
static void Main(string[] args)
{
Console.WriteLine("메신저의 핸들값은?");
Console.WriteLine("캡션값만 입력한 핸들값 : "+FindWindow(null,"MSN Messenger").ToString());
Console.WriteLine("클래스명만 입력한 핸들값 :"+FindWindow("MSNMSBLClass",null).ToString());

 

//클래스명과 핸들값둘중 하나만 입력해도 값은 똑같이 리턴이 된다.
//그렇다면 이제 부모핸들값을들 저장시키면서 한번 그림 4에 있는 대화상대 추가 핸들값을 알아내보자. int hw1=FindWindow(null,"MSN Messenger");
Console.WriteLine("핸들값1:"+hw1.ToString());
int hw2=FindWindowEx(hw1,0,"PluginHostClass",null);
Console.WriteLine("핸들값2:"+hw2.ToString());
int hw3=FindWindowEx(hw2,0,"MSNMSBLGeneric",null);
Console.WriteLine("핸들값3:"+hw3.ToString());

 

//여기까지 눈으로 내려온것이 확인이 되었다면 밑에 클래스 명들을 살펴보자.
//그러면 분면 클래스 이름 두개가 같은것이 확인이 될것이다.그러기에 반드시 여기에는 캡션명을 같이 해주어야한다.

int hw4=FindWindowEx(hw3,0,"MSNMSBLGeneric","Task List");
Console.WriteLine("핸들값4:"+hw4.ToString());
int hw5=FindWindowEx(hw4,0,"Button","전화 걸기");
Console.WriteLine("핸들값5:"+hw5.ToString());
//여기까지 출력이 되었다면 버튼 핸들값까지 알아내는것이 성공!! }
}
}


[결과]

 

핸들값을 가져오는것이 성고했다면..다음과같은 숫자들이 출력될것이고,
실패하였을 경우 전부0을 리턴하게 될 것이다.
오늘은 핸들값을 가져오는 연습을 해봤고 이제
다음 강좌에서는 핸들에 이벤트를 발생시켜보도록 하겠다.

 

 작성자 : HOONS(박경훈)
이메일 : tajopkh@hanmail.net
홈페이지 : http://www.hoonsbara.com/

출처 : Tong - stream님의 stream님의 기본통통

 

리소스 파일을 활용해보자.

우선,

1. resx 파일로 가서 Name, Value, Commnet 에 해당 내용을 삽입한다.

예) Name Value Comment

name namevalue comment information

 

2. form이 load되었을때, 해당 리소스로부터 정보를 가져와 보자

//////////////////////////////

using 선언

using System.Reflection;
using System.Resources;

//////////////////////////////

 

Assembly asm = this.GetType().Assembly;
ResourceManager rm =

new ResourceManager("WindowsFormsApplication1.Form1", asm);
string name = rm.GetString("name");

MessageBox.Show(name);

이러면 리소스에서 name을 참조하여 namevalue를 가져와 출력하는것이 가능하다

DataGridView 소개

-데브피아 발췌-

 

새 데이터 표 소개

 

Matthew MacDonald

새로운 DataGridView는 .NET 1.x의 표준이었으며 안타깝게도 제 역할을 하지 못한 DataGrid 컨트롤에 대해 .NET 2.0이 제시하는 해결책입니다. Matthew MacDonald가 아래에서 설명할 많은 개선 사항 중에서도 DataGridView는 광범위한 사용자 지정 및 세부적인 서식 지정, 유연한 크기 조정 및 선택, 뛰어난 성능 그리고 다양한 이벤트 모델을 지원합니다.

 

.NET Framework의 처음 두 릴리스(.NET 1.0 및 .NET 1.1)는 데이터 바인딩 부문에서 명백한 차이를 남겼습니다. 개발자들은 거의 모든 컨트롤을 거의 모든 데이터 원본에 연결할 수 있는 유연하고 구성 가능한 모델을 가지고 있었지만 전체 정보 테이블을 표시하기 위한 실질적인 방법은 없었습니다. 이를 위해 포함된 유일한 도구는 DataGrid 컨트롤로, 간단한 데모에서는 원활하게 작동했지만 실제 코드에서는 턱없이 부족한 성능을 나타냈습니다.

 

Microsoft는 이러한 격차를 줄이는 것을 .NET 2.0의 핵심 목표로 삼아 완전히 새로운 표 컨트롤인 DataGridView로 문제를 해결했습니다. DataGridView의 주요 원칙은 두 가지입니다. 먼저, 이 컨트롤은 단 몇 줄의 코드만 작성하여 마스터-세부 목록, 유효성 검사 및 데이터 서식 지정 같은 일반적인 작업을 지원하는 것을 목표로 합니다. 또한 무엇보다도 낮은 수준의 해킹 및 "편법" 프로그래밍에 의지하지 않고 필요한 특수 기능을 통합할 수 있도록 처음부터 확장성을 염두에 두고 디자인되었습니다.

 

기본적인 데이터 바인딩

 

DataGridView에 익숙해지는 가장 효과적인 방법은 속성을 하나도 구성하지 않고 직접 사용해 보는 것입니다. DataGrid와 마찬가지로 DataSource 속성을 사용하여 DataTable 개체(또는 DataTable에서 파생된 개체)를 바인딩할 수 있습니다.

 

Dim ds As DataSet = GetDataSet()

 

DataGridView1.DataSource = ds.Tables("Customers")

 


 

DataGrid와 달리 DataGridView는 테이블을 한 번에 하나씩만 나타낼 수 있습니다. 전체 DataSet를 바인딩하는 경우 나타낼 테이블 이름으로 DataMember 속성을 설정하지 않으면 아무런 데이터도 표시되지 않습니다.

 


 

DataGridView1.DataSource = ds

 

DataGridView1.DataMember = "Customers"

 


 

기본적인 DataGridView 표시는 다음과 같은 몇 가지 간단한 규칙을 따릅니다.

 


 

데이터 원본의 각 필드에 대해 한 열씩 만듭니다.

 

필드 이름을 사용하여 열 헤더를 만듭니다. 열 헤더는 고정되어 있어 사용자가 목록 아래로 이동해도 계속 볼 수 있습니다.

 

Windows XP 방식의 표시 스타일을 지원합니다. 열 헤더는 세련된 평면 모양이며 사용자가 헤더 위로 마우스를 이동하면 강조 표시됩니다.

 

쉽게 눈에 띄지는 않지만 DataGridView에는 다음과 같은 다양한 기본 동작이 포함되어 있습니다.

 


 

현재 위치 내 편집이 가능합니다. 사용자는 셀을 두 번 클릭하거나 F2 키를 눌러 현재 값을 수정할 수 있습니다. 유일한 예외는 필드의 DataColumn.ReadOnly가 true로 설정되어 있는 경우입니다(예: 현재 예제의 OrderID 필드).

 

자동 정렬을 지원합니다. 사용자는 열 헤더를 한 번 또는 두 번 클릭하여 해당 필드의 값을 기반으로 값 순서를 오름차순 또는 내림차순으로 정렬할 수 있습니다. 기본적으로 정렬은 데이터 형식을 고려하며 사전순 또는 숫자 순서대로 이루어집니다. 사전순 정렬은 대/소문자를 구분합니다.

 

다양한 형식을 선택할 수 있습니다. 사용자는 클릭하여 끌어 오는 방식으로 하나 이상의 셀 또는 여러 행을 강조 표시할 수 있습니다. DataGridView 왼쪽 위에 있는 사각형을 클릭하면 전체 테이블이 선택됩니다.

 

자동 크기 조정 기능을 지원합니다. 사용자가 헤더 사이의 열 구분선을 두 번 클릭하면 왼쪽 열이 셀 내용에 맞게 자동으로 확장 또는 축소됩니다.

 


 

멋진 모양의 DataGridView 만들기

 


 

DataGridView의 기본 모양은 DataGrid에 비해 약간 개선되었습니다. 하지만 몇 가지 간단한 세부 작업을 통해 모양을 멋지게 다듬을 수 있습니다.

 

한 가지 문제는 열이 해당 데이터에 맞게 자동으로 확장되지 않는다는 것입니다. 이 문제는 DataGridView.AutoSizeColumns() 메서드와 DataGridViewAutoSizeColumnCriteria 열거형 값 중 하나를 사용하여 해결할 수 있습니다. 열의 크기는 헤더 텍스트 너비, 현재 표시된 행 또는 테이블의 모든 행을 기반으로 조정하도록 선택할 수 있습니다.

 


 

' 헤더에서 가장 큰 텍스트의 너비

 

' 또는 이 열의 행 중 하나에 대해

 

' 열 크기를 조정합니다.

 

DataGridView1.AutoSizeColumns(DataGridViewAutoSizeColumnCriteria.AllCells)

 

 

(참고: 프로그래머 코멘트는 샘플 프로그램 파일에는 영문으로 제공되며 기사에는 설명을 위해 번역문으로 제공됩니다.)

 


 

이 메서드는 데이터 바인딩이 끝난 후 호출해야 하며 그렇지 않으면 어떤 영향도 미치지 않습니다. 또한 사용자가 편집한 후에 사용하는 경우도 있습니다(DataGridView.CellValueChanged 같은 이벤트에 대한 응답으로).

 


 

이에 맞게 열 크기를 넓히는 대신 행 크기를 변경할 수도 있습니다. 기본적으로 열의 텍스트는 여러 줄에 겹치게 됩니다. DataGridView.AutoSizeRows() 메서드를 사용하는 경우 행은 내용에 맞게 높이가 조정됩니다. 특히 필드에 텍스트의 양이 많은 경우에는 이 메서드를 사용하기 전에 열 크기를 넓혀야 할 수 있습니다. 예를 들어 다음 코드 조각은 Description 열의 크기를 네 배로 늘린 다음 내용에 맞게 행의 크기를 조정합니다.

 


 

DataGridView.Columns("Description").Width *= 4

 

DataGridView.AutoSizeRows(DataGridViewAutoSizeRowsMode.HeaderAndColumnsAllRows)

 

 


 

그림 1에서는 DataGridView의 크기를 자동으로 조정하는 서로 다른 접근 방식을 비교합니다.

 


 

또 다른 적절한 변화 중 하나로 각 열에 나타나는 헤더 텍스트를 변경하는 기능을 들 수 있습니다. 예를 들어 필드 이름인 "OrderDate"보다는 "Order Date"라는 제목을 붙이는 것이 보다 전문적인 느낌을 줍니다. 이러한 변화는 매우 간단하게 이루어집니다. 다음과 같이 DataGridView.Columns 컬렉션에서 적절한 DataGridViewColumn을 검색한 다음 해당 HeaderText 속성을 수정하면 됩니다.

 


 

DataGridView.Columns("OrderID").HeaderText = "Order ID"

 


 

DataGridView를 사용하여 셀 선택

 

DataGridView를 사용하면 기본적으로 자유로운 선택이 가능합니다. 사용자는 개별 셀, 셀 그룹, 모든 셀을 한 번에(표 오른쪽 위의 사각형을 클릭하여) 또는 하나 이상의 행(헤더 열의 행을 클릭하여)을 강조 표시할 수 있습니다. 선택 모드에 따라 사용자는 열 헤더를 선택하여 하나 이상의 열을 선택할 수도 있습니다. 이 동작을 제어하려면 다음 설명과 같이 DataGridViewSelectionMode 열거형 값 중 하나로 DataGridView.SelectionMode 속성을 선택합니다.

 


 

CellSelect셀은 선택할 수 있지만 전체 행 또는 헤더는 선택할 수 없습니다. DataGridView.MultiSelect가 True이면 여러 개의 셀을 선택할 수 있습니다.

 

FullColumnSelect 열 헤더를 클릭하여 전체 열만 선택할 수 있습니다. DataGridView.MultiSelect가 True이면 여러 개의 열을 선택할 수 있습니다. 이 모드를 사용하면 열 헤더를 클릭해도 표가 정렬되지 않습니다.

 

FullRowSelect 행 헤더를 클릭하여 전체 행만 선택할 수 있습니다. DataGridView.MultiSelect가 True이면 여러 개의 행을 선택할 수 있습니다.

 

ColumnHeaderSelect CellSelect 또는 FullColumnSelect 선택 모드를 사용할 수 있습니다. 이 모드를 사용하면 열 헤더를 클릭해도 표가 정렬되지 않습니다.

 

RowHeaderSelect CellSelect 또는 FullRowSelect 선택 모드를 사용할 수 있습니다. 이것이 기본 선택 모드입니다.

 

DataGridView는 다음 세 가지 속성을 사용하여 선택한 셀을 손쉽게 검색할 수 있도록 해줍니다. SelectedCells, SelectedRows 및 SelectedColumns. SelectedCells는 사용하는 선택 모드에 관계 없이 DataGridViewCell 개체의 컬렉션을 반환합니다. 반면 SelectedRows는 행 헤더를 선택하여 전체 행을 선택한 경우 정보만 반환하며 SelectedColumns는 열 헤더를 사용하여 전체 열을 선택한 경우 정보만 반환합니다.

 


 

예를 들어 다음 코드 조각은 선택한 전체 행을 확인합니다. 행을 찾으면 다음과 같이 메시지 상자에 CustomerID 열의 해당 값을 표시합니다.

 


 

For Each SelectedRow As DataGridViewRow In _

 

DataGridView1.SelectedRows

 

MessageBox.Show( _

 

SelectedRow.Cells("CustomerID").Value)

 

Next

 


 

이 작업은 CurrentCell 또는 CurrentCellAddress 속성을 사용하여 현재 셀에 대한 참조를 검색하는 것만큼이나 간단합니다. DataGridView를 사용하면 현재 셀이 검정 점선 사각형 모양의 포커스 영역으로 둘러싸입니다. 이 영역이 현재 사용자의 위치입니다.

 


 

CurrentCellAddress 속성은 읽기 전용이지만 CurrentCell을 사용하면 현재 위치를 프로그래밍 방식으로 변경할 수 있습니다. 그런 후에 현재 위치가 표시되도록 DataGridView가 스크롤됩니다.

 


 

' 11번째 행의 네 번째 셀로 이동합니다.

 

DataGridView.CurrentCell = DataGridView.Rows(10).Cells(3)

 

 


 

DataGridView 개체

 


 


 

지금까지는 현재 선택한 행, 셀 및 열 집합과 상호 작용하는 방법을 확인했습니다. DataGridView는 전체 데이터 집합을 작업할 수 있게 해주는 두 개의 주요 컬렉션을 제공합니다. 이들은 Columns(DataGridViewColumn 개체의 컬렉션) 및 Rows(각각 DataGridViewCell 개체의 컬렉션을 참조하는 DataGridViewRow 개체의 컬렉션)로 나타납니다. 그림 2는 개체 모델을 보여 줍니다.

 

일반적으로 열 표시 속성, 서식 지정 및 헤더 텍스트를 구성하려면 DataGridViewColumn 개체를 사용합니다. 또한 DataGridViewRow 및 DataGridViewCell 개체는 바인딩된 레코드에서 실제 데이터를 검색하는 데 사용합니다. DataGridViewCell의 데이터를 수정하면 다음과 같이 사용자가 편집하는 것과 동일한 방식으로 처리됩니다. 적절한 DataGridView 변경 이벤트가 발생되면 기본 DataTable이 수정됩니다.

 


 

이제 DataGridView 개체 모델을 이해했으므로 테이블을 순환하는 코드를 쉽게 만들 수 있습니다. 행, 열 또는 셀을 선택하기 위해 해야 할 일은 해당하는 DataGridViewRow, DataGridViewColumn 또는 DataGridViewCell 개체를 찾아 IsSelected 속성을 true로 설정하는 것뿐입니다.

 


 

다음 예제는 OrderID 필드가 100 미만인 모든 행을 프로그래밍 방식으로 선택합니다.

 


 

For Each Row As DataGridViewRow In DataGridView1.Rows

 

If Row.Cells("OrderID").Value < 100 Then

 

Row.Selected = True

 

End If

 

Next

 


 

DataGridViewColumn에서 여러 개의 서로 다른 클래스가 파생되는 것에는 신경쓰지 않아도 됩니다. 이러한 클래스는 셀에서 값을 그리고 편집하는 방식을 제어할 수 있습니다. .NET에는 다음과 같은 5개의 미리 빌드된 DataGridView 열 클래스가 포함되어 있습니다. DataGridViewButtonColumn, DataGridViewCheckBoxColumn, DataGridViewComboBoxColumn, DataGridViewImageColumn 및 DataGridViewTextBoxColumn.

 


 

DataGridView 스타일

 

DataGridView를 디자인하는 데 발생하는 한 가지 문제는 서로 다른 수준의 서식 지정을 적용할 만큼 유연하면서 매우 큰 테이블에 대해서도 효과적인 서식 지정 시스템을 만드는 것이었습니다. 유연성을 고려할 경우 최상의 접근 방식은 개발자가 각 셀을 개별적으로 구성할 수 있게 하는 것입니다. 그러나 효율성 측면에서 보면 이 접근 방식은 매우 열악하다고 할 수 있습니다. 테이블에 행이 수천 개면 셀은 수만 개가 되므로 셀마다 개별적으로 서식 지정을 관리한다는 것은 보나마나 많은 양의 메모리만 낭비하는 헛수고가 될 것입니다.

 

이 문제를 해결하기 위해 DataGridView는 DataGridViewCellStyle 개체를 사용하는 다계층 모델을 사용합니다. DataGridViewCellStyle 개체는 셀의 스타일을 나타내며 색, 글꼴, 맞춤, 줄 바꿈 및 데이터 서식 지정 같은 세부 사항이 포함되어 있습니다. 단일 DataGridViewCellStyle을 만들면 전체 테이블의 기본 서식을 지정할 수 있습니다. 또한 열, 행 및 개별 셀에 대해 기본 서식을 지정할 수 있습니다. 서식을 보다 세부적으로 지정할수록 더 많은 DataGridViewCellStyle 개체를 만들며 솔루션의 확장 가능성은 떨어지게 됩니다. 그러나 주로 열 기반 및 행 기반 서식 지정을 사용하고 개별 셀의 서식은 가끔씩 지정하는 경우 DataGridView의 메모리가 DataGrid보다 훨씬 많을 필요는 없습니다.

 


 

DataGridView는 서식 지정을 적용할 때 다음과 같은 우선 순위를 따릅니다(내림차순으로).

 


 

1. DataGridViewCell.Style

 


 

2. DataGridViewRow.DefaultCellStyle

 


 

3. DataGridView.AlternatingRowsDefaultCellStyle

 


 

4. DataGridView.RowsDefaultCellStyle

 


 

5. DataGridViewColumn.DefaultCellStyle

 


 

6. DataGridView.DefaultCellStyle

 


 

여기서 이해해야 할 부분은 스타일 개체가 전혀 적용되지 않는다는 점입니다. 대신 DataGridView는 각 개별 속성을 확인합니다. 예를 들어 DataGridViewCell.Style 속성을 사용하여 사용자 지정 글꼴을 적용하지만 사용자 지정 전경색은 설정하지 않는 셀이 있다고 가정합시다. 이렇게 되면 글꼴 설정은 다른 모든 스타일 개체의 글꼴 정보보다 우선하여 적용되지만 전경색은 null이 아닌 경우 계층 구조의 다음 스타일 개체에서 상속됩니다(이 경우 DataGridViewRow.DefaultCellStyle).

 


 

DataGridViewCellStyle은 다음과 같은 두 가지 유형의 서식 지정을 정의합니다. 데이터 및 모양. 데이터 서식 지정이란 데이터 바인딩된 값이 표시되기 전에 수정되는 방식을 지정하는 것을 말합니다. 여기에는 일반적으로 서식 지정 문자열을 사용하여 숫자 또는 날짜 값을 텍스트로 전환하는 작업이 포함됩니다. 데이터 서식 지정을 사용하려면 DataGridViewCellStyle.Format 속성을 사용하여 형식 지정자 또는 사용자 지정 형식 문자열을 설정하면 됩니다.

 


 

예를 들어 다음 코드 조각은 UnitCost 필드에 있는 모든 숫자의 서식을 지정하여 이들 숫자가 통화 값으로 표시되고 두 개의 소수 자릿수와 적절한 통화 기호가 로컬 설정에 정의됩니다.

 


 

DataGridView1.Columns("UnitCost").DefaultCellStyle.Format = "C"

 

 

모양 서식 지정에는 색 및 글꼴 같은 표면적인 묘사가 포함되어 있습니다. 예를 들어 다음 코드는 UnitCost 필드를 오른쪽으로 맞추고 굵은 글꼴을 적용하며 셀 배경을 노랑으로 변경합니다.

 


 

DataGridView1.Columns("UnitCost"). DefaultCellStyle.Font = _

 

 

New Font(DataGridView.Font, FontStyle.Bold)

 

DataGridView1.Columns("UnitCost"). _

 

DefaultCellStyle.Alignment = _

 

DataGridViewContentAlignment.MiddleRight

 

DataGridView1.Columns("UnitCost"). _

 

DefaultCellStyle.BackColor = Color.LightYellow

 


 

다른 서식 지정 관련 속성에는 ForeColor, SelectionForeColor, SelectionBackColor, WrapMode(공간이 허용되는 경우 텍스트를 여러 줄에 겹치게 할지 잘라낼지 여부를 제어) 및 NullValue(null 값을 대체하는 값)가 있습니다.

 


 

또한 DataGridView에는 디자인 타임에 열 스타일을 구성할 수 있게 해주는 디자이너가 포함되어 있습니다. 속성 창에서 DataGridView Properties 링크를 선택하거나 AutoFormat을 선택하여 다양한 미리 빌드된 스타일 설정에서 선택하면 됩니다.

 


 

사용자 지정 셀 서식 지정

 

셀의 서식을 지정하기 위해 첫 번째로 선택할 사항은 상위 DataGridView, DataGridViewColumn 및 DataGridViewRow 속성을 통해 작업하는 것입니다. 그러나 때로는 특정 개별 셀의 스타일을 설정해야 하는 경우도 있습니다. 예를 들어 열의 데이터가 특정 값보다 크거나 작으면 데이터를 플래그해야 하는 경우가 있습니다. 이에 대한 한 가지 예는 프로젝트 일정 목록에서 기한이 경과된 만기일 또는 매출 분석에서 마이너스 수익률을 강조 표시하는 것입니다. 위의 두 경우 모두 개별 셀의 서식을 지정해야 합니다.

 

DataGridView 개체 모델에 대한 지식을 활용하여 값을 강조 표시할 것으로 예상되는 특정 열에서 셀의 컬렉션을 반복하고 싶을 수도 있습니다. 이러한 접근 방식은 사용할 수는 있지만 최선의 선택은 아닙니다. 여기서 중요한 문제는 사용자가 데이터를 편집하거나 코드가 바인딩된 데이터 원본을 변경하는 경우 셀 강조 표시가 일치하도록 업데이트되지 않는다는 것입니다.

 


 

다행히도 DataGridView는 이를 위해 CellFormatting 이벤트를 제공합니다. CellFormatting은 셀 값이 그려지기 직전에 발생되며 내용을 기반으로 셀 스타일을 업데이트할 수 있습니다. 다음은 특정 고객을 확인하여 그에 따라 셀을 플래그하는 예제입니다.

 


 

Private Sub DataGridView1_CellFormatting( ByVal sender As System.Object, _

 

ByVal e As System.Windows.Forms. DataGridViewCellFormattingEventArgs) _

 

Handles DataGridView1.CellFormatting

 


 

' 맞는 열인지 확인합니다.

 

If DataGridView1.Columns(e.ColumnIndex).Name = "CustomerID" Then

 

' 맞는 값인지 확인합니다.

 

If e.Value = "ALFKI" Then

 

e.CellStyle.ForeColor = Color.Red

 

e.CellStyle.BackColor = Color.Yellow

 

End If

 

End If

 

End Sub

 


 

표의 모양에 영향을 주는 세부 사항은 스타일뿐만이 아닙니다. 사용자가 오른쪽으로 스크롤하더라도 계속 표시되도록 열을 숨기고 위치를 이동하며 잠글 수도 있습니다. 이러한 기능은 모두 다음 설명과 같이 DataGridViewColumn 클래스의 속성을 통해 제공됩니다.

 


 

DisplayIndex DataGridView에서 열이 표시되는 위치를 설정합니다. 예를 들어 DisplayIndex가 0인 열은 가장 왼쪽 열에 자동으로 표시됩니다. DisplayIndex가 동일한 열이 둘 이상이면 컬렉션의 첫 번째 열이 가장 먼저 표시됩니다. 따라서 DisplayIndex를 사용하여 열을 왼쪽으로 옮기는 경우 가장 왼쪽 열의 DisplayIndex도 오른쪽으로 옮기도록 설정해야 합니다. 처음에 DisplayIndex는 DataGridView.Columns 컬렉션의 DataGridViewColumn 개체의 인덱스와 일치합니다.

 

Frozen True인 경우 사용자가 추가 열을 확인하기 위해 오른쪽으로 스크롤해도 테이블 왼쪽에 표시된 상태로 고정됩니다.

 

HeaderText 열 헤더에 표시되는 텍스트를 설정합니다.

 

Resizable 및 MinimumWidth Resizable을 False로 설정하여 사용자가 열 크기를 조정하지 못하게 하거나 MinimumWidth를 최소 허용 픽셀 수로 설정합니다.

 

Visible False로 설정하여 열을 숨깁니다.

 

단추 열

 

DataGridView를 위해 제공되는 열 형식 중 하나는 모든 항목 옆에 단추를 표시하는 DataGridViewButtonColumn입니다. 이 단추의 클릭에 응답하는 것은 물론 이를 사용하여 다른 작업을 시작하거나 새 폼을 표시할 수도 있습니다.

 

다음은 단추 텍스트가 "Details..."인 간단한 단추 열을 만드는 예제입니다.

 


 

' 단추 열을 만듭니다.

 

Dim Details As New DataGridViewButtonColumn()

 

Details.Name = "Details"

 


 

' 데이터 바인딩을 해제하고 정적 텍스트를 표시합니다.

 

' (대신 DataPropertyName 속성을 설정하여

 

' 테이블의 속성을 사용할 수도 있습니다.)

 

Details.DisplayTextAsFormattedValue = False

 

Details.Text = "Details..."

 


 

' 헤더의 텍스트를 지웁니다.

 

Details.HeaderText = ""

 


 

' 열을 추가합니다.

 

DataGridView1.Columns.Insert( DataGridView1.Columns.Count, Details)

 

 


 

그림 3은 새 열이 표시된 표를 보여 줍니다. 다음은 모든 열에서 단추 클릭에 반응하여 해당되는 레코드 정보를 표시하는 코드입니다.

 


 

Private Sub DataGridView1_CellClick( _

 

ByVal sender As System.Object, _

 

ByVal e As System.Windows.Forms. _

 

DataGridViewCellEventArgs) _

 

Handles DataGridView1.CellClick

 


 

If DataGridView1.Columns(e.ColumnIndex).Name = _

 

"Details" Then

 

MessageBox.Show("You picked " & _

 

DataGridView1.Rows(e.RowIndex). _

 

Cells("CustomerID").Value)

 

End If

 

End Sub

 


 

좀 더 현실적인 시나리오에서는 여기서 새 창을 만들고 표시한 다음 선택한 레코드에 대한 정보를 새 창으로 전달하여 전체 정보를 쿼리하고 표시할 수 있도록 합니다.

 


 

이미지 열

 

DataGridView를 위해 제공되는 또 다른 열 형식은 셀 범위에 그림을 표시하는 DataGridViewImageColumn입니다. DataGridViewImageColumn.Layout 속성을 설정하면 크기에 맞게 늘이든 너무 큰 경우 그냥 잘라내든 간에 셀에 그림이 표시되는 방법을 구성할 수 있습니다.

 

DataGridViewImageColumn은 다음 두 가지 방법으로 사용할 수 있습니다. 먼저, DataGridViewButtonColumn과 동일한 방식으로 직접 이를 만들어 추가할 수 있습니다. 이 방법은 DataSet에 제공되지 않는 관련된 이미지 데이터를 표시하려는 경우에 유용합니다. 예를 들어 파일 이름(예: ProductPic002.gif)을 가져와 네트워크 드라이브에서 해당 파일을 읽은 다음 이를 표에 표시하는 경우가 있습니다. 이를 수행하려면 CellFormatting처럼 DataGridView 이벤트에 반응해야 하며, 이 때 열의 Value 속성을 사용하여 해당하는 행의 그림을 읽고 이미지 데이터를 가져오며 이를 삽입합니다.

 


 

DataSet에 수작업이 필요 없는 이진 그림 데이터가 들어 있으면 상황은 훨씬 간단해집니다. 이에 대한 한 가지 예는 회사 로고가 포함되어 있는 SQL Server pubs 데이터베이스의 pub_info 테이블입니다. 이 테이블에 바인딩할 때는 추가 단계를 수행할 필요 없이 DataGridView가 현재 이미지를 사용하고 있음을 자동으로 인식하여 필요한 DataGridViewImageColumn을 만들어 줍니다. (이 기법의 예제는 이 기사의 다운로드를 참조하십시오.)

 


 

DataGridView를 사용하는 편집

 

DataGrid는 사용자 입력에 유연성이 떨어지기로 악명 높아 셀의 유효성을 검사하고 오류를 보고하는 방식을 사용자 지정하기가 거의 불가능했습니다. 반면 DataGridView를 사용하면 편집 프로세스의 모든 단계에서 발생하는 수많은 서로 다른 단계에 반응하여 동작을 제어할 수 있습니다.

 

기본적으로 DataGridView 셀은 사용자가 마우스로 셀을 두 번 클릭하거나 F2 키를 누르면 편집 모드로 들어갑니다. 또한 DataGridView.EditCellOnEnter 속성을 true로 설정하면 사용자가 셀로 이동하자마자 편집 모드로 전환되도록 DataGridView를 구성할 수 있습니다. DataGridView의 BeginEdit(), CancelEdit(), CommitEdit() 및 EndEdit() 메서드를 사용하여 셀 편집을 프로그래밍 방식으로 시작 및 중단할 수도 있습니다. 사용자가 셀을 편집하면 행 헤더에 연필 모양의 편집 아이콘이 표시됩니다.

 


 

사용자가 Esc 키를 누르면 편집을 취소할 수 있습니다. EditCellOnEnter 속성을 true로 설정하면 셀이 편집 모드로 유지되지만 모든 변경 내용이 삭제됩니다. 변경을 커밋하려면 사용자가 새로운 셀로 이동하거나 포커스를 다른 컨트롤로 변경하면 됩니다. 코드에서 현재 셀 위치를 옮기면 변경 내용도 함께 커밋됩니다.

 


 

셀을 편집하지 못하게 하려면 DataGridViewCell, DataGridViewColumn, DataGridViewRow 또는 DataGridView의 ReadOnly 속성을 설정하면 됩니다(변경하지 못하게 하려는 대상이 해당 셀인지, 해당 열의 모든 셀인지, 해당 행의 모든 셀인지 또는 테이블의 모든 셀인지에 따라 다름). DataGridView는 또한 시도한 편집을 취소하기 위해 처리할 수 있는 CellBeginEdit 이벤트를 노출합니다.

 


 

오류 처리

 

기본적으로 DataGridViewTextBoxColumn을 사용하면 사용자가 현재 셀에 허용되지 않을 수 있는 문자를 포함하여 모든 문자를 입력할 수 있습니다. 예를 들어 사용자가 숫자 필드에 숫자가 아닌 문자를 입력하거나 DataSet에 정의된 ForeignKeyConstraint 또는 UniqueConstraint를 위반하는 값을 지정할 수도 있습니다. DataGridView는 이러한 문제를 다음과 같이 다양한 방식으로 처리합니다.

 

편집한 값을 필요한 데이터 형식으로 변환할 수 있으면(예: 사용자가 숫자 열에 텍스트를 입력한 경우) 사용자는 변경 내용을 커밋하거나 다른 행으로 이동할 수 없게 됩니다. 대신 변경 내용을 취소하거나 값을 편집해야 합니다.

 

편집한 값이 DataSet의 제약 조건을 위반하는 경우 사용자가 다른 행으로 이동하거나 Enter 키를 눌러 이를 커밋하고 나면 바로 변경 내용이 취소됩니다.

 

이처럼 명백한 기본값은 대부분의 시나리오에서 원활하게 적용됩니다. 그러나 필요한 경우 DataGridView가 데이터 원본에서 오류를 차단할 때 발생하는 DataGridView.DataError 이벤트에 응답하여 오류 처리에 참가할 수도 있습니다.

 


 

입력 유효성 검사

 

유효성 검사는 오류 처리와는 약간 다른 작업입니다. 오류 처리의 경우 DataSet를 통해 보고되는 문제를 처리합니다. 하지만 유효성 검사의 경우 DataSet에는 허용될 수 있지만 응용 프로그램에는 적절하지 않은 데이터 같은 고유한 사용자 지정 오류 조건을 찾아냅니다.

 

사용자가 새로운 셀로 이동하여 변경 내용을 커밋하면 DataGridView 컨트롤은 CellValidating 및 CellValidated 이벤트를 발생시킵니다. 또한 이러한 이벤트 다음에는 RowValidating 및 RowValidated 이벤트가 발생됩니다. 여러분은 이러한 이벤트에 응답하고 사용자가 입력한 값이 올바른지 확인하며 필요한 처리 후 작업을 수행할 수 있습니다. 값이 올바르지 않으면 일반적으로 변경 내용 및 셀 탐색을 취소하거나(EventArgs 개체의 Cancel 속성을 true로 설정하여) 사용자에게 알려줄 몇 가지 오류 텍스트를 설정하여 응답하게 됩니다. 오류 텍스트는 다른 컨트롤에 배치하거나 다음과 같이 해당하는 DataGridViewRow 및 DataGridViewCell의 ErrorText 속성을 사용하여 DataGrid에 표시할 수 있습니다.

 


 

DataGridViewCell.ErrorText를 설정하면 셀에 느낌표 아이콘이 표시됩니다. 마우스로 이 아이콘을 가리키면 오류 메시지가 표시됩니다.

 

DataGridViewRow.ErrorText를 설정하면 행 왼쪽의 행 헤더에 느낌표 아이콘이 표시됩니다. 마우스로 이 아이콘을 가리키면 오류 메시지가 표시됩니다.

 

일반적으로 이들 속성은 모두 함께 사용하며 행과 셀 모두에 오류 메시지를 설정하게 됩니다. 다음은 CompanyName 필드의 너무 긴 텍스트 항목을 확인하는 예제입니다. 문제가 되는 값을 발견하면 문제가 설명된 도구 설명 텍스트와 함께 오류 그림(빨강 느낌표 표시)이 셀에 추가됩니다.

 


 

Private Sub DataGridView1_CellValidating( ByVal sender As System.Object, _

 

ByVal e As System.Windows.Forms. DataGridViewCellValidatingEventArgs) _

 

Handles DataGridView1.CellValidating

 


 

If DataGridView1.Columns(e.ColumnIndex).Name = _

 

"CompanyName" Then

 

If CType(e.FormattedValue, String).Length > 50 Then

 

DataGridView1.Rows( e.RowIndex).Cells(e.ColumnIndex). _

 

ErrorText = "회사 이름이 너무 깁니다.

 

End If

 

End If

 

End Sub

 


 

목록 열로 선택 사항 제한

 


 


 

유효성 검사를 사용하면 모든 오류 조건을 잡아낼 수 있습니다. 그러나 이 접근 방식을 사용하면 잘못된 내용을 입력한 다음 이를 사후에 수정할 수 있기 때문에 반드시 최선의 방법이라고 할 수는 없습니다. 보다 바람직한 방법은 우선 사용자가 잘못된 내용을 입력하지 못하도록 하는 것입니다.

 

한 가지 일반적인 예는 열을 미리 정의한 값 목록으로 제한해야 하는 경우에 나타납니다. 이 시나리오에서는 사용자가 직접 입력하기보다 목록에서 올바른 값을 선택하는 것이 가장 쉽습니다. 무엇보다도 DataGridViewComboBoxColumn을 사용하면 이 디자인을 가장 손쉽게 구현할 수 있습니다.

 


 

DataGridViewComboBoxColumn의 항목 목록은 ListBox에서처럼 항목 컬렉션을 사용하여 직접 추가할 수 있습니다. 또한 DataGridViewComboBoxColumn을 또 다른 데이터 원본에 바인딩할 수 있습니다. 이 경우 DataSource 속성을 사용하여 데이터 원본을 지정하고 DisplayMember 속성을 사용하여 열에 표시해야 하는 값을 나타내며 ValueMember 속성을 사용하여 기본 열 값에 사용해야 하는 값을 나타냅니다.

 


 

이에 대한 데모로 Products 테이블에 작업하는 다음 예제를 살펴보겠습니다. 이 테이블의 모든 레코드는 해당 CategoryID 필드를 통해 Categories 테이블의 레코드에 연결되어 있습니다. 제품의 범주를 변경하려면 사용자는 올바른 ID를 기억하여 CategoryID 필드에 입력해야 합니다. 보다 나은 방법은 Categories 테이블에 바인딩되는 DataGridViewComboBoxColumn을 사용하는 것입니다. 이 열은 CategoryName을 표시 멤버로 사용하지만 실제 기본 값으로 CategoryID를 갖게 됩니다. 또한 이 열은 DataProperyName 속성을 통해 Products 테이블에 계속 바인딩되어 사용자가 목록에서 새 Category를 선택하면 제품 레코드의 CategoryID 필드가 자동으로 변경됩니다.

 


 

다음은 이 테이블을 구성하는 데 필요한 코드입니다.

 


 

' 자동으로 생성된 CategoryID 열을 제거합니다.

 

DataGridView1.Columns.Remove("CategoryID")

 


 

' CategoryID의 목록 열을 만듭니다.

 

Dim List As New DataGridViewComboBoxColumn()

 

List.DisplayIndex = 0

 

List.HeaderText = "Category"

 


 

' 이 열은

 

' Products.CategoryID 필드에 바인딩됩니다.

 

List.DataPropertyName = "CategoryID"

 


 

' Categories 테이블에서 목록을 채웁니다.

 

List.DataSource = ds.Tables("Categories")

 

List.DisplayMember = "CategoryName"

 

List.ValueMember = "CategoryID"

 


 

' 열을 추가합니다.

 

DataGridView1.Columns.Add(List)

 


 

결론

 


 


 

이 기사에서는 .NET에서 가장 기대되는 새로운 컨트롤 중 하나인 DataGridView의 개요를 설명했습니다. DataGrid와 달리 DataGridView는 데이터 바인딩 및 사용자 편집 시나리오와 정적 텍스트의 표시와만 관련된 시나리오를 포함하여 다양한 실제 시나리오에서 올바르게 작동합니다. 머지 않아 .NET Framework가 설치된 통합 기능 데이터 솔루션이자 Windows Forms 개발자들이 .NET 2.0으로 업그레이드하려는 가장 큰 이유 중 하나인 DataGridView를 직접 경험할 수 있을 것입니다.

 

 

데브피아에서 발췌함.

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=70808&ref=70808

// ------------------------------------------------------------------------------

// DataGridView의 Cell 속성을 ComboBox로 했을경우의 몇가지 팁.

// (C#에 입문한지 얼마 안되서 최적의 방법인지 어쩐지는 모르겠습니다)

// ------------------------------------------------------------------------------

 

int nYourRow = 1;

int nYourCell = 2;

int nSelectedIndex = 0;

String strCurrentValue = "";

int nCurrentSelectedIndex = 0;

 

DataGridViewComboBoxCell dgvCb = (DataGridViewComboBoxCell)dataGridView.Rows[nYourRow].Cells[nYourCell];

 

// ComboBox를 원하는 값으로 지정할 경우

dgvCb.Value = dgvCb.Items[nSelectedIndex].ToString();

 

// ComboBox의 선택된 값을 가져올 경우

strCurrentValue = (String)dgvCb.Value; // (String)생략 가능

 

// ComboBox의 선택된 Index 가져올 경우

nCurrentSelectedIndex = dgvCb.Items.IndexOf(dgvCb.Value);

 

 

/*

* 정말 오랫만에 데브피에 로긴을 하네요..

* "아마 Rows.Selected() 인가가 있을겁니다. 확인해 보세요 ^^; "

* 이건 행이 선택되어졌는가를 반환합니다.

*/

 

 

C#의 리소스는 모두 .resx 확장자를 가지는 XML 파일로 관리된다. 기존 Windows C/C++ 프로그래밍에서 .rc 파일을 컴파일 하여 .res의 바이너리 파일로 만들어 링크하던 것과는 많은 차이가 있는 것 같다. C#의 리소스는 리소스가 할당되는 위치에 따라 두 가지로 나뉘어지는데, 프로젝트 리소스 (Project Resource)로컬 리소스 (Local Resource)이다.

  • 프로젝트 리소스 (Project Resource)
    특징 : 프로젝트에 속해 있는 공유개념의 리소스이며, 주로 외부 파일로 관리된다.
    장점 : 프로젝트 내에서는 리소스의 공유가 쉽다.
    단점 : 프로젝트 리소스를 사용하는 폼을 복사할 때에는 사용하는 리소스도 함께 복사해 주어야 한다.
  • 로컬 리소스 (Local Resource)
    특징 : 폼 혹은 컨트롤에 속해 있는 리소스이다.
    장점 : 폼이나 컨트롤의 소스를 복사할 때 리소스도 같이 복사되므로 모듈화가 쉽다.
    단점 : 같은 리소스를 여러 폼에서 사용할 때 중복 포함되어 낭비가 발생한다.

어떤 폼이나 컨트롤에서 리소스를 지정하려 하면 다음의 그림과 같이 리소스 유형을 고르도록 한다. (예를 들어, 버튼의 이미지를 지정하는 등의 방법을 말한다.)

리소스 파일(.resx)의 형식은 XML 형태이지만 내부에 포함하는 형식에는 다음과 같이 약간의 차이가 있다.

  • 프로젝트 리소스 (Project Resource)
    • 위치 : Properties / Resources.resx 파일 (다음의 왼쪽 그림 참조)
    • 내부 : 리소스에 대한 설명 (리소스 이름, 리소스 유형, 외부 파일이름 등)이 기록
      (단, 문자열 리소스는 Resources.resx 파일에 직접 기록)
    • 위의 그림에서 프로젝트 리소스를 선택하는 경우에는 필요한 리소스를 새로 생성하거나 기존 리소스를 선택하여 사용할 수 있음
  • 로컬 리소스 (Local Resource)
    • 위치 : Form1.cs / Form1.resx 파일 (다음의 오른쪽 그림 참조)
    • 내부 : 프로젝트 리소스와는 달리 모든 리소스의 바이너리 값도 Form1.resx 파일에 직접 기록
    • 위의 그림에서 로컬 리소스를 선택하는 경우에는 필요한 리소스의 중복여부와 관계없이 항상 새로 리소스를 생성한다.

각 리소스들은 프로그램 작성시 통합환경에서 버튼과 같은 컨트롤의 이미지를 지정하여 사용할 수도 있으며, 여기서 설명하려고 하는 실행 시 로드/지정할 수도 있다. 통합환경에서 지정하는 것은 매우 쉽기 때문에 여기서는 설명을 생략한다. 다음에는 실행 시에 동적으로 리소스를 로드하고 지정하는 방법을 설명한다.

 

(1) 프로젝트 리소스 직접 지정하기

프로젝트 리소스는 다음과 같이 컴파일 시에 직접 지정되도록 할 수 있다.

1
2
3
4
5
6
private void button3_Click(object sender, EventArgs e)
{
// direct name indicating for global resources
pictureBox1.Image = Properties.Resources.Image1;
pictureBox2.Image = Properties.Resources.Image2;
}

이것은 다음의 그림과 같이 프로젝트 리소스 생성시에 Resources라는 클래스를 만들어 해당 리소스의 이름으로 속성 (Property)를 만들어 주기 때문이다.

지정하고자 하는 리소스는 <Project Namespace>.Properties 네이스페이스의 Resources 클래스의 정적 속성 (static property)로 선언되어 있다.

 

(2) 프로젝트 리소스 실행 시 로드하기

실행 시에 프로젝트 리소스를 로드하기 위해서는 System.Resources.ResourceManager 클래스를 이용한다. ResourceManager 객체 생성 시에 리소스의 루트이름과 어셈블리를 지정하고, ResourceManager 객체의 GetObject( ) 메서드를 이용하여 리소스를 로드한다. 프로젝트 리소스의 루트이름은 다음과 같다.

<Assembly Name>.Properties.Resources

다음의 C# 코드를 보면 좀 더 이해하기 쉬울 것이다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
using System.Resources; // ResourceManager
...
...
protected object LoadProjectResource(string strResName)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string strBaseName = assembly.GetName().Name + "." + "Properties.Resources";
// strBaseName = "csharp_ResourceTest.Properties.Resources";
ResourceManager rm = new ResourceManager(strBaseName, assembly);
return rm.GetObject(strResName); // load resource from item name
}
private void button4_Click(object sender, EventArgs e)
{
...
pictureBox1.Image = (Image) LoadProjectResource("Image1");
...
}

 

(3) 로컬 리소스 실행 시 로드하기

앞에서도 설명했지만, 로컬 리소스는 폼이나 컨트롤에 속해 있는 리소스이다. 실행 시에 로컬 리소스를 로드하기 위해서는 프로젝트 리소스와 같이 System.Resources.ResourceManager 클래스를 이용한다. ResourceManager 객체 생성 시에 해당 폼의 타입을 지정하고, ResourceManager 객체의 GetObject( ) 메서드를 이용하여 리소스를 로드한다. 다음 C# 코드는 로컬 리소스를 로드하는 예제는 다음과 같다.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
using System.Resources; // ResourceManager
...
...
protected object LoadFormResource(string strResName)
{
ResourceManager rm = new ResourceManager(this.GetType());
return rm.GetObject(strResName);
// load resource from item name
}
private void button4_Click(object sender, EventArgs e)
{
// indirect load local resource
Image image2 = (Image) LoadFormResource("button1.Image");
pictureBox2.Image = image2;
}

 

어떤 형식의 리소스를 사용하고 어떻게 로드 하는지는 정답이 있는 것이 아니라 각 상황에 맞추어 결정해야 할 것이다.

 

 

[지식인] 해시코드로 알아본 string의 메모리 저장방법 | UX 정보

전체공개 0 / 2008.07.03 16:38

<form name="articleHeadModifyForm" action="/ArticleHeadModify.nhn"><input name="m" value="modifyInArticle" type="hidden" /><input name="cluburl" value="kaonsi" type="hidden" /><input name="clubid" value="14637374" type="hidden" /><input name="menuid" value="14" type="hidden" /><input name="articleid" value="127" type="hidden" /><input name="targetarticleheadid" value="" type="hidden" />

</form>

쇼티(sepiashorty)
우수멤버 1:1대화
http://cafe.naver.com/kaonsi/127<input id="cafeurlstr" name="cafeurlstr" value="http://cafe.naver.com/kaonsi/127" type="hidden" />

 

 

출처 : http://kin.naver.com/detail/detail.php?d1id=1&dir_id=10115&eid=09OM57qkiCBgzAiDwSqwpQc0Gay+5a7h&qb=Z2V0aGFzaGNvZGU=

 

 

using System;

public class B{
public string aa = "Hello";
public B() { //생성자
Console.WriteLine("생성자 해시코드 : {0}", aa.GetHashCode());

}
~ B(){ //소멸자
Console.WriteLine("소멸자 해시코드 : {0}", aa.GetHashCode());
}
}


public class A{
public static void Main(){

B test = new B();
test.aa = "ello";
Console.WriteLine("참조값 해시코드 : {0}", test.aa.GetHashCode());

}
}

 

컴파일결과

생성자 해시코드 : -694847

참조값 해시코드 : -1518819367

소멸자 해시코드 : -1518819367

 

 

여기서 궁금한것이 있습니다.

 

최초 객체 test를 생성하고 aa라는 문자열 참조변수가 생성되었을 겁니다.

생성당시 해시코드는 (-694847) 입니다.

 

그런데 객체를 통해 aa에 접근해서 다른 문자열을 넣어주니 해시코드가 바뀌었습니다.

제가 알기론 해시코드는 객체가 생성되면 그 객체를 참조할 수 있는 고유한 값이라고 알고 있습니다.

 

1. 그렇다면 기존 "Hello"와는 따로 "ello"의 메모리가 새로 생성된 것인지,,??

 

2. 그래서 참조변수 aa에 "ello"에 대한 참조값이 들어갔다면 기존에 있던 "Hello"를 참조할 수 있는 방법은 없는건지?

 

3. 메모리상에는 "Hello"가 여전히 존재 하고 있는건지..?

 

4. 만약 존재한다면 가비지콜렉터의 대상으로 "Hello"의 메모리가 소멸되어야 하는것은 아닌지?

 

5. 그런데 위에 소멸자를 보면 "ello"의 메모리가 소멸되고 있습니다. 이건 왜 이런건지?

 

완전 초보입니다..

좀 도와 주십시오~

 

 

============================================================================================================

 

안녕하세요?

 

C#에서 문자열은 특수한 개념을 가지고 있습니다.

 

String a = "asd";

 

위와 같이 a라는 문자열 클래스를 생성하면, 일반적으로 생각하는대로 메모리에 "asd"라

 

는 상수가 생길 것이고, a라는 변수가 이를 가르킬 것입니다.

 

그런데 만약에

 

a = a + "s";

 

라는 코드를 작성하게 되면, 우리가 직관적으로 생각하듯이 "asd" 라는 문자열 뒤에 "s"가

 

붙는것이 아닌, 새로운 문자열이 생성됩니다. 즉, "asd" + "s" 인 "asds"라는 문자열이

 

새로운 공간에 생성되고, a는 이를 가르킵니다. 따라서 해쉬코드가 바뀌게 됩니다.

 

이러한 특성은 대입문이라서 일어나는 것이 아닌 문자열 변경인 모든 경우에

 

일어납니다. 즉, 문자열을 변경하고 이를 대입하게되면 새로운 객체가 생성됩니다.

 

이러한 특성때문에 문자열을 통해 많은 연산을 할 경우 성능을 떨어트리는 직접적인

 

원인이 될 수 있습니다.

 

예를 들어 다음과 같은 문자열 연산을 한다고 가정해봅시다.

 

string myString = "";

for (int i=0; i<100; i++)

{

myString += i.ToSting();

}

 

위와 같은 코드를 실행하게되면, 각 for문 마다(즉, 100번) 새로운 문자열을 위한 공간이

 

할당됩니다. 즉, 계속해서 문자열이 새로 생성되는 것입니다.

 

"1"

"12"

"123"

....

이런 식으로 말입니다..

 

그럼 이와 원리를 바탕으로 답변하겠습니다..

 

1. 그렇다면 기존 "Hello"와는 따로 "ello"의 메모리가 새로 생성된 것인지,,??

 

따로 생성이 되것입니다.

 

2. 그래서 참조변수 aa에 "ello"에 대한 참조값이 들어갔다면 기존에 있던 "Hello"를 참조할 수 있는 방법은 없는건지?

 

새로운 대입을 하기전 참조를 다른곳에 저장해두면 가능합니다.

 

위에서 설명이 부족했거나 빠트린것 같습니다만..

 

문자열 객체가 아무때나 새로이 생성되는 것은 아닙니다.

 

어떤 문자열을 저장하고 있다가, 그 문자열이 변경되게 되면 새로 생기게 됩니다.

 

따라서 어느 문자열에 대해서 두 개의 참조를 생성하고 하나를 변경하게 되면

 

원래의 것은 남습니다.

 

예를 들어 a와 b라는 문자열이 있을때,

 

string a = "temp";

b = a; // 여기까지는 새로운 문자열이 생성되는 것이 아닙니다.

// 문자열에 대한 변경이 없었기 때문입니다.

b = b + "1" // 새로운 문자열 "temp1"이 할당됩니다. 따라서 기존의 참조는 삭제됩니다.

 

하지만 a는 원래의 참조를 가지고 있습니다.

 

3. 메모리상에는 "Hello"가 여전히 존재 하고 있는건지..?

 

그럴 수도 있으며, 아닐 수도 있습니다. 물론 존재하리라 생각이 듭니다. 명시적으로 가비지콜렉터를 호출해주지 않으셨으므로, 남아있을 가능성이 큽니다. 하지만 확신할 수는 없습니다. 그것은 가비지콜렉터의 정책에 대해 확실하게 알아보는 것이 좋을듯 합니다.

 

 

4. 만약 존재한다면 가비지콜렉터의 대상으로 "Hello"의 메모리가 소멸되어야 하는것은 아닌지?

 

맞습니다. 가비지 콜렉터가 동작한다면, 소멸될 것입니다.

 

5. 그런데 위에 소멸자를 보면 "ello"의 메모리가 소멸되고 있습니다. 이건 왜 이런건지?

 

소멸자에서 찍어보았다고해서 소멸되는 것이라고 확신하는 것은 틀린 것입니다.

 

만약 "ello"가 소멸된다고 해도, 객체에 할당되어 있는 것이 "ello"가 맞기 때문에

 

정상적으로 동작하는 것입니다.

 

 

추가 : 위와 같은 문자열을 특성으로인해 성능이 엄청 떨어지는 경우가 있을 수 있습니다.

문자열을 사용하고, 연산하는 것은 일반적으로 너무나 자주 쓰는 것이기 때문입니다.

이러한 문자열의 단점(?)을 해결하기 위해서 닷넷은 StringBuilder 라는 클래스를

제공하고 있습니다. 사용법은 간단합니다.^^; MSDN을 검색하시면 쉽게 아실 수 있습니다.

 

 

그럼 좋은 하루 되시길.. 부족한 답변은 쪽지나 메일 또는 다른 답변을 이용해주세요^^

 

 

==================================================================================================================

 

몇주전 사내 세미나였던 박정오 사원의 'StringBuilder'의 사용 의의와 일맥상통하는 부분이 있군요.

 

개념적인 이야기니.. 퍼포먼스를 고려하시는 개발자분들께선 참고하셔도 좋을 것 같습니다. ^^

 

#region 역할: window usb list 장착 여부 확인 (SafeNet USB SuperPro/UltraPro)
private void GetUsbDeviceList()
{
var usbDevices = GetUSBDevices();
foreach (var usbDevice in usbDevices)
{
string szBuf = string.Format("Device ID: {0}, PNP Device ID: {1}, Description: {2}",
usbDevice.DeviceID, usbDevice.PnpDeviceID, usbDevice.Description);
}

}

static List<USBDeviceInfo> GetUSBDevices()
{
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();

ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_USBHub"))
collection = searcher.Get();

foreach (var device in collection)
{
devices.Add(new USBDeviceInfo(
(string)device.GetPropertyValue("DeviceID"),
(string)device.GetPropertyValue("PNPDeviceID"),
(string)device.GetPropertyValue("Description")
));
}

collection.Dispose();
return devices;
}


class USBDeviceInfo
{
public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
{
this.DeviceID = deviceID;
this.PnpDeviceID = pnpDeviceID;
this.Description = description;
}
public string DeviceID { get; private set; }
public string PnpDeviceID { get; private set; }
public string Description { get; private set; }
}
#endregion

 

 

MainForm_KeyDown += ..
이벤트를 등록한 후 키보드 입력을 했는데도 불구하고 이벤트가 발생이 안됩니다.

// 폼의 커트롤에 대한 키보드 이벤트를 폼에 등록할지 여부를 결정하도록 설정
this.KeyPreview = true;

--> Form의 속성 창에 존재함.


다음과 같이 프로퍼티를 설정하게 되면 사용 가능하게 됩니다.

참고로

private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.Alt && e.KeyCode == Keys.F4)
e.Handled = true; // alt + F4의 키를 수행하지 않도록 방지
}

 

+ Recent posts