XPLATFORM 101
- Grid > Cell 병합

아마도 셀 병합은 국내 기업 환경에서 많이 요구하는 사항일 겁니다.
요구사항은 엑셀이지만 아무래도 그정도까지 가져가기는 쉽지 않지만
엑스플랫폼에서 제공하는 그리드 옵션에서는 상당히 많은 부분까지 쉽게 구현할 수 있는 구조를 제공하고 있습니다.

일반적으로 많이 사용하는 동일한 데이터 병합을 살펴보겠습니다.
단순 셀 병합

1. 그리드를 만듭니다.
2. 데이터셋을 만들고 데이터를 집어넣습니다.
3. 그리드에 데이터셋을 바인딩합니다.


뭐 여기까지는 앞에서 많이 보았으니깐..ㅎㅎ

이제 그리드를 더블클릭하고 에디터로 들어가서
병합을 하고자 하는 셀의 suppress 속성을 사용합니다.
이 속성의 설명을 보면 '복수개의 Row에 같은 Column의 Cell의 값이 같을 때, 이를 하나로 합하는 레벨을 설정하는 Property 입니다.'라고 되어있습니다. Cell의 값이라는 것은 실제적인 텍스트를 이야기하는 거죠. 위의 예제에서 col1에서 라면류, 빙과류와 col2에서 심양, 농섬, 방그레, 로데...와 같은 것을 의미합니다.

설정은 각 컬럼 body 셀에 설정하게 되어 있습니다.


이렇게 설정을 해주면 동일한 그리드에 동일한 바인딩인데 결과가 달라집니다.


여기서 한가지 제약은 데이터셋의 순서가 정렬되어있어야 합니다.
그래서 필요하다면 ds_suppress.keystring  = "S:col1"; 와 같이 정렬 처리를 할 수 있습니다.

이번에는 좀 더 복잡한 셀 병합을 시도해보겠습니다.

조금 복잡한 셀 병합

1. 그리드를 만듭니다.
2. 데이터셋을 만들고 데이터를 집어넣습니다.
3. 그리드에 데이터셋을 바인딩합니다.
- 이때 주의할점은 데이터셋을 그냥 드래그해서 가져가게 되면 자동으로 그리드 포맷까지 생성이 됩니다.
뭐 그렇게 만들어놓고 지우고 다시 만들어도 상관은 없지만 번거러운 작업이니 그냥 binddataset 속성에 값만 정의해줍니다.

4. 이제 그리드를 더블 클릭하고 에디터로 들어가서
먼저 헤드를 추가해주고


바디 영역을 추가해줍니다.


그리고 컬럼을 추가해주면 모양이 완성됩니다.


이제 모양을 만들 차례입니다.
앞에서 간단한 병합 처리는 suppress 속성을 사용했는데
이번에는 포맷에서 직접 병합을 지시할겁니다.
좀 의미가 다른게 포맷에서 병합을 하는 것은 데이터 자체를 병합하는 게 아니라 셀의 레이아웃을 병합하는 겁니다.


그냥 합치고자 하는 셀을 선택하고 Merge Cells 을 선택하기만 하면 됩니다.
그리고 나면 메시지가 나오는데


쉽게 말하면 subcell 구조를 유지할 것인지 말것인지에 대해 결정하라는 겁니다.
subcell 구조를 유지하면 보이기에는 병합된것처럼 보이지만 각 셀단위로 컨트롤이 가능하게 됩니다.

생성된 코드를 보면 이런 차이를 가지게 됩니다.
<Band id="body">
<Cell rowspan="2">
<Cell/>
<Cell row="1"/>
</Cell>
<Cell col="1" colspan="2"/>
<Cell row="1" col="1"/>
<Cell row="1" col="2"/>
</Band>

<Band id="body">
<Cell rowspan="2"/>
<Cell col="1" colspan="2"/>
<Cell row="1" col="1"/>
<Cell row="1" col="2"/>
</Band>
겉으로 보기에는 동일하지만 코드에서 보는 것처럼 병합된 셀 안에 또 다시 셀 구조를 가지게 됩니다.
그리고 당연한 이야기겠지만 헤드와 바디는 병합이 되지 않습니다.

