GSI

Posted by gsi
:

단위 전략과 단위 전략 클래스..

예로 들 것은 객체를 생성할때 template를 통해서 new와 malloc를 선택적으로 해주기 위함입니다.
다른 방법들도 몇가지 덧 붙여서 설명하겠습니다.

단위 전략의 설명은 책에 잘 나와 있으니 패스 ^^

우선 단위 전략에 필요한 인터페이스 즉, new, malloc로 구현된 두개의 struct 문입니다.

template < class T >
struct OpNewCreator
{
   static T* Create()
   { return new T }
};

template < class T >
struct MallocCreator
{
   static T* Crate()
   {
      void* buf = std::malloc(sizeof(T));
      if(!buf) return 0;
      return new(buf) T;
   }
};

두개의 단위 전략을 생성 했다면 아래의 단위 전략을 사용할 클래스를 하나 제작 합니다.

template < class CreationPolicy >
class WidgetManager : public CreationPolicy
{
public:
   WidgetManager()
   {
      CTest* p = CreationPolicy().Create();
      ...
   }
   ...
};

어때요?.. 멋지지 않나요?..  CreationPolicy 에 들어 오는 타입에 따른 Create() 를 호출하게 되는 구조입니다.
사용할때는 아래와 같이 적용이 가능해 집니다.

typedef WidgetManager< OpNewCreator<CTest> > MyWidgetMgr;
MyWidgetMgr a;

즉, OpNewCreatorMallocCreator 로 바꾸면 다른 기능을 할 수 있다는 거죠.
이게 바로 단위 전략이라는 거 같네요..

이것을 조금더 자연스럽게 해주는 기능이 또 있습니다.
바로 템플릿 템플릿 인자를 통해서요.
이 설명은 좀더 정리 되면 적도록 하구요..

다른 내용을 주제로 더 적도록 하겠습니다.

위의 단위 전략의 Create() 함수에 다른 인자를 더 넣는건 어떻게 할까.. 고민을 해봤어요.
즉 template 의 인자를 하나더 주자는 예기죠.

아래와 같이 바꾸어 보겠습니다.

template < class T, class U >
struct OpNewCreator
{
   static T* Create( U* u )
   {
      ...
   }
};

이렇게 말이죠. U 의 타입을 받아서 T를 생성한다.
이때는 아래와 같이 타입을 하나더 넣어 주어야 합니다.

typedef WidgetManager< OpNewCreator<CTest, CTest2> > MyWidgetMgr;

이렇게 해서 두개의 타입을 연결하였습니다.
근데 이때, U 에 들어 가는 타입을 디폴트 타입으로 지정하면 어떨까 생각 했죠.

template < class T, class U = CTest2 >
...

이렇게 하면 뒤어 인자를 적어줄 필요가 없게 됩니다.

typedef WidgetManager< OpNewCreator<CTest> > MyWidgetMgr;

바로 이렇게 말이죠.

그렇다면 Create( U* u ) 의 U 인자를 특정 타입으로 특화 하면 어떨까 생각 했죠.

template < class T, class U = CTest2 >
struct OpNewCreator
{
   static T* Create( CTest2* u )
   {
      ...
   }
};

이렇게 하게 되면 WidgetManager 내부에서 제한적인 코딩이 가능해 집니다.
즉, CTest2 의 인자만을 Create()에 집어 넣지 않으면 컴파일 에러가 발생하게 되죠.
다른 인자를 집어 넣는 실수를 막을 수 있을거 같네요.

CTest* p2 = new CTest();
CTest* p = CreationPolicy().Create( p2 );

이렇게 하면 p2가 CTest2가 아니기 때문에 에러가 나게 됩니다. ^^

Tip.
template 를 사용하게 되면 프로그램의 실수를 컴파일 타임에 잡아 주기 위한 기능으로도
적용이 되고 있습니다. 여러사람이 작업을 하거나 제한적인 코딩을 통한 에러를 막기 위한 방법으로
괜찮은 기능인거 같아요.

이상..

잘못된 내용이나 궁금증은 언제든 환영합니다. ^^
Posted by gsi
:

template 를 보고 있는데요.
개인적으로 저에게 가장 많은 생각을 하게 만든건 "Modern C++ Design" 입니다.

현재 1단원만 몇번째 보고 있지만 완전하게 이해 하기가 힘들군요.
하지만 C++ 에서 template 가 왠지 저를 잠못자게 하네요..

