Creative Wrong Answer

오늘 좀 한가하다 -ㅅ-;;;
덕분에 하나 더 올리게 되었다.

이번 포스팅은 이전 포스팅의 계량형이라고 할까.. 업그레이드 버전 정도 된다.
2010/03/09 - [Flex/Event] - 데이터 그리드 ListEvent / DatagridEvent 특정 칼럼만 에디트 하기

목적

이전 포스팅에서 더블클릭으로 아이템 에디트 모드로 들어가고 클릭으로 선택 하는 형식으로 만들었는데 이번에는 itemRenderer를 이용해서 에디트 버튼을 가지고 있는 데이터 그리드를 만든다.
그리고 디비에 데이터를 업데이트 한다고 할때 어느 시점에서 업데이트를 해야 하는지 CollectionEvent를 이용해서 알아본다.

										rinn				퍼플린				B										magis				들캔디				A										zzatung				짝퉁				??									.btnEditColumn{			upSkin: Embed(source="assets/edit.png");			overSkin: Embed(source="assets/edit_over.png");			downSkin: Embed(source="assets/edit.png");		}																


기본적으로 mxml 부분은 이전과 같다.
Datagrid 에 붙어있던 각종 이벤트 처리 부분이 랜더러로 빠져나가기 때문에 Datagrid 태그쪽만 좀 한가해졌다.

아이템랜더러는 엄청나게 많이 사용되고 질문또한 많이 올라오는 부분중에 하나이다. 간단하게 버튼을 넣는거 부터 복잡하게는 랜더러와 에디터를 따로 커스터마이징 해서 사용하는 경우까지 상황이 다양하지만. 기본적으로는 비슷하기 때문에 이 포스트에서 다루는 정도만 해도 기초적인것은 커버 될꺼라 생각한다.

위 소스 에서 <mx:DataGridColumn dataField="name" itemRenderer="renAddEditButton"> 이곳에 설정된 itemRenderer 로 사용되고 있는 renAddEditButton 이 이번 포스트의 핵심이다.

일단 소스 풀소스를 보자.

package{	import flash.events.MouseEvent;		import mx.containers.Canvas;	import mx.controls.Button;	import mx.controls.DataGrid;	import mx.controls.Label;	import mx.controls.dataGridClasses.DataGridColumn;	import mx.controls.dataGridClasses.DataGridListData;	import mx.controls.listClasses.BaseListData;	import mx.controls.listClasses.IDropInListItemRenderer;	import mx.core.ClassFactory;	public class renAddEditButton extends Canvas implements IDropInListItemRenderer	{		private var _btnEdit:Button;		private var _label:Label;		private var _listData:DataGridListData;				public function renAddEditButton()		{			super();		}				override protected function createChildren():void		{			super.createChildren();			this.horizontalScrollPolicy = "off";						_label = new Label();			addChild(_label);			_btnEdit = new Button();			_btnEdit.styleName = "btnEditColumn";			_btnEdit.visible = false;			addChild(_btnEdit);						_btnEdit.addEventListener(MouseEvent.CLICK, hBtnClick);						this.addEventListener(MouseEvent.MOUSE_OVER, hRollOver);			this.addEventListener(MouseEvent.MOUSE_OUT, hRollOut);		}				override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void		{			super.updateDisplayList(unscaledWidth, unscaledHeight);			_label.width = unscaledWidth-21;			_label.x = 3;			_label.y = 2;			_btnEdit.x = unscaledWidth-18;		}				private function hBtnClick(e:MouseEvent):void		{			var dg:DataGrid = DataGrid(listData.owner);			dg.editable = true;			dg.editedItemPosition = {columnIndex:1, rowIndex:listData.rowIndex};			trace("aa");		}				private function hRollOver(e:MouseEvent):void		{			_btnEdit.visible = true;		}				private function hRollOut(e:MouseEvent):void		{			_btnEdit.visible = false;		}				public function set listData(value:BaseListData):void		{			_listData = DataGridListData(value);			_label.text = listData.label;		}				public function get listData():BaseListData		{			return _listData;		}			}}


복잡해보이지만 별건 없다.
데이터 그리드의 아이템 랜더러를 사용하기 위해서 IDropInListItemRenderer를 구현한다. 저 인터페이스가 요구하는건 데이터 그리드의 listData를 엑세스 하기 위한 getter/setter 뿐이다.

기본흐름

createChildren을 override 해서 버튼과 라벨을 만들어준다.
updateDisplayList 에서는 위치를 잡아주고
데이터 그리드에 오버 했을때 버튼이 나타나도록 하기 위해서 해당 이벤트 핸들러를 등록한다.
랜더러 안에 있는 버튼을 클릭했을때 해당 데이터 그리드의 참조를 획득해서 editable = true 로 세팅해주고 이전 소스에서 나왔던 Datagrid.editedItemPosition 을 사용해서 해당 셀을 에디트 가능 상태로 만든다.

DataGrid 에는 itemEditEnd 이벤트를 걸어주고 아이템이 수정이 끝났을때 editable = false 로 다시 세팅해준다.

메인 mxml 부분의 스크립트를 보자.

import mx.events.CollectionEvent;import mx.collections.ArrayCollection;import mx.events.ListEvent;[Bindable]private var acData:ArrayCollection;private function init():void{	acData = new ArrayCollection(csData.node);	acData.addEventListener(CollectionEvent.COLLECTION_CHANGE, hCollectionChange);}private function hCollectionChange(e:CollectionEvent):void{	if(e.kind == "update")	{		if(e.items[0].property == "name")		{			trace(e.items[0].newValue);		}	}}


dataProvider로 사용하는 ArrayCollection 에 CollectionEvent를 받을수 있는 이벤트핸들러를 등록한다.
CollectionEvent는 ArrayCollection이나 XMLListCollection 같은 ListCollectionView를 구현하고 있는 넘들의 데이터의 변화 상황을 알려준다.

Inheritance : CollectionEvent Inheritance Event Inheritance Object

The mx.events.CollectionEvent class represents an event that is dispatched when the associated collection changes.

CollectionEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, kind:String = null, location:int = -1, oldLocation:int = -1, items:Array = null)

아이템이 수정이 완료 되고 데이터 프로바이더에 반영되고나면 CollectionEvent가 발생하게 되고 이걸 캐치 하게 되면 수정사항이 반영된 ArrayCollection에 접근할 수 있다.

CollectionEvent.COLLECTION_CHANGE 이벤트의 핸들러인 hCollectionChange 함수에서 이벤트의 kind 를 확인 해서 업데이트가 된것인지 삭제가 된것인지등의 컬렉션의 변화 사항을 체크 한다.

e.kind 의 값은 "add", "update", "remove" 가 있고 update 일 경우 items 값은 mx.events.PropertyChangeEvent 가 된다.
정석으로 하려면 e.items 를 PropertyChangeEvent로 캐스팅 하고 거기에서 kind 값을 다시 받아서 "update"인지를 확인 하고 해야겠지만.. 어차피 이 데이터 그리드에서는 변경이 라벨 변경밖에 되지 않으니.. 그냥 0 으로 받고 변경된 property가 name 필드 인지만 확인 하는 정도로 하자.

trace(e.items[0].newValue);


에서 새로 입력된 값이 나오게 된다. 이 시점에서 디비에 수정된 사항을 업데이트 하는 정도로 구현하면 될것이다.

CollectionEvent는 이 경우 처럼 라벨을 수정하는 정도에서는 간단하게 처리가 되지만.
드래그앤 드랍이라던지. Tree에서 이용하기 위해서는 경우의 수가 많아진다.

따라서 그때는 CollectionEvent를 사용하지 않고 따로 업데이트 되는 시점을 체크 할 수 있는 방법이 필요하다.
드랍될때 데이터를 세팅한다거나 수정 할때 기본 itemEditor를 사용하지 않고 custom itemEditor를 사용해서 에디터 내부에서 변경된 값을 넘겨준다거나 하는 방식으로 하는것이 더 효율적일 수 있다.

하지만 CollectionEvent는 실제 작업을 할때 이용하지 않더라도 데이터 이동을 디버깅할때 여간 편하게 쓰이기 때문에 한번씩 사용해 보는것이 좋을 것이다.



ps.
이걸 만들면서도 뭔가 더 깔쌈한 방법이 있을꺼같은데 저정도 밖에 안되나 하는 생각이 들었는데. 좀더 나은 구현방법이 있으신분은 그냥 가지 마시고 꼭 댓글이나 트랙백을 달아주셔서 안목을 넓혀주셨으면 합니다.
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0

데이터 그리드는 RIA 에서 빈번히 사용되는 컨트롤중 하나 이다.
관련된 이벤트도 많고 중요도도 높은 편이다.

데이터 그리드에서 editable = true 로 세팅 해주면 해당 칼럼을 그리드 내부에서 편집할 수 있다.
이것은 itemEditor를 통해서 가능해진다.

이 포스트에서는 특정 칼럼만 편집할 수 있도록 하고 몇가지를 더 추가 해서 완성하는 것을 해본다.

일단 mxml 부분 (태그 앞부분이 소문자로 나오는건 syntaxHighliter 때문이니 신경쓰지 않아도 된다)


	
		
			
				rinn
				퍼플린
				B
			
			
				magis
				들캔디
				A
			
			
				zzatung
				짝퉁
				??
			
		
	
	
		
			
			
			
		
	


아이디와 이름 혈액형을 model 로 데이터를 세팅하고 데이터 그리드에 바인딩 한다.
데이터 그리드에 연결되어있는 이벤트는 아래쪽에서 구현 예정이다.

아이디와 혈액형은 수시로 바뀌는게 아니기 때문에 여기서는 이름 필드만 편집 가능할 수 있도록 세팅 해보자.

여기서 사용 되는 것이 데이터그리드의 editedItemPosition 이다.
editedItemPosition property  
editedItemPosition:Object

The column and row index of the item renderer for the data provider item being edited, if any.

This Object has two fields, columnIndex and rowIndex, the zero-based column and row indexes of the item. For example: {columnIndex:2, rowIndex:3}

Setting this property scrolls the item into view and dispatches the itemEditBegin event to open an item editor on the specified item renderer.

The default value is null.

This property can be used as the source for data binding. When this property is modified, it dispatches the itemFocusIn event.


columnIndex와 rowIndex를 필드로 받고 아이템 에디트가 일어날때 그 값으로 세팅하게 되어있다.

아이템 에디트가 발생하게 되면 itemEditBegin 이벤트가 시작되고 이때 특정 칼럼만을 선택할 수 있도록 만들어준다.

private function dgEditBegin(e:DataGridEvent):void
{
	dg.editedItemPosition = {columnIndex:1, rowIndex:e.rowIndex};
}

columnIndex 를 1 로 고정하고 rowIndex 는 이벤트가 발생한 rowIndex 를 세팅 해주었으니 다른 칼럼을 선택하더라도 이름쪽만 에디트 될 수 있도록 바뀌게 된다.

하지만 매번 클릭시에 발생하게 되므로 사용하기에는 그리 모양새가 좋지 않다. 클릭 했을때는 해당 아이템이 선택되도록 하고 더블 클릭 했을때 수정 할 수있도록 수정하는것이 최종 목표이다.

더블 클릭을 사용할때 유의 할점은 일반 클릭도 함께 발생한다는 점이다.
마우스 클릭이 발생하게 되면 이게 더블클릭인지를 기다리는게 아니고 일단 마우스 이벤트는 진행이 되고 인터벌 안에 한번더 클릭이 들어오면 더블클릭으로 판단 하기 때문에 두가지 이벤트를 구분할수 있는 flag 를 세팅하고 아이템 에디트가 시작될때 클릭인지 더블클릭인지 확인하는 로직을 만들면 된다.

private var _isDClick:Boolean = false;

private function dgItemClick(e:ListEvent):void
{
	_isDClick = false;
}

private function dgDoubleClick(e:MouseEvent):void
{
	_isDClick = true;
}

private function dgEditBegin(e:DataGridEvent):void
{
	if(_isDClick)
		dg.editedItemPosition = {columnIndex:1, rowIndex:e.rowIndex};
	else
		e.stopImmediatePropagation();
}


isDClick 이라는 flag 를 itemClick 시에 false 로 세팅하고 doubleClick 시에 true 로 세팅해준다.
itemEditBegin 에서 flag 값에 따라서 이벤트를 블록 시킬 것인지 에디트를 진행할 것인지 결정하게 된다.



참고 사항

에디트 자체는 더블클릭일때만 활성화가 되지만 에디트를 하기 위해 더블클릭 하게 되면 itemClick 함수가 한번 실행 되게 된다.
이것을 클릭일때는 itemClick의 내용만 실행하고 더블 클릭일때는 에디트만 실행되도록 하기 위해서는 그냥 더블 클릭을 판단하는 로직자체를 타이머를 이용해서 구현하고 일반클릭일때는 더블클릭인지 판단하는 타이머가 끝날때 특정 행동을 수행하도록 수정해야 한다.

이 경우 그냥 클릭만 할때 바로 실행되지 않고 더블클릭인지 확인 하기 위해서 딜레이가 생기기 때문에 즉각적인 반응이 나오지 않아서 프로그램자체가 문제가 있지 않느냐는 평가를 받을 수도 있으므로 딜레이타임의 세팅에 세심한 주의가 필요하다.

이부분의 구현은 트랙백을 부탁한다.. -ㅅ-;;; 결코 귀찮아서가 아니다..;;


저작자 표시 비영리 동일 조건 변경 허락
신고

Comment +3

  • 이 포스트도 도움이 많이 되었습니다. 구글 검색하다보니 자꾸 이쪽 블로그로 오게 되네요.ㅋ
    밥이라도 한끼 사드려야 할듯..하군요..ㅋㅋ

    • 감사합니다 ㅎㅎ
      유입경로는 다 구글이더군요.
      다른 검색사이트(대표적으로 네이버)들은 자체 디비 검색을 우선하기 때문에 검색자체가 안되요

  • 신입개발자 2013.10.12 02:30 신고

    도움받아서 저도 글을 올립니다.
    많은 시간이 지나서 보실지 모르겠습니다.
    클릭 / dbl클릭의 구현에서 주기능은 타이머를 사용하지 않아도 됩니다.
    단지 그것을 위해서 사용하던 파라메터를 일정시간안에 초기화 해주는
    타이머 하나면 됩니다. 실제 컴퓨터에서 사용하는 더블클릭을 구현
    한다고 생각하면 편할듯 하고 이벤트는 ItemEditBegin을 이용하시면 일반 더블클릭과 동일한 결과물이 나옵니다.