이렇게 만들고 각 셀에 해당하는 컬럼을 바인딩해주면 조금 복잡한 셀병합이 완료됩니다.

그냥 이렇게 만들고 완료 처리를 해주면


그리드 영역이 애매하게 남아버립니다.
이럴때에는 그리드에서 autofittype을 col로 설정해서 화면에 적절하게 배치되도록 조정해줍니다.

* 한가지 추가적으로 셀 병합시 서브셀을 남겨놓았을때 각 서브셀의 속성을 지정할 필요가 있는데
이럴 경우에는 에디터에서 Alt 키를 누른채로 해당 셀을 선택하면 됩니다.
아래 그림의 경우 첫번째 헤더는 병합된 상태인데 각각 텍스트 속성이 들어가있습니다.


화면에서 보면 아래와 같이 보이게 되죠.


그리고 소스코드를 보면 아래와 같이 표현됩니다.
<Band id="head">
<Cell rowspan="2">
<Cell text="col4"/>
<Cell row="1" text="col4"/>
</Cell>
<Cell col="1" colspan="2" text="col1"/>
<Cell row="1" col="1" text="col2"/>
<Cell row="1" col="2" text="col3"/>
</Band>
http://cafe.naver.com/xplatform101/159
XPLATFORM 101
- Grid > CheckBox & Radio

앞에서 언급했던 체크박스를 만드는 것과 좀 더 향상된 옵션 그리고 라디오 콘트롤까지 다룹니다.

체크박스 전체 선택
- (8)번에서 다루었던것과 유사한데 여기에 전체 선택후 사용자가 일부 항목을 해제한 경우
헤더 영역의 체크박스가 다른 형태로 보이게 됩니다.
ㅠㅠ 말로 하면 잘 이해가 안되고 그림으로...


그리드 만들고 이벤트 붙이는 것은 전과 동일하고
헤더 클릭시 이벤트 내용이 조금 달라집니다.
<font class="Apple-style-span" color="#666666"></font>
function fn_checkAll(obj, nCell, visibleColid)
{
if (visibleColid === null || visibleColid === undefined) {
visibleColid = "";
}
var objDs = this.objects[obj.binddataset];
var colid = obj.getCellProperty("body", nCell, "text").replace(/bind:/, "");
var val = parseInt(obj.getCellProperty("head", nCell, "text")) == 0 ? 1 : 0;

if (obj.controlchecktype === -1) {
val = 0;
}
obj.controlchecktype = val;
obj.head.getCell(0).style.controlbackground = obj.orgcontrolbackground;

obj.setCellProperty("head", nCell, "text", val);

objDs.enableevent = false;
for (var i=0; i<objDs.rowcount; i++) {
if (visibleColid.length > 0 && parseInt(objDs.getColumn(i, visibleColid)) == 0) {
continue;
}
objDs.setColumn(i, colid, val);
}
objDs.enableevent = true;
}
특이한 내용을 살펴보면 앞선 코드에서 eval 로 처리했던 부분을 폼의 objects 속성으로 접근하게 했습니다.
objects[index], objects["id"], objects.length 와 같은 방식으로 접근할 수 있기 때문에 id를 사용할 수 있습니다.
visibleColid를 처리한것은 예제중에 일부 데이터만 체크박스를 보여주는 부분이 있어서 그렇습니다.
체크박스 처리가 필요없는 부분은 스킵하는 형식이죠.

그리드에서는 다음과 같이 처리합니다.


앞에서 본 것처럼 헤더의 체크박스 영역을 바꾸어주는 부분은 데이터셋에 oncolumnchanged 이벤트로 처리합니다.
<font class="Apple-style-span" color="#444444"></font>
function fn_setCheckHalf(objDataset, objGrid, nCell)
{
if (parseInt(objGrid.getCellProperty("head", nCell, "text")) == 1) {
if (objDataset.findRowExpr("parseInt(CHECKED) <> 1") > -1) {
objGrid.setCellProperty("head", nCell, "text", "0");
objGrid.controlchecktype = -1;
objGrid.orgcontrolbackground = objGrid.head.getCell(nCell).currentstyle.controlbackground;
objGrid.head.getCell(nCell).style.controlbackground = "URL('Img::img_checksome.png') center middle";
}
}
}
헤더 체크박스의 상태가 선택인 경우 findRowExpr() 함수에서 데이터셋에서 체크되지 않은 항목을 찾으면 해당 그리드의 헤더 체크박스 영역의 이미지를 살짝 바꾸어놓는 트릭입니다.

