GSI

CXImage 연동 라이브러리.

C++ 2007. 12. 3. 18:09 |

CxImage 라이브러리 폴더를 해당 프로젝트에 추가 한다.

1. 인클루드를 추가한다.

#include "./CXImage599_Lib/ximage.h"
#pragma comment(lib, "./CXImage599_Lib/cximaged.lib")

2. 해당 경로명의 확장자(이미지 타입)을 얻어 오는 함수를 추가한다.

// CDlgSequencePage 메시지 처리기입니다.
int GetTypeFromFileName(CString& str)
{
 CString fileName = str;
 CString ext3=fileName.Right(3);
 CString ext4=fileName.Right(4);
#if CXIMAGE_SUPPORT_PNG
 if(ext3.CompareNoCase(_T("png"))==0)
  return CXIMAGE_FORMAT_PNG;
#endif
#if CXIMAGE_SUPPORT_BMP
 else if(ext3.CompareNoCase(_T("bmp"))==0)
  return CXIMAGE_SUPPORT_BMP;
#endif
#if CXIMAGE_SUPPORT_JPG
 else if(ext3.CompareNoCase(_T("jpg"))==0)
  return CXIMAGE_SUPPORT_JPG;
#endif

 return CXIMAGE_FORMAT_UNKNOWN;
}

3. 이미지 읽어 오는 함수를 추가한다.

CxImage* CDlgProgrammingPage::GetImage(CString& strPath)
{
 int nImageType=GetTypeFromFileName(strPath);
 if(nImageType==CXIMAGE_FORMAT_UNKNOWN) {
  MessageBox("해당 파일을 읽을 수 없습니다.", "에러", MB_OK);
  return NULL;
 }

 CxImage* pImg = new CxImage(strPath, nImageType);

 if(pImg->IsValid() == false) {
  delete pImg;

  MessageBox("해당 파일을 읽을 수 없습니다.", "에러", MB_OK);
  return NULL;
 }

 return pImg;
}

관련 라이브러리 :

Posted by gsi
:

컨트롤의 특정 부분을 사용해서 타이틀바로 인식하게 하고 싶을때가 있습니다.
그럴때는 아래와 같은 코드를 사용하면 됩니다.

void CDlgTest01::OnLButtonDown(UINT nFlags, CPoint point)
{
 CDialog::OnLButtonDown(nFlags, point);
 TRACE2("mousepos : %d, %d\n", point.x, point.y);
 PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKEWPARAM(point.x, point.y));
}

이거 말고 Static 컨트롤을 사용해서 클릭 이벤트를 추가 하고 캡션 처리는 아래와 같이 하면 되는거 같네요.

우선 Dialog에 Static 컨트롤을 추가 하고 그 컨트롤을 사용해서 처리 해줄때
Static의 "IDC_STATIC_CAPTION" 이벤트 함수를 추가 했습니다.
하지만 해보면 이 이벤트 함수는 호출이 되지 않는데요.
왜냐 하면 스타일에 "SS_NOTIFY"가 없어서 그렇습니다.

BOOL CDlgTest01::OnInitDialog() 함수 안에 아래의 코드를 추가 합니다.

GetDlgItem(IDC_STATIC_CAPTION)->ModifyStyle(0, SS_NOTIFY);

이제 디버깅을 해보시면 호출이 되는걸 확인할 수 있습니다.

마지막으로 작업해 주어야 하는 부분이
마우스 위치를 얻어와서 PostMessage를 호출해 주면 됩니다.

void CDlgTest01::OnStnClickedStaticCaption()
{
 CPoint mousePos;
 GetCursorPos(&mousePos);
 ScreenToClient(&mousePos);
 //TRACE2("Edit mousepos : %d, %d\n", mousePos.x, mousePos.y);

 PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKEWPARAM(mousePos.x, mousePos.y));
}

Posted by gsi
:

Gsi Image Viewer

내용 : CxImage를 이용한 MFC 버전 이미지 뷰어 (확대 가능)
코드 기술 :
1. MFC 를 이용, ListCtrl을 사용하였음
2. CxImage를 이용해서 DC에 이미지 출력

관련 이미지 :

사용자 삽입 이미지










관련 소스 :

Posted by gsi
:

싱글톤 패턴

C++ 2007. 11. 19. 16:46 |

싱글톤 패턴.. - 내가 자주 쓰는 패턴..

class SymbolArray
{
 SymbolArray(void);
public:
 static SymbolArray& GetInstance()
 {
  static SymbolArray sym;
  return sym;
 }
 ~SymbolArray(void);
...
}

///
싱글톤에서 에러를 유발할 수 있는 요소를 아래와 같이 작성해서 처리 합니다.
 SymbolArray();
 SymbolArray(const SymbolArray&) {}
 SymbolArray& operator=(const SymbolArray&) {}
Posted by gsi
:

C++ 의 변수를 C#의 프로퍼티 처름 사용하기..

말은 거창하지만 다른건 없다. 해당 값을 입력 받을 경우 다른 변수에 값을 추가 하거나 기타 다른 이벤트를 처리 할때 해당 값에 의존적인 클래스에서 처리 할 수 있다는 부분이다.
m_Val = 100 이렇게 입력 받은 후에 다른 이벤트 처리를 의존 프로퍼티 클래스를 상속받은 클래스에서 처리가 가능하게 만들어 본겁니다. ^^..
사용은 해봐야 알겠지만. 괜찮은 확장이 가능해 질거 같네요..