특화.. 라는 말을 많이 듣게 되는데요.
가령 예를 들어서 SmartPtr<T>템플릿을 사용하면서, SmartPtr<CTest> 에 대한 모든 멤버 함수를 특화 시킬 수 있습니다.. 이런 말이 나오네요.

제가 이해 하기로는 CTest 함수를 템플릿에 넣어서 특화 시킬 수 있다. 특별하게 처리할 수 있다
전 이렇게 이해가 됩니다...

그렇다면 이번 주제는 함수를 오버로딩하는 건데요.

C++ 에서는 아래와 같이 함수를 오버로딩할 수 있습니다.

void Test(int a) {}
void Test(string a) {}

이런 식이죠.

우선 이것을 지나기 전에 전 기존에 template 가 cpp 쪽의 함수 구현이 어떻게 되어야 하는지 몰랐네요 ^^

헤더 파일에 아래와 같은 클래스와 함수가 있다고 가정을 합시다.

template < class T>
CTest
{
  ...
  void Fun();
};

이렇게 있다고 했을때 cpp 에서는 똑같이 함 적어 볼께요.

template < class T >
void CTest::Fun() {}

아 이렇게 하면 두개의 에러가 나옵니다.
> 함수 정의를 기존 선언과 일치시킬 수 없다와 템플릿 클래스를 사용하려면 템플릿 인수 목록이 있어야 합니다.
이런 에러가 나오게 됩니다.

그래서 조금 다르게 해서 아래와 같이 작성하면 에러가 없어 집니다.

template < class T >
void CTest< T >::Fun() {}


이건 알고 계신다구요?.. -.- 전 이제까지 몰랐답니다. ㅋㅋ..

여기 까지는 대부분 아는 내용일테구요..

이제 여기 까지 하게 되면 멤버 함수 특화 라는 말이 나오게 되요. @.@

이 말도 정말 이해가 안되더군요 ^^..
하지만 특화라는 말은 template 의 T의 성분을 특정 타입으로 지정한다..
이렇게 전 아직 이해 한답니다. <--- 이거 잘못된건지 아시면 꼭 지적 부탁 드려요..

그래서 아래와 같이 함수 특화를 해볼께요..

template <>
void CTest< char >::Fun() {}


이렇게 해서 char 형태로 특화를 했습니다.

이렇게 하고 나면 어떤 현상이 발생 하냐 하면요..

이 클래스를 사용하는 부분에서.

typedef CTest<int> A;
A a;


이렇게 작성했다고 합시다.
우리가 이전에 Fun() 함수는 char 형태로만 특화 되어 있는걸 아시죠?
즉, int 형을 특화 시켰기 때문에 에러가 발생 합니다.

이것만 가지고도 사용자의 실수를 방지 할 수 있는 좋은 수단이 되지요. ^^

그렇다면 내가 원하는 특정한 타입을 T로 받아 들이고 그 타입에 따른 Fun() 함수를 따로 구현하고 싶을때

필요한 타입을 다 제작을 합니다.

template < class T >
void CTest<T>::Fun() {}

template <>
void CTest<char>::Fun() {}

template <>
void CTest<int>::Fun() {}


이렇게 3가지를 구현했습니다.

T의 값이 int, char 일때는 아래 두개의 함수로 분기 되게 됩니다.
그리고 나머지 모든 값은 제일 위의 함수로 분기 되죠.

어때요?.. 전 이거 보고 너무 좋았답니다. ^^

Tip.

위의 3개의 함수를 보시면 위쪽은 class T 가 되어 있는걸 아시겠죠?.
아래는 안되어 있구요..
개인적으로 다 해보세요.. 위와 같이 안하면 에러가 나요 ^^

왜냐구요?.. 아직 잘 모르겠어요 ㅋㅋ.. 아시면 꼭 얘기 부탁 해요.. ^^
Posted by gsi
:

책을 2, 3번 정도 읽으니 조금 이해가 되네요.
아직 100%는 아니지만 ^^

처음에는 템플릿으로 참 알아 보지도 못하는 것을 구현하기에.. 난해 했지만.

지금 조금 이해가 되는 부분에서 느껴 지는 것은
템플릿을 사용해서 컴파일 타임에서 오류를 잡아 주거나,
메모리 생성하는 규칙을 템플릿으로 typedef 잡아서 사용하거나 하는 부분들이
잘만 활용하면 상속 만큼이나 아니면 더 많은 부분을 효과적으로 구현하게
해주는 것인지도 모르겠다.

아래의 예를 잠깐 보면..

// TemplateTest.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"

template <class T>
struct OpNewCreator
{
 static T* Create()
 {
  printf("OpNewCreator - new 사용\n");
  return new T;
 }
};

template <class T>
struct MallocCreator
{
 static T* Create()
 {
  printf("MallocCreator - malloc 사용\n");
  void* buf = std::malloc(sizeof(T));
  if(!buf) return 0;
  return new(buf) T;
 }
};

class Widget
{
public:
 Widget() { printf("Widget 생성자 호출\n"); }
 ~Widget();
};

class CTest : public Widget
{
public:
 CTest() { printf("Widget 생성자 호출\n"); }
 ~CTest() {}
};

template < template <class Created> class CreationPolicy >
class WidgetManager : public CreationPolicy< Widget >
{
public:
 WidgetManager()
 {
  Dosomething();
 }

 ~WidgetManager() {}

 void Dosomething()
 {
  CTest* pw = CreationPolicy<CTest>().Create();
 }
};

typedef WidgetManager< OpNewCreator > MyWidgetMgr_New;
typedef WidgetManager< MallocCreator > MyWidgetMgr_Malloc;


int _tmain(int argc, _TCHAR* argv[])
{
 MyWidgetMgr_New a;
 MyWidgetMgr_Malloc b;

 return 0;
}

어쩌면.. 위의 typedef 부분인거 같다.
내가 new를 사용해서 객체를 할당 한다거나 하다가 상황에 따라서 malloc를 사용해야 한다고 하면
코드를 고치기 보다 typedef 부분을 수정해 주므로 모든게 해결이 된다.

아래는 실행된 모습이다.

사용자 삽입 이미지


하지만 여기서 궁금한건...
Widget를 Class 부분에서 미리 정의 해 주는 부분인데.. 잘 이해가 되지 않는다.
Posted by gsi
:

스크롤 뷰와 비슷하게 미니맵을 통한 이미지 뷰어나 기나 에디터 프로그램의 프레임으로 적용할려고 합니다.
드로잉은 동적 컨트롤에 의존 하지 않고 메인 뷰에서 다 처리 하도록 하여 Flicker 현상을 없앨려고 합니다.
미니맵의 베이스 크기만을 적용해 봤기 때문에 아주 심플한 코드 입니다.

사용자 삽입 이미지


관련 코드 :
기본 모델

화면에 보여지는 것만 더 드로잉 해본것..
Canvas 가 이동될때 파란색 사각형(보여지는 부분 표시)의 움직임 처리를 해봐야 할 소스
Posted by gsi
:

MFC 에서 ListBox 에 내용을 추가 하는 코드로 테스트 해본겁니다.

void UTRACE(char* fmt...)
{
 //CString output;
 CTime time = CTime::GetCurrentTime();

 std::ostringstream outstream;
 outstream << "[" << time.GetHour() << ":" << time.GetMinute() << ":" << time.GetSecond() << "] ";

 va_list ap;            //argument pointer

 char*p; int ival; double dval;
 char pBuf[256];  // 버퍼용

 va_start(ap, fmt);        //make ap point to the final named arg
 for(p=fmt; *p; p++) {
  if(*p !='%'){
   outstream << *p;
   //putchar(*p);
   continue;
  }

  switch(*++p){
    case 'd':
     ival = va_arg(ap, int);        //get the argument and move on to the next
     outstream << ival;
     /*itoa(ival, pBuf, 10);
     for(int i=0 ; i < (int)strlen(pBuf) ; i++)
     {
      putchar(pBuf[i]);
     }*/
     break;

    case 'f':
     dval = va_arg(ap, double);        //get the argument and move on to the next
     outstream << dval;
     /*dval = atof( pBuf );
     for(int i=0 ; i < (float)strlen(pBuf) ; i++)
     {
      putchar(pBuf[i]);
     }*/
     break;

    case 's':
     sarg = va_arg(ap, const char *);        //get the argument and move on to the next
     outstream << sarg;
     break;
    case 't':
     outstream << "\t";
     break;

    default:
     outstream << *p;
     break;

  }
 }
 va_end(ap);

 g_pDebugListBox->AddString( outstream.str().c_str() );
 g_pDebugListBox->SetCurSel( g_pDebugListBox->GetCount()-1 );

// 여기에서 콘솔 모드면 아래 코드를 적용하면 될듯 하다.
 memset( pBuf, 0, sizeof(char) * 256 );
 strcpy( pBuf, outstream.str().c_str() );
 //p = pBuf;
 printf( pBuf );
}

Posted by gsi
:

사용자 삽입 이미지

기본 UI만 구현해 봤어요.

UserControl의 기능을 처리 하기 위해서 의존 프로퍼티도 있어야 할거 같은데요. Click 버튼을 누르면 UserControl의 시작 스토리 보드 객체를 Begin() 하면 될듯, 더 내용을 추가 하고 몇가지 테스트를 해보면서 조금씩 기본기를 익혀야 할거 같다.

관련 코드
Posted by gsi
:

사용자 삽입 이미지

Win Form 에서 WPF 를 추가 해서 사용할때는 ElementHost를 사용합니다.
자세한 내용은 Win Form 에 WPF 컨트롤 붙이기 를 참고 하시구요.

그렇다면 이제 반대로 WPF 에서 Win Form을 추가 해서 쓰고 싶을때는 어떻게 할까 고민해보니.
WindowsFormsHost 라는 객체를 이용하면 처리가 되네요.

<Grid>
    <my:WindowsFormsHost ... />
</Grid>

이렇게 xaml 코드를 추가한 후에 cs 파일에서 추가를 아래와 같이 합니다.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    WindowsFormsControlLibrary1.UserControl1 uc =
        new WindowsFormsControlLibrary1.UserControl1();

    windowsFormsHost1.Child = uc;
}

중점 사항

  • UserControl 클래스를 추가 해서 사용해야 합니다. 즉, userControl 의 dll 파일을 참조 추가 해서
    사용해야 한다는 말이죠.
  • WPF 에서 Win Form의 UserControl의 객체를 접근하기 위해서는 노출이 되어 있지 않기 때문에 게터 메소드를 하나 만들어서 해야 합니다.
    예)  public string TextValue { get { return textBox1.Text; } }
  • 위의 게터 메소드가 노출되어 있다면 아래와 같이 WPF를 통해서 값을 참조할 수 있네요.
    예) WindowsFormsControlLibrary1.UserControl1 uc =
            (WindowsFormsControlLibrary1.UserControl1)windowsFormsHost1.Child;
         textBox1.Text = uc.TextValue;

다른 몇가지 사항에 대해서는 더 테스트를 해보지 않았습니다.
우선 자세한 코드는 아래 파일을 보세요.
더 자세한 사항이나 기타 의논하고 싶으신 부분 있으시면 언제든 연락 부탁 해요.

관련 파일:


Posted by gsi
:

Iterator 는 반복자로 알고 있는데요.

헤더 퍼스트 책을 보니
한개 이상의 다른 클래스들을 Iterator를 통해서 출력 부분을 동일화 해준다.
실제 데이터 클래스에 반복자 부분을 넣지 않고, Iterator를 직접 생성해서 관리 한다..
이정도로 생각하면 될까요?.

자바 클래스로 샘플 자료가 인터넷에 있던걸 C++로 처리를 해봤는데요.
코드는 아래에 위치 합니다.

여기서 몇가지 궁금증이 발생 합니다.
1. PersonIterator* personiter = (PersonIterator*)persons.CreatIterator(); 를 통해서
   객체를 Iterator로 보내기 위해서요. 내부에 new 를 사용했습니다.
   이때 C++에서는 삭제를 해줘야 하는 번거로움이 발생을 하게 됩니다.
   그래서 참조 형태로 함수에서 인자로 넘기는 것도 고려를 해봤지만. 코드가 깔끔하지 않더라구요.
   다르게 PersonIterator 쪽에 operator=()를 사용해서 Persons 객체를 PersonIterator로 대입하도록 처리도 해봤지만.
   UML의 그림이 조금 이상하게 나와서 ^^..
   자바나 C# 같은 경우는 가비지가 있어서 그런지 모르겠지만 포인터라는 개념 자체가 조금 다르게 되다 보니
   넘길때도 new 해서 객체를 생성해서 넘기게 되니 C++의 형태와 같다는 생각도 들구요.

2. UML을 표현하는데 있어서 아래와 같이 작성을 해봤습니다.
   헤더 파일만 리버스 해서 StarUML에 적용해 봤습니다. 그런데 리버스 할때 상속 구조는 화살표가 생기는데요.
   vector<>를 사용한 부분에서는 제대로 표현이 안되네요.

3. Iterator 패턴을 사용함에 있어서 반복해서 출력해 주거나, 특정 객체를 찾아 주거나, 기타 다른 작업들을
   데이터가 들어가 있는 클래스에 계속 구현하는게 아니라 Iterator를 상속 받아서 여러개를 만들어서 관리를 하는게
   더 좋다고 생각하면 될까요?.. 기존에는 작업을 하나의 데이터 포멧이 있다면 그곳에 드로잉, 탐색, 기타 위치 조정 등에
   대한 코든 코드를 다 넣다 보니 비대해 지고 관리가 힘들어져서 Iterator를 사용해서 분리를 시켜서 적용하면
   더 좋을거 같다는 생각은 드네요.

제가 아직 경험이 부족하니 아래 코드를 보고 잘못 된 점이 있다면 지적 많이 부탁 드려요 ^^..

[코드를 UML로 표현]

사용자 삽입 이미지

/** 코드 구현부 시작  **/

// 헤더 파일///////////////////////////////////////////////////////////////
#pragma once

class Person;
class Persons;

/************************************************************************/
/*                                                                      */
/************************************************************************/
class Iterator
{
public:
 Iterator() {}
 ~Iterator() {}

 virtual bool hasNext() = 0;
 virtual Person* Next() = 0;
};


/************************************************************************/
/*                                                                      */
/************************************************************************/
class PersonIterator : public Iterator
{
public:
 PersonIterator() {}
 PersonIterator( Persons* p );
 ~PersonIterator();

 PersonIterator& operator = ( Persons* p );

 bool  hasNext();
 Person*  Next();

protected:
 Persons*     m_pPersons;
// vector< Person >*   vperson;
// vector< Person >::iterator vpersoniter;
 int       number;
};


/************************************************************************/
/*                                                                      */
/************************************************************************/
class Person
{
public:
 Person( string name );
 ~Person();

 void  Print();
 string&  getName();

public:
 string m_name;
};


/************************************************************************/
/*                                                                      */
/************************************************************************/
class Aggregate
{
public:
 Aggregate() {}
 ~Aggregate() {}

 virtual void iterator( Iterator* iter ) {}
};


/************************************************************************/
/*                                                                      */
/************************************************************************/
class Persons : public Aggregate
{
public:
 Persons();
 ~Persons();

 void  AddPerson( Person& p );
 Iterator* CreatIterator();
public:
 int     m_Number;
 vector< Person > m_Array;
};

// 소스 파일///////////////////////////////////////////////////////////////

// IteratorPattern.cpp : 콘솔 응용 프로그램에 대한 진입점을 정의합니다.
//

#include "stdafx.h"
#include "IteratorPattern.h"

/************************************************************************/
/*                                                                      */
/************************************************************************/
Person::Person( string name )
{
 m_name = name;
}

Person::~Person()
{
}

string& Person::getName()
{
 return m_name;
}

void Person::Print()
{
 cout << "이름:" << m_name.c_str() << endl;
}


/************************************************************************/
/*                                                                      */
/************************************************************************/
PersonIterator::PersonIterator( Persons* p )
{
 m_pPersons  = p;
 number   = 0;
}

PersonIterator::~PersonIterator()
{
}

PersonIterator& PersonIterator::operator = ( Persons* p )
{
 m_pPersons  = p;
// vperson   = &rhs.m_Array;
// vpersoniter  = vperson->begin();
 number   = 0;
 return *this;
}

bool PersonIterator::hasNext()
{
 if( number < (int)m_pPersons->m_Array.size() )
  return true;
 else
  return false;
}

Person* PersonIterator::Next()
{
 vector< Person >::iterator iter = m_pPersons->m_Array.begin();
 iter += number++;
 return (Person*)&(*iter);
}


/************************************************************************/
/*                                                                      */
/************************************************************************/
Persons::Persons()
{
 m_Number = 0;
 m_Array.clear();
}

Persons::~Persons()
{
}

void Persons::AddPerson( Person& p )
{
 m_Array.push_back( p );
}

Iterator* Persons::CreatIterator()
{
 PersonIterator* pi = new PersonIterator( this );
 return pi;
}

/** 코드 구현부 끝 **/

/** 메인 시작 **/
int _tmain(int argc, _TCHAR* argv[])
{
    Persons persons;

    persons.AddPerson( Person( "손병욱" ) );
    persons.AddPerson( Person( "천성구" ) );
    persons.AddPerson( Person( "박창준" ) );

    PersonIterator* personiter = (PersonIterator*)persons.CreatIterator();

    while( personiter->hasNext() )
    {
        Person* pp = personiter->Next();
        if( pp )
        {
            pp->Print();
        }
    }
    delete personiter;

    return 0;
}
/** 메인 끝 **/

Posted by gsi
:

Google
 
UML을 사용하다 보면 아직까지 익숙하지 않아서 화살표의 의미 자체가 조금 힘들게 받아 들여 진다.
아래의 이미지는 Iterator 패턴을 사용해서 화살표가 의미 하는 것을 적어 보았다.

사용자 삽입 이미지
Posted by gsi
:

Google
 

UML을 사용하다 보면 아직까지 익숙하지 않아서 화살표의 의미 자체가 조금 힘들게 받아 들여 진다.
아래의 이미지는 Iterator 패턴을 사용해서 화살표가 의미 하는 것을 적어 보았다.

사용자 삽입 이미지

Posted by gsi
:

Google
 
CView를 사용해서 해당 뷰를 만들고 그 위에 CStatic를 생성해서 처리를 하고 있습니다.
CStatic에는 해당 컬러 정보를 사용해서 드로잉이 되며, 마우스로 이동, 크기변경 등의 이벤트가
발생하게 되는데요.

이때 드로잉 되는 순서가 CView가 먼저 드로잉 되고, CStatic가 드로잉 되게 되면서
강한 Flicker 현상(번쩍임)이 발생을 하네요..

이것저것 처리를 해봐도 언뜻 보면 드로잉 순서로 인해서 어쩔 수 없을거 같다는 생각이 드네요.

그래서 조금 우회를 시켜서 CStatic의 드로잉 부분을 CView에 직접 그리기로 했습니다.
CStatic는 보이지는 않지만 자기 기능을 대신 하고 있는거구요.

이렇게 하니 Flicker는 해결이 되었네요 ^^..

원래 이렇게 하는걸까요?.. 아님 다른 방법이 있는 걸까요?
Posted by gsi
:

세상의 모든 부분이 변화 해왔고 변하고 있다.
어쩌면 다른 부분의 변화 자체가 나에게 직접적인 원인을 제공하지 않았기 때문에 모를 뿐일 것이다.

황사가 지금이야 우리 나라에 피해를 주고 있지만 그것 또한 어느 순간 부터 크지고 있었다는 것을 알지 못했듯이
난 지금 프로그램이라는 부분에서 내가 피부로 느끼고 있는 시기인듯 하다.
OpenGL을 해오다가 DirectX 를 하다가 이제는 MFC를 메인으로 삼고 해왔던 몇년의 시기가
지금 MS에서 밀고 있는 WPF 까지 나아가고 있다.

MFC보다 조금더 발전되어진 형태 같은 WinForm을 하면서 UI 구축이 참 쉽다는게 느껴졌고,
WPF를 하면서 UI의 발전이 한층더 발전했다는 것을 느끼게 된다.
하지만 WPF를 하면서 줄곧 힘들게 느껴지는 부분은 스크립트화 되어 가는 프로그래밍 부분에서
그래픽 적인 화려함이나 다자이너의 성향을 알지 못해서 표현이 서툴고 보기가 좋지 않은 것이 때로는 나를 힘들게 하고
짜증나게 까지 하고 있다. 결국 이 부분까지 디자이너의 몪이 되어 가는걸지도 모른다.

예전에 내가 해왔던 부분도 누군가가 해왔던 부분을 더 쉽게 해놓았기 때문에 내가 지금 자리를 꿰차고 있는것같다는 느낌이 든다.
스크립트화 되고 툴로 쉽게 만들 수 있는 이 부분 조차도 이제 디자이너의 일 부분으로서 자리를 내줘야 할지도 모른다.

요즘 화두가 되어 지는 디자인 패턴이나 아키텍처, 프레임웍, 등 다양한 말들이 나에게 들려 온다.
그리고 그 부분으로 변화되어 갈 프로그램의 길 또한 나에게 있어서 선택의 길이기도 할 것이다.

아주 고차원 적인 코어 부분의 이해를 하면서 다양한 어플리케이션을 짤 것인가 아니면 디자이너의 성향을 가지고 응용 프로그램을 멋지게 표현할 것인가 하는 것과 웹이 점점더 클라이언트의 부분을 잠식해 가는 부분을 하게 될지..