Cell.style.controlbackground [=stxBackGround] 을 사용하면 셀 단위의 배경 이미지를 바꿀 수 있습니다.

라디오버튼
- 그리드에서 라디오버튼을 사용할 일은 많지 않은데 그래도 필요하다면 사용할 수 있는 대안입니다.
displaytype은 none으로 설정하고 background 값을 표현식으로 처리해줍니다.


셀 배경값 처리의 우선순위는
background(테마, CSS) > background(CELL) > cellbackground (Grid Band) > background (ROW)

그리고 데이터의 처리는 그리드에서 oncellclick 이벤트를 사용해 처리합니다.
function fn_setRadio(obj, e, visibleColid)
{
var objDs = this.objects[obj.binddataset];
var colid = obj.getCellProperty("body", e.cell, "text").replace(/bind:/, "");

if (visibleColid !== null && visibleColid !== undefined) {
if (parseInt(objDs.getColumn(e.row, visibleColid)) == 0) return;
}
objDs.enableevent = false;
for (var i=0; i<objDs.rowcount; i++) {
objDs.setColumn(i, colid, (i == e.row ? 1 : 0));
}
objDs.enableevent = true;
}
데이터가 변경되면 바인딩된 background 가 바뀌면서 마치 선택한것과 동일한 효과를 만들어줍니다.
주의할점은 라디오버튼은 여러개가 있을때 한개만 선택된다는 거죠.
그래서 선택된 아이디를 제외한 나머지는 미선택으로 처리됩니다.

http://cafe.naver.com/xplatform101/158 
XPLATFORM 101
- Grid > 전체 선택/해제

아마 일반적인 웹 애플리케이션에서 가장 많이 사용하는 패턴이 아닌가 싶습니다.
순서대로 따라가보겠습니다.


1. 적절한 Dataset을 만들어줍니다.

2. 그리드를 만듭니다.

3. 그리드에 Dataset을 연결해줍니다.

4. 디자인모드에서 그리드를 더블클릭하고 Grid Contents Editor 에서
첫번째 컬럼(또는 체크박스를 넣고자 하는 컬럼)의 헤드와 바디 영역의 속성 중에서
displaytype 과 edittype을 'checkbox'로 변경합니다.


5. 바디 영역은 체크여부를 가져오는 데이터 컬럼과 연결해줍니다.
<Band id="body">
<Cell displaytype="checkbox" edittype="checkbox" text="bind:CHECKED"/>
<Cell col="1" displaytype="normal" text="bind:TEST_VAL"/>
</Band>
6. 그리드에 onheadclick 이벤트를 추가합니다.
체크박스를 지정한 컬럼인 경우에 관련 이벤트를 처리하는 함수를 연결합니다.

7. gf_SetGridCheckAll()
var gv_IsGridCheckAll = 0;
function gf_SetGridCheckAll(obj:Grid, e:GridClickEventInfo)
{
var dsObj = eval(obj.binddataset);
var v_Colid = obj.getCellProperty("body", e.cell, "text").replace("bind:", "");
gv_IsGridCheckAll = (gv_IsGridCheckAll ? 0 : 1);
dsObj.enableevent = false;
for (var i=0; i<dsObj.getRowCount(); i++) {
dsObj.setColumn(i, v_Colid, gv_IsGridCheckAll);
}
obj.setCellProperty( "Head", 0, "expr", gv_IsGridCheckAll);
dsObj.enableevent = true;
}
범용으로 사용하는 함수이기 때문에 데이터셋을 직접 지정하지 않았네요.
obj.binddataset 을 바로 가지고 오면 문자열 형태로 오기 때문에 eval로 감싸주었습니다.
v_Colid 변수에는 바인딩된 컬럼 이름을 담아냅니다.
그리고 나서 해당 컬럼의 데이터를 일괄적으로 변경해줍니다.

* 쇼케이스에서 제공하는 형식은 보편적인 방식이고 애플리케이션에 따라
조금씩 요구사항이 달라질 수 있습니다.

http://cafe.naver.com/xplatform101/157