--라이브러리 클래스--

class CGsiDependencyProperty
{
public:
 CGsiDependencyProperty() {}
 virtual ~CGsiDependencyProperty() {}

 //값이 바뀌게 되면 호출할 수 있도록 바인딩 되는 함수
 virtual void PropertyChanged() {
  int iii = 0;
 };
};

template<class T>
class CGsiDataBind
{
public:
 CGsiDataBind() { m_pDependencyProp = NULL; }
 virtual ~CGsiDataBind() { }

 // int a = ?? 이런 코드가 가능합니다.
 operator T() {
  return m_Data;
 }

 // ?? = 100 이런 코드가 가능합니다.
 T operator = (T value) {
  m_Data = value;

  if(m_pDependencyProp)
   m_pDependencyProp->PropertyChanged();

  return m_Data;
 }

 friend class CGsiDependencyProperty;

 void SetDepend(CGsiDependencyProperty* refOper0 = NULL) {
  m_pDependencyProp = refOper0;
 }

protected:
 T      m_Data;
 CGsiDependencyProperty* m_pDependencyProp;
};

-- 사용법--
해당 변수의 값을 입력 할때 특정 클래스의 함수가 호출 되도록 하는 구조를 취할때
사용하면 됩니다.

호출 되는 함수가 있는 클래스 생성할때 CGsiDependencyProperty을 상속 받아서, virtual로된 함수 PropertyChanged() 를 선언해야합니다

class CDependA : public CGsiDependencyProperty
{
 virtual void PropertyChanged() {
  //값이 바뀌면 호출됨
 };
};

이제 변수를 선언하고 프로퍼티 클래스를 선언해서 연결한 후에
변수를 사용하면 됩니다.

 // 의존 프로퍼티 선언
 CDependA    m_Val0Property;

 // 변수 선언
 CGsiDataBind<int>  m_Val0;
 m_Val0.SetDepend(&m_Val0Property);

 m_Val0 = 100;           // 100을 입력하면 m_Val0Property의 클래스 내부 함수가 호출됩니다.
 int value = m_Val0;    // 해당 값을 일반 변수로 리턴받을 수 있습니다.

Posted by gsi
:

MFC DLL 사용하기

C++ 2007. 11. 16. 17:58 |

MFC DLL 사용하기

MFC DLL 프로젝트를 하나 생성한다. ( DllLib 라는 이름으로 프로젝트를 생성한다.)

DllLib.cpp 파일에 해당 하는 함수나 클래스 인스턴스를 하나 만듭니다.
이때 AFX_API_EXPORT를 앞에 붙여서 만들면 됩니다.

AFX_API_EXPORT int Plus(int _first, int _second);
AFX_API_EXPORT void DrawTest(CDC* pDC);

AFX_API_EXPORT int Plus(int _first, int _second)
{
 _first += m_Test.value;

    return (_first+_second);
}

AFX_API_EXPORT void DrawTest(CDC* pDC)
{
 pDC->MoveTo(10, 10);
 pDC->LineTo(100, 100);
}

이렇게 만든 후에 MFC Dialog 베이스로 프로그램을 하나 만들어서 테스트를 해봅니다.
(DllTestView 라는 이름으로 프로젝트를 생성한다.)

원하는 코드에서 해당 함수를 호출 하기 위해서 DllLib에서 만들어 놓았던 함수들을
선언해 줍니다.

AFX_API_IMPORT int Plus(int _first, int _second);
AFX_API_IMPORT void DrawTest(CDC* pDC);

이후에 사용은 아래와 같이 하시면 됩니다.

void CDllTestViewDlg::OnPaint()
{
......
 {
  CPaintDC dc(this);
  DrawTest(&dc);        // DC를 넘겨서 라인을 찍는 함수 테스트
  CDialog::OnPaint();
 }
}

// 두개의 에디터 박스에서 값을 int 형으로 받아서 합계를 내주는 함수 사용
void CDllTestViewDlg::OnBnClickedButton1()
{
 UpdateData(TRUE);
 m_Sum = Plus(m_Left, m_Right);
 UpdateData(FALSE);
}

요즘은 프로젝트가 대형화 되고, 한사람이 짜는 프로젝트가 많이 없다 보니
슬슬 DLL을 써야 할거 같다.

Posted by gsi
:

가끔 요즘 나오는 기술들을 보다 보면 데이터 바인딩에 대한 내용이 잘 구현되어있는거 같다. 물론 이해는 100% 되지 않지만 말이다.

그래서 C++에서 나름대로 사용하면 이런 형태는 어떨까 해서 하나 만들어 봤다. ^^
조금더 다듬어서 일부 기능은 이것을 사용해도 될거 같다.

사용되는 구조는 데이터를 가지고 있는 객체를 CContain 클래스 처름 구헌한다고
가정을 해보면 CDataBind 클래스와 연결후에 CDataBind의 객체만 SetValue 하면 된다.
이때 CContain 인스턴서의 PropertyChanged()가 자동으로호출 될 수도 있다.

