2013-04-05 49 views
0

我正在處理一個需要我讀取/寫入Excel中給定單元格中的複選框狀態的問題。如何在C++中讀取/寫入excel複選框狀態

我知道你可以訪問使用COM/OLE訪問的ActiveX控件。但是,我一直無法找到解決這個問題的辦法。事實上,我甚至不確定您是否可以使用行列訪問複選框。我研究了activeX複選框的屬性。發現頂部和左側的屬性,但不是行列

我想問: 1.是否有任何直接的方法來做到這一點。 2.如果不是,任何間接的方式,比如說獲得頂部/左側列,然後獲得行/列位置並比較兩者。 3.有什麼辦法可以用表單控件做同樣的事情嗎?

回答

2

感謝大家的回覆。我在MSDN上發現了一些有用的東西。已經編輯了一點,併爲您的支持,每個人都與您分享..謝謝

(原諒格式)

從MSDN

Autowrap()函數是完全未經編輯:

HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...) { 
// Begin variable-argument list... 
va_list marker; 
va_start(marker, cArgs); 

if(!pDisp) { 
    MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010); 
    _exit(0); 
} 

// Variables used... 
DISPPARAMS dp = { NULL, NULL, 0, 0 }; 
DISPID dispidNamed = DISPID_PROPERTYPUT; 
DISPID dispID; 
HRESULT hr; 
char buf[200]; 
char szName[200]; 


// Convert down to ANSI 
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL); 

// Get DISPID for name passed... 
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID); 
if(FAILED(hr)) { 
    sprintf(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr); 
    MessageBox(NULL, buf, "AutoWrap()", 0x10010); 
    _exit(0); 
    return hr; 
} 

// Allocate memory for arguments... 
VARIANT *pArgs = new VARIANT[cArgs+1]; 
// Extract arguments... 
for(int i=0; i<cArgs; i++) { 
    pArgs[i] = va_arg(marker, VARIANT); 
} 

// Build DISPPARAMS 
dp.cArgs = cArgs; 
dp.rgvarg = pArgs; 

// Handle special-case for property-puts! 
if(autoType & DISPATCH_PROPERTYPUT) { 
    dp.cNamedArgs = 1; 
    dp.rgdispidNamedArgs = &dispidNamed; 
} 

// Make the call! 
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL); 
if(FAILED(hr)) { 
    sprintf(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr); 
    MessageBox(NULL, buf, "AutoWrap()", 0x10010); 
    _exit(0); 
    return hr; 
} 
// End variable-argument section... 
va_end(marker); 

delete [] pArgs; 

return hr; 
} 

下面是編輯main()函數:

int main() 
{ 
// Initialize COM for this thread... 
    CoInitialize(NULL); 

    // Get CLSID for our server... 
    CLSID clsid; 
    HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid); 

    if(FAILED(hr)) { 

     ::MessageBox(NULL, "CLSIDFromProgID() failed", "Error", 0x10010); 
     return -1; 
    } 

    // Start server and get IDispatch... 
    IDispatch *pXlApp; 
    hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pXlApp); 

    if(FAILED(hr)) { 
     ::MessageBox(NULL, "Excel not registered properly", "Error", 0x10010); 
     return -2; 
    } 

    // Make it visible (i.e. app.visible = 1) 
    { 
     VARIANT x; 
     x.vt = VT_I4; 
     x.lVal = 1; 
     AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"Visible", 1, x); 
    } 

    // Make it visible (i.e. app.visible = 1) 
    { 
     VARIANT x; 
     x.vt = VT_BSTR; 
     x.bstrVal = ::SysAllocString (L"D:\\"); 
     AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, L"DefaultFilePath", 1, x); 
    } 

    // Get Workbooks collection 
    IDispatch *pXlBooks; 
    { 
     VARIANT result; 
    VariantInit(&result); 
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, L"Workbooks", 0); 
    pXlBooks = result.pdispVal; 
    } 

    // Call Workbooks.Add() to get a new workbook... 
    IDispatch *pXlBook; 
    { 
     VARIANT parm; 
    parm.vt = VT_BSTR; 
    parm.bstrVal = ::SysAllocString(L"a.xlsx"); 

    VARIANT result; 
    VariantInit(&result); 
    AutoWrap(DISPATCH_METHOD, &result, pXlBooks, L"Open", 1,parm); 
    pXlBook = result.pdispVal; 
    } 

    // Get ActiveSheet object 
    IDispatch *pXlSheet; 
    { 
enter code here 
    VARIANT result; 
    VariantInit(&result); 
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlBook, L"ActiveSheet",0); 
    if(result.pdispVal != NULL) 
    pXlSheet = result.pdispVal; 
    } 

    //Get shapes collection.. 
    IDispatch *pShapes; 
    { 
     VARIANT result; 
    VariantInit(&result); 
    AutoWrap(DISPATCH_PROPERTYGET, &result, pXlSheet, L"Shapes",0); 
    pShapes = result.pdispVal; 
} 

int nControls; 
{ 
    VARIANT result; 
    VariantInit(&result); 
    AutoWrap(DISPATCH_PROPERTYGET, &result, pShapes, L"Count",0); 
    if(result.vt == VT_I4) 
     nControls = result.lVal; 
} 

for(int i = 1; i <= nControls; ++i) { 

    IDispatch * pShape; 

    { 
     VARIANT x; 
     x.vt = VT_I4; 
     x.lVal = i; 

     VARIANT result; 
     VariantInit(&result); 
     AutoWrap(DISPATCH_METHOD, &result, pShapes, L"Item",1, x); 
     pShape = result.pdispVal; 
    } 

    IDispatch *pControlFormat = NULL; 
    { 
     VARIANT result; 
     VariantInit(&result); 
     AutoWrap(DISPATCH_PROPERTYGET, &result, pShape, L"Type",0); 

     // msoFormControl 
     if(result.lVal == 8) 
     { 
      VARIANT result0; 
      VariantInit(&result0); 
      AutoWrap(DISPATCH_PROPERTYGET, &result0, pShape, L"FormControlType",0); 

      // xlCheckBox 
      if(result0.lVal == 1) { 

       // Get range containing cell to be tested 
       VARIANT result2; 
       VariantInit(&result2); 
       IDispatch * range; 
       { 
        VARIANT param1; 
        param1.vt= VT_BSTR; 
        param1.bstrVal = ::SysAllocString(L"F5"); 

        VARIANT result3; 
        VariantInit(&result3); 
        AutoWrap(DISPATCH_PROPERTYGET, &result3, pXlSheet,L"Range",1,param1); 
        range = result3.pdispVal; 
       } 

       // Get the top, left, bottom, right of cell 
       DOUBLE top, left, bottom, right; 
       { 
        VARIANT result4; 
        VariantInit(&result4); 

        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Top",0); 
        top = result4.dblVal; 

        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Left",0); 
        left = result4.dblVal; 

        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Height",0); 
        bottom = top + result4.dblVal; 

        AutoWrap(DISPATCH_PROPERTYGET,&result4, range,L"Width",0); 
        right = left + result4.dblVal; 
       } 
       range->Release(); 

       // Get the top, left of checkbox 
       FLOAT shapetop, shapeleft; 
       { 
        VARIANT result5; 
        VariantInit(&result5); 

        AutoWrap(DISPATCH_PROPERTYGET,&result5, pShape,L"Top",0); 
        shapetop = result5.fltVal; 

        AutoWrap(DISPATCH_PROPERTYGET,&result5, pShape,L"Left",0); 
        shapeleft = result5.fltVal; 
       } 

       // Get hold of control format 
       VARIANT result1; 
       VariantInit(&result1); 
       AutoWrap(DISPATCH_PROPERTYGET, &result1, pShape, L"ControlFormat",0); 
       pControlFormat = result1.pdispVal; 



       // Check if the checkbox if within the range boundary. If yes, then check it, else dont 
       if((top <= shapetop) && (bottom >= shapetop) && (left <= shapeleft) && (right >= shapeleft)) { 

        // Perpare the parameter 
        VARIANT parm; 
        VARIANT_BOOL t = VARIANT_TRUE;; 
        parm.vt = VT_BOOL; 
        parm.pboolVal = &t; 

        AutoWrap(DISPATCH_PROPERTYPUT, NULL, pControlFormat, L"Value", 1, parm); 
       }     
      } 
     } 
    } 

    if(pControlFormat != NULL) pControlFormat->Release(); 
    pShape->Release(); 
} 

    // to save when we tell Excel to quit... 
    { 
     VARIANT x; 
    x.vt = VT_I4; 
    x.lVal = 1; 
    AutoWrap(DISPATCH_METHOD, NULL, pXlBook, L"Save", 0); 
    } 

    // Wait for user... 
    ::MessageBox(NULL, "All done.", "Notice", 0x10000); 

    // Tell Excel to quit (i.e. App.Quit) 
    AutoWrap(DISPATCH_METHOD, NULL, pXlApp, L"Quit", 0); 

    // Release references... 

    pShapes->Release(); 

    pXlSheet->Release(); 
    pXlBook->Release(); 
    pXlBooks->Release(); 
    pXlApp->Release(); 


    // Uninitialize COM for this thread... 
    CoUninitialize(); 

    return 0; 
} 

進一步的細節:
http://support.microsoft.com/kb/216686
http://msdn.microsoft.com/en-us/library/office/bb149081%28v=office.12%29.aspx

0

儘管出現了,但電子表格並未包含複選框,他們被繪在它的頂部。所以他們不知道任何關於行或列的信息,你不能以這種方式引用它們。

複選框保存在OLEObjects集合中。從here這個例子應該給你的信息,你需要:

Private Sub chkFinished_Click() 
    ActiveSheet.OLEObjects("CheckBox1").Object.Value = 1 
End Sub 
0

我不認爲複選框的是「細胞」,有一對夫婦的方式(即我能想到的),你可以得到的值。

第一:不要檢查複選框的值,檢查它的鏈接的單元格的值(如果其被分配的)

二:使用類似下面的僞代碼來確定哪些控制是哪裏。

ForEach Sheet.Controls As Control 
    If Control.Type = Checkbox Then 
    Row = 1 
    YPos = 0 
    Do 
     If YPos + Sheet.Rows(Row).RowHeight > Control.Top Then 
     Exit Do 
     Else 
     YPos = YPos + Sheets.Rows(Row).RowHeight 
     End If 
    Loop 
    Column = 1 
    XPos = 0 
    Do 
     If XPos + Sheet.Columns(Column).ColumnWidth > Control.Left Then 
     Exit Do 
     Else 
     XPos = XPos + Sheets.Columns(Column).ColumnWidth 
     End If 
    Loop 
    Debug.Print Control.Name & " is in Cell(" & XPos & ", " & YPos & ")" 
    End If 
End ForEach 
+0

嗨@nick,我感謝您的幫助與算法..因爲你看到我提到了類似的技術在我的問題。我正在尋找的是一些關於COM在C++中的幫助。你能幫我用上面的算法運行的消息嗎?我一直無法找到與自己相關的任何東西。 – skaur 2013-04-08 04:21:00

+0

我從來沒有真正使用過com,所以不確定。你在使用excel互操作嗎?我的代碼只會在你的腦海中運行,就像僞代碼一樣。我認爲C++有一些類似VBA的對象庫?您需要查看控件集合的可用對象以及與行高和寬度有關的內容。 – NickSlash 2013-04-08 11:16:31

相關問題