반응형

MFC에서 Doc/View 구조가 아닌 프로젝트를 생성하여, MainFrame에서 View클래스의 포인터를 얻기 위해 GetActiveView()함수를 사용하니 NULL을 반환

그냥, 
View.cpp 파일에서 전역변수로 선언하고 OnCreate시에 자기를 할당하고, MainFrame에서는 extern으로 받아서 사용하니 동작됨. 하지만, 좀 더 안전이 보장되는 방법을 알아야 할 필요성이 있음

Example.
MainView.cpp
CMainView * pMainView;
int CMainView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
pMainView = this;
}


MainFrm.cpp
extern CMainView * pMainView;

// 함수내에서
pMainView->SetTranslationDirection(Y, m_bTranslation[Y]);




알아냄. CMainFrame class내에 view의 객체가 존재함 -_-;
저작자 표시 비영리
if(/MSIE [0-6]\./.test(navigator.userAgent)){for(var i=0;i<2;i++){var el=document.getElementById('ccl-icon-23-'+i);el.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+el.src+'",sizingMethod="image")';el.src='http://cfs.tistory.com/static/admin/form/s.gif';}}
 [출처 : http://chaoskcuf.com]

1. 개요 

MFC 9.0 Beta 버전에 포함된 Office 2007 스타일의 Ribbon Bar를 만드는 방법을 알아본다. 
(Application Wizard가 자동으로 생성하는 코드에 대한 이해를 목적으로 한다.)

 

2. 프로젝트 생성


  ProjectStyleSetting 
[그림 1 Application Type]

a) [그림 1]과 같이 Project Style을 Office로 선택하고 마음에 드는 Style Thema를 선택한다. 
(여기서 주의할 점이 있는데 Enable visual style switching 체크를 해제하게 되면 컴파일 에러가 나게 되는데, 물론 쉽게 에러나는 곳을 고칠 수 있으나 성가신 사항이므로 일단 체크를 하자. 컴파일 에러에 관한 내용은 다음 기회에 포스팅할 예정이다.)


ProjectUIFeatures 
[그림 2 User Interface Features]

b) [그림 2] 와 같이 User Interface Features의 옵션을 선택하는 부분에서 우리는 Ribbon Bar를 사용하여야 하기 때문에 User a ribbon 라디오 버튼에 체크하자. (뭐 기본 선택사항이니 가만히 두어도 된다)


 ProjectAdvancedFeatures 
[그림 3 Advanced Features]

c) [그림 3]에 보면 Advanced docking panes 옵션을 보면 5가지를 추가로 선택할 수 있다. 
위의 3개의 체크 박스는 Visual Studio Style의 프로젝트를 만들때 흔히 쓰일 듯한 옵션이다. 
4번째의 Navigation pane은 쉘 폴더 트리뷰와 달력 뷰가 샘플로 들어가는 옵션인데 일단 지금은 Ribbon Bar에 집중하기 위해서 일단 체크를 해제하도록 한다. 5번째의 Caption bar는 Ribbon bar 밑에 나오는 녀석으로 사용자에게 간단한 메세지를 출력해주는 기능을 하는 것으로 예를 들면 IE에서 보안설정으로 파업차단, ActiveX  설치시 삑 하고 나타나는 영역이라고 생각하면 되겠다.

d) 여기까지 했다면 Finish 버튼을 클릭하면 프로젝트를 생성할 수 있는데, 바로 빌드를 해보면 [그림 4]와 같이 정말 Office 2007스러운 미려한 디자인의 프로그램을 만날 수 있다.

SampleUI 
[그림 4 샘플]

 

3. Application Wizard가 생성한 코드 이해하기


a) CMainFrame의 OnCreate 이해 하기