이것과 다르게 함수포인터를 연동하는 부분도 추가를 해봤다. 클래스 내부의 함수가
함수 포인터로 하기에는 조금 몇가지 제약사항이 있기 때문에 전역 함수를 포인터와
연동하고 값이 바뀌게 되면 해당 함수가 호출 되는 구조를 취하게 된다.

혹.. 필요하신분 있으시면 요청시 설명해드릴게요 ^^
혹.. 좋은 의견 있음 코멘트 부탁 해요. ^^

Posted by gsi
:

사용자 삽입 이미지

이번에 하게된 테스트 프로젝트에서 사용하게 되는 모듈 중에 CScrollView를 사용한
화면 구현 부분을 작성해 봤습니다.

기능 :
- CScrollVew
- ZoomIn, ZoomOut
- MouseLButton 연동한 해당 지점 찍기

스크롤을 하거나, 줌인이 되면서 스크롤 되거나 하는 부분에서
마우스의 위치 처리 부분이 참 궁금했습니다.
이것으로 테스트는 마치고 작업에 들어가야 하겠네요.

구체적인 코드 설명은 오늘은 영.. @.@..
요청이 있을시에는 설명 해 드리겠습니다. ^^..

<참고> 코드 구루 인가 그쪽에서 가져온 ZoomView를 사용해서 확장한 겁니다.

소스 파일 :

Posted by gsi
:

비주얼 C++에서 자동으로 이 문제를 지원하는 방법은 없고, 다만 이에 대한 함수가 공개되어 있을 뿐입니다.
MSJDrawTransparentBitmap이라는 함수가 마이크로소프트의 knowledgebase 079212에 나와있습니다. 다음 소스 코드를 보면 이 함수가 하는 일은 비트맵을 불러오고 페인팅 표면을 투명하게 칠하기 위해 마스크로 보내는 작업을 한다는 것을 알 수 있습니다(자세한 내용은 MSJ 한국판 98년 1월호 108쪽 참고).

// MSJDrawTransparentBitmap
// 함수는 복사되었고 Knowledgebase 기사 Q79212으로부터 적용되었다.
// 제목 : 투과되는 비트맵 그리기
void MSJDrawTransparentBitmap(CDC* pDC, CBitmap* pBitmap, int xStart,
    int yStart, COLORREF cTransparentColor)
{
    CBitmap bmAndBack, bmAndObject, bmAndMem, bmSave;
    CDC dcMem, dcBack, dcObject, dcTemp, dcSave;
    dcTemp.CreateCompatibleDC(pDC);
    dcTemp.SelectObject(pBitmap); //비트맵 선택하기
    BITMAP bm;
    pBitmap->GetObject(sizeof(BITMAP), (LPSTR)&bm);
    CPoint ptSize;
    ptSize.x = bm.bmWidth;         // 비트맵의 폭
    ptSize.y = bm.bmHeight;        // 비트맵의 높이
    dcTemp.DPtoLP(&ptSize, 1);     // 논리 포인트로부터 장치 포인트로 변환
    // 임시 데이터를 보관하기 위해 몇몇 DC를 생성
    dcBack.CreateCompatibleDC(pDC);
    dcObject.CreateCompatibleDC(pDC);
    dcMem.CreateCompatibleDC(pDC);
    dcSave.CreateCompatibleDC(pDC);
    // 각 DC를 위해 비트맵을 생성. DC는 GDI의 숫자만큼 필요하다.
    functions.
    // 모노크롬 DC
    bmAndBack.CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
    // 모노크롬 DC
    bmAndObject.CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
    bmAndMem.CreateCompatibleBitmap(pDC, ptSize.x, ptSize.y);
    bmSave.CreateCompatibleBitmap(pDC, ptSize.x, ptSize.y);
    // 각 DC는 픽셀 데이터를 저장하기 위해 비트맵 객체를 선택해야 한다.
    CBitmap* pbmBackOld = dcBack.SelectObject(&bmAndBack);
    CBitmap* pbmObjectOld = dcObject.SelectObject(&bmAndObject);
    CBitmap* pbmMemOld = dcMem.SelectObject(&bmAndMem);
    CBitmap* pbmSaveOld = dcSave.SelectObject(&bmSave);
    // 적당한 맵핑 모드를 설정한다.
    dcTemp.SetMapMode(pDC->GetMapMode());
    // 이리로 보내진 비트맵을 저장한다.
    dcSave.BitBlt(0, 0, ptSize.x, ptSize.y, &dcTemp, 0, 0, SRCCOPY);
    // 투영되는 비트맵에 포함되는 색으로 소스 DC의 배경색을 설정한다.
    COLORREF cColor = dcTemp.SetBkColor(cTransparentColor);
    // 소스 비트맵에서 모노크롬 비트맵까지 BitBlt를 수행해서
    // 비트맵용 오브젝트 마스크를 생성한다.
    dcObject.BitBlt(0, 0, ptSize.x, ptSize.y, &dcTemp, 0, 0, SRCCOPY);
    // 원래 색으로 되돌리는 소스 DC의 배경색을 설정한다.
    dcTemp.SetBkColor(cColor);
    // 객체 마스크를 반전시킨다.
    dcBack.BitBlt(0, 0, ptSize.x, ptSize.y, &dcObject, 0, 0, NOTSRCCOPY);
    // 목적지에 메인 DC의 배경을 복사한다.
    dcMem.BitBlt(0, 0, ptSize.x, ptSize.y, pDC, xStart, yStart, SRCCOPY);
    // 비트맵이 놓여지는 위치를 마스크한다.
    dcMem.BitBlt(0, 0, ptSize.x, ptSize.y, &dcObject, 0, 0, SRCAND);
    // 비트맵의 투명 색상 픽셀을 마스크한다.
    dcTemp.BitBlt(0, 0, ptSize.x, ptSize.y, &dcBack, 0, 0, SRCAND);    
    // 목적지 DC상에서 배경색과 비트맵을 XOR 연산한다.
    dcMem.BitBlt(0, 0, ptSize.x, ptSize.y, &dcTemp, 0, 0, SRCPAINT);
    // 스크린에 목적지를 복사한다.
    pDC->BitBlt(xStart, yStart, ptSize.x, ptSize.y, &dcMem, 0, 0, SRCCOPY);
    // 보내진 비트맵으로 원래 비트맵을 대체한다.
    dcTemp.BitBlt(0, 0, ptSize.x, ptSize.y, &dcSave, 0, 0, SRCCOPY);
    // 메모리 비트맵을 리셋시킨다.
    dcBack.SelectObject(pbmBackOld);
    dcObject.SelectObject(pbmObjectOld);
    dcMem.SelectObject(pbmMemOld);
    dcSave.SelectObject(pbmSaveOld);
    // 메모리 DC와 비트맵 객체들은 자동으로 삭제된다.
}

