퇴근5분전

사용자 삽입 이미지
<< 객체 표현 >>

IUserData  라는 인터페이스에 프로퍼티를 이용하여 객체에 고정된 값에 대한 추출을 이용할때

AbstractObject 처럼 상속받아 구현후
프로퍼티 get; 에 가상함수를 배치하여 재정의 된 메서드를 콜할수 있게 만들어놓으면

이를 상속해서 GetData() 재정의 할경우 구현하는 객체기준으로 데이타를 추출할수 있게 됨.

말이 좀 이상한가...

현재 프로젝트상에서
폼 작업시 특정 컨트롤에서 데이타를 추출할때 이 컨트롤을 사용자 컨트롤로 만든다고 했을때
인터페이스 상속후 컨트롤에 값을 노출하는 프로퍼티를 만들어주고
 이에 대한 접근으로 데이타를 뽑아낸다고 했을 때, 만약 이와 같은 유형에 컨트롤이 많을 때,
이와 같은 방법으로 각 유형별 컨트롤에서 GetData()를 재정의 해서 다른 데이타를 추출해줄수 있음.

단, 프로퍼티의 데이타 타입은 규격을 정해줌. (uniList 라는 객체로 규격화 하여 사용하고 있음. )

 
사용자 삽입 이미지
<< 객체 관련 >>

컨트롤 : 팝업시키는 폼 또는 유저 컨트롤이 됨.
팝업 : 기초 코드를 가지는 팝업 폼
ICodeList :  코드리스트를 사용하는 측에서 팝업에서 선택된 코드를 받아서 데이타를 처리하는 메서드에 대한 인터페이스를 정의함.

대리자는 ICodeList.SelectedCode 를 대신함.

팝업을 생성해서 코드 목록을 구현하고 이 코드 목록이 선택이 되면
대리자를 통해 컨트롤로 선택된 코드를 전달하게 됨.

 몇가지 방법 중 간단하게 폼간 데이타 전달을 쉽게 구현할수 있고, 각 객체간에 응집력을 낮춰놓을수 있는
소스임.

 객체지향을 항상 골똘히 생각하고 그에 맞게 코딩하고자 여러가지 생각을 하면서
머리속에 혼란스러워질 때가 있어서 정리해보려 함.

그중 하나로 대리자의 역할과 쓰임새에 대한 고민이 커졌다.
오늘 퇴근하면서 "버스에 내가 탔다!" 로 시작했다.

[버스에 내가 타는것]과 [버스가 나를 태운 것]으로 생각해보았다.
( 물론 위 문구는 몇번이나 생각해보았던 것이고 알맞게 정리 하였음. )

나만에 방식으로 몇번을 그림을 그려보는데 헷갈려서 그냥 집에 와서 코드소스로 만들었다.

아래 소스는 몇번 더 고민한끝에 수정된 소스이다. 
( 처음 적절하지 못한 사용에 대한 처리방법은 아래 주석처리가 되어있음. )

적절하지 못했던 방법부터 얘기를 풀어볼까낭.

버스를 코딩한답시고 타고, 내리고를 구현했고,  사람을 코딩한다고  타고, 내리고를 똑같이 코딩했었고
뭔가 맞지 않고 서로간에 연계가 이상했다.

처음 버스에 내가 탄것과, 버스가 나를 태운것과 처음 혼란스러웠다고 하면 바보 같겠지!
거창하게 말하자면 버스, 사람에 대한 두 객체설계를 했는데 그 과정에서 코드까지 풀어갈수 있게
나만에 스케치 방법에 착오로 인해 소스가 분명해지지 않고 맴도는 상태가 되어 더욱더 혼란스러워졌다.

해결책으로 생각해낸것이 주체를 정하는 것이었다.

버스 - 대리자 - 사람    이렇게 세개의 객체가 연결관계를 맺을 때 대리자는 버스와 사람간에 어떤 역할을
대신 해주는것이므로 방향을 지정해준다. 버스에서 사람으로 가는 메세지를 대신할것인지.
사람에서 버스로 가는 메세지를 대신 할것인지.

여기서 [버스 -> 사람] 버스가 사람을 태운다
          [사람 -> 버스] 사람이 버스에 탄다.

그렇게 생각하고 구현한고 테스트한게 아래 소스이다.

a.BusGetOn(bus.GetON);
a.BusGetOff(bus.GetOff);
bus.GetOn(b);
bus.GetOff(b);        

동일하게 버스에 태우고 내리고를 하게 되는 구문이다. 해석에 의문이 생길뿐이고...
몇가지 생각을 해보았다.

a가 버스에 타는 것.
버스에 a가 태워지는것,

이건 class 구문 내부를 보게 되었을 때...  아리까리함이 ... 처음엔 더 혼란스러웠다.

public class BusObject
{
         ...
        public void GetOn( PeopleObject  people)
        {
            // 사람이 버스에 탄다.
            if (!peoples.Contains(people))
            peoples.Add(people);
        }
}

public class PeopleObject {
        ...
        public void BusGetOn(_OnOut bus)
        {
            bus(this);  // 뭔지 모르고 자기를 던져 넣는다.
        }
}

 여기서 사람은 자기가 뭔가에 태워지게 되는데 뭔지 모르고 자기사진을 맡기게 되는 것.
 버스는 확실히 사람을 태우고 있고.

메서드 명이 뭔가 잘못된것이 보인다. 버스에 탄다 인데.. 버스를 받지 않고
대리자를 받고 있으니 어긋난 느낌이랄까?

 인자값으로 버스, 기차, 오토바이등의 상위 객체를 뽑아 탈것이라는 객체로 받는다면...
그것도 또 하나의 방법이 되겠지만... 지금 생각할것은 대리자의 쓰임새와 활용면을 고려해보려고 하니
좀더 고민하게 되었다.

그러다 찾아낸점이 사람이 타고, 내리고 라는 두개의 메세지가 존재하게 되는데, 현재 사용하고 있는 대리자가 있고  현재 타고, 내리고의 메서드는 동일하게

  bus(this);

이것만 있네?

좀더 고민을 통해 Do라는 메세지 1개로 절단 내버렸다.

대리자를 이용하니 이런게 되더라

Do라는 메세지하나로 버스에 타고, 내리고를 가리킬수 있게 되었다.

와우~~ 멋지다!


아래 보면 a.Do 라 해서 a에 Do라는 메세지로 어떤 행동을 지시할때 그 대상으로 bus.GetOn 이라는 대상을 제공한다.

a.Do( bus.GetOff );  a에게 행동하라고 지시하면서 버스에서 내려라! 라고 ... 되는것 같지 않은지?

나만 그렇게 보이는 건가?

bus에 GetOn 메세지를 날리면서 B를 제공한다.  
   GetOn은 "~에 태우다" 라는 뜻이공. GetOff는 "~에서 내리다."

대리자를 가지고 할수 있는 일 하나를 풀어보았는데 길다.
나도 읽을지 의문이 될정도로... 길게 썼넹.

이만~!

<< 이하 소스 전문 >>

     private void button1_Click(object sender, EventArgs e)
        {
            BusObject bus = new BusObject();

            PeopleObject a = new PeopleObject("A");
            PeopleObject b = new PeopleObject("B");
            PeopleObject c = new PeopleObject("C");

      // a.BusGetOn(bus.GetON);  // 처음 코딩했을 때.

            a.Do(bus.GetOn);   // a는 행동한다. 버스에 탔다.
            a.Do(bus.GetOff);   // a는 행동한다. 버스에서 내렸다.

            bus.GetOn(b);        // 버스에 b를 태우다.
            bus.GetOff(b);        // 버스에서 b를 내리다.

        }

    public delegate void _OnOff( PeopleObject  people);

    public class BusObject
    {
        List<PeopleObject> peoples = new List<PeopleObject>();

        public void GetOn( PeopleObject  people)
        {
            // 사람이 버스에 탄다.
            if (!peoples.Contains(people))
            peoples.Add(people);
        }

        public void GetOff(PeopleObject people)
        {
            if( peoples.Contains( people ) )
            peoples.Remove(people);
        }
    }

    public class PeopleObject
   
{
        string name = string.Empty;

        public PeopleObject( string _name)
        {
            name = _name;
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

     //public void BusGetOn(_OnOff bus)
        public void Do(_OnOut bus)
        {
            bus(this);
        }

        //public void BusGetOff(_OnOff bus)
        //{
        //    // 음 이건 자기가 뭐에서 내리는지도 모르고 내린다.
        //    bus(this);
        //}

    }

숫자만 넣고 싶거나, 숫자를 제외하고 ...

단 이것은 입력을 제한해버리기때문에... 오류메세지를 어딘가 보여줘야 할텐뎅... 우짤까낭!!!





        /// <summary>
        /// 숫자입력 판단 모드
        /// </summary>
        public enum InNumberMode
        {
            /// <summary>
            /// 숫자만
            /// </summary>
            Number,

            /// <summary>
            /// 숫자 제외
            /// </summary>
            Number_Not
              
        }

        /// <summary>
        /// Textbox에 값 입력시 동적으로 체크
        /// </summary>
        /// <param name="CurrentControl"></param>
        /// <param name="eNumMode"></param>
        public static void TextBox_Validation(Control CurrentControl, InNumberMode eNumMode)
        {           
                CurrentControl.KeyPress += delegate(object sender, KeyPressEventArgs e)
                {
                    if( char.IsNumber( e.KeyChar)) // e.Handled = true; 가 되면 이벤트가 막혀서 취소됨.
                        e.Handled = ( eNumMode == InNumberMode.Number_Not );                       
                    else
                        e.Handled = (eNumMode == InNumberMode.Number);
                };
        }
       



부모 - 자식(부) -- 자식(자식) --- 자식

위처럼 (부모 - 자식) 단위로 반복되는 트리구조형태일때

그 깊이를 알수 없으나 한번에 적용하고자 할때 재귀 호추를 이용해서 작업을 해왔음.

주의 : 반듯이 재귀호출은 탈출구가 필요함. ..
        --> 어차피 스택오버플로 예외가 발생할것임으로... 안되면 말자!

응용 예로는...
  파일 목록 읽어드리는 탐색기? ,  XML에서 구조파악 후 깊은곳에서 뭔가 꺼내와야 하는 탐색할때나???

가끔 만들어보는데 만들때마다 헷갈릴때가 있어서 기록해둠. ...


효심 깊은 자식들을 만들어볼까나...

{

체크(   부   ,  자 )

}
---------------------------

 체크 ( 부 , 자 )
{
     if( 자.자식들있나? )
          foreach( 자식  in 자.자식들 )
                      체크 ( 자, 자식 )
     else
          자.일(for 부모 );
}




 private void ucCompany_Body_Load(object sender, EventArgs e)
        {
            foreach (TabPage tp in tabControl1.TabPages)
            {
                foreach (Control ctrl in tp.Controls)
                {                                
                    fnTextBox_Next(tp, ctrl);          // 최초 함수 호출을 함.        
                }
            }
        }


        /// <summary>
        /// 나중에 이건 -> BaseUserCtrl 같은걸로 만들어서 빼야됨.
        /// </summary>
        /// <param name="pr"></param>
        /// <param name="tx"></param>
        private void fnTextBox_Next(Control pr, Control tx)
        {
            if (tx.HasChildren)
            {
                foreach (Control _pr in tx.Controls)
                {
                    fnTextBox_Next(tx, _pr);  // 함수내에서 다시 자기자신을 부르게 되는 재귀호출
                }
            }
            else
            {
                if (tx is TextBox) Control_AddIn_Function.TextBox_NextFocus(tx);

                 //재귀호출에 탈출구가 됨.
            }
        }



위 소스는 재귀에 사용하던 코드중 하나임.



VS 2005에서 코드조각이란게 존재함.

 이 코드조각 경로는 Vs2005 에 메뉴중 도구 ->[ 코드조각 ]에서 확인 가능하며
이중 한 파일을 복사 해서 자주 사용하는 코드블록을 아래처럼
수정해서 사용할 수 있음.

 수정된 파일을 따로 관리하면서 코드조각 추가시 해당 폴더를 찍어주면
그이하 파일들이 모두 등록되서 사용이 가능하게 됨. ( 폴더 지정시 vs를 리셋해주면 됨 )

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 <CodeSnippet Format="1.0.0">
  <Header>
   <Title>addtpc</Title>
   <Shortcut>addtpc</Shortcut>       // 코딩중 addtpc 탭탭 치면 아래 지정된 소스가 출력됨.
   <Description>상판 중앙 컨트롤 추가</Description>
   <Author>윤지송</Author>
   <SnippetTypes>        
    <SnippetType>Expansion</SnippetType>
    <SnippetType>SurroundsWith</SnippetType>        
   </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
     <Literal>
      <ID>FactoryBtnName</ID>               // 코드조각 변수임.  사용시 $변수명$
      <ToolTip>공용팩토리컨트롤명</ToolTip>
      <Default>factoryBtn</Default>           // 기본값
     </Literal>
    </Declarations>
    <Code Language="csharp">
    <![CDATA[ TopCenterPanel_ControlAdd(Factory_Control.Factroy_Control.GetButtonInstance($FactoryBtnName$.SetCode(UniManForDotNet.Forms.Factory_Control.Button_Codes.PreView$Selected$ $end$))); ]]>
   </Code>
  </Snippet>
 </CodeSnippet>
</CodeSnippets>

ps :
$Selected$ $end$ 
추가적으로 위와 같은 게 있는데 따로 설명은 찾기 어렵고 작업해보면
소스가 출력되었을때 변수가 수정된 후  커서가 위치하게 되는 곳이었다.

전에 폼 사이즈 변경 못하도록 하기 위해

Min, Max 사이즈를 레이아웃 다 잡고 복사해서 박았던 기억이 나서

기록해둠.


AutoSizeMode = GrowOnly; ( 기본값 )
AutoSizeMode = GrowAndShrink;  ( 변경 하지 못함 )

위 처럼 값을 바꿔주면 폼 경계에 가도 마우스 커서가 바뀌지 않고
사이즈 조절이 안됨.

오... 삽질에 삽질...

메인 폼을 MDI 컨테이너 설정을 한후

MDI자식창을 띠웠을때 MDI자식창이 메인폼 영역에서 움직여 경계면으로 가져갔을때

스크롤이 발생한다.

VScroll , HScroll , AutoScroll 다 필요없다.

무조건 뜬다!...

안뜨게 하는 방법이 있다!

AutoScrollMiniSize 속성에 값을 무식하게 5000, 5000 넣어준다..

사용자 삽입 이미지


음하하~~ 홀가분해진당!

이건 웹페이지에서임.
누님 공부를 도와주면서 설명해준 소스임.
방식은 앞서 트리뷰 노드 추가와 같으나
그 처리 방식이 조금 다름.

데이타를 가져와서
이걸 TreeNode를 상속받은 cTreeNode로 바꾼다
그리고 cTreeNode[] 를 순차적으로 돌면서
자기를 참조하고 있는 자식이 될 노드들을 찾아서 추가하게 됨.

이때 자신이 참조하는 값이 0이면 트리뷰에 직접 붙인다.( Root 를 가리키는 셈임 )

  protected void Page_Load(object sender, EventArgs e)
    {
        TreeView1.SelectedNodeChanged += new EventHandler(TreeView1_SelectedNodeChanged);

        if (!IsPostBack)
        {  
            SetPage(); 
        }
    }

    public class cTreeNode : TreeNode
    {
        bool isDelete = false;

        public bool IsDelete
        {
            get { return isDelete; }
            set { isDelete = value; }
        }

        int seqNo = 0;

        public int SeqNo
        {
            get { return seqNo; }
        }

        int parentNo = 0;

        public int ParentNo
        {
            get { return parentNo; }
        }

        public cTreeNode(int seq, int pno, string _text, string _value)
        {
            this.seqNo = seq;
            this.parentNo = pno;
            this.Text = _text;
            this.Value = _value;
        }
    }

    private void SetPage() 
    {
        try
        {
            TreeView1.Nodes.Clear();
            DataSet ds = new BusinessLayer().SelectCategory();
            if (ds != null && ds.Tables.Count > 0)
            {
                cTreeNode[] ctreenodes = GetTreeNode(ds.Tables[0]); //목록이 담긴 테이블을 넘겨준다.                   
                Binding(ctreenodes); //트리뷰에 바뀐 TreeNode를 집어 넣는 부분
                ds.Clear();
                ds.Dispose();            
            }
            ds = null; 
        }
        catch(Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }


    /// <summary>
    /// 1. DataRow -> TreeNode 변환 
    /// </summary>
    /// <param name="dt"></param>
    /// <returns></returns>
    #region
    private cTreeNode[] GetTreeNode(DataTable dt)
    {
        cTreeNode[] trNodes = new cTreeNode[dt.Rows.Count];
        int cnt = 0;
        int seq  = 0;
        int pno = 0;
        string title = string.Empty;

        foreach (DataRow dr in dt.Rows)
        {
            seq  = Convert.ToInt32( dr["SEQNO"] );
            pno = Convert.ToInt32(dr["PARENTNO"] == DBNull.Value ? "0" : dr["PARENTNO"]);
            title  =  dr["TITLE"].ToString();
            trNodes[ cnt++ ] = new cTreeNode( seq, pno, title, title+"_value" ); //cTreeNode가 1개씩 만들어져서 채워짐
        }
        return trNodes;
    }
    #endregion

    /// <summary>
    /// 트리노드 하나로 묶어주기
    /// </summary>
    /// <param name="trNodes"></param>
    /// <returns></returns>
    #region Binding(cTreeNode[] trNodes)
    private void Binding(cTreeNode[] trNodes)
    {
        TreeNode tNode = null;

        foreach (cTreeNode trNode in trNodes)
        {        
            if (trNode.ParentNo == 0)      
            {
               TreeView1.Nodes.Add( trNode );
               tNode = trNode;
            }

            TreeDepth(trNodes, trNode);          
        }
    }
    #endregion

    /// <summary>
    /// 자기를 참조 하고 있는 노드를 아래로 붙이는 메서드.
    /// </summary>
    /// <param name="trNodes">목록리스트</param>
    /// <param name="trNode">선택된값</param>
    #region TreeDepth(cTreeNode[] trNodes, cTreeNode trNode)
    private void TreeDepth(cTreeNode[] trNodes, cTreeNode trNode)
    {
        foreach (cTreeNode tNode in trNodes)
        {
            if (trNode.SeqNo == tNode.ParentNo) //자기를 참조하고 있는 노드인지
            {
                trNode.ChildNodes.Add(tNode);   //자신을 참조하고 있으면 자기 아래로 붙이기.
            }
        }
    }
    #endregion


    private void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
    {
        ClientScript.RegisterStartupScript(this.GetType(), "selectTreeNode", "alert('" + TreeView1.SelectedNode.Text + "를 선택했습니다.');", true);
    }

'# 2) .Net ( Vs 2005 ) > 기타' 카테고리의 다른 글

Form 사이즈 변경 못하게 막기  (0) 2009.05.14
MDI 창에서 스크롤바~  (0) 2009.05.14
트리뷰] 트리뷰 노드추가하기 1  (0) 2009.05.09
Html Table 파싱  (0) 2009.05.09
게임 챗팅창처럼...  (0) 2009.05.09

사용자 삽입 이미지

 옛날 옛날에 만들었던...
트리는 구조만 제대로 파악한다면, 의외로 쉽다.
디자인 패턴에서 Composite도 트리형태로 구현이 가능하다.
예전에 만들었던 내용대로 올림.

간단하게 우측에 보이는 데이타 구조에서
메뉴네임을 순차적으로 돌면서 참조하고있는 노드를 자기 아래노드로 추가시키는 방법임.

재귀 호출관련해서 알고 있으면 이해가 편함.


// 아래는 소스임

 DataTable TreeMenu  = new DataTable();


  private void Form1_Load(object sender, System.EventArgs e)
  {
   TreeMenu.Columns.Add("MenuName");
   TreeMenu.Columns.Add("Parents");
   
   DataRow dr ;

   dr = TreeMenu.NewRow();
   dr[0] = "A";   dr[1] = "root";

   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "B";   dr[1] = "A";
   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "C";   dr[1] = "A";
   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "D";   dr[1] = "B";
   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "E";   dr[1] = "D";
   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "F";   dr[1] = "B";
   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "G";   dr[1] = "C";
   TreeMenu.Rows.Add( dr);

   dr = TreeMenu.NewRow();
   dr[0] = "H";   dr[1] = "root";
   TreeMenu.Rows.Add( dr);

   this.dataGrid1.DataSource = TreeMenu;


   // root 꼭데기 찾아서 추가!!
   DataRow[]   selectRows = TreeMenu.Select("Parents='root'");

   for( int i = 0 ; i < selectRows.Length ; i++)
    this.treeView1.Nodes.Add( new TreeNode( selectRows[i][0].ToString(), GetTreeNodes( TreeMenu , selectRows[i][0].ToString()) ) );
 

   DataSet ds = new DataSet();
   ds.Tables.Add( TreeMenu );

   ds.WriteXml(@"C:\treeMenu.xml", XmlWriteMode.IgnoreSchema );
  }


  // 재귀함수에 의해 treenode추가!
  private TreeNode[] GetTreeNodes( DataTable dtSource,  string Parens )
  {
   TreeNode[] returnTreeNodes;
   
   DataRow[]   selectRows = dtSource.Select("Parents='"+Parens+"'");
   returnTreeNodes = new TreeNode[ selectRows.Length ];
   for( int idx  = 0; idx < selectRows.Length ; idx++)
   {
    returnTreeNodes[idx] = new TreeNode( selectRows[idx][0].ToString() , GetTreeNodes( dtSource , selectRows[idx][0].ToString() ) );
    returnTreeNodes[idx].Tag = selectRows[idx][0].ToString();
   }
   return returnTreeNodes;
  }

  private void treeView1_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
  {
   if(e.Node.GetNodeCount( true ) == 0)
    this.Text = e.Node.Text;
  }






'# 2) .Net ( Vs 2005 ) > 기타' 카테고리의 다른 글

MDI 창에서 스크롤바~  (0) 2009.05.14
트리뷰] 트리뷰 노드추가하기 2  (0) 2009.05.09
Html Table 파싱  (0) 2009.05.09
게임 챗팅창처럼...  (0) 2009.05.09
드레그 앤 드랍! (2)  (0) 2009.05.09