MFC 8.0 이하의 버전에서는 MDI 프로젝트에서 CMainFrame 클래스는 CFrameWnd를 상속받았었다. 그러나 리본바를 사용하기 위해서는 CFrameWnd 클래스 대신에 이의 확장 버전인 CMDIFrameWndEx 클래스를 상속받고 있다. 아래의 코드는 Wizard가 자동으로 생성해주는 코드인데 MFC 8.0 이하 버전과의 차이점을 중심으로만 살펴보기로 하겠다.

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 

    if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1) 
        return -1; 
    // set the visual manager and style based on persisted value 
    OnApplicationLook(theApp.m_nAppLook); 

    CMDITabInfo mdiTabParams; 
    mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // other styles available... 
    mdiTabParams.m_bActiveTabCloseButton = TRUE;      // set to FALSE to place close button at right of tab area 
    mdiTabParams.m_bTabIcons = FALSE;    // set to TRUE to enable document icons on MDI taba 
    mdiTabParams.m_bAutoColor = TRUE;    // set to FALSE to disable auto-coloring of MDI tabs 
    mdiTabParams.m_bDocumentMenu = TRUE; // enable the document menu at the right edge of the tab area 
    EnableMDITabbedGroups(TRUE, mdiTabParams); 

    m_wndRibbonBar.Create(this); 
    InitializeRibbon(); 

    if (!m_wndStatusBar.Create(this)) 
    { 
        TRACE0("상태 표시줄을 만들지 못했습니다.\n"); 
        return -1;      // 만들지 못했습니다. 
    } 

    CString strTitlePane1; 
    CString strTitlePane2; 
    strTitlePane1.LoadString(IDS_STATUS_PANE1); 
    strTitlePane2.LoadString(IDS_STATUS_PANE2); 
    m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE), strTitlePane1); 
    m_wndStatusBar.AddExtendedElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE2, strTitlePane2, TRUE), strTitlePane2); 

    // enable Visual Studio 2005 style docking window behavior 
    CDockingManager::SetDockingMode(DT_SMART); 
    // enable Visual Studio 2005 style docking window auto-hide behavior 
    EnableAutoHidePanes(CBRS_ALIGN_ANY); 

    // Create a caption bar: 
    if (!CreateCaptionBar()
    { 
        TRACE0("Failed to create caption bar\n"); 
        return -1;      // 만들지 못했습니다. 
    }

    // Enable enhanced windows management dialog 
    EnableWindowsDialog(ID_WINDOW_MANAGER, IDS_WINDOWS_MANAGER, TRUE);

    return 0; 
}

  • 처음 부분에 등장하는 생소한 함수가 보일 것이 바로 OnApplicationLook()라는 함수인데, 이 함수는 바로 Look & Feel을 선택할 수 있게 해주는 CMDIFrameWndEx 의 센스쟁이 멤버 함수이다. 파라미터로 넘어가는 thhApp.m_nAppLook 은 CMainFrame의 생성자에서 대입된다. 아래와 같이 정의된 값을 대입해보면서 스타일이 어떻게 변하는지 직접 느껴보기 바란다.
    • ID_VIEW_APPLOOK_WIN_2000
    • ID_VIEW_APPLOOK_OFF_XP
    • ID_VIEW_APPLOOK_WIN_XP
    • ID_VIEW_APPLOOK_OFF_2003
    • ID_VIEW_APPLOOK_VS_2005
    • ID_VIEW_APPLOOK_OFF_2007_BLUE
    • ID_VIEW_APPLOOK_OFF_2007_BLACK
    • ID_VIEW_APPLOOK_OFF_2007_SILVER
    • ID_VIEW_APPLOOK_OFF_2007_AQUA

EnableMDITabbedGroups  
[그림 5 예전 MDI 방식]

  • MDI 형식의 프로젝트이기 때문에 여러개의 문서를 어떤 식으로 표시를 해줄 것인지를 결정하는 함수가 EnableMDITabbedGroups() 이다. 첫번째 파라미터를 FALSE로 넘기면 Tab 방식으로 문서를 정렬하는 방식이 아닌 [그림 5]와 같이 예전 MDI 방식으로 사용할 수 있다. 
    두번째 파라미터로 넘기는 CMDITabInfo 클래스는 아래와 멤버 변수를 public으로 가지고 있다.

class CMDITabInfo 