Posted by gsi
:

다음 소스 프로그램은 현재 디렉토리의 모든 서브 디렉토리에 있는 파일을 찾아서 파일 이름을 모두 소문자로 바꾸는 루틴입니다. 자세한 내용은 각 함수별로 도움말을 참조하기 바랍니다.

void lookup()
{
    char new_filename[100];
    int leng;
    struct _finddata_t c_file;
    long hFile;
    int temp = 0;

    if ((hFile = _findfirst("*.*",&c_file)) == -1L) {
     cout << "error" << endl;
    }

    for (; temp == 0; temp = _findnext(hFile,&c_file)){
        leng = strlen(c_file.name);
        if ((c_file.attrib & _A_SUBDIR) == _A_SUBDIR){
            if (c_file.name[0] != '.'){
                _chdir(c_file.name);
                lookup();
                _chdir("..");
            }
        continue;
        }
        else if ((c_file.attrib & _A_ARCH) != _A_ARCH) continue;
        for(int i = 0;i < leng;i++){
            new_filename[i] = tolower(c_file.name[i]);
        }
        new_filename[i] = '\0';
        rename(c_file.name, new_filename);
    }
    _findclose(hFile);
}

Posted by gsi
:

2개의 툴바를 한줄에

C++ 2007. 10. 27. 02:19 |

class CMainFrame : public CFrame
{
protected:
    CToolBar wnd_myToolBar;
    CToolBar wnd_othToolBar;
    ...
};

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ...
    wnd_myToolBar.Create(...);
    wnd_othToolBar.Create(...);
    ...
}

DockControlBarLeftOf 함수를 이용하면 가능합니다. DockControlBarLeftOf(CToolBar* Bar, CToolBar* LeftOf)처럼 두 개의 툴바를 인수로 받기 때문입니다. 따라서 Bar와 LeftOf 툴바는 한줄에 출력될 것입니다. 다음 코드를 참고하기 바랍니다.

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ...
    EnableDocking(CBRS_ALIGN_ANY);

    m_wndToolBar.SetWindowText(_T("myToolBar"));
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar, AFX_IDW_DOCKBAR_TOP);

    wnd_othToolBar.SetWindowText(_T("otherToolBar"));
    wnd_othToolBar.EnableDocking(CBRS_ALIGN_ANY);
    DockControlBarLeftOf(&m_wndToolBar, &wnd_othToolBar);
   ...
}

void CMainFrame::DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf)
{
    CRect rect;
    DWORD dw;
    UINT n;
    // get MFC to adjust the dimensions of all docked ToolBars
    // so that GetWindowRect will be accurate
    RecalcLayout();
    LeftOf->GetWindowRect(&rect);
    rect.OffsetRect(1,0);

    dw=LeftOf->GetBarStyle();
    n = 0;
    n = (dw&CBRS_ALIGN_TOP) ? AFX_IDW_DOCKBAR_TOP : n;
    n = (dw&CBRS_ALIGN_BOTTOM && n==0) ? AFX_IDW_DOCKBAR_BOTTOM : n;
    n = (dw&CBRS_ALIGN_LEFT && n==0) ? AFX_IDW_DOCKBAR_LEFT : n;
    n = (dw&CBRS_ALIGN_RIGHT && n==0) ? AFX_IDW_DOCKBAR_RIGHT : n;
    // When we take the default parameters on rect, DockControlBar will dock
    // each Toolbar on a seperate line.  
    // By calculating a rectangle, we in effect
    // are simulating a Toolbar being dragged to that location and docked.
    DockControlBar(Bar,n,&rect);
}

Posted by gsi
:

(펌) - http://blog.naver.com/stkov?Redirect=Log&logNo=90014489284

기본적으로, MFC는 CFrameWnd 클래스를 상속한 객체만 툴바와 상태바를 추가할 수 있도록 허용한다. 이 기능이 좋긴 하나 만약 툴바와 상태바를 다이알로그에 넣으려고 한다면, 그것을 쉽게 할 수 없기 때문에 바보 같다는 기분을 느낄 것이다.

첫 번째 문제는 모든 핸들러 함수가 자신의 기반 클래스가 CFrameWnd를 상속한 객체라고 예상하고 있으며, 나머지 하나는 다이알로그가 IDLE 상태일 때만 툴팁이나 상태바 메시지를 보일 수 있다는 것이다. 사실, 컨트롤 바(control bar)들이 제대로 작동하려면, 프레임 창이 사용하는 몇몇 메시지를 처리해야 한다.

나는 이 일을 하는 데 도움이 될 만한 몇몇 문서를 찾아서 분석해 봐야 했다. Mihai Filimon ( 1998년 3월 16일 월요일 기사)이 내게 보여준 기사는 좋았으나, 바를 어떤 정적 객체와 함께 삽입해야하기 때문에 깔끔하지는 않았다. (나는 내가 터널의 끝을 본 데에 대해서 그를 매우 고맙게 생각한다.)

네가 만약 프로그램 상의 툴바(도킹된 상태로. 다른 방법이 없다)와 상태바와 통신이 가능한 메뉴를 넣는 방법을 설명할 것이다.

나는 툴바를 다이알로그에 넣는 방법만을 설명할 것이다. (메뉴를 넣는 것은 리소스 편집기로 메뉴를 정의하기만 하면 되는 뻔한 작업이기 때문이다)

첫째로, 툴바의 위치를 방해하지 않도록 다이알로그를 만들고 CDialog 클래스를 Dialog template으로 상속받는다. (지금까지는 별다른 것이 없다)

그리고 CDialog::OnInitDialog 함수에 다음의 코드를 넣는다. (단, m_wndToolBar는 CToolBarEx 타입):

BOOL CMyDlg::OnInitDialog()
{
// TODO: Add extra initialization here
CDialog::OnInitDialog();

// 툴바를 삽입한다.
if (!m_wndToolBar.Create( this ) ||
!m_wndToolBar.LoadToolBar(IDR_CORPS_EMIS) )
{
TRACE0("Failed to create toolbar\n");
return -1;      // 생성 실패
}

// TODO: 사이즈를 조절 할 수 있고 툴팁이 표시되는 툴바를 원하지 않으면 이 코드를 삭제할 것.
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY  );

// 컨트롤 바를 넣기 위해서 다이알로그의 크기를 조절할 필요가 있다.
// 첫째로, 컨트롤 바가 얼마나 큰지 알아보자.
CRect rcClientStart;
CRect rcClientNow;
GetClientRect(rcClientStart);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
   0, reposQuery, rcClientNow);

// 나머지 클라이언트 영역 안에서 같은 상대적인 위치를 가지도록 모든 컨트롤을 옮긴다.
 CPoint ptOffset(rcClientNow.left - rcClientStart.left,
rcClientNow.top - rcClientStart.top);

CRect  rcChild;
CWnd* pwndChild = GetWindow(GW_CHILD);
while (pwndChild)
{
pwndChild->GetWindowRect(rcChild);
ScreenToClient(rcChild);
rcChild.OffsetRect(ptOffset);
pwndChild->MoveWindow(rcChild, FALSE);
pwndChild = pwndChild->GetNextWindow();
}

// 대화창의 크기와 위치를 조절한다
 CRect rcWindow;
GetWindowRect(rcWindow);
rcWindow.right += rcClientStart.Width() - rcClientNow.Width();
rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height();
MoveWindow(rcWindow, FALSE);

// 컨트롤 바를 옮긴다
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}

여기서 뻔하지 않은 것들을 볼 수 있다. (나는 이것을 DLGCBR32라는 좀 더 복잡한 예제에서 찾았고, 반드시 삽입되어야 하는 함수들을 찾아내느라 고생했다.)

여기서, 툴바는 다이알로그의 맨 위쪽에만 삽입되게 된다. (위의 코드가 충분히 일반적이기 때문에, 위 대화창에 보여진 요구 사항들이 어떤 툴바에서도 충족하는 것을 알 수 있다). 다이알로그에 여러 개의 툴바를 넣을 수도 있지만, 그 위치는 알아서 조절해야 한다.

이제 툴바에 툴팁을 표시하고 싶다면, TTN_NEEDTEXTA과 TTN_NEEDTEXTW (ANSI와 유니코드 모두를 위함) 메시지를 다음의 방법으로 처리해야 한다.

1. 메시지 핸들러를 다음과 같이 선언한다.

BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
...
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP() 

2. 헤더 파일에 함수를 다음과 같이 정의한다.

// Generated message map functions
//{{AFX_MSG(CMyDlg)
afx_msg BOOL OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG 

3. 마지막으로 OnToolTipText 함수를 다음과 같이 코딩한다

(MFC 예제에서 퍼옴) :

BOOL CMyDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
 ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW); 
 // 메시지를 처리하기 위해 최상위 계층의 routing frame을 허용 (?)
 if (GetRoutingFrame() != NULL) return FALSE; 
 
 // ANSI와 유니코드 메시지를 모두 처리한다.
 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
 TCHAR szFullText[256];
 CString cstTipText;
 CString cstStatusText;
 UINT nID = pNMHDR->idFrom;
 
 if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) 
 || pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND)) { 
 // idFrom은 사실 툴바의 핸들이다. 
 nID = ((UINT)(WORD)::GetDlgCtrlID((HWND)nID));
 }
 
 if (nID != 0) // 구분자에서는 0. 
 { AfxLoadString(nID, szFullText); 
 // 이건 button index가 아니라 command ID. 
 AfxExtractSubString(cstTipText, szFullText, 1, '\n');
 AfxExtractSubString(cstStatusText, szFullText, 0, '\n'); }
 // 툴팁 윈도우에는 ANSI만 표시 가능
 
 if (pNMHDR->code == TTN_NEEDTEXTA)
 lstrcpyn(pTTTA->szText, cstTipText, 
 (sizeof(pTTTA->szText)/sizeof(pTTTA->szText[0]))); 
 else _mbstowcsz(pTTTW->szText, cstTipText,
 (sizeof(pTTTW->szText)/sizeof(pTTTW->szText[0]))); 
 
 *pResult = 0; 
 // 툴팁 창을 모든 팝업 윈도우의 위에 둔다. 
 ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, 
 SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE);
return TRUE; // 메시지가 처리됨 
} 

이제 툴바에 툴팁이 삽입된 것을 볼 수 있을 것이다. (툴팁에 보여지는 텍스트는 리소스 에디터를 쓰는 고전적인 방법으로 정의할 수 있다. 휴~ ;-) )

만약 메인프레임의 상태바에 텍스트를 보여주고 싶다면 단 한 줄의 코드를 위의 함수에 리턴하기 바로 전에 넣으면 된다: (단, m_wndStatusBar가 public인 경우에만)

// 메인프레임의 상태바에 텍스트를 표시한다 (단, 도움말 표시부가 인덱스 0에 있을 경우)

((CMainFrame*)GetParent())->m_wndStatusBar.SetPaneText(0, cstStatusText);

이것이 오늘 할 말의 끝이다.

아, 한 가지만 더 말하도록 하겠다. 만약 다이알로그 메뉴가 메인프레임의 상태바에 텍스트를 표시하게 하고 싶다면 (다른 상태바라도 됨) WM_MENUSELECT (window-type 핸들러)의 핸들러 함수에 다음의 코드를 넣으면 된다:

void CMyDlg::OnMenuSelect(UINT nItemID, UINT nFlags, HMENU hSysMenu) { 
 CDialog::OnMenuSelect(nItemID, nFlags, hSysMenu); 
 TCHAR szFullText[256]; 
 CString cstStatusText; 
 
 // TODO: Add your message handler code here
 // 메인프레임의 상태바 창에 출력. 
 if (nItemID != 0) 
 // 구분자에서는 반드시 0.
 { 
 AfxLoadString(nItemID, szFullText); 
 // 이것은 button index가 아닌 command id. 
 AfxExtractSubString(cstStatusText, szFullText, 0, '\n');
 ((CMainFrame*)GetParent())->m_wndStatusBar.SetPaneText(0,cstStatusText);
 } 
} 
 
다이알로그가 IDLE 상태일 때에만 새로 고쳐지기 때문에 이 툴바에는 ON_UPDATE_COMMAND
_UI 메시지를 쓸 수 없다는 것에 유의하자. 만약 그것을 하고 싶다면, CToolBar (또는 CStatus
Bar) 클래스를 상속받아서 - CMyToolBar는 예를 든 것이다 - WM_IDLEUPDATECMDUI 핸들러를
추가하고, 다음의 코드를 그 함수에 넣자:
 
////////////////////////////////////////////////////////////////////////
// CMyToolBar::OnIdleUpdateCmdUI 
// OnIdleUpdateCmdUI는 WM_IDLEUPDATECMDUI 메시지를 처리하는데, 그것은 MFC 프레임
// 워크 안에서 UI 요소의 상태를 업데이트하는 데 쓰인다. (?) 
 
// 여기서 나는 약간의 트릭을 썼다: CToolBar::OnUpdateCmdUI는 첫 번째 인수로 CFrame
// Wnd 포인터를 요구한다. 하지만, 그 함수는 아무것도 하지 않고 그 인수를 CCmdTarget 
// 포인터를 요구하는 다른 함수에 넘긴다. 우리는 CFrameWnd가 아닌 CCmdTarget인 CWnd 
// 포인터를 부모 윈도우에서 받을 수 있다.
// 그렇다면, CToolBar::OnUpdateCmdUI를 기쁘게 만들기 위해서 여기서 CWnd 포인터를
// 잠깐 CFrameWnd로 바꾸기로 하자.
 
LRESULT CMyToolBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM) 
{
 if (IsWindowVisible())
 {
 CFrameWnd *pParent = (CFrameWnd *)GetParent();
 if (pParent)
 OnUpdateCmdUI(pParent, (BOOL)wParam);
 }
 return 0L;
} 
 
AfxLoadString 때문에, "Afxpriv.h"를 CPP 파일 맨 위에 포함(Include)시켜야 컴파일 할 
수 있다.
Posted by gsi
:

(펌) - http://fefene.tistory.com/88

34. ToolBar에 ComboBox붙이기
  CComboBox m_combo; //객체생성
  ID 등록 => view 메뉴 => resource symbol => new => ID_COMBO
  oncreate 에 내용 추가 (콤보를 만들고 표시하는 내용)
  m_wndToolBar.SetButtonInfo(10,IDC_COMBO,TBBS_SEPARATOR,150); 
  //툴바의 10번째버튼을 편집한다
  CRect itemRect; //콤보를넣을 사각형을 만든다
  m_wndToolBar.GetItemRect(10,&itemRect); //툴바의 10번째 버튼을 사각형에 넣는다
  itemRect.left+=5; //앞여백
  itemRect.right+=5; //뒤여백
  itemRect.bottom+=100; //콤보가열릴 공간확보
   m_combo.Create(WS_CHILD|WS_VISIBLE|CBS_DROPDOWN,itemRect,&m_wndToolBar,IDC_COMBO);
  //콤보박스를 툴바에 붙여준다
   m_combo.AddString("이름"); //내용추가
   m_combo.SetCurSel(0); //셀 선택

35.  Toolbar에 수동으로넣은 ComboBox 사용하기
  afx_msg void [안내]태그제한으로등록되지않습니다-xxOnSelectCombo(); //원형
  ON_CBN_SELCHANGE(IDC_COMBO,[안내]태그제한으로등록되지않습니다-xxOnSelectCombo) //메세지맵에 추가
  CMainFrame *pMain=(CMainFrame *)GetParent(); //메인프레임 주소참조
  CComboBox *pCom=(CComboBox *)(pMain->m_wndToolBar.GetDlgItem(IDC_COMBO));
  //콤보박스의 주소를 가져온다, 접근할 때 메인프레임 -> 툴바 -> 콤보박스 의 순서로 가야한다
  int n=pCom->GetCurSel(); //현재선택된 셀의 인덱스를 가져온다
  if(n==CB_ERR) return; //선택된셀이 없으면 중지한다
  CString str;
  pMain->m_combo.GetLBText(n,str); //선택된셀의 Text를 가져온다

36. UPDATE_COMMAND 사용하기
  pCmdUI->Enable(TRUE); //버튼 활성화
  pCmdUI->SetText((bAdd)?"취소":"신규"); //버튼의 text 설정
  pCmdUI->SetCheck(TRUE); //버튼 체크

37. 프로그램정보저장
  CWinApp::GetProfileString(섹션명,항목명,기본값); // 함수를 사용한다. (문자열)
  CWinApp::GetProfileInt(섹션명,항목명,기본값); //불러올때사용 (숫자) 
  CWinApp::WriteProfileString(섹션명,항목명,값); //저장할때 사용 (문자열)
  CWinApp::WriteProfileInt(섹션명,항목명,값); //저장할때 사용 (숫자)
  //불러올때 사용할함수
  void CMainFrame::ActivateFrame(int nCmdShow) //프로그램 실행후 프레임생성될때 실행
  //저장할 때 WM_DESTROY 메시지 사용

38. 컨트롤바 표시하기
  CMainFrame *pMain=(CMainFrame *)GetParent(); //MainFrame 주소가져오기
  //툴바를 bTool2 에따라 보이고 감춘다
  pMain->ShowControlBar(&pMain->m_wndToolBar,bTool1,FALSE);

39. Window 창크기,위치정보 저장하기
  MainFrame 의 WM_DESTROY 에
    WINDOWPLACEMENT w;
    this->GetWindowPlacement(&w); //윈도우의 정보를 저장한다.
    CString strRect;
    strRect.Format("%04d,%04d,%04d,%04d", //04d 는 4자리 확보하고 남은건 0으로 채워라
         w.rcNormalPosition.left,w.rcNormalPosition.top,
         w.rcNormalPosition.right,w.rcNormalPosition.bottom); //윈도우의 위치,크기 확보..
        
        BOOL bMax,bMin; //윈도우의 상태를 저장하기위한 변수
        //w.falg 는 이전상태의 정보를 가지고 잇다!!
        if(w.showCmd==SW_SHOWMINIMIZED)           //최소화 상태
        {
                bMin=TRUE;
                if(w.flags==0) //falg 값이 0 이면 이전 상태가 보통상태이다!!
                        bMax=FALSE;
                else   //이전상태가 최대화 상태
                        bMax=TRUE;
        }
        else                            
        {
                if(w.showCmd==SW_SHOWMAXIMIZED) //최대화상태
                {
                        bMax=TRUE;
                        bMin=FALSE;
                }
                else //보통 상태
                {
                        bMax=FALSE;
                        bMin=FALSE;
                }
        }
        AfxGetApp()->WriteProfileString("WinStatus","Rect",strRect);
        AfxGetApp()->WriteProfileInt("WinStatus","Max",bMax);
        AfxGetApp()->WriteProfileInt("WinStatus","Min",bMin);


//읽어올차례..
ActivateFrame 함수로 가서
        WINDOWPLACEMENT w;  //윈도우의 상태를 저장하는 구조체..
        BOOL bMax,bMin;               //최대,최소상태를 저장할 변수
        CString strRect; //창크기를 받아올 변수
        strRect=AfxGetApp()->GetProfileString("WinStatus","Rect","0000,0000,0500,0700");
        bMin=AfxGetApp()->GetProfileInt("WinStatus","Min",FALSE);
        bMax=AfxGetApp()->GetProfileInt("WinStatus","Max",FALSE);
        int a=atoi(strRect.Left(4)); //문자열을 int 로 바꿔준다.
        int b=atoi(strRect.Mid(5,4));     //atoi 아스키 값을 int형으로 바꿔준다..
        int c=atoi(strRect.Mid(10,4));
        int d=atoi(strRect.Mid(15,4));
        w.rcNormalPosition=CRect(a,b,c,d);
        if(bMin)
        {
                w.showCmd=SW_SHOWMINIMIZED;
                if(bMax)
                {
                        w.flags=WPF_RESTORETOMAXIMIZED  ;
                }
                else
                {
                        w.flags=0;
                }
        }
        else
        {
                if(bMax)
                {
                        w.showCmd=SW_SHOWMAXIMIZED;
                }
                else
                {
                        w.showCmd=SW_SHOWNORMAL;
                }
        }
        this->SetWindowPlacement(&w); //설정된 값으로 윈도우를 그리게 한다..
        
        //CFrameWnd::ActivateFrame(nCmdShow); //이건 반드시 주석처리한다..

Posted by gsi
:

CTabCtrl 컨트롤을 사용하게 되면 탭의 내용을 추가해주어야 합니다.

아래와 같이 하나하나 추가 하게 되는데요.
m_tbMain.InsertItem(0, "Pixel Map");
m_tbMain.InsertItem(1, "Programming");
m_tbMain.InsertItem(2, "Color...");


사실 이렇게 하게 되면 탭이 늘어나거나 코드의 분량이 많아 지거나 하는 조금 지저분하게 되는 경향이 생기기도 합니다. 중간에 고치기 위해서 해당 코드를 찾아야 하구요.
그래서 아래와 같이 바꿔 보았습니다.

탭 컨트롤이 많은 어플에서는 유용할지도 모르겠어요.

탭에 들어갈 이름을 배열로 선언해 놓습니다.
TCHAR * tab_MainHeadersName[] = {
        _T("Pixel Map"),
        _T("Programming"),
        _T("Color..."),
        NULL };


이것을 사용하기 위해서 아래와 같이 for문을 사용해서 자동으로 입력 받습니다.
for (int i = 0; ; i++) {
 if (tab_MainHeadersName[i] == NULL) break;
 m_tbMain.InsertItem(i, tab_MainHeadersName[i]);
}


이렇게 하게 되면 코드 분량도 작아 지고 수정도 용이 하게 됩니다.
하지만 이런 코드를 컨트롤마다 다 넣어 주자니 귀찮아 집니다. ^^
그래서 아래와 같이 디파인으로 묶어 보았습니다. ^^

// 탭 컨트롤 해당 헤더 정보를 사용해서 자동으로 이름 부여 하는 디파인
#define TABCTRL_AUTOINPUT_INSERTITEM(ctl, headerlist) \
 for (int i = 0; ; i++) {       \
  if (headerlist[i] == NULL) break;    \
  ctl.InsertItem(i, headerlist[i]);    \
 }


이제 코드에서는 한줄로 해당 탭 이름을 추가할 수 있습니다.
// 탭 추가
TABCTRL_AUTOINPUT_INSERTITEM(m_tbMain, tab_MainHeadersName);
// 첫번째 항목이 선택되게.
m_tbMain.SetCurSel(0);


어때요?.
코드의 정리 면에서 더 효율이 있을가요? ^^
이런 항목들이 많아 진다면 config.h, cpp 등을 두어서 그쪽에 다 모아 놓으면 될거 같구요.
mfc를 할때는 그냥 stdafx.h, cpp 에 몰아 놓고 작업할때도 있어요.

나름대로 정리해본 내용입니다.
더 좋은 방법이 있다면 연락 주시거나 코멘트 남겨 주세요.
Posted by gsi
:

사용자 삽입 이미지

컬럼 정보를 크기에 맞도록 채우는 코드 입니다.
넓이 값을 가져 와서 비율 적으로 처리 하는 부분도 있지만.
그 부분을 빼고 직접 넓이를 지정하게 하는 구조를 취하게 했습니다.

코드는 아래와 같습니다.

//컬럼 채우기
int m_nColWidths[] = { 90, 60}; // sixty-fourths
TCHAR * lpszHeaders[] = {
       _T("Port/Sequence"),
       _T("Address"),
       NULL };

int i;
LV_COLUMN lvcolumn;
memset(&lvcolumn, 0, sizeof(lvcolumn));

// add columns
for (i = 0; ; i++)
{
 if (lpszHeaders[i] == NULL)
  break;

 lvcolumn.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
 lvcolumn.fmt = LVCFMT_LEFT;
 lvcolumn.pszText = lpszHeaders[i];
 lvcolumn.iSubItem = i;
 lvcolumn.cx = m_nColWidths[i];
 m_listPortSequencePerAddress.InsertColumn(i, &lvcolumn);
}

lpszHeaders, m_nColWidths 의 정보를 사용해서 for문을 통해서
컬럼 정보를 입력 하는 구조입니다.

하드 코딩 하는거 보다는 이 방법을 취하는게 중간 중간 컬럼명이 바뀔 경우
lpszHeaders의 내용만 수정해 주면 되니까 편한듯 합니다.

Posted by gsi
: