時計部品作成

アプリケーション編

完成画面
  • VC++を起動する。
  • ファイルメニュー新規作成MFC AppWizard(exe)を選び、プロジェクト名ClockSample とし OK を押す。
    作成するアプリケーションの種類は、 ダイアログベースとし終了ボタンを押す。
  • 画面にピクチャーコントロールを適当に貼る。 IDIDC_CLOCK とする。 スタイル通知をチェックする。
  • エディットボックスコントロールを適当に貼る。 ID は IDC_TIME とし、スタイルの テキストの配置右端にする。
  • キャンセルボタンのキャプション閉じるにする。
  • 表示メニューClassWizard を選ぶ。 クラスの追加を行う。クラス名は、 CClock とし、基本クラスCStatic とする。
  • メンバ変数カテゴリで、 クラス CClockSampleDlgIDC_CLOCK に メンバ変数を追加する。変数名は m_clock とし、 カテゴリは、コントロールとし、 タイプCClock とする。
    同様に IDC_TIME にメンバ変数を追加する。 変数名は m_time とし、カテゴリはとし、 タイプは CString とする。
  • メッセージマップカテゴリで、 クラス CClockSampleDlg に、 オブジェクトIDIDOK とし、 メッセージ BN_CLICKED に関数 OnOK を追加する。
    同様にオブジェクトID を IDC_TIME とし、 メッセージ EN_KILLFOCUS に関数 OnKillfocusTime を追加する。
    次に、クラス CClock を選ぶ。(変更しますか?には「はい」)
    オブジェクトID を CClock とし、 メッセージ WM_PAINT に関数 OnPaint を追加する。
    同様に、メッセージ WM_LBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP に関数を追加する。ClassWizard の OK ボタンを押す。
  • 画面で、OK ボタンとスタティックテキストを削除し、次のようにする。
  • ClockSampleDlg.h の クラス定義の前に、 #include "Clock.h" を追加。
  • Clock.h の クラス定義の前に、 #include <cmath>const double pi = 3.14159265358979; を追加。 また、クラス CClock 内の をアトリビュートとオペレーションを次のように修正。
    // アトリビュート
    private:
        int  m_moveType;   // 0:hour,1:min
        int  m_hour;       // 1-12
        int  m_min;        // 0-59
        void (*m_func)();  // 変更時に呼ばれる
    
    // オペレーション
    public:
        void SetCurrent();
        void SetCurrent(unsigned int h,unsigned int m);
        int Hour()const{return m_hour;}
        int Minute()const{return m_min;}
        CString ToString()const;
        void SetFunc(void (*f)()){m_func = f;}
    
  • ClockSampleDlg.cppCClockSampleDlg::CClockSampleDlg を以下のように修正。
    static void UpdateTime()
    {
        CClockSampleDlg* pWnd = (CClockSampleDlg*)AfxGetMainWnd();
        pWnd->m_time = pWnd->m_clock.ToString();
        pWnd->UpdateData(FALSE);
    }
    
    CClockSampleDlg::CClockSampleDlg(CWnd* pParent /*=NULL*/)
        : CDialog(CClockSampleDlg::IDD, pParent)
    {
        //{{AFX_DATA_INIT(CClockSampleDlg)
        m_time = _T("");
        //}}AFX_DATA_INIT
        m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
        m_time = m_clock.ToString();
        m_clock.SetFunc(&UpdateTime);
    }
    また、メッセージハンドラを以下のように修正。
    void CClockSampleDlg::OnOK() 
    {
        int    h,m;
        UpdateData();
        h = m = 0;
        sscanf(m_time,"%d:%d",&h,&m);
        m_clock.SetCurrent(h,m);
        m_clock.Invalidate();
    }
    
    void CClockSampleDlg::OnKillfocusTime() 
    {
        OnOK();
    }
  • Clock.cppCClock::CClock を以下のように修正。
    void CClock::SetCurrent(unsigned int h,unsigned int m)
    {
        m_hour = (h+11)%12 + 1;
        m_min = m%60;
    }
    
    void CClock::SetCurrent()
    {
        time_t tt = time(0);
        tm* t = localtime(&tt);
        SetCurrent(t->tm_hour,t->tm_min);
    }
    
    CString CClock::ToString()const
    {
        CString	s;
        s.Format("%2d:%.2d",m_hour,m_min);
        return s;
    }
    
    CClock::CClock()
    {
        SetCurrent();
        m_func = 0;
    }
    また、メッセージハンドラを以下のように修正。
    void CClock::OnPaint() 
    {
        CPaintDC dc(this);
        CRect    rc,rct,rr;
        int      i;
        double   d,w,h,r = 0.98, rp = 0.85;
        CPoint   pc,pt,pt2;
        CSize    sz;
        CString  s;
        GetClientRect(&rc);
        w = rc.Width() * 0.5;
        h = rc.Height() * 0.5;
        dc.Ellipse(&rc);
        pc = rc.CenterPoint();
        dc.DrawText("12",2,&rct,DT_CALCRECT);
        rct.OffsetRect(rc.CenterPoint() - rct.CenterPoint());
        sz = CSize(10,10);
        for (i=0;i<12;++i) {
            d = i*pi/6;
            pt.x = long(w * sin(d));
            pt.y = long(-h * cos(d));
            rr = rct;
            sz = CSize(int(pt.x*rp),int(pt.y*rp));
            rr.OffsetRect(sz);
            s.Format("%d",i?i:12);
            dc.DrawText(s,&rr,DT_CENTER | DT_VCENTER);
            sz = CSize(int(pt.x*r),int(pt.y*r));
            dc.MoveTo(pc+sz);
            dc.LineTo(pc+pt);
        }
        d = (m_hour + m_min/60.)*pi/6;
        pt.x = long(w * sin(d));
        pt.y = long(-h * cos(d));
        sz = CSize(int(pt.x*.6),int(pt.y*.6));
        CPen    pen1(PS_SOLID,int(max(4,w/20)),RGB(0,0,0)),
                pen2(PS_SOLID,int(max(2,w/50)),RGB(0,0,0)),*pOld;
        pOld = dc.SelectObject(&pen1);
        dc.MoveTo(pc);
        dc.LineTo(pc+sz);
        d = (m_min)*pi/30;
        pt.x = long(w * sin(d));
        pt.y = long(-h * cos(d));
        sz = CSize(int(pt.x*.9),int(pt.y*.9));
        dc.SelectObject(&pen2);
        dc.MoveTo(pc);
        dc.LineTo(pc+sz);
        dc.SelectObject(pOld);
    }
    
    void CClock::OnLButtonDown(UINT nFlags, CPoint point) 
    {
        CRect    rc;
        GetClientRect(&rc);
        CPoint   pc = rc.CenterPoint();
        double   r = atan2(point.x-pc.x,-(point.y-pc.y)),rh,rm;
        if (r < 0) r += 2*pi;
        rh = (m_hour+m_min/60.)*pi/6;
        rm = m_min*pi/30;
        if (fabs(r-rh) < 10*pi/180) m_moveType = 0;
        else if (fabs(r-rm) < 10*pi/180) m_moveType = 1;
        else return;
        SetCapture();
    }
    
    void CClock::OnMouseMove(UINT nFlags, CPoint point) 
    {
        if (GetCapture() != this) return;
        CRect    rc;
        GetClientRect(&rc);
        CPoint   pc = rc.CenterPoint();
        double   r = atan2(point.x-pc.x,-(point.y-pc.y));
        int      pre = m_min;
        if (r < 0) r += 2*pi;
        switch (m_moveType) {
        case 0:
            m_hour = int(r*6/pi+11.5)%12+1;
        break;
        case 1:
            m_min = int(r*30/pi+.5)%60;
            if (pre < 10 && m_min > 50) m_hour = (m_hour+10)%12+1;
            else if (pre > 50 && m_min < 10) m_hour = m_hour%12+1;
            break;
        }
        if (m_func) (*m_func)();
        Invalidate();
    }
    
    void CClock::OnLButtonUp(UINT nFlags, CPoint point) 
    {
        if (GetCapture() != this) return;
        ReleaseCapture();
    }
    
  • コンパイルして実行。針を動かしてみよう。
  • うまくいったら ClassViewCClock を選択し 右クリックのメニューで ギャラリに追加を選ぶ。

コンポーネント編

  • VC++を起動する。
  • ファイルメニュー新規作成で MFC AppWizard(exe)を選び、プロジェクト名を ClockSample2 とし OK を押す。
    作成するアプリケーションの種類は、ダイアログベースとし終了ボタンを押す。
  • 画面にピクチャーコントロールを適当に貼る。 ID は IDC_CLOCK とする。スタイルの通知をチェックする。
  • プロジェクトメニュープロジェクトへ追加コンポーネントおよびコントロールを選び、 ClockSampleClock.ogxを選んで挿入する。
  • Exploler で CalcSample2 フォルダ内の CalcSample2.clw を削除する。
  • VC++に戻り、ClassWizard を開く。 ソースファイルからビルドしますか?はいを選び、選択画面でそのままOKを押す。
  • メンバ変数カテゴリで、クラス CClockSampleDlg の IDC_CLOCK に メンバ変数を追加する。変数名は m_clock とし、 カテゴリは、コントロールとし、タイプは CClock とする。
  • コンパイルして実行。部品を作ってギャラリに追加すればこのように簡単に利用できる。