public: 
    CMDITabInfo(); 
    void Serialize(CArchive& ar);

    CMFCTabCtrl::Location m_tabLocation;    // Tab에 위치를 아래에 둘지 위의 둘지 선택 (기본 위) 
    CMFCTabCtrl::Style    m_style;                // Tab Look & Feel 선택 (아래 참조)

    BOOL m_bTabIcons;                              // Tab에 아이콘을 표시할 것 인지 
    BOOL m_bTabCloseButton;                     // Tab에 닫기 버튼을 표시할 것 인지  (m_bActiveTabCloseButton 가 FALSE 일 때)

    BOOL m_bTabCustomTooltips; 
    BOOL m_bAutoColor;                             // 새로운 문서가 추가될 때 마다 Tab의 색상이 바뀔것 인지 
    BOOL m_bDocumentMenu;                     // 문서가 많이 열렸을 때 좌우 스크롤을 할지 Menu에 표시할 지       
    BOOL m_bEnableTabSwap;                    // 드래그 앤 드랍으로 탭의 위치를 변경할 수 있게 만들지 
    BOOL m_bFlatFrame;                             // Frame 의 모습 
    BOOL m_bActiveTabCloseButton;            // 활성화된 Tab에 닫기 버튼이 표시되게 할 지 Tab영역 오른쪽 가장자리에  표시할 지 
    int  m_nTabBorderSize;                          // Frame Border의 굵기 
};

  • CMdITabInfo의 m_style이라는 멤버 변수는 아래와 같은 값을 가질 수 있다.
    • STYLE_3D
    • STYLE_FLAT
    • STYLE_FLAT_SHARED_HORZ_SCROLL
    • STYLE_3D_SCROLLED
    • STYLE_3D_ONENOTE       
    • STYLE_3D_VS2005       
    • STYLE_3D_ROUNDED                     
    • STYLE_3D_ROUNDED_SCROLL
  • InitializeRibbon() 함수에 리본바를 구성하는 모든 코드들이 속해있다. (기본적으로 Wizard가 샘플로 만들어 놓은 함수이다)

 

b) Main Category 부분 구성하기

MainPanel(desc) 
[그림 6 Main Category의 구성]

MFC 9.0과 같이 나온 미려한(?) 디자인의 MFC이 아이콘이 속해있는 버튼이 보이는가? 그 버튼을 누르면 위의 [그림 6]처럼 Main Category라는 익숙한 창이 나타나게 되는데 일단 MainCategory를 뛰워주게 하는 이 Application Button이라고 부르는 녀석을 먼저 살펴보도록 한다. 

CMainFrame는 멤버 변수로 CMFCRibbonApplicationButton class의 객체를 m_MainButton라는 이름으로 가지고 있다. 이 버튼의 이미지는 Bitmap파일의 리소스 아이디를 SetImage함수를 통해 전달해 주면 된다. 

그리고 Alt 키를 누르면 각각의 Ribbon Element들이 가지는 단축키들이 표시되는데, 이 단축키를 세팅해주는 방식 두가지가 있다.

m_MainButton.SetText(_T("\nf"));  // Element의 이름과 단축키를 동시에 설정하는 방법 
m_MainButton.SetKeys(_T("f"));      // 단축키 값만을 설정하는 방법


그런데 한가지 의문점이 든다. SetText의 문자열이 개행문자로 시작하는 부분이다. 
CMFCRibbonApplicationButton의 멤버함수(정확히는 CMFCRibbonBaseElement의 멤버함수) SetText 함수는 단축키와 표시될 이름을 동시에 설정하는 함수이다. 단, 위와 같이 '\n' 개행문자를 구분자로 표시될 이름과 단축키가 한 문자열로 전달되어야 한다. m_MainButton에 SetText를 하는 것은 약간 이례적인 경우인데, Application Button(m_MainButton)은 [그림 6]을 보다시피 문자열로 표현되는 부분이 없다. 그래서 SetText함수를 사용하여

m_MainButton.SetText(_T("MainButton!\nf"));


위와 같이 코딩하여도  '\n' 앞의 문자열 MainButton은 쓰여질 곳은 없기 때문에 굳이 넣지 않아도 된다
이제 의문이 해소되었는가? 
아직도 이상하다고 생각되면 그냥 SetKeys함수를 사용하여 그 의미를 명확하게 하자.

참고로 사실 Application Wizard가 생성해주는 코드에 저 부분이 들어있어서 설명해주고 싶었다. 
ApplicationButton의 경우 이상하다고 느낄지는 모르겠지만 예를 들어 다른 Ribbon Element 들을 만들때 생성자로

CMFCRibbonButton* pBtnPaste = new CMFCRibbonButton(ID_EDIT_PASTE, _T("Paste\nV"), 0, 0);


와 같이 두번째 파라미터로 문자열 값을 넣었을 때 생성자 내부에서 CMFCRibbonBaseElement의 SetText함수를 호출하게 되어 있어서 그 Element는 Paste라는 이름을 가지게 되고 단축키는 V를 가지게 된다. 
주의할 점은 소문자를 넣어도 Alt를 눌렀을 때의 모든 단축키의 모습은 아래 그림과 같이 대문자로 나온다는 것이다.

Keys 
[그림 7 단축키 표현 방식]

마지막으로 버튼이라면 마우스가 오버 했을 때 툴팁을 뛰워주야하지 않겠는가? 그래서 그 툴팁 텍스트를 설정하는 부분이 바로

m_MainButton.SetToolTipText(_T("File"));


SetToolTipText 함수를 사용하는 부분이다.

이렇게 하면 Button에 대한 속성은 거의 다 정해준 것 같다. 
그러나 여기까지는 그냥 버튼을 하나 생성시켜서 메모리 어느 공간에 있을 뿐이다. 
이 버튼이 ApplicationButton이다!! 라고 설정을 시켜주어야 한다.

OnCreate 함수에서 Create 시켜주었던 RibbonBar 객체가 생각나는가? 그 RibbonBar에다 이 버튼이 Application이고 버튼 크기는 얼마였으면 좋겠다라고 등록시켜주는 함수가 있다.

m_wndRibbonBar.SetApplicationButton(&m_MainButton, CSize (45, 45)); //가로 45px, 세로 45px 크기로 ApplicationButton으로 등록


위의 코드처럼 SetApplicationButton이라는 함수인데, 만약에 위와 같이 m_MainButton을 RibbonBar에 저 함수를 통해 등록하지 않으면 어떻게 될까?

WithoutAppButton 
[그림 8 Application Button이 없는 리본바]

[그림 8] 처럼 ApplicationButton이 없는 UI가 나타난다. 
경우에 따라  ApplicationButton이 없는 경우를 선호할 때는 CMFCRibbonApplicationButton 을 설정하고 RibbonBar에 등록하는 부분을 주석처리하면 그 뿐인 것이다.

길게 길게 ApplicationButton에 대해서 설명을 했다. 
그렇다면 이번에는 [그림 6] 처럼 MainCategory에 New Open 처럼 항목을 집어 넣는 부분을 살펴보자. 
일단 MainCategory는 좀 특이하다. 
RibbonBar에 항상 나타나 있는 것이 아니라 ApplicationButton을 통해서 나타나는 것도 그렇고, 기본적으로 MDI 프로그램에 꼭 필요한 항목들만 들어 있다. 그래서인지 RibbonBar에서는 AddMainCategory 함수가 따로 존재한다. 
(뒤에 보면 알겠지만 그냥 AddCategory 함수가 있다. AddCategory는 여러 카테고리를 추가할 수 있지만, AddMainCategory는 당연히 한번만 호출 되어서 하나의 MainCategory만 존재해야 한다.)

CMFCRibbonMainPanel* AddMainCategory( 
   LPCTSTR lpszName,                           // 메인카테고리의 이름 
   UINT uiSmallImagesResID,                   // 작은 이미지 리소스 아이디 
   UINT uiLargeImagesResID,                   // 큰 이미지 리소스 아이디 
   CSize sizeSmallImage=CSize(16, 16) ,  // 작은 이미지 하나의 크기 
   CSize sizeLargeImage=CSize(32, 32)   // 큰 이미지 하나의 크기 
);


너무나 당연한 이야기지만, 리소스에 포함된 이미지는 파라미터로 넘겨주는 크기의 이미지 조각을 연속적으로 붙여놓은 하나의 이미지여야한다.

CMFCRibbonMainPanel* pMainPanel = m_wndRibbonBar.AddMainCategory(_T("File"), IDB_FILESMALL, IDB_FILELARGE);

그런데 여기서 질문.

  1. 메인 카테고리 이름이라고 준 File 이라는 문자열이 Application을 눌러봐도 나오는 부분이 없는 것 같다
  2. MainCategory창에는 전부 큰 이미지(Large Icon)만 보이는데 굳이 작은 이미지를 넣을 필요가 있는가?

차근 차근 위의 질문에 대한 답을 찾아보자. 
정답은 QAT(Quick Access Toolbar)의 지원때문이라고 간단히 말할 수도 있겠다.

CustomizeQAT 
[그림 9 QAT]

Office UX에서 사용자가 가장 자주 쓰는 기능을 ApplicationButton 옆에 보이는 QAT로 모아서 편리하게 쓸 수 있게끔 하는 것이 있다. 
그 옆에  아래로 향하는 화살표 부분을 클릭하면 QAT를 Customize할 수 있는 메뉴가 나타난다.

QATMenu

반응형

+ Recent posts