顯示具有 class design 標籤的文章。 顯示所有文章
顯示具有 class design 標籤的文章。 顯示所有文章

class//Ca210real

這一個就是真正操作CA-SDK的類別,並且將其函數通通重新詮釋成CA-210的操作行為。
裡面包含設定、量測、互動設計的部份。程式設計上,也有許多地方使用條件編譯,但是其實,在主程式的地方就將Ca210sim設定成debug版,Ca210real設定成release版。以免發佈時,還是模擬器的程式碼,量出一堆假的值。

CA-SDK本身,其實是使用Design Pattern的組合模式(Composite pattern)將CA-SDK做一個巧妙的設計,符合每一種可能的需求,而身為一個SDK應用開發,除了必須了解SDK的功能之外,還要了解如何將複雜的SDK包裝成適合自己需求的介面,降低複雜度,方便未來程式碼的維護和擴充速度。

Ca210real.h

#ifndef CA210REAL_H
#define CA210REAL_H

#include "CA210.h"
#include "ca200srvr.h"
#include "Bullet.h"

//單獨控制CA-210模組是不是要進入 DEBUG模式
//#define _CA210DEBUG _DEBUG

class Ca210real : public Ca210
{
    ICa200          *m_pICa200;
    ICa             *m_pICa;
    IProbe          *m_pIProbe;
    IMemory         *m_pIMemory;
    IProbeInfo      *m_pIProbeInfo;

    CaState m_caState;
    CaState m_caStateTemp;
    CString str;

    CString ImpsbStr;
    void MsgFrmt(CException* e, const CString&, const CString&);
    void MsgFrmt(const CString&);

public:
    Ca210real();
    virtual ~Ca210real();

public:
    const CaState CalZero();
    const CaState Measure();

    void LinkMemory();

    const MsrAiState MsrAI(const float& MsrDeviation = 0.645);//any constant value

    void SetOnline(const BOOL& b = TRUE);
    const BOOL isOnline() const
    { return m_caState == CA_Offline ? FALSE : TRUE; };

    const CString GetLcmSize();
    const CString GetChData();// const;
    const Bullet  GetMsrData();

//for setup Dialog Value
    const   float GetRangeColor1() const;
    const   float GetRangeColor2() const;
    const   float GetRangeFAM()    const;

             void SetChId(const LPCTSTR& Id)    { m_pIMemory->SetChannelID(Id); };
    const CString GetChId()               const {                               return m_pIMemory->GetChannelID(); };
    const CString GetChId(const long& No) const { m_pIMemory->SetChannelNO(No); return m_pIMemory->GetChannelID(); };

             void SetChNo(const long &No)  { m_pIMemory->SetChannelNO(No); };
    const    long GetChNo()          const { return m_pIMemory->GetChannelNO(); };
    const CString GetChStrNo() /*const*/   { str.Format("%ld", m_pIMemory->GetChannelNO()); return str; };

    const CString GetProb()       const { return m_pIProbe->GetSerialNO(); };
    const CString GetDeviceType() const { return m_pICa->GetCAType();      };
    const CString GetCaVersion()  const { return m_pICa->GetCAVersion();   };

    const CString GetRefProbe() const;
    const CString GetCalProbe() const;
    const CString GetCalMode()  const;

    const CString GetRefLv() const;
    const CString GetRefSx() const;
    const CString GetRefSy() const;

//for setup dialog combo box ItemString
    const CString GetSynMode      (const   SynMode&) const;
    const CString GetDisplayMode  (const   DisPlay&) const;
    const CString GetDisplayDigits(const DisDigits&) const;
    const CString GetAvgingMode   (const   AvgMode&) const;
    const CString GetBrigUnit     (const  BrigUnit&) const;
    const CString GetCalStandard  (const  CalStand&) const;

//Get and Set of CA-SDK 
    void SetSynMode      (const   SynMode&); const float GetSynMode()       const;
    void SetDisplayMode  (const   DisPlay&); const long  GetDisplayMode()   const;
    void SetDisplayDigits(const DisDigits&); const long  GetDisplayDigits() const;
    void SetAvgingMode   (const   AvgMode&); const long  GetAvgingMode()    const;
    void SetBrigUnit     (const  BrigUnit&); const long  GetBrigUnit()      const;
    void SetCalStandard  (const  CalStand&); const long  GetCalStandard()   const;
    
private:  //for setup CA-SDK parameter
    const float chooseSynMode      (const   SynMode&) const;
    const int   chooseDisplayMode  (const   DisPlay&) const;
    const int   chooseDisplayDigits(const DisDigits&) const;
    const int   chooseAvgingMode   (const   AvgMode&) const;
    const int   chooseBrigUnit     (const  BrigUnit&) const;
    const int   chooseCalStandard  (const  CalStand&) const;

private:  //for real CA-210 USB connect initial
    const BOOL initCreatCa200();
    const BOOL initConnectCa210();
    const BOOL initAttachCa();
    const BOOL initAttchProbe();
    
#ifdef _CA210DEBUG
    const CString GetSetupValue()             const;
             void debugMessageBox(const CString&) const;
#endif
};

#endif

Ca210real .cpp

初始化,初始化很多東西

CA-SDK的初始化,其實是要初始化不少東西。一步一步來,一步一步切開。為了讓出問題的那一步突顯出來,所以將try-catch給分開做,每一個Exception都會顯示自己所屬的函數,萬一出錯,還會告訴我是出錯在哪一步。在這一個地方,不會和IMemory連線,因為和它連線也是要花一段時間。連上IMemory的目的就只是要切換channel,這個部份就留在觸設定的地方再連線就可以了。
Ca210real::Ca210real():
ImpsbStr("1. 按「Prt Scm鍵」抓下目前的螢幕,並開小畫家貼上,另存成圖檔\n2. Mail給此程式設計者,詳細描述使用過程並將圖檔存成附件\n這是尚未預測到出現的問題。(應該不會發生的那種。)"),
m_pICa200(0), m_pICa(0), m_pIProbe(0), m_pIProbeInfo(0), m_pIMemory(0)
{
    DebugCode( DBugModeBox("TRUE of Ca210real()"); )

    if (initCreatCa200())
    if (initConnectCa210())
    if (initAttachCa())
    if (initAttchProbe())
        m_caState = CA_not_yet_ZeroCal;
}

const BOOL Ca210real::initCreatCa200()
{
    try
    {
        if (m_pICa200 == 0)            
            m_pICa200 = new ICa200();
        m_pICa200->CreateDispatch("CA200Srvr.Ca200.1");
        return TRUE;
    }
    catch (CException* e)
    {
        MsgFrmt(e, "CreatCa200();出問題", "1. 檢查USB線和量筒\n2. 按確定");        
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

const BOOL Ca210real::initConnectCa210()
{
    try
    {
        m_pICa200->m_bAutoRelease = TRUE;        
        m_pICa200->AutoConnect();
        return TRUE;
    }            
    catch (CException* e)
    {
        MsgFrmt(e, "LPDISPATCH pICa = m_pICa200->ConnectCa210();出問題", ImpsbStr);
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

const BOOL  Ca210real::initAttachCa()
{
    try
    {
        if (m_pICa == 0)
            m_pICa = new ICa();
        LPDISPATCH pICa(0);
        pICa = m_pICa200->GetSingleCa();
        m_pICa->AttachDispatch(pICa);
        return TRUE;
    }            
    catch (CException* e)
    {
        MsgFrmt(e, "LPDISPATCH pICa = m_pICa200->GetSingleCa();出問題", ImpsbStr);
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

const BOOL Ca210real::initAttchProbe()
{
    try
    {
        if (m_pIProbe     == 0) m_pIProbe     = new IProbe();
        if (m_pIProbeInfo == 0) m_pIProbeInfo = new IProbeInfo();

        LPDISPATCH pIProbe(0);
        pIProbe = m_pICa->GetSingleProbe();

        m_pIProbe->AttachDispatch(pIProbe);
        m_pIProbeInfo->AttachDispatch(pIProbe);
        return TRUE;
   }            
    catch (CException* e)
    {
        MsgFrmt(e, "LPDISPATCH pIProbe = m_pICa->GetSingleProbe();出問題", ImpsbStr);
        m_pICa->DetachDispatch();
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

Zero Cal

真實的CA-210是需要Zero Cal的,而....它總是會讓你忘記。一下要轉MEAS檔一下要轉0-Cal檔的。在此,程式上的設計會提醒你接下來該做什麼。
可以發現,設計上,會有一個迴圈將try-catch包起來,try裡只有m_pICa->CalZero();這個重點。
如果出錯了,就提醒你目前岀錯的地方是Probe沒有轉到0-Cal。
再執行一次,如果轉到0-Cal則會順利開始0-Cal,但是若你無視上一次的警告訊息,這次的警告訊息就會用簡短的話來「提醒」你。
畢竟剛準備量測時,還沒集中注意力,被程式發現,會改變語氣和你對話。這種被抓包的感覺,我想多多少少會提神一下。
const CaState Ca210real::CalZero()
{
    /*
    連線 實機
    caes 11   執行ZeroCal
                例外處理:
                  給MessageBox
                  再執行ZeroCal
    default   回傳FALSE  無法執行
    */
    if (isOnline())
    {
        DebugCode( DBugModeBox("TRUE of CalZero()"); )
        int flag(0);
        do 
        {
            try
            {
                m_pICa->CalZero();
                flag = 0;
            }
            catch (CException* e)
            {
                if (flag < 1)
                    MsgFrmt(e,"Probe還沒轉至0-Cal就執行Zero Cal", "1. 將量測筒轉至0-Cal檔。\n2. 按「確定」");
                else
                    MsgFrmt("1. 將量測筒轉至0-Cal檔。\n2. 按「確定」");
                SetOnline();
                flag++;
            }
        } while (flag);
        return m_caState = CA_ZeroCalMode;
    }
    else
    {
        DebugCode( DBugModeBox("CalZero() Offline."); )
        return m_caState = CA_Offline;
    }
}

取得量測值

和ZeroCal()一樣,用一個迴圈將try-catch包起來,不過,在此有一件特別的事要提醒,就是這一段程式碼,實現無形的Zero Cal,自然而然的完成了Zero Cal,你再也不需要記得,CA-210剛啟動時要Zero Cal。
m_pICa->Measure(0)如果出錯,有兩個原因

  1. 還沒ZeroCal
  2. 量測時疑似抓值太快造成出錯的不明原因
這兩個原因,我們都在catch處理。
如果沒有出錯,大多就是ZeroCal已完成,還要看一下,現在的Probe是不是還在0-Cal,還沒切換到MEAS。

catch的部份,先確定有連線再看看,前CA-SDK的狀態是否已經ZeroCal。
再各別處理之。

const CaState Ca210real::Measure()
{
    CaState Mode = m_caState;
    if(isOnline())
    {
        DebugCode(  DBugModeBox("TRUE of Measure()"); )
        int flag = 0;

        do 
        {
            try
            {
                m_pICa->Measure(0);

                //已經ZeroCal,檢查檔位是否正確
                if (m_pIProbe->GetLv() < 0.01)
                    Mode = CA_ZeroCalMode;     //is0-Cal檔
                else
                    Mode = CA_MsrMode;        //已ZeroCal, 檔位正確
                flag = 0;
            }
            catch (CException* e)
            {
                //MsrError
                SetOnline();
                if (m_caState == CA_not_yet_ZeroCal)  //還沒ZeroCal
                {
                    if (CalZero() == CA_Offline)
                        Mode = CA_Offline;
                    ++flag;
                }
                else
                {
                    TCHAR buf[255];
                    e->GetErrorMessage(buf, 255);
                    ++flag;
                    if (flag > 10)
                        //重覆太多次,導致類當機的情況。
                        break;
                    else
                        //在這不做任何動作,重覆執行迴圈內的東西。                      
                        continue;
                }
            }
        } while (flag);
    }
    return Mode;
}

自動抓值量測

為了互動上的設計,出現了一種自己判斷目前是否可以抓值的程式段,降低使用者使用時操作的IO設備以及降低操作人手。
const MsrAiState Ca210real::MsrAI(const float& MsrDeviation)
{
    DebugCode( DBugModeBox("TRUE of MsrAI(float MsrDeviation)"); )
        //第一筆資料暫存空間  //宣告誤差值計算空間
    
    float XFristValue = 0.0, deltaX = 0.0,
          YFristValue = 0.0, deltaY = 0.0,
          ZFristValue = 0.0, deltaZ = 0.0, deltaAll;
    
    if ( Measure() == CA_MsrMode )
    {
        //抓參考值
        XFristValue = m_pIProbe->GetX();
        YFristValue = m_pIProbe->GetY();
        ZFristValue = m_pIProbe->GetZ();
        
        if ( Measure() == CA_MsrMode )
        {
            //誤差取絕對值
            deltaX = ((XFristValue-m_pIProbe->GetX()) >= 0) ? XFristValue - m_pIProbe->GetX() : m_pIProbe->GetX() - XFristValue;
            deltaY = ((YFristValue-m_pIProbe->GetY()) >= 0) ? YFristValue - m_pIProbe->GetY() : m_pIProbe->GetY() - YFristValue;
            deltaZ = ((ZFristValue-m_pIProbe->GetZ()) >= 0) ? ZFristValue - m_pIProbe->GetZ() : m_pIProbe->GetZ() - ZFristValue;
        
            deltaAll = deltaX * deltaY * deltaZ;

            if (deltaAll < MsrDeviation )    return MA_InDeviation;//門檻值內
            else                             return MA_OutDeviation;//門檻值外
        }
        else
            return MA_nonMsr;
    }
    else
        return MA_nonMsr;  //無效量測,可能是沒連線之類的
}

讓程式知道目前量測的LCM是幾吋的

量測幾吋的LCM就要切換到對應的Channel,Channel的名稱都會記錄這是適用幾吋,將其字串取出,就可以少問使用者一個令人心煩意亂+腦袋空白的問題了。
const CString Ca210real::GetLcmSize()
{
    if(isOnline())
    {
        DebugCode( DBugModeBox("TRUE of GetLcmSize()"); )           
        try
        {
            if (m_LCMsize.IsEmpty())
                m_LCMsize.Format("%s", m_pIMemory->GetChannelID().Left(2));
        }
        catch (CException* e)
        {
            MsgFrmt(e, "模組尺寸大小設定出問題", ImpsbStr);
            m_LCMsize.Format("-1");
        }
    }
    else 
        m_LCMsize.Format("-1");

    return m_LCMsize;
}

設定CA-210的相關選項

重新將CA-SDK介面設計得和Setup CA的概念一樣。在CaSetupDlg時,程式碼將會更加精鍊。
const float Ca210real::GetRangeColor1() const
{
    float clr1, clr2;
    m_pICa->GetAnalogRange(&clr1, &clr2);
    return clr1;
}

const float Ca210real::GetRangeColor2() const
{
    float clr1, clr2;
    m_pICa->GetAnalogRange(&clr1, &clr2);
    return clr2;
}

const float Ca210real::GetRangeFAM() const
{
    float FMA;
    m_pICa->GetFMAAnalogRange(&FMA);
    return FMA;
}

const CString Ca210real::GetRefSx() const
{
    float Lv, Sx, Sy;
    m_pIMemory->GetReferenceColor(m_pIProbe->GetId(), &Sx, &Sy, &Lv);
    CString strSx;
    strSx.Format("%1.4f", Sx);
    return strSx;
}

const CString Ca210real::GetRefSy() const
{
    float Lv, Sx, Sy;
    m_pIMemory->GetReferenceColor(m_pIProbe->GetId(), &Sx, &Sy, &Lv);
    CString strSy;
    strSy.Format("%1.4f", Sy);
    return strSy;
}

const CString Ca210real::GetRefLv() const
{
    float Lv, Sx, Sy;
    m_pIMemory->GetReferenceColor(m_pIProbe->GetId(), &Sx, &Sy, &Lv);
    CString strLv;
    strLv.Format("%f", Lv);
    return strLv;
}

const CString Ca210real::GetRefProbe() const
{
    long CalProbe, RefProbe, CalMode;
    m_pIMemory->GetMemoryStatus(m_pIProbe->GetNumber(), &CalProbe, &RefProbe, &CalMode);
    CString str;
    str.Format("%ld", RefProbe);
    return str;
}

const CString Ca210real::GetCalProbe() const
{
    long CalProbe, RefProbe, CalMode;
    m_pIMemory->GetMemoryStatus(m_pIProbe->GetNumber(), &CalProbe, &RefProbe, &CalMode);
    CString str;
    str.Format("%ld", CalProbe);
    return str;
}

const CString Ca210real::GetCalMode() const
{
    long CalProbe, RefProbe, CalMode;
    m_pIMemory->GetMemoryStatus(m_pIProbe->GetNumber(), &CalProbe, &RefProbe, &CalMode);
    CString str;
    str.Format("%ld", CalMode);
    return str;
}
//////////////////////////////////////////////////////////////////////////

void Ca210real::SetSynMode(const SynMode& SmType)
{
    m_pICa->SetSyncMode(chooseSynMode(SmType));
}

const float Ca210real::chooseSynMode(const SynMode& SmType) const
{
    switch(SmType)
    {
    case SM_NTSC:  return  0.0;
    case SM_PAL:   return  1.0;
    case SM_EXT:   return  2.0;
    case SM_UNIV:  return  3.0;
    case SM_INT:   return  4.0;
    default:       return -1.0;
    }
}

const CString Ca210real::GetSynMode(const SynMode& SmType) const
{
    switch(SmType)
    {
    case SM_NTSC:   return "NTSC";
    case SM_PAL:    return "PAL";
    case SM_EXT:    return "EXT";
    case SM_UNIV:   return "UNIV";
    case SM_INT:    return "INT";
    default:        return "No this Mode";
    }
}

const float Ca210real::GetSynMode() const
{
    return m_pICa->GetSyncMode();
}
//////////////////////////////////////////////////////////////////////////
void Ca210real::SetDisplayMode(const DisPlay& DpType)
{
    m_pICa->SetDisplayMode(chooseDisplayMode(DpType));
}

const int Ca210real::chooseDisplayMode(const DisPlay& DpType) const
{
    return (int)DpType;
}

const CString Ca210real::GetDisplayMode(const DisPlay& DpType) const
{
    switch(DpType)
    {
    case DP_Lvxy:                return "xyLv";
    case DP_duvT:                return "duvTLv";    //return "duvT";
    case DP_Analyzer_NoDisplay:    return "RGB";        //return "Analyzer mode";
    case DP_Analyzer_Gstand:    return "RGB(G)";    //return "Analyzer mode(G)";
    case DP_Analyzer_Rstand:    return "RGB(R)";    //return "Analyzer mode(R)";
    case DP_dudv:               return "u'v'Lv'";    //return "u'v'";
    case DP_FMA:                return "FMA";
    case DP_XYZ:                return "XYZ";
    case DP_JEITA:              return "JEITA";
    default:                    return "No this Mode";
    }
}

const long Ca210real::GetDisplayMode() const
{
    return m_pICa->GetDisplayMode();
}
//////////////////////////////////////////////////////////////////////////

void Ca210real::SetDisplayDigits(const DisDigits& DdType)
{
    m_pICa->SetDisplayDigits(chooseDisplayDigits(DdType));
}

const int Ca210real::chooseDisplayDigits(const DisDigits& DdType) const
{
    switch(DdType)
    {
    case DD_3DisDigits: return 0;
    case DD_4DisDigits: return 1;
    default: return -1;
    }    
}

const CString Ca210real::GetDisplayDigits(const DisDigits& DdType) const
{
    switch(DdType)
    {
    case DD_3DisDigits: return "3";
    case DD_4DisDigits: return "4";
    default: return "Out of SPEC";
    }
}

const long Ca210real::GetDisplayDigits() const
{
    return m_pICa->GetDisplayDigits();
}
//////////////////////////////////////////////////////////////////////////
void Ca210real::SetAvgingMode(const AvgMode& AmType)
{
    m_pICa->SetAveragingMode(chooseAvgingMode(AmType));
}

const int Ca210real::chooseAvgingMode(const AvgMode& AmType) const
{
    switch(AmType)
    {
    case AM_SLOW: return 0;
    case AM_FAST: return 1;
    case AM_AUTO: return 2;
    default:      return -1;
    }    
}

const CString Ca210real::GetAvgingMode(const AvgMode& AmType) const
{
    switch(AmType)
    {
    case AM_SLOW: return "Slow";
    case AM_FAST: return "Fast";
    case AM_AUTO: return "Auto";
    default:      return "No this Mode";
    }
}

const long Ca210real::GetAvgingMode() const
{
    return m_pICa->GetAveragingMode();
}
//////////////////////////////////////////////////////////////////////////
void Ca210real::SetBrigUnit(const BrigUnit& BuType)
{
    m_pICa->SetBrightnessUnit(chooseBrigUnit(BuType));
}

const int Ca210real::chooseBrigUnit(const BrigUnit& BuType) const
{
    switch(BuType)
    {
    case BU_fL:   return 0;
    case BU_cdm2: return 1;
    default:      return -1;
    }    
}

const CString Ca210real::GetBrigUnit(const BrigUnit& BuType) const
{
    switch(BuType)
    {
    case BU_fL:   return "fL";
    case BU_cdm2: return "cd/m2";
    default:      return "No this Unit";
    }
}

const long Ca210real::GetBrigUnit() const
{
    return m_pICa->GetBrightnessUnit();
}

//////////////////////////////////////////////////////////////////////////
void Ca210real::SetCalStandard(const CalStand& CsType)
{
    m_pICa->SetCalStandard(chooseCalStandard(CsType));
}

const int Ca210real::chooseCalStandard(const CalStand& CsType) const
{
    switch(CsType)
    {
    case CS_6500K: return  1;
    case CS_9300K: return  2;
    default:       return -1;
    }
}

const CString Ca210real::GetCalStandard(const CalStand& CsType) const
{
    switch(CsType)
    {
    case CS_6500K: return "6500K";
    case CS_9300K: return "9300K";
    default:       return "Out of SPEC";
    }
}

const long Ca210real::GetCalStandard() const
{
    return m_pICa->GetCalStandard();
}

設定警告標示的標準格式

將錯誤訊息標準化的方式之一,就是將它做成專屬的函式或類別。在此使用相同的函式,使得CA-SDK複雜的錯誤訊息可以翻譯成讓使用者明白的文字訊息,只不過,有時候會有點囉嗦。
void Ca210real::MsgFrmt(CException* e, const CString& mean, const CString& steps)
{
    CString str;
    TCHAR buf[255];
    e->GetErrorMessage(buf, 255);
    
    str.Format("糟糕!!!出現ERROR!!!\n\n原廠錯誤訊息:\n%s\n\n看不懂沒關係,意思就是:\n「%s」\n\n你只要做:\n%s", buf, mean, steps);
    AfxMessageBox(str);
}

第二次的警告訊息

可以發現Measure和ZeroCal的功能中,都有一個迴圈將try-catch包起來,而第二次警告訊息總是精簡用字,因為發呆時被抓包,總是不會受到親切的訊息。
void Ca210real::MsgFrmt(const CString& steps)
{
    CString str;
    str.Format("你發呆嗎?\n%s", steps);
    AfxMessageBox(str);
}

class//Ca210sim

這可以在不需要連CA-210的情況之下,讓軟體可以執行並且進入量測畫面相關的部份(若使用Ca210real則一定要有儀器才可以連線,否則程式會停止執行)方便Demo展示以及加快debug速度,但是唯一無法利用這個類別進行debug的功能,就是nits追縱量測。

Ca210sim .h

#ifndef CA210SIM_H
#define CA210SIM_H

#include "CA210.h"

class Ca210sim : public Ca210
{    
    Bullet* m_pIProbe;

//simulation setup dialog item
    SynMode m_syncMode;
    DisPlay m_displayMode;
    DisDigits m_displayDigits;
    AvgMode m_averageMide;
    BrigUnit m_brightnessUnit;
    CalStand m_calStandard;

    CaState m_caState;
    CaState m_caStateTemp;
    CString str;
    
    CString ImpsbStr;
//////////////////////////////////////////////////////////////////////////
    void MsgFrmt(CException* , const CString&, const CString&){};
    void MsgFrmt(const CString&){};
public:
    Ca210sim();
    virtual ~Ca210sim();
    
public:
    const CaState CalZero();
    const CaState Measure();
    
             void LinkMemory();
    
    const MsrAiState MsrAI(const float& MsrDeviation = 0.0513);  //any constant value
    
             void SetOnline(const BOOL& b = TRUE);
    const    BOOL isOnline() const { return m_caState == CA_Offline ? FALSE : TRUE; };
    const CString GetLcmSize();
    const CString GetChData();
    const  Bullet GetMsrData();
    
//for setup Dialog Value
    const float GetRangeColor1() const;
    const float GetRangeColor2() const;
    const float GetRangeFAM()    const;
    
             void SetChId(const LPCTSTR &)    {  };
    const CString GetChId()             const {  return "SimCh"; };
    const CString GetChId(const long &) const {  return "SimCh"; };
    
             void SetChNo(const long &) {  };
    const CString GetChStrNo()          { return "0"; };
    const    long GetChNo() const       { return  0;  };
    
    const CString GetProb()       const { return "8115678";   };
    const CString GetDeviceType() const { return "SimCA-210"; };
    const CString GetCaVersion()  const { return "Simulator"; };
    
    const CString GetRefProbe() const;
    const CString GetCalProbe() const;
    const CString GetCalMode()  const;
    
    const CString GetRefLv() const;
    const CString GetRefSx() const;
    const CString GetRefSy() const;
    
    //for setup dialog combo box ItemString
    const CString GetSynMode      (const   SynMode&) const;
    const CString GetDisplayMode  (const   DisPlay&) const;
    const CString GetDisplayDigits(const DisDigits&) const;
    const CString GetAvgingMode   (const   AvgMode&) const;
    const CString GetBrigUnit     (const  BrigUnit&) const;
    const CString GetCalStandard  (const  CalStand&) const;
    
    //Get and Set of CA-SDK 
    void SetSynMode      (const   SynMode&); const float GetSynMode()       const;
    void SetDisplayMode  (const   DisPlay&); const long  GetDisplayMode()   const;
    void SetDisplayDigits(const DisDigits&); const long  GetDisplayDigits() const;
    void SetAvgingMode   (const   AvgMode&); const long  GetAvgingMode()    const;
    void SetBrigUnit     (const  BrigUnit&); const long  GetBrigUnit()      const;
    void SetCalStandard  (const  CalStand&); const long  GetCalStandard()   const;
    
#ifdef _CA210DEBUG
    const CString getSetupValue() const;
    void debugMessageBox(const CString&) const;
#endif
    
private:  //for setup CA-SDK parameter
    const float chooseSynMode      (const   SynMode&) const;
    const int   chooseDisplayMode  (const   DisPlay&) const;
    const int   chooseDisplayDigits(const DisDigits&) const;
    const int   chooseAvgingMode   (const   AvgMode&) const;
    const int   chooseBrigUnit     (const  BrigUnit&) const;
    const int   chooseCalStandard  (const  CalStand&) const;
};

#endif

Ca210sim .cpp

模擬抓值

量測這一個動作主要就是將量測值「生」出來。在此使用rand()生出這些值,所生出來的值只是模擬動作,並不能取代量測的動作,因為,不管你現在是什麼畫面,值都會差不多。
const CaState Ca210sim::Measure()
{
    CaState Mode = m_caState;
    if(isOnline())
    {
        // Make Data for m_pIProbe;
        srand(time(NULL));
        m_pIProbe->i(VluK_Lv, (rand()%100000)/100.0);    //%3.2f
        m_pIProbe->i(VluK_Sx, (rand()%10000 +1)/10000.0);  //%1.4f
        m_pIProbe->i(VluK_Sy, (rand()%10000 +1)/10000.0);  //%1.4f
        
        m_pIProbe->i(VluK_T, rand()%1000);//%3d
        m_pIProbe->i(VluK_Duv, (rand()%100000)/10000);  //%1.4f
        
        m_pIProbe->i(VluK_Du, (rand()%100000)/10000.0);  //%1.4f
        m_pIProbe->i(VluK_Dv, (rand()%100000)/10000.0);  //%1.4f
        
        m_pIProbe->i(VluK_X, (rand()%100000)/100.0);  //%3.2f
        m_pIProbe->i(VluK_Y, (rand()%100000)/100.0);  //%3.2f
        m_pIProbe->i(VluK_Z, (rand()%100000)/100.0);  //%3.2f
    }
    return Mode;
}

自動抓值量測

為了互動上的設計,出現了一種自己判斷目前是否可以抓值的程式段,降低使用者使用時操作的IO設備以及降低操作人手。
const MsrAiState Ca210sim::MsrAI(const float& MsrDeviation)
{
    //第一筆資料暫存空間  //宣告誤差值計算空間
    float XFristValue = 0.0, deltaX = 0.0,
          YFristValue = 0.0, deltaY = 0.0,
          ZFristValue = 0.0, deltaZ = 0.0, deltaAll;
    
    if (Measure() == CA_MsrMode)
    {
        //抓參考值
         XFristValue = m_pIProbe->oFlt(VluK_X);
         YFristValue = m_pIProbe->oFlt(VluK_Y);
         ZFristValue = m_pIProbe->oFlt(VluK_Z);
        
        Measure();
        
        //誤差取絕對值
         deltaX = ((XFristValue-m_pIProbe->oFlt(VluK_X))>=0) ? XFristValue - m_pIProbe->oFlt(VluK_X) : m_pIProbe->oFlt(VluK_X) - XFristValue;
         deltaY = ((YFristValue-m_pIProbe->oFlt(VluK_Y))>=0) ? YFristValue - m_pIProbe->oFlt(VluK_Y) : m_pIProbe->oFlt(VluK_Y) - YFristValue;
         deltaZ = ((ZFristValue-m_pIProbe->oFlt(VluK_Z))>=0) ? ZFristValue - m_pIProbe->oFlt(VluK_Z) : m_pIProbe->oFlt(VluK_Z) - ZFristValue;
        
        
        deltaAll = deltaX * deltaY * deltaZ;
        
        if (deltaAll < MsrDeviation )    return MA_InDeviation;
        else                             return MA_OutDeviation;
    }
    else
        return MA_nonMsr;
}

class//Ca210

這一個類別,是不亞於xlsfile的另一個厲害的類別。它除了讓CA-SDK的動作可以重新詮釋成CA-210的操作。還可以使用繼承的方式做出好多種不同的CA-210。這個什麼意思呢?請看它的兩個子類別(Ca210sim和Ca210real)就懂了!

這一個類別,本質上就只有皮。

Ca210.h

#ifndef CA210_H
#define CA210_H

#include "ca200srvr.h"
#include "Bullet.h"

//單獨控制CA-210模組是不是要進入 DEBUG模式
//#define _CA210DEBUG _DEBUG

enum SynMode   { SM_NTSC = 0, SM_PAL, SM_EXT, SM_UNIV, SM_INT};
enum DisPlay   { DP_Lvxy = 0, DP_duvT, DP_Analyzer_NoDisplay, DP_Analyzer_Gstand, DP_Analyzer_Rstand, DP_dudv, DP_FMA, DP_XYZ, DP_JEITA};
enum DisDigits { DD_3DisDigits = 0, DD_4DisDigits};
enum AvgMode   { AM_SLOW = 0, AM_FAST, AM_AUTO};
enum BrigUnit  { BU_fL =0, BU_cdm2 };
enum CalStand  { CS_6500K = 1, CS_9300K };

enum CaState    { CA_Offline, CA_not_yet_ZeroCal, CA_ZeroCalMode, CA_MsrMode};
enum MsrAiState { MA_nonMsr, MA_InDeviation, MA_OutDeviation };

class Ca210
{
    CString ImpsbStr;
    CaState m_caState;
    CaState m_caStateTemp;
protected:
    CString m_LCMsize;
    Bullet m_blt;
//////////////////////////////////////////////////////////////////////////
protected:
    virtual void MsgFrmt(CException* e, const CString&, const CString&) = 0;
    virtual void MsgFrmt(const CString&) = 0;

public:
    Ca210(): m_LCMsize(""){};
    virtual ~Ca210(){};

public:
    virtual const CaState CalZero() = 0;
    virtual const CaState Measure() = 0;
 
    virtual          void LinkMemory() = 0;
 
    virtual const MsrAiState MsrAI(const float&) = 0;
 
    virtual          void SetOnline(const BOOL&) = 0;
    virtual    const BOOL isOnline() const = 0;
                     void SetLcmSize(const CString& _S){ m_LCMsize = _S; };
    virtual const CString GetLcmSize() = 0;
    virtual const CString GetChData() = 0;
    virtual const Bullet  GetMsrData() = 0;
            const CString OutData();

 //for setup Dialog Value
    virtual const float GetRangeColor1() const = 0;
    virtual const float GetRangeColor2() const = 0;
    virtual const float GetRangeFAM()    const = 0;
 
    virtual          void SetChId(const LPCTSTR& )   = 0;
    virtual const CString GetChId()            const = 0;
    virtual const CString GetChId(const long&) const = 0;
 
    virtual          void SetChNo(const long&) = 0;
    virtual const    long GetChNo() const      = 0;
    virtual const CString GetChStrNo()         = 0;

    virtual const CString GetProb()       const = 0;
    virtual const CString GetDeviceType() const = 0;
    virtual const CString GetCaVersion()  const = 0;

    virtual const CString GetRefProbe() const = 0;
    virtual const CString GetCalProbe() const = 0;
    virtual const CString GetCalMode()  const = 0;
    
    virtual const CString GetRefLv() const = 0;
    virtual const CString GetRefSx() const = 0;
    virtual const CString GetRefSy() const = 0;

//for setup dialog combo box ItemString
    virtual const CString GetSynMode      (const   SynMode&) const = 0;
    virtual const CString GetDisplayMode  (const   DisPlay&) const = 0;
    virtual const CString GetDisplayDigits(const DisDigits&) const = 0;
    virtual const CString GetAvgingMode   (const   AvgMode&) const = 0;
    virtual const CString GetBrigUnit     (const  BrigUnit&) const = 0;
    virtual const CString GetCalStandard  (const  CalStand&) const = 0;

//Get and Set of CA-SDK 
    virtual void SetSynMode      (const   SynMode&) = 0; virtual const float GetSynMode()       const = 0;
    virtual void SetDisplayMode  (const   DisPlay&) = 0; virtual const long  GetDisplayMode()   const = 0;
    virtual void SetDisplayDigits(const DisDigits&) = 0; virtual const long  GetDisplayDigits() const = 0;
    virtual void SetAvgingMode   (const   AvgMode&) = 0; virtual const long  GetAvgingMode()    const = 0;
    virtual void SetBrigUnit     (const  BrigUnit&) = 0; virtual const long  GetBrigUnit()      const = 0;
    virtual void SetCalStandard  (const  CalStand&) = 0; virtual const long  GetCalStandard()   const = 0;
};

#endif

class//BkNits

對於亮度指定量測的背景,在這設計了自動追縱。
電腦只知道0~255的背景色,儀器知道這樣的背景色是多少ntis(亮度單位),我們可以指定我們要多少量nits的亮度,讓電腦調整背景色,並且同時量測,就可以做到自動調整。
在此追縱亮度量測的規格,會制定兩種
  1. 接近該亮度,未滿該亮度
  2. 接近該亮度,超過該亮度
以20nits為例:
  1. 就是19.9、19.8、19.7...的量測最大值
  2. 就是20.1、20.2、20.3...的量測最小值
因為背景色並不能精準的指定發光的亮度值,只能找一個近似的值,而這個近似值就有大於和小於兩種,但是都是找最接近的。

BkNits.h

#ifndef BKNITS_H
#define BKNITS_H

#include "BkMaker.h"

class BkNits : public BkMaker
{
    //m_pCA210
    void n2cPos(ColorRef&);
    void n2cNeg(ColorRef&);
public:
     BkNits(const NitsDirect&);
    BkNits(const BkNits& bkN);

    void NT_SetWnd(CWnd* cWnd){ m_pdlgcWnd = cWnd; };
    void NT_SetCa(Ca210* pCa){ m_pCa210 = pCa; };

    void NT_colorReviseByNits(ColorRef&);

    void NT_SetNitsLv(const int&);
    const int  NT_GetNitsLv()const;

};

#endif

BkNits.cpp

如何自動追縱亮度的實作?

在此若不設計自動追縱,而是靠手動追縱的話,還是一次使用兩個IO設備,不是Probe+Mouse就是Probe+Keyboard,不管哪一種組合都會降低人類操作的愉悅程度 以及增加手忙腳亂的程度。(我曾經是苦主)
所以,設計了自動追縱,但是,在程式碼的介面設計上,也是有用心著墨。
  • 可設定目標nits值,要追縱到什麼ntis就寫入什麼值。
  • 初始化時,立即設定要指定值以上,還是以下。
追縱的兩個函數,內容不一樣,初始化的背景值是hard coding每次都一樣。
要注意的是,兩個迴圈的累加計數不一樣。

BkNits::BkNits(const NitsDirect& nt)
{
    BkMaker::BkMaker();
    m_NitsDirect = nt;
};

void BkNits::NT_SetNitsLv(const int& _N)
{ m_NitsLv = _N*100; };

inline void BkNits::NT_colorReviseByNits(ColorRef& Bkclr)
{
    switch(m_NitsDirect)
    {
    case ND_Pos: n2cPos(Bkclr); break;
    case ND_Neg: n2cNeg(Bkclr); break;
    default:     ASSERT(0);
    }
};

void BkNits::n2cPos(ColorRef& Bkclr)
{
    int _gl= 60;
    int fLv = 100*100;
    while(fLv > m_NitsLv)  //若亮度還沒有到xx以下,就減少
    {
        _gl -= 2;
        Bkclr.iGray(_gl);
        m_pdlgcWnd->Invalidate();
        m_pdlgcWnd->UpdateWindow();
        //量測抓值
        if (m_pCa210->Measure() == CA_ZeroCalMode)
            AfxMessageBox("警告:Probe請轉到MEAS");
        fLv = (int)((m_pCa210->GetMsrData().oFlt(VluK_Lv))*100);
    }
    
    while(fLv < m_NitsLv)   //若亮度還在xx以下,就...變亮
    {
        ++_gl;
        Bkclr.iGray(_gl);
        m_pdlgcWnd->Invalidate();
        m_pdlgcWnd->UpdateWindow();
        Sleep(60);
        //抓值
        m_pCa210->Measure();
        fLv = (int)((m_pCa210->GetMsrData().oFlt(VluK_Lv))*100);
    }
    _SetBkColor(Bkclr);
}

void BkNits::n2cNeg(ColorRef& Bkclr)
{
    int fLv = 0;
    int _gl = 55;
    
    while(fLv < m_NitsLv)  //若亮度還沒有到xx以下,就減少
    {
        _gl += 2;
        Bkclr.iGray(_gl);
        m_pdlgcWnd->Invalidate();
        m_pdlgcWnd->UpdateWindow();
        //抓值
        if (m_pCa210->Measure() == CA_ZeroCalMode)
            AfxMessageBox("警告:Probe請轉到MEAS");
        fLv = (int)((m_pCa210->GetMsrData().oFlt(VluK_Lv))*100);
    }
    
    while(fLv > m_NitsLv)   //若亮度還在xx以下,就...變亮
    {
        --_gl;
        Bkclr.iGray(_gl);
        m_pdlgcWnd->Invalidate();
        m_pdlgcWnd->UpdateWindow();
        Sleep(60);
        //量測抓值
        m_pCa210->Measure();
        fLv = (int)((m_pCa210->GetMsrData().oFlt(VluK_Lv))*100);
    }
    _SetBkColor(Bkclr);
}

class//BkMaker

其實,我一直覺得,這一個類別的創造是好設計,但是這一個類別的內部,是不好的設計,因為創造它的過程,是一次的程式大改。為了讓Cartridge2內可以記載的背景資訊,留下一個一致的介面讓CPartternDialog啟動背景繪圖,剩下的細節都交給它。
  • 一個ColorRef是背景色,每一個背景都適用。
  • 一個ColorRef是給CrossTalk中間的方塊。
  • Ca210*是給Nits做自動亮度追縱用的。
在此有一個特別的要講一下,就是BkMaker本身是一個SmartPointer,可以計數自己指向的物件數,讓nits的點位,可以都用同一個BkMaker物件(中心點以外的背景色和中心點追縱到的背景色為同一筆資料),並且不會隨著Cartridge2物件複製(只複製BkMaker的address)而銷毀BkMaker的物件。

最後,可以發現,在*.h檔中,罕見的出現了空函數。這樣的設計在於這些特殊的函數,必須在BkMaker有virtual的介面,可以配合「繼承」使用「動態連結」的方式操作其它的子類別的函式實作。

BkMaker.h

#ifndef BKMAKER_H
#define BKMAKER_H

#include "CColorRef.h"
#include "CA210.h"

enum NitsDirect{ND_NoDefine = 0, ND_Pos, ND_Neg};

class BkMaker
{
//smart ptr
    int* ptr_i;
    int* cnt;
public:
    const BOOL IsPtrToNull() const;
    const int GetPtrCnt() const;
public:
    BkMaker();
    BkMaker(const BkMaker& bkN);
    ~BkMaker();
    BkMaker& operator=(const BkMaker& sp);

//背景色
private:
    ColorRef m_BkColor;
public:
    const ColorRef _GetBkColor() const;
              void _SetBkColor(const ColorRef&);
//共用介面
public:
    virtual void Draw(CPaintDC& dc);

//Crosstalk的介面
protected:
    ColorRef  m_rectColor;
    CRect     m_centerRect;
public:
    virtual void CT_SetRect(const CRect& _rect, const ColorRef& clr){ };

//Nits的介面
protected:
    CWnd*  m_pdlgcWnd;
    Ca210* m_pCa210;
public:
    virtual void NT_SetWnd(CWnd* cWnd){ };
    virtual void NT_SetCa(Ca210* pCa){ };

protected:
    NitsDirect m_NitsDirect;
    int m_NitsLv;  //參數
public:
    virtual    void NT_SetNitsLv(const int&){  };
    virtual    void NT_colorReviseByNits(ColorRef&){  };

};

#endif

class//RNA

裝載著每一個點特有參數的Cartridge2,將它串成一串vector,包成RNA(因為要由DNA轉錄出來),一些特定的點集成一串(9個點集成一串),就成為一個量測項目(白色9點),它可以由vector::iterator的操作,來切換量測點位,並且排序成特別的順序。
RNA主要是將vector<Cartridge2>包起來,並且可以操作「除了vector之外的相關操作」。而它最特別的操作,就是取值這件事。

為了將某特定點的特定資料「找出來」,在此是使用CartridgeFinder重建關鍵字,再將關鍵字放進for迴圈對整串走訪,做字串的比對,問題就在於,萬一找不到,要返回什麼值?在下面會有詳細的解說以及Sample code

另外,有一個防呆設計
sFind和fFind,是回傳條件搜尋的結果,只是這兩個函數的關鍵字都需要加上雙引號。有時忘記加雙引號,在此,將錯用的介面設計出來,在函數的實作上做assert(),可以將錯誤突顯出來並更正,加上條件編譯,不影響release的code大小。

RNA.h

#ifndef RNA_H
#define RNA_H

#include <vector>
#include <iterator>
#include "Cartridge2.h"
#include "CartridgeFinder.h"
#include "TxtFile.h"
#include "debugFile.h"

class RNA
{
    std::vector<Cartridge2> m_CarChain2;
    CString m_desktopPath;

//建解構子
public:
    RNA();
    virtual ~RNA();

//完全不像vector的操作
public:
    void CutEqualCell(const RNA& );
    void DeleteEmptyCell();

public:
    std::vector<int>::size_type HaveSeveral(const CString& color   = "", const CString& ptTotal = "", 
                                            const CString& mrIndex = "", const CString& fePara  = "");
    float   fFind(const CString&, const CString&, const CString&, const CString&, const ValueKind&);
    CString sFind(const CString&, const CString&, const CString&, const CString&, const ValueKind&);
#ifdef _DEBUG
public:
    float   fFind(const CString&, const int&    , const CString&, const CString&, const ValueKind&);
    CString sFind(const CString&, const int&    , const CString&, const CString&, const ValueKind&);
    float   fFind(const CString&, const CString&, const int&    , const CString&, const ValueKind&);
    CString sFind(const CString&, const CString&, const int&    , const CString&, const ValueKind&);
    float   fFind(const CString&, const int&    , const int&    , const CString&, const ValueKind&);
    CString sFind(const CString&, const int&    , const int&    , const CString&, const ValueKind&);
private:
    void paraError();
#endif
public:
    Cartridge2& Find(const CString&, const CString&, const CString&, const CString&);
    Cartridge2& Find(const CString& );
public:
    void Empty(); 
    const BOOL IsEmpty() const;
    
//像vector的函數
public:
    const Cartridge2& At(const std::vector<Cartridge2>::size_type& _P) const;

    std::vector<Cartridge2>::iterator       Begin();
    std::vector<Cartridge2>::const_iterator Begin() const;
    
    std::vector<Cartridge2>::iterator       End();
    std::vector<Cartridge2>::const_iterator End() const;

    const std::vector<Cartridge2>::size_type Size() const;
    void AddCell(const RNA&);
    void AddCell(const Cartridge2& _X);

//sorting
public:
     void SortQuackMsr();
     void SortOrigMsr();
private:
    static bool areaPriority(const Cartridge2 &sp1, const Cartridge2 &sp2);
    static bool origPriority(const Cartridge2 &sp1, const Cartridge2 &sp2);
    
//operator
public:
    void operator+=(const RNA& _R);
    void operator+=(const Cartridge2&  _X);

//search
private:
    CartridgeFinder finder;

//debug
private:
    debugFile debugLogMaker;
};

#endif

RNA.cpp

x設計及其修正設計

在RNA設計了一個隱藏的元素x,在RNA的construct裡push_back進入m_CarChain2 (vector<Cartridge2>),若找不到這個元素,就返回這一個Cartridge x,它裡面的值都是垃圾值。為了對應x的設計,Begin()、Size()、Empty()都另外做了修正設計。
RNA::RNA()
{
    Cartridge2 x;
    m_CarChain2.push_back(x);
}

std::vector<Cartridge2>::iterator RNA::Begin()
{
    return (m_CarChain2.size() <= 1) ? m_CarChain2.end() : m_CarChain2.begin() +1;
}

const std::vector<Cartridge2>::size_type RNA::Size () const
{
    return m_CarChain2.size()-1;
};

void RNA::Empty()
{
    m_CarChain2.clear();
    Cartridge2 x;
    m_CarChain2.push_back(x);
}

簡化增加元素的介面

用一致的介面來表達一致的概念,即使C++的STL是使用不同的介面,也要用overload包成相同的介面。
在此的概念:可以增加一個量測點位,也可以增加一個量測項目。
void RNA::AddCell(const RNA& _R)
{
    m_CarChain2.insert(End(), _R.Begin(), _R.End());
}

void RNA::AddCell(const Cartridge2& _X)
{
    m_CarChain2.push_back(_X);
}

剪掉相同的元素

這是一個STL沒有而需要的功能,量測點不重覆,再新增進來的相同點,必須是覆蓋原有的量測點。所以,在新增量過的量測點進來之前,要先去除掉原有而相同的量測點。在此有兩個版本,其實都一樣,一個是主要功能版(較少的那一段),另一個是主要功能+程式記錄功能,幫助debug用的。
void RNA::CutEqualCell(const RNA& compData)
{
    if (!compData.IsEmpty())//裡面這些不要修改,影響再次量測的資料擺放
    {
        //在這時m_CarChain2是舊, compData是新的
        std::vector::const_iterator rnaitor = 0, compItor;
        //remove & cut 在新的裡面,比對舊的,代表重覆,重覆量測去除掉
        std::vector::iterator removeItor(End());

        for (compItor = compData.Begin(); compItor != compData.End(); ++compItor)
            //移動一個元素到最後,就刪掉
            removeItor = std::remove(Begin(), removeItor, *compItor);//確認compData的資料是沒錯的
        
        m_CarChain2.erase(removeItor, End());
    }
}
加入一些debug的code,讓程式的運作有記錄,利用條件編譯的技巧讓複雜的debug code可以一直留著,不影響release的效能。
void RNA::CutEqualCell(const RNA& compData)
{
    if (!compData.IsEmpty())//裡面這些不要修改,影響再次量測的資料擺放
    {
        CString str;
        debugLogMaker.Clear();
        //在這時m_CarChain2是舊, compData是新的
        std::vector::const_iterator rnaitor = 0, compItor;
        debugCode(
            str.Format("Begin: %X, End: %X\n", Begin(), End());
            debugLogMaker.Add(str);

            debugLogMaker.Add("\nRNA原本的位址\n");
                for (rnaitor  = m_CarChain2.begin();
                     rnaitor != m_CarChain2.end(); ++rnaitor)
                {
                    str.Format("%X, c(%d, %d, %d), P(%d, %d), status: %s\n",\
                        rnaitor,\
                        rnaitor->GetBkColor().R(), rnaitor->GetBkColor().G(), rnaitor->GetBkColor().B(),\
                        rnaitor->GetPointPosi().x, rnaitor->GetPointPosi().y,\
                        rnaitor->GetStrBkStatus()); 
                    debugLogMaker.Add(str);
                }

            debugLogMaker.Add("\ncompData的位址\n");
                for (compItor = compData.Begin(); compItor != compData.End(); ++compItor)
                {
                    str.Format("%X, c(%d, %d, %d), P(%d, %d), status: %s\n",\
                        compItor,\
                        compItor->GetBkColor().R(), compItor->GetBkColor().G(), compItor->GetBkColor().B(),\
                        compItor->GetPointPosi().x, compItor->GetPointPosi().y,\
                        compItor->GetStrBkStatus()); 
                    debugLogMaker.Add(str);
                }
        )
        //remove & cut 在新的裡面,比對舊的,代表重覆,重覆量測去除掉
        debugCode(
            str.Format("\nremoveItor:\n");
            debugLogMaker.Add("\nremoveItor:\n");
        )
        std::vector::iterator removeItor(End());
        for (compItor = compData.Begin(); compItor != compData.End(); ++compItor)
        {
            //移動一個元素到最後,就刪掉
            debugCode(
                str.Format("B. %X\n", removeItor);
                debugLogMaker.Add(str);
            )
            removeItor = std::remove(Begin(), removeItor, *compItor);//確認compData的資料是沒錯的

            debugCode(
                str.Format("A. %x\n", removeItor);
                debugLogMaker.Add(str);
            )
        }
        
        m_CarChain2.erase(removeItor, End());

        debugCode(
            debugLogMaker.Add("\nRNA後來的位址\n");
            for (rnaitor = m_CarChain2.begin();
                 rnaitor != m_CarChain2.end(); ++rnaitor)
            {
                str.Format("%X, c(%d, %d, %d), P(%d, %d), status: %s\n",\
                    rnaitor,\
                    rnaitor->GetBkColor().R(), rnaitor->GetBkColor().G(), rnaitor->GetBkColor().B(),\
                    rnaitor->GetPointPosi().x, rnaitor->GetPointPosi().y,\
                    rnaitor->GetStrBkStatus());
                debugLogMaker.Add(str);
            }
        )
    }

    CColorEyeIApp* pApp = dynamic_cast(AfxGetApp());
    ASSERT_VALID(pApp);

    DebugCode( m_desktopPath.Format("%s", pApp->GetDesktopPath()); )
    debugLogMaker.Out2File(m_desktopPath+"//RNA_address.log");
}

去除空的元素

這是一個STL沒有而需要的功能,先辨別出什麼是空的,串成一串,再丟進剪掉相同的元素
void RNA::DeleteEmptyCell()
{
    RNA ready2Cut;
    for (std::vector::iterator itor = Begin(); itor != End(); ++itor)
    {
        if (itor->GetBullet().IsEmpty() == TRUE)
            ready2Cut.AddCell(*itor);           //要剪掉的
    }
    CutEqualCell(x);
}

用精準Search、條件Search出想要的元素

三個功能
  1. 有沒有存在: 回傳存在幾個
  2. 條件搜尋: 回傳搜尋到的元素,並且檢查是否唯一
  3. 精準搜尋: 回傳搜尋到的元素,並且檢查是否唯一
std::vector::size_type RNA::HaveSeveral(const CString& color, const CString& ptTotal, const CString& mrIndex, const CString& fePara)
{
    CString clr(color);
    CString ptTtl(ptTotal);
    CString mrIdx(mrIndex);
    CString fePr(fePara);
    
    std::vector vSub;

    if ( finder.ReconstrKeyWord(clr, ptTtl, mrIdx, fePr) )
    {
        for (std::vector::iterator itor = m_CarChain2.begin(); itor != m_CarChain2.end(); ++itor)
        {
            if ( (itor->GetDescrip().Find(clr)   >= 0) && (itor->GetDescrip().Find(ptTtl) >= 0) && 
                 (itor->GetDescrip().Find(mrIdx) >= 0) && (itor->GetDescrip().Find(fePr)  >= 0)  )
                vSub.push_back(itor - m_CarChain2.begin());
        }
    }
    return vSub.size();
}

Cartridge2& RNA::Find(const CString& color, const CString& ptTotal, const CString& mrIndex, const CString& fePara)
{
/*    
將關鍵字切成  1. x色, 2. x點, 3. 第x點, 4. 離邊參數   三個部份
然後重建成三個詞(三個函數)
再進行交集搜尋(試試可以可以使用遞迴)
*/
    CString clr(color);
    CString ptTtl(ptTotal);
    CString mrIdx(mrIndex);
    CString fePr(fePara);

    CString sample;
    std::vector vSub;
    if (finder.Reconstr(clr, ptTtl, mrIdx, fePr))
    {
        sample.Format("%s%s%s%s", clr, ptTtl, mrIdx, fePr);
        for ( std::vector::const_iterator itor = m_CarChain2.begin(); itor != m_CarChain2.end(); ++itor)
        {    
            if ( itor->GetDescrip().Find(sample) >= 0 )
            {
                CString debugCharPtr = itor->GetDescrip();
                vSub.push_back(itor - m_CarChain2.begin());
            }
        }
    }
    if (vSub.size() == 1)
        return m_CarChain2.at(vSub.at(0));
    else
        return m_CarChain2.at(0);
}

Cartridge2& RNA::Find(const CString& sample)
{
/*    
將關鍵字切成  1. x色, 2. x點, 3. 第x點, 4. 離邊參數   三個部份
然後重建成三個詞(三個函數)
再進行交集搜尋(試試可以可以使用遞迴)
*/
    CString smple(sample);
    std::vector vSub;
    if (finder.Reconstr(smple))
    {
        for (std::vector::const_iterator itor = m_CarChain2.begin(); itor != m_CarChain2.end(); ++itor)
        {    
            //if (sample == itor->GetDescrip())
            if ( itor->GetDescrip().Find(smple) >= 0 )
                vSub.push_back(itor - m_CarChain2.begin());
        }
    }
    if (vSub.size() == 1)
        return m_CarChain2.at(vSub.at(0));
    else
        return m_CarChain2.at(0);
}

class//CXlsTable

Visual C++6對於動態連結的語法是不標準的,它必須return父類別,而不是自己這個類別,但是在實作上,return this就可以。

CXlsTable.h

// CXlsTable.h: interface for the CXlsTable class.
#ifndef CXLSTABLE1
#define CXLSTABLE1

#include "XlsFile2.h"

class CXlsTable : public CXlsFile2
{
  int  m_CellNO;
  void idW9();
  void idAvg();
  void idMedian();
  void idMax();
  void idMin();

public:
  void InitForm();

  CXlsFile2* iCellNO(std::vector<Cartridge2>::size_type);

  CXlsFile2* iChannel(CString, std::vector<Cartridge2>::size_type );
  CXlsFile2* iProb   (CString, std::vector<Cartridge2>::size_type );
  CXlsFile2* iData   (RNA&   , std::vector<Cartridge2>::size_type );

  CXlsFile2* iChannel(CString );
  CXlsFile2* iProb   (CString );
  CXlsFile2* iData   (RNA&    );

  CXlsFile2* operator->() { return this; };
};

#endif

CXlsTable.cpp

準備表格(畫出表格格線、顏色)

在這裡是每一個表格都會有的動作,表格初始化,將表格的格線和顏色標示上去。若是無格線的table,則將此函數設計成空函數。
void CXlsTable::InitForm()
{
    int cell_count = 0;
    
    SelectCell("A1","E3")->SetMergeCells()->SetCellBorder(1, 3);
    SelectCell("F1","AM1")->SetMergeCells()->SetCellColor(35)->SetCellBorder(1, 3)->SetFontSize(24)->SetCell(/*Table title...*/);
    SelectCell("F2","AM2")->SetMergeCells()->SetCellColor(39)->SetCellBorder(1, 3)->SetFontSize(14)->SetCell("WHITE-9點");
    
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("Cell NO");
//...
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("量測日期");
    
    SelectCell("F3","H3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(1);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    SelectCell("I3","K3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(2);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    SelectCell("L3","N3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(3);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    SelectCell("O3","Q3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(4);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    SelectCell("R3","T3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(5);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    SelectCell("U3","W3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(6);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    SelectCell("X3","Z3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(7);
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell((char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    //----------------------------------------------------------
    cell_count = 0;
    SelectCell("AA3","AC3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(8);
    SelectCell('A', (char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell('A', (char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell('A', (char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    
    SelectCell("AD3","AF3")->SetMergeCells()->SetCellColor(6)->SetCellBorder(1, 3)->SetCell(9);
    SelectCell('A', (char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("x");
    SelectCell('A', (char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCell("y");
    SelectCell('A', (char)('A'+cell_count++), 4)->SetCellBorder(1, 3)->SetCellColor(41)->SetCell("L");
    
    SelectCell('A', (char)('A'+cell_count), 3, 'A', (char)('A'+cell_count), 4)->SetMergeCells()->SetCellColor(40)->SetCellBorder(1, 3)->SetCell("CA210-CH");    cell_count++;
    SelectCell('A', (char)('A'+cell_count), 3, 'A', (char)('A'+cell_count), 4)->SetMergeCells()->SetCellColor(40)->SetCellBorder(1, 3)->SetCell("CA210-Probe"); cell_count++;
    SelectCell('A', (char)('A'+cell_count), 3, 'A', (char)('A'+cell_count), 4)->SetMergeCells()->SetCellColor(40)->SetCellBorder(1, 3)->SetCell("Avg");         cell_count++;
    SelectCell('A', (char)('A'+cell_count), 3, 'A', (char)('A'+cell_count), 4)->SetMergeCells()->SetCellColor(40)->SetCellBorder(1, 3)->SetCell("Median");      cell_count++;
    SelectCell('A', (char)('A'+cell_count), 3, 'A', (char)('A'+cell_count), 4)->SetMergeCells()->SetCellColor(40)->SetCellBorder(1, 3)->SetCell("Max");         cell_count++;
    SelectCell('A', (char)('A'+cell_count), 3, 'A', (char)('A'+cell_count), 4)->SetMergeCells()->SetCellColor(40)->SetCellBorder(1, 3)->SetCell("Min");         cell_count++;
}

擬操作Excel動作的程式碼

讓程式碼本身,就像是人類用電話告訴操作者如何操作Excel一樣,這一切的一切的設計都是為了這一刻!這一點很重要,在程式語言的設計上,可以達到這一點並且善用的,真的會讓程式碼用起來有一種很爽的感覺。
CXlsFile2* CXlsTable::iData(RNA& vCar)
{
    m_vCar = vCar;
    
    if (vCar.HaveSeveral("白","9","","1/4"))  idW9();
    idAvg();
    idMedian();
    idMax();
    idMin();

    SelectSheet(1);
    SelectCell("A", 5, "AM", 5+m_CellNO)->SetCellBorder(1, 3)->SetHorztlAlgmet(HA_CENTER);    //設定對齊方式
    SelectCell("A1","AM1")->AutoFitWidth();    //最適欄寬
    SetVisible(true);

//    SelectCell("A", 5, "AM", 5+m_CellNO)->Sort("B5",1,"C5",1);    //排序
//    SelectCell("A1","E3")->InsertImage(CurrentPath, 141, 40);    //貼Logo
    return this;
}

void CXlsTable::idAvg()
{
    SelectSheet(1);
    CString strTemp;
    strTemp.Format("=SUM(AF%d,AC%d,Z%d,W%d,T%d,Q%d,N%d,K%d,H%d)/9",\  //填公式
        5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO);
    SelectCell("AI", 5+m_CellNO)->SetCellBorder(1, 3)->SetCell(strTemp);  //Avg
}

void CXlsTable::idMedian()
{
    SelectSheet(1);
    CString strTemp;
    strTemp.Format("=T%d", 5+m_CellNO);
    SelectCell("AJ", 5+m_CellNO)->SetCellBorder(1, 3)->SetCell(strTemp); //Median
}

void CXlsTable::idMax()
{
    SelectSheet(1);
    CString strTemp;
    strTemp.Format("=MAX(AF%d,AC%d,Z%d,W%d,T%d,Q%d,N%d,K%d,H%d)",\  //填公式
        5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO);
    SelectCell("AK", 5+m_CellNO)->SetCellBorder(1, 3)->SetCell(strTemp); //Max
}

void CXlsTable::idMin()
{
    SelectSheet(1);
    CString strTemp;
    strTemp.Format("=MIN(AF%d,AC%d,Z%d,W%d,T%d,Q%d,N%d,K%d,H%d)",\  //填公式
        5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO, 5+m_CellNO);
    SelectCell("AL", 5+m_CellNO)->SetCellBorder(1, 3)->SetCell(strTemp); //Min
}

class//CXlsFile2

這個類別,主要的目的在於不要修改xlsfile,又重新包裝它的動作,簡化成可以做動態連結的function。

此類別的動作,設計成簡單固定統一的做法。讓化可以實現Design Pattern中的工廠模式。
此類別設計了許多純虛擬函式,原因是在於它本是一個抽象的概念表示,無法實作成物件。
此類別再繼承下去,就是個別的表格做的動作,在此作品集就不公開,但是會有示範其程式碼,提供參考。

CXlsFile2.h

#ifndef XLSFILE2
#define XLSFILE2

#include "xlef.h"
#include "RNA.h"

class CXlsFile2 : public xlsFile
{
protected:
    RNA m_vCar;
public:
    virtual void       InitForm() = 0;
    virtual CXlsFile2* iCellNO (std::vector<Cartridge2>::size_type) = 0;

    virtual CXlsFile2* iPanelID(CString, std::vector<Cartridge2>::size_type ) = 0;
    virtual CXlsFile2* iProb   (CString, std::vector<Cartridge2>::size_type ) = 0;
    virtual CXlsFile2* iChannel(CString, std::vector<Cartridge2>::size_type ) = 0;
    virtual CXlsFile2* iNitsLv (CString, std::vector<Cartridge2>::size_type ) = 0;
    virtual CXlsFile2* iData   (RNA&   , std::vector<Cartridge2>::size_type ) = 0;

    virtual CXlsFile2* iPanelID(CString) = 0;
    virtual CXlsFile2* iProb   (CString) = 0;
    virtual CXlsFile2* iChannel(CString) = 0;
    virtual CXlsFile2* iNitsLv (CString) = 0;
    virtual CXlsFile2* iData   (RNA&   ) = 0;
};

#endif

class//[轉錄]xlsFile

此篇為全文轉錄。
原文來自用MFC的手操作「真.Excel」-《Darkblack》

這篇文章提及的程式碼,最早是由 自動產生Excel-流浪小築 學來的,這個網站除了這個,還有很多相關的技術可以學唷!很棒的前輩。

如果把Excel的OLE加到MFC的Project也請參考那篇文章就可以了(請務必完全照做一次)。
之後對於xlsFile的了解會更有幫助。

Excel的程式控制,似乎是辦公室應用裡算是高階的應用。用C++幾乎就是天一般的高度呀(哈哈)畢竟語法細節太多,整個類別全部使用的話,複雜度又太恐怖了。
雖然網路上有一個包好的XLSLIB可以使用,但是它....不適合我,我就自己做了一個。

這是以操作Excel的概念去設計的類別。每一個函數都盡可能的簡單好用。
只是最後插入圖表的部份因為我個人沒有在使用的關係,所以沒有做概念上的最佳化。
個人建議使用Design Pattern的組合模式來實現。

這個類別有公佈在PTT的CandCpp版、Google code、Github中。
在ptt獲得網友Edison.Shin的支援,讓它的功能更豐富。在此完全公開其程式碼提供大家使用。

xlsFile.h

/*******************************************************************
 *                                                                 *
 * 此class由kx設計,並發佈初版                                        *
 * 二版則由Edison.Shih.提供函式,補足初版之不足                        *
 *                                                                 *
 * 由Edison.Shih.提供的函式補足,會做edisonx的記號                    *
 *                                                                 *
 * Class由 Visual C++ 6 開發                                        *
 * 適用於Microsoft Excel 2003 於 Microsoft Excel 2003 SP3 測試無誤   *
 * 測試平台 Microsoft Windows XP SP3                                *
 *                           2011/7/11                             *
 *******************************************************************/
#ifndef XLSFILE_H
#define XLSFILE_H

#include "excel.h"
#include <vector>

enum Boarder_Style
{
    BS_NONE    = 0,      //無框線
    BS_SOLIDLINE,        //一般線
    BS_BIGDASH,          //小間隔虛線- - - - - -有粗細
    BS_SMALLDASH,        //大間隔虛線- - - - - -無粗細
    BS_DOTDASH,          //虛線-.-.-.-.-.-.
    BS_DASHDOTDOT,       //虛線.-..-..-..-..-.
    BS_DOUBLSOLID = 9,   //雙線============(不受粗細改變)
    BS_SLASHDASH  = 13   //雙線-/-/-/-/-/-/(不受粗細改變)
};
enum Boarder_Weight    //(粗細)
{
    BA_HAITLINE = 1,     //比一般小(所以用虛線表示)
    BA_THIN,             //一般
    BA_MEDIUM,           //粗
    BA_THICK             //厚
};
enum Horizontal_Alignment
{
    HA_GENERAL = 1,
    HA_LEFT,           //edisonx
    HA_CENTER,
    HA_RIGHT,          //edisonx
    HA_FILL,           //重複至填滿    //edisonx
    HA_JUSTIFYPARA,    //段落重排(有留白邊,有自動斷行)
    HA_CENTERACROSS,   //跨欄置中(不合拼儲存格)
    HA_JUSTIFY,        //分散對齊(縮排)
};

enum Vertical_Alignment
{
    VA_TOP = 1,        //edisonx
    VA_CENTER,         //edisonx
    VA_BOTTOM,         //edisonx
    VA_JUSTIFYPARA,    //段落重排(有留白邊,有自動斷行)
    VA_JUSTIFY         //分散對齊
};

enum Histogram_Chart_Type
{
    CT_AREA = 0,       //區域
    CT_COLUMN,         //方柱
    CT_CONE,           //圓錐
    CT_CYLINDER,       //圓柱
    CT_PYRAMID         //金字塔
};

enum Stock_Type
{
    ST_HLC = 0,    //最高-最低-收盤
    ST_OHLC,       //開盤-最高-最低-收盤
    ST_VHLC,       //成交量-最高-最低-收盤
    ST_VOHLC       //成交量-開盤-最高-最低-收盤
};

class xlsFile
{
protected:
    COleVariant   VOptional, VTRUE, VFALSE;  
    _Application  objApp;
     Workbooks    objBooks;
    _Workbook     objBook;
     Sheets       objSheets;
    _Worksheet    objSheet,objSheetT;
    Range         range,col,row;
    Interior      cell;
    Font          font;
    COleException e;

    LPDISPATCH    lpDisp;
    ChartObjects  chartobjects;
    ChartObject   chartobject;
    _Chart        xlsChart;
    VARIANT       var;
    
    Shapes        shapes;

    char buf[200];  //暫存的字串
    char buf1[200];
    char buf2[200];
      
public:
    xlsFile();
    ~xlsFile();
    //回傳xlsFile* //開了檔案之後可以繼續選擇Sheet和命名
    xlsFile* New();
    xlsFile* Open(const char*);
    void SaveAs(const char*);
    void Save();
    
    void Quit();
    void SetVisible(bool);//設定視窗為看得見,並把控制權交給使用者
    //----------------------------------------------------
    //Sheet操作
    long SheetTotal();                     //取得 Sheet 個數
    void SetSheetName(short, const char*); //由SheetNumber    指定SheetName
    CString GetSheetName(short);           //由SheetNumber    取得SheetName
    
    xlsFile* SelectSheet(const char*);     //由SheetName      選擇Sheet
    xlsFile* SelectSheet(short);           //由SheetNumber    選擇Sheet
    void CopySheet(const char*);           //複製SheetName    指定插入Sheet的位置,並指定新Sheet名稱
    void CopySheet(short);                 //複製SheetNumber  指定插入Sheet的位置,並指定名稱
    void DelSheet(const char*);            //選SheetName      刪除Sheet
    void DelSheet(short);                  //選SheetNumber    刪除Sheet
    //-----------------------------------------------------
    //
    long GetHorztlStartCell(); // 起始行
    long GetVrticlStartCell(); // 起始列
    long GetHorztlTotalCell(); // 總行數
    long GetVrticlTotalCell(); // 總列數
    //-----------------------------------------------------
    //回傳xlsFile* 選了格子之後可以繼續下「讀」「寫」的成員函數
    //選一格
    xlsFile* SelectCell(const char* );
    xlsFile* SelectCell(const char* , int );
    xlsFile* SelectCell(char,int);
    xlsFile* SelectCell(char,char,int);
    //選一個範圍
    xlsFile* SelectCell(const char* , const char* );
    xlsFile* SelectCell(const char* , int ,const char* , int );
    xlsFile* SelectCell(char,int,char,int);
    xlsFile* SelectCell(char,char,int,char,char,int);
    //--------------------------------------------
    void ClearCell();                           //清除儲存格
    xlsFile* SetMergeCells(short vMerge = TRUE, //合併儲存格(通常會配跨欄置中)
                           bool isCenterAcross = true);
    //--------------------------------------------
    //對齊
    xlsFile* SetHorztlAlgmet(short);    //水平對齊
    xlsFile* SetVrticlAlgmet(short);    //垂直對齊
    xlsFile* SetTextAngle(short Angle); //方向-文字角度
    xlsFile* AutoNewLine(bool NewLine); //自動換行
    //---------------------------------------------
    //格線
    xlsFile* SetCellBorder(long BoarderStyle = 1, 
    int BoarderWeight = 2, long BoarderColor = 1);  //設定框線粗細和顏色
    //---------------------------------------
    //儲存格大小
    void AutoFitHight();           //自動調整列高
    void AutoFitWidth();           //自動調整欄寬
    xlsFile* SetCellHeight(float); //設定列高
    xlsFile* SetCellWidth(float);  //設定欄寬
    //---------------------------------------------
    //字
    xlsFile* SetFont(const char* fontType = "新細明體");  //設定字型(預設新細明體)
    xlsFile* SetFontBold(bool isBold = true);           //粗體
    xlsFile* SetFontStrkthrgh(bool isBold = true);      //刪除線
    xlsFile* SetFontSize(short fontSize = 12);          //設定字體大小(預設12pt)
    xlsFile* SetFontColor(short colorIndex = 1);        //字型顏色(預設黑色)
    //---------------------------------------------
    xlsFile* SetCellColor(short);//設定底色
    //---------------------------------------------
    //(17-32隱藏版也有收進來)
    //Microsoft Excel 的顏色排序是依
    //紅、橙、黃、綠、藍、靛、紫、灰(y),由深到淺(x)
    //不過絕對RGB並沒有規律的存在這個表裡
    short SelectColor(short x = 8, short y = 7);   //依excel介面的座標選擇顏色
    short SelectColor(const char ColorChar = 'W'); //快速版(黑D、白W、紅R、綠G、藍B、黃Y)
    //---------------------------------------------
    //設定資料進儲存格(存成字串)
    //一般版
    void SetCell(int);
    void SetCell(double);
    void SetCell(long);    
    void SetCell(const char* );    
    void SetCell(CString );    
    //自訂細部格式版
    void SetCell(const char*, int);
    void SetCell(const char*, double);
    void SetCell(const char*, long);
    //--------------------------------------------
    //取值
    int     GetCell2Int();
    CString GetCell2CStr();
    double  GetCell2Double();
    //--------------------------------------------
    //排序(依列排序)//edisonx
    void Sort(CString IndexCell1     , long DeCrement1,
              CString IndexCell2 = "", long DeCrement2 = 1,
              CString IndexCell3 = "", long DeCrement3 = 1);
    //--------------------------------------------

    //圖表皆由edisonx提供函數資料

    //儲存圖表圖片.bmp(.jpg亦可以)
    void SaveChart(CString FullBmpPathName);

    //圖表(三類型的函數在每次建立都要使用)
    //使用前必須選擇貼上Chart的儲存格範圍
    
    //選擇資料範圍
    xlsFile* SelectChartRange(const char* , const char* );    
    xlsFile* SelectChartRange(const char* , int ,const char* , int );
    xlsFile* SelectChartRange(char,int,char,int);    
    xlsFile* SelectChartRange(char,char,int,char,char,int);
    //設定Chart參數
    xlsFile* SetChart(short XaxisByToporLeft = 2, bool isLabelVisable = 1, 
    CString = "" , CString = "" , CString = "" );
    //區域、直方、方柱、圓柱、圓錐、金字塔
    void InsertHistogramChart(int shapeType = CT_COLUMN, 
                              bool is3D = 0, 
                              int isVrticlorHorztlorOther = 0, 
                              int isNone_Stack_Percent = 0);
    //其它(特殊圖表)   
    void InsertBubleChart    (bool is3D    = 0);                          //泡泡圖
    void InsertDoughnutChart (bool Explode = 0);                          //圓環圖
    void InsertSurfaceChart  (bool is3D    = 0, bool isWire         = 0); //曲面圖
    void InsertRadarChart    (bool isWire  = 0, bool isDot          = 1); //雷達圖
    void InsertPieChart      (bool Explode = 0, int  type2Dor3DorOf = 0); //圓餅圖
    void InsertLineChart     (bool isDot   = 1, bool is3D           = 0, 
                              int  isNone_Stack_Percent = 0);             //折線圖
    void InsertXYScatterChart(bool isDot, bool isLine, bool Smooth);      //離散圖
    void InsertStockChart    (int);                                       //股票圖
    //--------------------------------------------
    void InsertImage(const char* , float , float );  //插入圖片
    void InsertImage(const char* );                  //插入圖片(先選取範圍,圖檔必失真)
protected:
   void xlsFile::newChart();  //在Sheet新增圖表
//    防止任何運算
private:
    void operator+(const xlsFile&);
    void operator-(const xlsFile&);
    void operator*(const xlsFile&);
    void operator/(const xlsFile&);
    void operator%(const xlsFile&);
    void operator=(const xlsFile&);

    bool operator<(const xlsFile&);
    bool operator>(const xlsFile&);
    bool operator>=(const xlsFile&);
    bool operator<=(const xlsFile&);
    bool operator==(const xlsFile&);
    bool operator!=(const xlsFile&);
    bool operator&&(const xlsFile&);
    bool operator&(const xlsFile&);
    bool operator||(const xlsFile&);
    bool operator|(const xlsFile&);

    bool operator>>(const xlsFile&);
    bool operator<<(const xlsFile&);
};
#endif
//備用語法
    range.SetFormula(COleVariant("=RAND()*100000"));  //套公式
    range.setSetValue(COleVariant("Last Name"));      //輸入值
    range.SetNumberFormat(COleVariant("$0.00"));      //數字格式

    //插圖
    Shapes shapes = objSheet.GetShapes(); 
    range = objSheet.GetRange(COleVariant("J7"),COleVariant("R21")); 
  
    //range.AttachDispatch(pRange);
    shapes.AddPicture(
                    "c:\\CHILIN.bmp",                //LPCTSTR Filename
                    false,                           //long    LinkToFile
                    true,                            //long    SaveWithDocument
                    (float)range.GetLeft(  ).dblVal, //float   Left
                    (float)range.GetTop(   ).dblVal, //float   Top
                    (float)range.GetWidth( ).dblVal, //float   Width
                    (float)range.GetHeight().dblVal  //float   Height
    );
    range.Sort(
                key1,        //  key1
                DeCrement1,  // long Order1, [ 1(ascending order) or 2(descending order) ]
                key2,        // key2, 
                VOptional,   // type, [xlSortLabels, xlSortValues]
                DeCrement2,  // long Order2, [ 1(升冪) or 2( 降) ]
                key3,        // key3
                DeCrement3,  // long Order3, [ 1(升冪) or 2( 降) ]
                2,           // Header, [0,1 : 不含 title 2 : title (選取範圍)一起排
          //進階
                VOptional,   // OrderCustom [從1開始,自定義排序順序列表中之索引號,省略使用常規]
                _variant_t((short)TRUE),    // MatchCase [TRUE分大小寫排]
                1,           // Orientation : [排序方向, 1:按列, 2:按行)
                1,           // SortMethod : [1:按字符漢語拼音順序, 2:按字符筆畫數]
       //未知選項
                1,           // DataOption1 可選 0 與 1
                1,           // DataOption2 可選 0 與 1
                1            // DataOption3 可選 0 與 1
    );

xlsFile.cpp

簡化初始化動作

在細部操作裡,初始化設計實在是一件,不容易的事。
做太多,失去彈性,做太少,又增加函數之間的耦合力,容易讓人產生「誰知道要呼叫這個」的OS。在此,盡量設計的和Excel操作一樣,打開App這一步。而關掉也是設計對應的動作。
在construct有一個條件編譯的部份,是在debug模式中,盡快的顯示Excel,但是在release模式中,就把程式化的表格動作結束後,再顯示比較好。
xlsFile::xlsFile(): 
VOptional((long)DISP_E_PARAMNOTFOUND,VT_ERROR), VFALSE((short)FALSE), VTRUE((short)TRUE)
{
    ZeroMemory(buf,sizeof(buf));
    ZeroMemory(buf1,sizeof(buf1));
    ZeroMemory(buf2,sizeof(buf2));
    //Step 1.叫Excel應用程式
    if(!objApp.CreateDispatch("Excel.Application",&e))
    {
        CString str;
        str.Format("Excel CreateDispatch() failed w/err 0x%08lx", e.m_sc);
        AfxMessageBox(str, MB_SETFOREGROUND);
    }
#ifdef _DEBUG
    SetVisible(true);
#endif
};

xlsFile::~xlsFile()
{
    range.ReleaseDispatch();
    objSheet.ReleaseDispatch();
    objSheets.ReleaseDispatch();
    objBook.ReleaseDispatch();
    objBooks.ReleaseDispatch();
    objApp.ReleaseDispatch();
}

維持 開新檔案/開啟舊檔/存檔/另存新檔 的整體概念

概念描述
xlsFile* xlsFile::New()
{
    objBooks = objApp.GetWorkbooks();
    objBook = objBooks.Add(VOptional);    //開新檔案
    objSheets = objBook.GetWorksheets();
    return this;
}

xlsFile* xlsFile::Open(const char* path)
{
    objBooks = objApp.GetWorkbooks();
    objBook.AttachDispatch(objBooks.Add(_variant_t(path))); //開啟一個已存在的檔案
    objBook.Activate();
    objSheets = objBook.GetWorksheets();
    return this;
}

void xlsFile::SaveAs(const char* strTableName)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf, "%s", strTableName);
    objBook.SaveAs(
        COleVariant(buf),
        VOptional, VOptional, 
        VOptional, VOptional, 
        VOptional, 1,
        VOptional, VFALSE,
        VOptional, VOptional, VOptional); 
}

void xlsFile::Save()
{
    objBook.Save();
}

畫出視窗

就是把視窗畫出來,之後把控制權給使用者。
//SetVisible()
void xlsFile::SetVisible(bool a)
{
    objApp.SetVisible(a);    //顯示Excel檔
    objApp.SetUserControl(a);//使用者控制後,就不可以自動關閉
}

對Sheet的操作

其中,在選擇sheet時,常常會出現,程式碼指定選擇一個不存在的sheet,這時出錯了,只有一個空白的MessageBox(),出現了也不知道出了什麼事?哪裡出了問題,所以就在SelectSheet()時做try-catch的設計
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Sheet操作

//-------------------------
////取得 Sheet 個數
long xlsFile::SheetTotal()
{
    return objSheets.GetCount();//edisonx
}
//-------------------------
//由SheetIndex 指定SheetName
void xlsFile::SetSheetName(short SheetIndex, const char* SheetName)
{
    try
    {
        objSheet = objSheets.GetItem(COleVariant(SheetIndex));
        objSheet.SetName(SheetName);//設定sheet名稱
    }
    catch (...)
    {
        CString str;
        str.Format("設定第%d個Sheet的名字為%s出錯了!", SheetIndex, SheetName);
        AfxMessageBox(str);
    }
}
//-------------------------
//由SheetIndex 取得SheetName
CString xlsFile::GetSheetName(short SheetIndex)
{
    try
    {
        objSheet = objSheets.GetItem(COleVariant(SheetIndex));
    }
    catch (...)
    {
        CString str;
        str.Format("取得第%d個Sheet名字出錯了!", SheetIndex);
        AfxMessageBox(str);
    }
    return objSheet.GetName();//edisonx
}
//-------------------------
//選擇Sheet
//由SheetName
xlsFile* xlsFile::SelectSheet(const char* SheetName)
{
    try
    {
        objSheet = objSheets.GetItem(_variant_t(SheetName));
        objSheet.Activate();//edisonx
    }
    catch (...)
    {
        CString str;
        str.Format("選擇Sheet: %s出錯了!", SheetName);
         AfxMessageBox(str);
    }
    return this;
}
//由SheetIndex 
xlsFile* xlsFile::SelectSheet(short SheetIndex)
{
    try
    {
        objSheet = objSheets.GetItem(COleVariant(SheetIndex));
        objSheet.Activate();//edisonx
    }
    catch (...)
    {
        CString str;
        str.Format("選擇第%d個Sheet出錯了!", SheetIndex);
        AfxMessageBox(str);
    }
    return this;
}
//-------------------------
//複製SheetName 指定插入Sheet的位置,並指定新Sheet名稱
void xlsFile::CopySheet(const char* SheetName)
{
    objSheet.AttachDispatch(objSheets.GetItem(_variant_t(SheetName)),true);
    objSheet.Copy(vtMissing,_variant_t(objSheet));
}
//複製SheetIndex 指定插入Sheet的位置,並指定名稱
void xlsFile::CopySheet(short SheetIndex)
{
    objSheet.AttachDispatch(objSheets.GetItem(COleVariant(SheetIndex)));
    objSheet.Copy(vtMissing,_variant_t(objSheet));
}
//-------------------------
//刪除Sheet
//選SheetName 
void xlsFile::DelSheet(const char* SheetName)
{    
    objSheet = objSheets.GetItem(_variant_t(SheetName));
    objSheet.Delete();//edisonx
}
//選SheetIndex
void xlsFile::DelSheet(short SheetIndex)
{
    objSheet = objSheets.GetItem(COleVariant(SheetIndex));
    objSheet.Delete();//edisonx
}

對儲存格(Cell)的操作

一般常見的cell操作,都在上面了
///////////////////////////////////////////////////////////////////////////////////////////
//Cell操作
//Cell計數計算
// 取得起始列
long xlsFile::GetHorztlStartCell()
{
    Range usedrange;
    usedrange.AttachDispatch(objSheet.GetUsedRange());
    return usedrange.GetColumn();
}
// 取得起始行
long xlsFile::GetVrticlStartCell()
{    
    Range usedrange;
    usedrange.AttachDispatch(objSheet.GetUsedRange());
    return usedrange.GetRow();
}
// 取得總列數
long xlsFile::GetHorztlTotalCell()
{
    Range usedrange;
    usedrange.AttachDispatch(objSheet.GetUsedRange());
    range.AttachDispatch(usedrange.GetColumns());
    return range.GetCount();
}
// 取得總行數
long xlsFile::GetVrticlTotalCell()
{
    Range usedrange;
    usedrange.AttachDispatch(objSheet.GetUsedRange());
    range.AttachDispatch(usedrange.GetRows());
    return range.GetCount();
}
//清除儲存格
void xlsFile::ClearCell()
{
    //先選取一個範圍的儲存格
    range.Clear();//edisonx
}
//合併儲存格
xlsFile* xlsFile::SetMergeCells(short vMerge, bool isCenterAcross)
{
    //先選取一個範圍的儲存格
    range.SetMergeCells(_variant_t(vMerge));
    if(isCenterAcross) SetHorztlAlgmet(HA_CENTERACROSS);
    return this;
}

選擇儲存格(Cell)

選擇格子的動作,就像是用滑鼠點選儲存格一樣直覺,分成兩種,點選格子和點選範圍
其中,和選擇sheet一樣,若出現了選擇了超過Z的格子(用迴圈控制,就可能會超過Z),就會出問題,也設計了try-catch來顯示出問題的格子在哪
//-------------------------
//Cell格式設定
//-------------------------
//選格子
//選一格
xlsFile* xlsFile::SelectCell(const char* x)
{
    try
    {
        range=objSheet.GetRange(COleVariant(x),COleVariant(x));
        ASSERT(range);
    }
    catch (...)
    {
        CString str;
        str.Format("選擇儲存格%s出錯了!", x);
        AfxMessageBox(str);
    }
    return this;
}

xlsFile* xlsFile::SelectCell(const char* x, int y)
{
    try
    {
        ZeroMemory(buf,sizeof(buf));
        sprintf(buf,"%s%d",x,y);
        range=objSheet.GetRange(COleVariant(buf),COleVariant(buf));
        ASSERT(range);
    }
    catch (...)
    {
        CString str;
        str.Format("選擇儲存格%s%d出錯了!", x, y);
        AfxMessageBox(str);
    }
    return this;
}
//小於Z
xlsFile* xlsFile::SelectCell(char x, int y)
{
    if (x >= 'A' && x <= 'Z')
    {
        ZeroMemory(buf,sizeof(buf));
        sprintf(buf,"%c%d",x,y);
        range=objSheet.GetRange(COleVariant(buf),COleVariant(buf));
        ASSERT(range);
    }
    else
    {
        CString str;
        str.Format("選擇儲存格%c%d出錯了!", x, y);
        AfxMessageBox(str);
        ASSERT(x >= 'A' && x <= 'Z');
    }
    return this;
}
//大於Z,開始選AA
xlsFile* xlsFile::SelectCell(char x1, char x2, int y)
{
    if ( (x1 >= 'A' && x1 <= 'Z') && (x2 >= 'A' && x2 <= 'Z'))
    {
        ZeroMemory(buf,sizeof(buf));
        sprintf(buf,"%c%c%d",x1,x2,y);
        range=objSheet.GetRange(COleVariant(buf),COleVariant(buf));
        ASSERT(range);
    }
    else
    {
        CString str;
        str.Format("選擇儲存格%c%c%d出錯了!", x1, x2, y);
        AfxMessageBox(str);
        ASSERT(x1 >= 'A' && x1 <= 'Z');
        ASSERT(x2 >= 'A' && x2 <= 'Z');
    }
    return this;
}
//-------------------------
//選範圍
xlsFile* xlsFile::SelectCell(const char* x1, const char* x2)
{
    try
    {
        range=objSheet.GetRange(COleVariant(x1),COleVariant(x2));
        ASSERT(range);
    }
    catch (...)
    {
        CString str;
        str.Format("選擇範圍,從%s到%s出錯了!", x1, x2);
        AfxMessageBox(str);
    }
    return this;
}

xlsFile* xlsFile::SelectCell(const char* x1, int y1, const char* x2, int y2)
{
    try
    {
        ZeroMemory(buf1,sizeof(buf1));
        ZeroMemory(buf2,sizeof(buf2));
        sprintf(buf1,"%s%d",x1,y1);
        sprintf(buf2,"%s%d",x2,y2);
        range=objSheet.GetRange(COleVariant(buf1),COleVariant(buf2));
        ASSERT(range);
    }
    catch (...)
    {
        CString str;
        str.Format("選擇範圍,從%s%d到%s%d的地方出錯了!", x1, y1, x2, y2);
        AfxMessageBox(str);
    }
    return this;
}
//小於Z
xlsFile* xlsFile::SelectCell(char x1, int y1, char x2, int y2)
{
    if (x1 >= 'A' && x1 <= 'Z')
    {
        ZeroMemory(buf1,sizeof(buf1));
        ZeroMemory(buf2,sizeof(buf2));
        sprintf(buf1,"%c%d",x1,y1);
        sprintf(buf2,"%c%d",x2,y2);
        range=objSheet.GetRange(COleVariant(buf1),COleVariant(buf2));
        ASSERT(range);
    }
    else
    {
        CString str;
        str.Format("選擇範圍,從%c%d到%c%d出錯了!", x1, y1, x2, y2);
        AfxMessageBox(str);
        ASSERT(x1 >= 'A' && x1 <= 'Z');
    }
    return this;
}
//大於Z,開始選AA
xlsFile* xlsFile::SelectCell(char xA1, char xB1, int y1, char xA2, char xB2, int y2)
{
    if ( (xA1 >= 'A' && xA2 <= 'Z') && (xB1 >= 'A' && xB2 <= 'Z') )
    {
        ZeroMemory(buf1,sizeof(buf1));
        ZeroMemory(buf2,sizeof(buf2));
        sprintf(buf1,"%c%c%d",xA1,xB1,y1);
        sprintf(buf2,"%c%c%d",xA2,xB2,y2);
        
        range=objSheet.GetRange(COleVariant(buf1),COleVariant(buf2));
        ASSERT(range);
    }
    else
    {
        CString str;
        str.Format("選擇範圍,從%c%c%d到%c%c%d出錯了!", xA1, xB1, y1, xA2, xB2, y2);
        AfxMessageBox(str);
        ASSERT(xA1 >= 'A' && xA2 <= 'Z');
        ASSERT(xB1 >= 'A' && xB2 <= 'Z');
    }
    return this;
}

靠左/靠右/靠上/靠下/置中 旋轉/文字自動換行

保持與Excel操作介面相同的思考方式,其參數在.h檔中,有列舉成文字,不需使用數字做辨識,可提高可讀性。
//-------------------------
//對齊方式
//水平對齊
xlsFile* xlsFile::SetHorztlAlgmet(short position)
{
    range.SetHorizontalAlignment(COleVariant(position));
    return this;
}
//垂直對齊
xlsFile* xlsFile::SetVrticlAlgmet(short position)
{
    range.SetVerticalAlignment(COleVariant(position));
    return this;
}
//對齊方式的方向幾度(+90~-90)
xlsFile* xlsFile::SetTextAngle(short Angle)
{
    range.SetOrientation(COleVariant(Angle)); 
    return this;
}
//設定文字自動換行
xlsFile* xlsFile::AutoNewLine(bool NewLine)
{
    if(NewLine)    range.SetWrapText(VTRUE);
    else           range.SetWrapText(VFALSE);
    return this;
}

設定框線粗細、框線顏色

用同一個介面來設定框線和框線顏色,對於造一個表格來說,是方便的事,若有需要,也可以將它拆開兩個各別處理。但是因為沒有遇到這樣的需求,所以就沒有設計這樣的介面。
//設定框線粗細、框線顏色
xlsFile* xlsFile::SetCellBorder(long BoarderStyle, int BoarderWeight, long BoarderColor)
{
    range.BorderAround(_variant_t(BoarderStyle), BoarderWeight, BoarderColor,_variant_t((long)RGB(0,0,0)));
    return this;
}

自動欄寬、列高

在Excel中,只要對著調整格線的地方點兩下,即可適動調整適當欄寬和列高。
Excel預設的介面,原本無法一個函數搞定,要先選取整排,再調整。包起來的設計,比較直覺。
//設定欄寬列高
//自動調整列高
void xlsFile::AutoFitWidth()
{
    col = range.GetEntireColumn();    //選取某個範圍的一整排
    col.AutoFit();                    //自動調整一整排的欄寬
}
//自動調整欄寬
void xlsFile::AutoFitHight()
{
    row = range.GetEntireRow();        //選取某個範圍的一整排
    row.AutoFit();                    //自動調整一整排的列高
}
//設定列高
xlsFile* xlsFile::SetCellHeight(float height)
{
    range.SetRowHeight(_variant_t(height));
    return this;
}
//設定欄寬
xlsFile* xlsFile::SetCellWidth(float height)
{
    range.SetColumnWidth(_variant_t(height));
    return this;
}

儲存格字型的操作

字型的顏色、粗體、字型大小...在工作列上的那一排常用的功能,都建上來了。
顏色對照,參考.....參考文件!XDDD
//設定字型
xlsFile* xlsFile::SetFont(const char* fontType)
{
    font = range.GetFont();
    font.SetName(_variant_t(fontType));//原本是韓文字型
    return this;
}
//粗體
xlsFile* xlsFile::SetFontBold(bool isBold)
{
    font = range.GetFont();
    if (isBold)    font.SetBold(VTRUE);//粗體
    else           font.SetBold(VFALSE);
    return this;
}
//刪除線
xlsFile* xlsFile::SetFontStrkthrgh(bool isStrike)
{
    font = range.GetFont();
    if (isStrike)    font.SetStrikethrough(VTRUE);    //edisonx
    else             font.SetStrikethrough(VFALSE);    //edisonx
    return this;
}
//字型大小
xlsFile* xlsFile::SetFontSize(short fontSize)
{
    font = range.GetFont();
    font.SetSize(_variant_t(fontSize));//字型大小pt
    return this;
}
//字型顏色
xlsFile* xlsFile::SetFontColor(short colorIndex)
{
    font = range.GetFont();
    font.SetColorIndex(_variant_t(colorIndex)); //字色(預設黑色)
    return this;
}

填滿色彩

在填滿色彩時,設計了使用座標的方式做操作,此是使用Excel視窗軟體介面上的調色盤的位置為準。
除此之外,還有快速選色介面,不過只有幾個純色支援此介面。
//設定底色
xlsFile* xlsFile::SetCellColor(short colorIndex)
{
    cell = range.GetInterior();                   //取得選取範圍,設定儲存格的記憶體位址
    cell.SetColorIndex(_variant_t(colorIndex));   //設定底色(查表)
    //cell.SetColor(_variant_t(colorIndex));
    return this;
}
//選擇顏色(適合字色和底色)依excel介面的座標選擇顏色
short xlsFile::SelectColor(short x, short y)
{
//Microsoft Excel 的顏色排序是依
//紅、橙、黃、綠、藍、靛、紫、灰(y)
//由深到淺(x)
    switch(x)
    {
    case 1:
             if(y == 1) return 1;
        else if(y == 2) return 9;
        else if(y == 3) return 3;
        else if(y == 4) return 7;
        else if(y == 5) return 38;

        else if(y == 6) return 17;
        else if(y == 7) return 38;
        break;
    case 2:
             if(y == 1) return 53;
        else if(y == 2) return 46;
        else if(y == 3) return 45;
        else if(y == 4) return 44;
        else if(y == 5) return 40;
        
        else if(y == 6) return 18;
        else if(y == 7) return 26;
        break;
    case 3:
             if(y == 1) return 52;
        else if(y == 2) return 12;
        else if(y == 3) return 43;
        else if(y == 4) return  6;
        else if(y == 5) return 36;
        
        else if(y == 6) return 19;
        else if(y == 7) return 27;
        break;
    case 4:
             if(y == 1) return 51;
        else if(y == 2) return 10;
        else if(y == 3) return 50;
        else if(y == 4) return  4;
        else if(y == 5) return 35;
        
        else if(y == 6) return 20;
        else if(y == 7) return 28;
        break;
    case 5:
             if(y == 1) return 49;
        else if(y == 2) return 14;
        else if(y == 3) return 42;
        else if(y == 4) return  8;
        else if(y == 5) return 34;
        
        else if(y == 6) return 21;
        else if(y == 7) return 29;
        break;
    case 6:
             if(y == 1) return 11;
        else if(y == 2) return  5;
        else if(y == 3) return 41;
        else if(y == 4) return 33;
        else if(y == 5) return 37;
        
        else if(y == 6) return 22;
        else if(y == 7) return 30;
        break;
    case 7:
             if(y == 1) return 55;
        else if(y == 2) return 47;
        else if(y == 3) return 13;
        else if(y == 4) return 54;
        else if(y == 5) return 39;
        
        else if(y == 6) return 23;
        else if(y == 7) return 31;
        break;
    case 8:
             if(y == 1) return 56;
        else if(y == 2) return 16;
        else if(y == 3) return 48;
        else if(y == 4) return 15;
        else if(y == 5) return  2;
        
        else if(y == 6) return 24;
        else if(y == 7) return 32;
        break;
    }
    return 2;//預設白色
}
short xlsFile::SelectColor(const char ColorChar)
{
    switch(ColorChar)
    {
    //黑色
    case 'D':
    case 'd':
        return 1;
        break;
    //白色
    case 'W':
    case 'w':
        return 2;
        break;
    //紅色
    case 'R':
    case 'r':
        return 3;
        break;
    //綠色
    case 'G':
    case 'g':
        return 4;
        break;
    //藍色
    case 'B':
    case 'b':
        return 5;
        break;
    //黃色
    case 'Y':
    case 'y':
        return 6;
        break;    }
    return 2;//預設白色
}

Cell填值

支援格種常用的儲存格格式,但是不支援時間格式。使用上要小心
//SetCell()
void xlsFile::SetCell(int Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,"%d",Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(long Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,"%d",Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(double Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,"%f",Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(const char* Data)
{
    ZeroMemory(buf,sizeof(buf));
    strcpy(buf,Data);
    //sprintf(buf,"%s",Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(CString Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,"%s",Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(const char* Format, int Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,Format,Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(const char* Format, double Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,Format,Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}

void xlsFile::SetCell(const char* Format, long Data)
{
    ZeroMemory(buf,sizeof(buf));
    sprintf(buf,Format,Data);
    range.SetItem(_variant_t((long)1),_variant_t((long)1),_variant_t(buf));
}
//-------------------------
CString xlsFile::GetCell2CStr()
{
    return (char*)_bstr_t(range.GetItem(_variant_t((long)1), _variant_t((long)1)));
}
int xlsFile::GetCell2Int()
{
    COleVariant vResult = range.GetValue2();   //edisonx 
    vResult.ChangeType(VT_INT);                //edisonx 
    return vResult.intVal;                     //edisonx 
}
double xlsFile::GetCell2Double()
{
    COleVariant vResult = range.GetValue2();  //edisonx 
    vResult.ChangeType(VT_R8);                //edisonx 
    return vResult.dblVal;                    //edisonx 
}

排序演算法

排序演算法有分主要順序和次要順序的設定,可以順序填值,也可以填完值再排序。
//排序
void xlsFile::Sort(CString IndexCell1, long DeCrement1, CString IndexCell2, long DeCrement2, CString IndexCell3, long DeCrement3)
{
    VARIANT key1, key2, key3;

    V_VT(&key1) = VT_DISPATCH;
    V_DISPATCH(&key1)=objSheet.GetRange(COleVariant(IndexCell1),COleVariant(IndexCell1));

    if(IndexCell2.IsEmpty())
    {
        range.Sort( key1, DeCrement1, VOptional, VOptional, 1, VOptional, 1, 2,//一般選項
                    VOptional, _variant_t((short)TRUE),//進階 
                    1, 1, 1, 1, 1);//未知選項//edisonx
    }
    else
    {
        V_VT(&key2) = VT_DISPATCH;
        V_DISPATCH(&key2)=objSheet.GetRange(COleVariant(IndexCell2),COleVariant(IndexCell2));
        
        if(IndexCell3.IsEmpty())
        {
            range.Sort( key1, DeCrement1, key2,    VOptional, DeCrement2, VOptional, 1, 2,
                        VOptional, _variant_t((short)TRUE),                                //進階 
                        1, 1, 1, 1, 1);                                                    //未知選項//edisonx
        }
        else
        {
            V_VT(&key3) = VT_DISPATCH;
            V_DISPATCH(&key3)=objSheet.GetRange(COleVariant(IndexCell3),COleVariant(IndexCell3));
            
            range.Sort( key1, DeCrement1, key2, VOptional, DeCrement2, key3, DeCrement3, 2, //一般選項
                        VOptional, _variant_t((short)TRUE),                                 //進階 
                        1, 1, 1, 1, 1);                                                     //未知選項//edisonx
        }
    }
}

其它

在其它,就將不常用的圖表/圖的新增與設定的操作放在這。
這裡的設計就比較不費心思,使用上也許會有種種困擾。小心使用!
void xlsFile::newChart()
{
    //在Sheet新增圖表
    lpDisp = objSheet.ChartObjects(VOptional);
    chartobjects.AttachDispatch(lpDisp);    
    chartobject = chartobjects.Add(
                                (float)range.GetLeft(  ).dblVal,
                                (float)range.GetTop(   ).dblVal, 
                                (float)range.GetWidth( ).dblVal, 
                                (float)range.GetHeight().dblVal  );    //圖表符合儲存格範圍的大小
    xlsChart.AttachDispatch(chartobject.GetChart());    //資料來源(範圍left, top預設為 比較Item和Group)
}
///////////////////////////////////////////////////////////////////////////////////////////
//圖表操作
//儲存圖表
//edisonx
void xlsFile::SaveChart(CString FullBmpPathName)
{
    xlsChart.Export(LPCTSTR(FullBmpPathName),VOptional,VOptional);
}
//選擇表格資料的範圍
xlsFile* xlsFile::SelectChartRange(const char* x1,const char* x2)
{
    newChart();

    lpDisp = objSheet.GetRange(COleVariant(x1),COleVariant(x2));
    range.AttachDispatch(lpDisp);
    
    return this;
}

xlsFile* xlsFile::SelectChartRange(const char* x1, int y1, const char* x2, int y2)
{
    newChart();    

    ZeroMemory(buf1,sizeof(buf1));
    ZeroMemory(buf2,sizeof(buf2));
    sprintf(buf1,"%s%d",x1,y1);
    sprintf(buf2,"%s%d",x2,y2);

    lpDisp = objSheet.GetRange(COleVariant(buf1),COleVariant(buf2));
    range.AttachDispatch(lpDisp);
    return this;
}
//小於Z
xlsFile* xlsFile::SelectChartRange(char x1, int y1, char x2, int y2)
{
    newChart();

    ZeroMemory(buf1,sizeof(buf1));
    ZeroMemory(buf2,sizeof(buf2));
    //發現了這個問題,但是忘記了是不是故意這樣寫的!
    //是x1, y1; x2, y2?
    sprintf(buf1,"%c%d",x1,y2);
    sprintf(buf2,"%c%d",x1,y2);

    lpDisp = objSheet.GetRange(COleVariant(buf1),COleVariant(buf2));
    range.AttachDispatch(lpDisp);
    return this;
}
//大於Z,開始選AA
xlsFile* xlsFile::SelectChartRange(char xA1, char xB1, int y1, char xA2, char xB2, int y2)
{
    newChart();
    ZeroMemory(buf1,sizeof(buf1));
    ZeroMemory(buf2,sizeof(buf2));
    sprintf(buf1,"%c%c%d",xA1,xB1,y1);
    sprintf(buf2,"%c%c%d",xA2,xB2,y2);    
    lpDisp = objSheet.GetRange(COleVariant(buf1),COleVariant(buf2));
    range.AttachDispatch(lpDisp);
    return this;
}
// 設定表格參數(預設會顯示立體直方圖)
xlsFile* xlsFile::SetChart(short XaxisByToporLeft, bool isLabelVisable, CString TitleString, CString XaxisTitle, CString YaxisTitle) 
{    
    var.vt = VT_DISPATCH;
    var.pdispVal = lpDisp;

    short LabelVisable(FALSE);
    LabelVisable = (isLabelVisable) ? (short)TRUE : (short)FALSE ;
        
    xlsChart.ChartWizard(
        var,                                   // const VARIANT& Source.
        COleVariant((short)11),                // const VARIANT& fix please, Gallery: 3d Column. 1 or 11 是否轉動3D(3D類適用, 1轉,11不轉)
        COleVariant((short)1),                 // const VARIANT& fix please, Format, use default
        COleVariant(XaxisByToporLeft),         // const VARIANT& PlotBy: 1.X  2.Y 圖表的x軸要使用 表格的1:X-top還是2:Y-left
        COleVariant((short)1),                 // const VARIANT& Category Labels fix please 不當軸的那個資料,從第幾個格子開始算(比較群組資料數量)
        COleVariant((short)1),                 // const VARIANT& Series Labels. Start X, 不當軸的那個資料,資料名稱要用幾排格子(更改名字)
        COleVariant(LabelVisable),             // const VARIANT& HasLegend. 是否要顯示群組資料標籤
        //以下可不填
        _variant_t(COleVariant(TitleString)),  // const VARITNT& Title
        _variant_t(COleVariant(XaxisTitle)),   // const VARIANT& CategoryTitle
        _variant_t(COleVariant(YaxisTitle)),   // const VARIANT& ValueTitle
        VOptional                              // const VARIANT& ExtraTitle
        );
    return this;
}
//插入圖表
void xlsFile::InsertHistogramChart(int shapeType, bool is3D, 
                                   int isVrticl_Horztl_Other, 
                                   int isNone_Stack_Percent )
{
    long ChartType = 51;
    if (shapeType == 0)//Area
    {
        if(!is3D)   //2D
        {
                 if (isNone_Stack_Percent == 0) ChartType = 1;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 77; //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 76; //有百分比
        }
        else        //3D
        {
                 if (isNone_Stack_Percent == 0) ChartType = -4098; //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 78;    //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 79;    //百分比
        }
    } 
    else if (shapeType == 1)//直方圖
    {
        if(isVrticl_Horztl_Other == 0)//直的
        {
            if(!is3D)   //2D
            {
                     if (isNone_Stack_Percent == 0) ChartType = 51; //無堆疊
                else if (isNone_Stack_Percent == 1) ChartType = 52; //有堆疊
                else if (isNone_Stack_Percent == 2) ChartType = 53; //有百分比
            }
            else        //3D
            {
                     if (isNone_Stack_Percent == 0) ChartType = 54; //無堆疊
                else if (isNone_Stack_Percent == 1) ChartType = 55; //有堆疊
                else if (isNone_Stack_Percent == 2) ChartType = 56; //百分比
            }
        }
        else if(isVrticl_Horztl_Other == 1)//橫的
        {
            if(!is3D)   //2D
            {
                     if (isNone_Stack_Percent == 0) ChartType = 57;
                else if (isNone_Stack_Percent == 1) ChartType = 58;
                else if (isNone_Stack_Percent == 2) ChartType = 59;
            }
            else        //3D
            {
                     if (isNone_Stack_Percent == 0) ChartType = 60;
                else if (isNone_Stack_Percent == 1) ChartType = 61;
                else if (isNone_Stack_Percent == 2) ChartType = 62;
            }
        }
        else                                        ChartType = -4100;    //平面 必3D
    }
    else if (shapeType == 2)//CONE
    {
        if(isVrticl_Horztl_Other == 0)//直的
        {
                 if (isNone_Stack_Percent == 0) ChartType = 92;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 93;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 94;  //百分比
        }
        else if(isVrticl_Horztl_Other == 1)//橫的
        {
                 if (isNone_Stack_Percent == 0) ChartType = 95;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 96;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 97;  //百分比
        }
        else                                    ChartType = 98;  //平面 必3D
    }
    else if (shapeType == 3)
    {
        if(isVrticl_Horztl_Other == 0)//直的
        {
                 if (isNone_Stack_Percent == 0) ChartType =  99;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 100;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 101;  //百分比
        }
        else if(isVrticl_Horztl_Other == 1)//橫的
        {
                  if(isNone_Stack_Percent == 0) ChartType = 102;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 103;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 104;  //百分比
        }
        else                    ChartType = 105;//平面 必3D
    }
    else if (shapeType == 4)
    {
        if(isVrticl_Horztl_Other == 0)//直的
        {
                 if (isNone_Stack_Percent == 0) ChartType = 106;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 107;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 108;  //百分比
        }
        else if(isVrticl_Horztl_Other == 1)//橫的
        {
                 if (isNone_Stack_Percent == 0) ChartType = 109;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 110;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 111;  //百分比
        }
        else                                    ChartType = 112;  //平面 必3D
    }
    xlsChart.SetChartType((long)ChartType);
}
///////////////////////////////////////////////////////////////
//泡泡
void xlsFile::InsertBubleChart(bool is3D)
{
    long ChartType = 51;

    if(is3D)    ChartType = 15;
    else        ChartType = 87;

    xlsChart.SetChartType((long)ChartType);
}
//圓環
void xlsFile::InsertDoughnutChart(bool Explode)
{
    long ChartType = 51;
    
    if(!Explode)    ChartType = -4120;
    else            ChartType = 80;

    xlsChart.SetChartType((long)ChartType);

}
//曲面
void xlsFile::InsertSurfaceChart(bool is3D, bool isWire)
{
    long ChartType = 51;
    
    if (is3D)
    {
        if (!isWire)    ChartType = 83;
        else            ChartType = 84;
    } 
    else
    {
        if (!isWire)    ChartType = 85;
        else            ChartType = 86;
    }

    xlsChart.SetChartType((long)ChartType);
}
//雷達
void xlsFile::InsertRadarChart(bool isWire, bool isDot)
{
    long ChartType = 51;
    
    if (isWire)
    {
        if (!isDot) ChartType = -4151;
        else        ChartType = 81;
    } 
    else            ChartType = 82;

    xlsChart.SetChartType((long)ChartType);
}
//圓餅
void xlsFile::InsertPieChart(bool Explode, int type2Dor3DorOf)
{
    long ChartType = 51;

    if(!Explode)
    {
             if (type2Dor3DorOf == 0)        ChartType = 5;
        else if (type2Dor3DorOf == 1)        ChartType = -1402;
        else if (type2Dor3DorOf == 2)        ChartType = 68;
    }
    else
    {
             if (type2Dor3DorOf == 0)        ChartType = 69;
        else if (type2Dor3DorOf == 1)        ChartType = 70;
        else if (type2Dor3DorOf == 2)        ChartType = 71;
    }
    
    xlsChart.SetChartType(ChartType);
}

void xlsFile::InsertLineChart(bool isDot, bool is3D, int isNone_Stack_Percent)
{
    long ChartType = 51;
    
    if(!is3D)//3D
    {
        if(!isDot)
        {
                 if (isNone_Stack_Percent == 0) ChartType =  4;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 63;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 64;  //有百分比
        }
        else
        {
                 if (isNone_Stack_Percent == 0) ChartType = 65;  //無堆疊
            else if (isNone_Stack_Percent == 1) ChartType = 66;  //有堆疊
            else if (isNone_Stack_Percent == 2) ChartType = 67;  //有百分比
        }
    }
    else                                        ChartType = -4101;  //3D

    xlsChart.SetChartType((long)ChartType);
}
//離散圖
void xlsFile::InsertXYScatterChart(bool isDot, bool isLine, bool Smooth)
{
    long ChartType = 51;
    if(!isLine)          ChartType = -4169;  //3D
    else
    {
        if(Smooth)
        {
            if(isDot)    ChartType = 72;
            else         ChartType = 73;
        }
        else
        {
            if(isDot)    ChartType = 74;
            else         ChartType = 75;
        }
    }
    xlsChart.SetChartType((long)ChartType);
}

//股票圖
void xlsFile::InsertStockChart(int StockType)
{
    long ChartType = 51;
    
         if (StockType == 0)    ChartType = 88;
    else if (StockType == 1)    ChartType = 89;
    else if (StockType == 2)    ChartType = 90;
    else if (StockType == 3)    ChartType = 91;

    xlsChart.SetChartType((long)ChartType);
}
//--------------------------------------------
//--------------------------------------------
//插入圖(從檔案)
void xlsFile::InsertImage(const char* FileNamePath, float Width, float Height)
{
    shapes = objSheet.GetShapes(); 
    shapes.AddPicture(
        FileNamePath,                   //LPCTSTR Filename
        false,                          //long LinkToFile
        true,                           //long SaveWithDocument
        (float)range.GetLeft().dblVal,  //float Left
        (float)range.GetTop().dblVal,   //float Top
        Width,                          //float Width
        Height                          //float Height
    );
}

void xlsFile::InsertImage(const char* FileNamePath)
{
    shapes = objSheet.GetShapes(); 
    shapes.AddPicture(
        FileNamePath,                   //LPCTSTR Filename
        false,                          //long LinkToFile
        true,                           //long SaveWithDocument
        (float)range.GetLeft().dblVal,  //float Left
        (float)range.GetTop().dblVal,   //float Top
        (float)range.GetWidth().dblVal, //float Width
        (float)range.GetHeight().dblVal //float Height
    );
}

文章分類維度

class design (22) 視覺介面設計 (9) Lv1 class (5) 互動設計 (5) CA-SDK (3) Excel (3) 網誌架構 (2) MFC (1)