[Flex 3]DataGrid의 Header Separator를 커스터마이징 하기

2009/07/05 22:44

Flex 3에서 DataGrid의 Header부분의 Separator는 headerSeparatorSkin 스타일로 정의되어 있다. 화면은 Flex DataGrid에 Separator가 붙은 보통의 모습이다.

 

 

위 프로그램은 아래와 같이 프로그래밍 한다.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
	<mx:DataGrid id="dataGrid">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

만약 Separator를 지우고 싶다면 단순히 headerSeparatorSkin 에 mx.skins.ProgrammaticSkin으로 정의하면 된다. 아래는 실행화면과 소스코드이다. mx.skins.ProgrammaticSkin는 프로그램적으로 스킨을 만들 필요가 있을때 사용하는 클래스로 이 클래스 내부에서는 어떤 렌더링도 하지 않기 때문에 headerSeparatorSkin 을 이 클래스로 대체하는 것만으로도 Header의 separator를 삭제할 수 있는 것이다.

 

 

 

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
	<mx:DataGrid id="dataGrid" 
		headerSeparatorSkin="mx.skins.ProgrammaticSkin">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

위 코드 대신 아래 코드처럼 <Style/> 블록이나 외부 .CSS 파일을 이용해서 headerSeparatorSkin의 스타일을 바꿀 수 있다.

 

 

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
    <mx:Style>
        DataGrid {
            headerSeparatorSkin: ClassReference("mx.skins.ProgrammaticSkin");
        }
    </mx:Style>	
	<mx:DataGrid id="dataGrid">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

또는 ActionScript 를 사용해 버튼을 누를때 동적으로 headerSeparatorSkin 스타일을 변경할 수 있도록 다음 코드처럼 만들 수 있다.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
    <mx:Script>
        <![CDATA[
            import mx.skins.ProgrammaticSkin;

            private function btn_click(evt:MouseEvent):void {
                dataGrid.setStyle("headerSeparatorSkin", ProgrammaticSkin);
            }
        ]]>
    </mx:Script>

    <mx:Button id="btn"
            label="Set header separator skin"
            click="btn_click(event);" />
            
	<mx:DataGrid id="dataGrid">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

좀더 파헤쳐보자.

 

DataGrid에는 drawSeparators() 메소드가 protected로 지정되어 있다. 만약 DataGrid를 커스터마이징해서 Separator 그리는 방식을 바꾸고 싶다면 drawSeparators() 부터 찾아가면 된다. 아래 코드는 DataGrid의 drawSeparators() 메소드 정의이다

/**
 *  Creates and displays the column header separators that the user 
 *  normally uses to resize columns.  This implementation uses
 *  the same Sprite as the lines and column backgrounds and adds
 *  instances of the headerSeparatorSkin and attaches mouse
 *  listeners to them in order to know when the user wants
 *  to resize a column.
 */
protected function drawSeparators():void
{
    DataGridHeader(header)._drawSeparators();
    if (lockedColumnHeader)
        DataGridHeader(lockedColumnHeader)._drawSeparators();
}

 

위 코드에서 볼 수 있듯이 결국 DataGridHeader에 정의된 _drawSeparators()쪽을 살펴봐야한다. 아래는 DataGridHeader의 _drawSeparators()와 drawSeparators() 부분이다.

mx_internal function _drawSeparators():void
{
    drawSeparators();
}

/**
 *  Creates and displays the column header separators that the user 
 *  normally uses to resize columns.  This implementation uses
 *  the same Sprite as the lines and column backgrounds and adds
 *  instances of the headerSeparatorSkin and attaches mouse
 *  listeners to them in order to know when the user wants
 *  to resize a column.
 */
protected function drawSeparators():void
{
    if (!separators)
        separators = [];

    var lines:UIComponent = UIComponent(getChildByName("lines"));
    
    if (!lines)
    {
        lines = new UIComponent();
        lines.name = "lines";
        addChild(lines); 
    }
    else
        setChildIndex(lines, numChildren - 1);

    // required to deal with some 2.x clipping behavior
    lines.scrollRect = new Rectangle(0, 0, unscaledWidth, unscaledHeight + 1);

    if (headerSepSkinChanged)
    {
        headerSepSkinChanged = false;
        clearSeparators();
    }

    var n:int = visibleColumns ? visibleColumns.length : 0;
    
    if (!needRightSeparator && n > 0)
    	n--;
    
    for (var i:int = 0; i < n; i++)
    {
        var sep:UIComponent;
        var sepSkin:IFlexDisplayObject;
        
        if (i < lines.numChildren)
        {
            sep = UIComponent(lines.getChildAt(i));
            sepSkin = IFlexDisplayObject(sep.getChildAt(0));
        }
        else
        {
            var headerSeparatorClass:Class =
                getStyle("headerSeparatorSkin");
            sepSkin = new headerSeparatorClass();
            if (sepSkin is ISimpleStyleClient)
                ISimpleStyleClient(sepSkin).styleName = this;
            sep = new UIComponent();
            sep.addChild(DisplayObject(sepSkin));
            lines.addChild(sep);
            
            separators.push(sep);
        }
        // if not separator
        if ( !(i == visibleColumns.length-1 && !needRightSeparatorEvents) )
        {
            DisplayObject(sep).addEventListener(
                MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler);
            DisplayObject(sep).addEventListener(
                MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler);
            DisplayObject(sep).addEventListener(
                MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler);
        }
		else
		{
            // if not separator
            if ( (i == visibleColumns.length-1 && !needRightSeparatorEvents) )
            {
                DisplayObject(sep).removeEventListener(
                    MouseEvent.MOUSE_OVER, columnResizeMouseOverHandler);
                DisplayObject(sep).removeEventListener(
                    MouseEvent.MOUSE_OUT, columnResizeMouseOutHandler);
                DisplayObject(sep).removeEventListener(
                    MouseEvent.MOUSE_DOWN, columnResizeMouseDownHandler);
            }
		}

        var cols:Array = visibleColumns;
        if (!(cols && cols.length > 0 || dataGrid.headerVisible))
        {
            sep.visible = false;
            continue;
        }

        sep.visible = true;
        sep.x = headerItems[i].x +
                visibleColumns[i].width - Math.round(sepSkin.measuredWidth / 2);
        if (i > 0)
        {
            sep.x = Math.max(sep.x,
                             separators[i - 1].x + Math.round(sepSkin.measuredWidth / 2));
        }
        sep.y = 0;
        sepSkin.setActualSize(sepSkin.measuredWidth, Math.ceil(cachedHeaderHeight));
        
        // Draw invisible background for separator affordance
        sep.graphics.clear();
        sep.graphics.beginFill(0xFFFFFF, 0);
        sep.graphics.drawRect(-separatorAffordance, 0,
							  sepSkin.measuredWidth + separatorAffordance,
							  cachedHeaderHeight);
        sep.graphics.endFill();
		sep.mouseEnabled = true;
    }

    while (lines.numChildren > n)
    {
        lines.removeChildAt(lines.numChildren - 1);
        separators.pop();
    }
}

 

DataGridHeader의 drawSeparators()에서 headerSeparatorSkin 스타일을 사용하는 것을 찾아볼 수 있다. 개발자는 이 부분을 커스터마이징할 수 있는 것이다.

 

DataGrid나 DataGridHeader를 커스터마이징할 필요없이 스킨만 변경하고 싶다면 headerBackgroundSkin의 기본 스킨인 mx.skins.halo.DataGridHeaderSeparator를 커스터 마이징하거나 mx.skins.Programmatics를 확장해서 사용하면 되겠다. 아래코드는 DataGrid에서 headerBackgroundSkin이 Style로 정의된 것을 보여주고 있다.

/**
 *  The class to use as the skin that defines the appearance of the  
 *  background of the column headers in a DataGrid control.
 *  @default mx.skins.halo.DataGridHeaderSeparator
 */
[Style(name="headerBackgroundSkin", type="Class", inherit="no")]

 

 

아래 코드는 mx.skins.halo.DataGridHeaderSeparator를 보여준다.

////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2005-2007 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package mx.skins.halo
{

import flash.display.Graphics;
import mx.skins.ProgrammaticSkin;

/**
 *  The skin for the separator between column headers in a DataGrid.
 */
public class DataGridHeaderSeparator extends ProgrammaticSkin
{
	include "../../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

	/**
	 *  Constructor.
	 */
	public function DataGridHeaderSeparator()
	{
		super();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Overridden properties
	//
	//--------------------------------------------------------------------------

	//----------------------------------
	//  measuredWidth
	//----------------------------------
	
	/**
	 *  @private
	 */
	override public function get measuredWidth():Number
	{
		return 2;
	}
	
	//----------------------------------
	//  measuredHeight
	//----------------------------------

	/**
	 *  @private
	 */
	override public function get measuredHeight():Number
	{
		return 10;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Overridden methods
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	override protected function updateDisplayList(w:Number, h:Number):void
	{
		super.updateDisplayList(w, h);
		var g:Graphics = graphics;
		
		g.clear();
		
		// Highlight
		g.lineStyle(1, 0xFFFFFF, 0.5);
		g.moveTo(0, 0);
		g.lineTo(0, h);
		g.lineStyle(1, getStyle("borderColor")); 
		g.moveTo(1, 0);
		g.lineTo(1, h);
	}

}

}

 

이외로 단순해서 놀랬는가? 결국 이 스킨은 borderColor 스타일로 지정된 색으로 구분자 선만 그어준다. 나는 이것을 바꿔서 실선을 점선을 그리도록 해보겠다. 아래 코드는 이를 구현한 DataGridHeaderDotSeparator 클래스이다.

 

package
{
import flash.display.Graphics;

import mx.skins.halo.DataGridHeaderSeparator;

/**
 * DataGrid Header에 점선을 그린다.
 */ 
public class DataGridHeaderDotSeparator extends DataGridHeaderSeparator
{
	/**
	 *  Constructor.
	 */		
	public function DataGridHeaderDotSeparator()
	{
		super();
	}
	
	/**
	 *  @private
	 */		
	override protected function updateDisplayList(w:Number, h:Number):void
	{
		var g:Graphics = graphics;
		
		g.clear();
		
		
		g.lineStyle(1, getStyle("borderColor"), 1); 
		var i:int;
		for( i = 0; i < h; i+=4 )
		{
			g.drawCircle( 1.5, i, 0.5 );
		}
	}		
}
}

 

 

아래 코드는 DataGridHeaderDotSeparator 스킨을 사용하는 예제이다.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2009/03/20/removing-the-header-separator-on-the-datagrid-control-in-flex/ -->
<mx:Application
	name="DataGrid_headerSeparatorSkin_test"
	xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="white" 
	layout="vertical">
	<mx:DataGrid id="dataGrid"
		headerSeparatorSkin="DataGridHeaderDotSeparator">
		<mx:dataProvider>
			<mx:ArrayCollection>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
				<mx:Object c1="1. One" c2="1. Two" c3="1. Three"/>
			</mx:ArrayCollection>
		</mx:dataProvider>
	</mx:DataGrid>
</mx:Application>

 

아래는 위 코드를 실행한 화면이다. Header부분에 구분자(Separator)가 점선으로 되었다. 좀 뭉뚝하지만 그냥 예제일 뿐이니깐 넘어가자. ^^;

 

 

Flex는 이처럼 프로그램적으로 스킨을 변경할 수 있다. 물론 이미지를 이용하는 방법도 있다. 이와 같은 방법으로 스킨을 변경하는 것은 Flex의 모든 컴포넌트에서 지원하므로 이런 스킬에 익숙해질 필요가 있겠다.

 

원본 예제 : Removing the header separator on the DataGrid control in Flex

 

주절주절 : 그냥 이렇게 가볍게 포스팅하는 것이 좋을 것 같다는 생각이 든다.

 

글쓴이 : 지돌스타(http://blog.jidolstar.com/546)

 

Adobe Flex / ActionScript 3.0 , , , , , , , , , ,

  1. Blog Icon
    박스농사

    좋은글 감사합니다.~

  2. ^^ 방가요

  3. Blog Icon
    차차

    ㅋㅋ마침 저도 이쪽 작업을 하고 있었는데..
    도움 받고 갑니다^^

  4. 도움이 되었다니 다행입니다. ^^

  5. Blog Icon
    나이유미

    좋은정보 감사합니다~~~