많은 고민을 하게 된다.



하지만 생각한 대로 하다 보면 답은 있을 것이다.
또한 그것이 내가 가야할 방향이고..

현재로서는 WPF와 웹의 방향성을 확실하게 파악하면서 구축을 해보는 것과 프로그램의 내부를 잘 다지기 위해서 디자인 패턴과 포사 등을 보면서 내실을 다져야할 때인거 같다.

내가 밤마다 잠을 자지 못하고 멍하니 있는 이 시기가 헛되이 지나가지 않기를 바란다.

프로그램을 하면서 고생하는 모든 개발자에게 화이팅 ^^.. 모두 잘되길 바란다.
Posted by gsi
:

C# 레지스트리 정보 읽기

private void ChkClientVersion() { RegistryKey reg = Registry.LocalMachine; reg = reg.CreateSubKey(@"Software\HI-NG\Azitro\AzitroAppLauncher", RegistryKeyPermissionCheck.ReadSubTree); FinalClientVersion[(int)CATEGORY_MODE.CAT_WORLD] = Convert.ToDouble((string)reg.GetValue("WorldVersion")); FinalClientVersion[(int)CATEGORY_MODE.CAT_ROOM] = Convert.ToDouble((string)reg.GetValue("RoomVersion")); FinalClientVersion[(int)CATEGORY_MODE.CAT_CON] = Convert.ToDouble((string)reg.GetValue("ConVersion")); reg.Close(); Console.WriteLine("<<클라이언트 레지스트리 버젼>>"); Console.WriteLine(" WorldVersion={0}", FinalClientVersion[(int)CATEGORY_MODE.CAT_WORLD]); Console.WriteLine(" RoomVersion={0}", FinalClientVersion[(int)CATEGORY_MODE.CAT_ROOM]); Console.WriteLine(" ConVersion={0}", FinalClientVersion[(int)CATEGORY_MODE.CAT_CON]); }

C# 레지스트리 정보 쓰기

private void UpdateVersionRegister() { RegistryKey reg = Registry.LocalMachine; reg = reg.CreateSubKey(@"Software\HI-NG\Azitro\AzitroAppLauncher", RegistryKeyPermissionCheck.ReadWriteSubTree); reg.SetValue("WorldVersion", upaser.FinalSvrVersion[(int)CATEGORY_MODE.CAT_WORLD]); reg.SetValue("RoomVersion", upaser.FinalSvrVersion[(int)CATEGORY_MODE.CAT_ROOM]); reg.SetValue("ConVersion", upaser.FinalSvrVersion[(int)CATEGORY_MODE.CAT_CON]); reg.Close(); }
Posted by gsi
:

Google
 


마우스를 통한 개발툴을 만들다 보면 대부분 스크롤 기능을 포함하는 경우가 많습니다.

CScrollView 화면을 통한 마우스 기반의 툴을 제작 하게 되면 불가피 하게 OnLButtonDown(), OnLButtonUp(), OnMouseMove()를 제어 하게 되어 있습니다.
여기서 부터 작업이 시작 되지요.
스크롤 뷰의 화면을 좀 크게 잡는다 싶으면, 스크롤 바를 움직여 보면 아시겠지만, 제대로 되지 않습니다. 16bit 형태의 값이였나, 아뭍턴 값의 한계로 인해서 스크롤 바의 위치가 제대로 처리 되지 않습니다.
그 경우 아래의 코드를 추가 해야 합니다.

BOOL CScrollTest2View::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll)
{
// When you drag the scroll box, the nPos value send from WM_HSCROLL/WM_VSCROLL is 16bit value.
// Therefore retrieve the 32bit scroll box position value.
if(SB_THUMBTRACK == LOBYTE(nScrollCode)) // WM_HSCROLL
{
SCROLLINFO info;
if(GetScrollInfo(SB_HORZ, &info, SIF_TRACKPOS))
{
nPos = info.nTrackPos; // 32bit position value.
}
}
else if(SB_THUMBTRACK == HIBYTE(nScrollCode)) // WM_VSCROLL
{
SCROLLINFO info;
if(GetScrollInfo(SB_VERT, &info, SIF_TRACKPOS))
{
nPos = info.nTrackPos; // 32bit position value.
}
}

return CScrollView::OnScroll(nScrollCode, nPos, bDoScroll);
}

위의 코드를 추가 하고 나면 넓은 영역의 뷰도 스크롤이 제대로 표현되는 것을 확인하실 수 있을겁니다.

이제 확인해야 될 부분이 마우스 이벤트에 들어 오는 CPoint point 부분입니다.
스크롤 뷰에서 제일 맘에 안드는 부분이 이 부분인데요. point가 왜 스크롤 되고 나면 현재 위치를
구해 주지 않는지 모르겠습니다.
사용자 삽입 이미지
위의 화면에서와 같이 마우스 위치의 지점을 클릭해 보겠습니다. 클릭하고 나면 대략 값이 x:188, y:49 가 나오게 됩니다.
하지만 아래의 스크롤 바를 보면 우측으로 많이 가 있는 것을 볼 수 있습니다.
즉, 스크롤이 움직여도 마우스의 위치 값은 화면에 보이는 영역에 대한 상대 값임을 알 수 있습니다.

그렇다면 여기서 스크롤 되는 정보를 얻어 와서 처리 해주어야 한다는 사실을 알게 될겁니다.
스크롤 정보를 알기 위해서 GetScrollPosition() 를 사용합니다.

이제 스크롤 된 후의 현재 마우스 위치 정보를 아래와 같이 구합니다.
CPoint scrollpos = point + GetScrollPosition();

※예로 제가 작업했던 코드 일부 여기에 포스트...
void CScrollTest2View::OnMouseMove(UINT nFlags, CPoint point) { if( m_oldPoint == point ) return; CPoint scrollpos = GetScrollPosition(); TRACE2("현재 스크롤값:%d, %d\n", scrollpos.x, scrollpos.y); int x = scrollpos.x % GridSize.cx; int y = scrollpos.y % GridSize.cy; m_ptGrid = CPoint( ((int)((point.x+x) / GridSize.cx)) * GridSize.cx, ((int)((point.y+y) / GridSize.cy)) * GridSize.cy ); TRACE2("현재 그리드 위치:%d, %d\n", m_ptGrid.x, m_ptGrid.y); m_ptGrid.x -= x; m_ptGrid.y -= y; TRACE2("현재 그리드 위치:%d, %d\n", m_ptGrid.x, m_ptGrid.y); XORPen(); m_oldPoint = point; CScrollView::OnMouseMove(nFlags, point); } void CScrollTest2View::OnLButtonDown(UINT nFlags, CPoint point) { CPoint pos = CPoint(0, 0); GetCursorPos( &pos ); this->ScreenToClient( &pos ); CScrollView::OnLButtonDown(nFlags, point); } void CScrollTest2View::OnLButtonUp(UINT nFlags, CPoint point) { TRACE2("MousePos:%d, %d\n", point.x, point.y); CPoint scrollpos = point + GetScrollPosition(); TRACE2("ScrollPosition:%d, %d\n", scrollpos.x, scrollpos.y); m_ptGrid = CPoint( ((int)(scrollpos.x / GridSize.cx)) * GridSize.cx, ((int)(scrollpos.y / GridSize.cy)) * GridSize.cy ); m_vPos.push_back( m_ptGrid ); OutCursorDCxRop(); Invalidate(); CScrollView::OnLButtonUp(nFlags, point); }

이 값을 사용해서 여러가지 동작들을 추가 하시면 됩니다.
하지만 이것저것 하다 보면 GetScrollPosition()의 값을 보정해서 해줘야 하는 경우가 많이 발생을 하게 됩니다. 그때 마다 너무 많은 작업량이 좀 힘들더라구요.

팁을 드리면 CScrollView에 CView를 추가한 후에 CScrollView의 Create(), OnSize() 부분에 코드를 적당히 넣어서 CView를 내부에 크기 만큼 심어 놓습니다.
그렇게 한후에 마우스 픽킹을 해보시면 마우스 이벤트가 CScrollView로 가지 않고 CView로 가는 것을
확인할 수 있을겁니다.
더욱더 놀라운건 ^^.. 스크롤로 해서 화면을 움직여도 현재 찍힌 화면의 좌표는 스크롤로 이동한 좌표 즉, 화면이 스크롤 만큼 이동한 후에 CView의 이동된 좌표값을 바로 리턴해 준다는 겁니다.

이상.. 가볍게 적었지만 CView 추가 하는 부분은 생략하도록 하겠습니다. (귀찮네요 ^^)
모르거나 필요하시면 제가 바로 답변 드릴테니 뎃글 달아 주세요..
Posted by gsi
: