Initializing DATE in MFC ATL control - atl

I have a method that returns DATE type, is there a way to initialize it before I make a use of it, other than the following:
DATE retValue = 0.0;
Thanks

Related

How to set cell values using Excel12v C interface

I have an Excel12v function using XLOPER to set some values on an Excel sheet. I can create XLLs fine as per Microsoft's XLL guide. I authored xladd-derive for Rust which enables this an allows returning scalars and ranges of values very simply.
However I would like, rather than return a value, to set a random cell to a value. There is xlSet function demonstrated below that does this and works fine.
short WINAPI xlSetExample()
{
XLOPER12 xRef, xValue;
xRef.xltype = xltypeSRef;
xRef.val.sref.count = 1;
xRef.val.sref.ref.rwFirst = 204;
xRef.val.sref.ref.rwLast = 205;
xRef.val.sref.ref.colFirst = 1;
xRef.val.sref.ref.colLast = 1;
xValue.xltype = xltypeInt;
xValue.val.w = 12345;
Excel12v(xlSet, 0, 2, (LPXLOPER12)&xRef, (LPXLOPER12)&xValue);
return 1;
}
but only works if it's called from a VBA macro
Sub test()
Application.Run("xlSetExample","12345")
End Sub
Is there an equivalent xlf* or xlc* function that allows one to set cell values but do not need to be called from a VBA macro
In general, Excel prevents spreadsheet functions from changing the values in cells. In effect, spreadsheet functions are given a read-only view of the values in the sheet.
This is the documentation for xlSet which states:
xlSet behaves as a Class 3 command-equivalent function; that is, it is
available only inside a DLL when the DLL is called from an object,
macro, menu, toolbar, shortcut key, or the Run button in the Macro
dialog box (accessed from View tab on the ribbon starting in Excel
2007, and the Tools menu in earlier versions).
The reason for this is to prevent circular references or other actions that would break or confuse the calculation tree. Excel would struggle to determine dependencies between cells if a function in one cell could change other cells' contents.
Consider the hypothetical function AddOne() which takes a number, adds one and uses this to set the cell immediately to the right via xlSet (or otherwise). What would happen if the formula in cell A1 were =AddOne(B1)?
This Excel SDK reference gives more information. Namely:
Different Types of Functions
Excel4 and Excel12 distinguish among three classes of functions. The
functions are classified according to the three states in which Excel
might call the DLL.
Class 1 applies when the DLL is called from a worksheet as a result of
recalculation.
Class 2 applies when the DLL is called from within a function macro or
from a worksheet where it was registered with a number sign (#) in the
type text.
Class 3 applies when a DLL is called from an object, macro, menu,
toolbar, shortcut key, ExecuteExcel4Macro method, or the
Tools/Macro/Run command. For more information, see Excel Commands,
Functions, and States.
Only Class 3 functions can call xlSet.
So, in summary, the Excel application really doesn't want users to change one cell from a function call in another. As always, if you work hard enough you could probably achieve this (eg get the COM application object pointer by some method and modify the cell that way, or set up a callback to modify the cell asynchronously), but you might have unpredictable results.

How can an Excel XLL tell if a UDF is being called from the formula bar?

I am looking for a way to determine inside my XLL whether the call is coming from the re-calculation process or from a new formula being entered for the cell. I have seen examples where the XLL can detect if it is being called from the Function Wizard (via checking the window class etc), but what if it is typed directly into the cell?
I can get the reference of the cell from the xlfCaller function:
XLOPER xlRef;
Excel(xlfCaller, &xlRef, 0);
but that doesn't get me very far. I am trying to mimic Excel's handling of the TODAY() function, which changes the cell number format if the formula is entered in the cell directly (rather than pasted or called from within another function).
It's possible to replicate this aspect of the TODAY() behaviour by hooking the SheetChange event - this will only fire when a formula is entered and not when it's recalculated (even with Ctrl-Alt-F9). I don't know if TODAY() uses this approach or perhaps it uses something not available to user-defined functions. In addition, replicating TODAY() would require parsing the formula and applying some logic to determine whether it applies the date format or not, this would be a bit tricky but there are some Excel formulas parsers out there.
In xlOil (disclaimer: I wrote it) it could be implemented as:
XLO_FUNC_START(testToday())
{
CallerInfo caller;
if (!caller.fullSheetName().empty()) // Check caller is worksheet
{
// Add a SheetChange handler
auto handle = xloil::Event::SheetChange().bind(
[=](const wchar_t* wsName, const Range& target)
{
// Need to check range here as well to avoid being triggered by another sheet change
if (wsName == caller.sheetName())
excelApp().Range[caller.writeAddress().c_str()]->NumberFormat = L"dd-mm-yyyy";
}
);
// Wait 0.5 sec then unbind the event handler by queuing a window message with Excel
auto milliSecsDelay = 500;
excelPost([=]() mutable
{
handle.reset(); // Removes the SheetChange handler
}, QueueType::WINDOW, 0, 0, milliSecsDelay);
}
// This is how to get the current date in C++
std::tm buf;
auto now = std::time(0);
localtime_s(&buf, &now);
return returnValue(buf); // xlOil will convert the std::tm to an Excel date
}
XLO_FUNC_END(testToday);
This is just a proof-of-concept and could do with some tidying for example the range should be checked and the overhead could be quite large if the function was called many times.

How to get the currently selected text in a CComboBoxEx?

My first approach to the problem was to call the GetWindowsText method on the CComboBoxEx control, but I found that there is no associated text. After analyzing the control with Spy++ and reading some documentation on CComboBoxEx, I realised that these type of controls are only the parent of a classic ComboBox:
I tried using the GetLBText() method on the child ComboBox, passing GetCurSel() as an argument, but I only get some wrong text (the correct text should be "English"):
Am I missing something? Thanks in advance!
What you want to do is map the control to a int variable using Class Wizard:
Now it is easy to access the selected text at any time. You need to use the GetItem function. For example (code not tested):
COMBOBOXEXITEM cmbItem;
CString strText;
cmbItem.mask = CBEIF_TEXT;
cmbItem.iItem = m_cbItemIndex;
cmbItem.pszText = strText.GetBuffer(_MAX_PATH);
m_cbMyCombo.GetItem(&cmbItem);
strText.ReleaseBuffer();
In short, you need to use the COMBOBOXEXITEM and initialise it with the right flags to state what information you want to get from the extended combo. That, and the item index. Job done!
I realise that you have your own inherited class, but the mechanics are the same. You don't use GetLBText. You use the structure with the index and GetItem to get the selected text.
In the end I managed to retrieve the correct name; as you can see in the image below, the ComboBox is only a child of a CombBoxEx32:
I retrieved the pointer to the parent ComboBoxEx32 from the child ComboBox, and searched for the text this way:
CString szText;
CComboBoxEx cbParentCombo ;
cbParentCombo.Attach( GetParent()->GetSafeHwnd()) ;
cbParentCombo.GetLBText( GetCurSel(), szText) ;
cbParentCombo.Detach() ;
My mistake was that I was calling GetLBText() directly from the child ComboBox, instead of the parent CComboBoxEx; because of that, all I was getting was some random gibberish. GetLBText() was indeed the correct solution.

MFC C++ CListBox get selected item

First let me say that I've been searching for a solution for couple of days now...
I'm trying to get selected item for ListBox. This is my code:
CListBox * pList1 = (CListBox *)GetDlgItem(IDC_LIST1);
CString ItemSelected;
// Get the name of the item selected in the Sample Tables list box
// and store it in the CString variable declared above
pList1->GetText(pList1->GetCurSel(), ItemSelected);
MessageBox(ItemSelected, "TEST", MB_OK);
Now when i try this i get an error message saying "The Parameter is incorect"
Your code looks OK except error handling. Also MessageBox parameters look incorrect. The first parameter should be of type HWND. I believe that this is the root cause of your problems. Use MFC standard AfxMessageBox instead:
CListBox * pList1 = (CListBox *)GetDlgItem(IDC_LIST1);
int nSel = pList1->GetCurSel();
if (nSel != LB_ERR)
{
CString ItemSelected;
pList1->GetText(nSel, ItemSelected);
AfxMessageBox(ItemSelected);
}
If the CListBox is in single selection mode, the CListBox::GetCurSel will return the selected index.
If the CListBox is in multi-selection mode, you should use CListBox::GetSelItems which will return a list of indices.
You cannot mix'n'match the functions.
And always check return codes (as others already wrote).
If You already have a data member MyList(of classCListBox) :
int nSel = MyList.GetCurSel();
CString ItemSelected;
if (nSel != LB_ERR)
{
MyList.GetText(nSel, ItemSelected);
}
CWnd class has a MessageBox function which does not need a HWND parameter. But yes, AfxMessageBox is a little bit more easier to use and can be called anywhere in the MFC code without having a CWnd-derived object. And a beside note: if call a WinAPI function inside MFC code (not needed here, but possible in other cases) it's good to prepend it with scope resolution operator in order to avoid any confusion, mistake and/or name conflict (e.g. ::MessageBox...).
One possible cause for "invalid parameter" error in OP code is that it uses an ANSI string literal ("TEST") in a UNICODE build configuration. This case, must use an UNICODE string literal (L"TEST") or a little bit better, use _T macro (_T("TEST")) that makes it possible to build in both ANSI and UNICODE configurations.

How do I rename the MFC ribbon panel?

I am programming a multi-language application which supports dynamical switch from one language to another. Since there is a GetName function in class CMFCRibbonPanel, I think there should also be a SetName function. But unfortunately I can't find the desired function. How do I rename a ribbon panel dynamically? Thank you very much.
the panel name is protected.
you can derive your own class from CMFCRibbon and add a "SetName" method.
class MyRibbonPanel : public CMFCRibbonPanel
{
public:
MyRibbonPanel(LPCTSTR lpszName = NULL, HICON hIcon = NULL ) : CMFCRibbonPanel(lpszName, hIcon) {};
void SetName(CString& name ) { m_strName = name; };
};
for example ( after creating a dummy SDI application in VS2010 )
CMFCRibbonCategory* pCategory = m_wndRibbonBar.AddCategory(_T("&Legume"),
IDR_PROPERTIES,
IDB_PROPERTIES_HC );
MyRibbonPanel* pMyPanel = (MyRibbonPanel*)pCategory->AddPanel(_T("Patate"), m_PanelImages.ExtractIcon(1));
pMyPanel->SetKeys(_T("zc"));
pMyPanel->SetCenterColumnVert();
pMyPanel->SetJustifyColumns();
CString s(_T("sdcasdc"));
pMyPanel->SetName(s);
It might also be useful to others if you have created your ribbons via the VS ribbon UI and don't want to have to manually create them.
Working on from the previous answer.
As panels don't have id's you can't select them to create a pointer too. But if the panels contain elements with ID's you can use these to create a pointer to the panel then rename instead of having to manually create it.
For example I have a combo on my panel, ID_TEST_COMBO
CMFCRibbonComboBox* m_RibbonTestCombo;
MyRibbonPanel* m_ribbonPanel;
m_RibbonTestCombo= DYNAMIC_DOWNCAST(CMFCRibbonComboBox,m_wndRibbonBar.FindByID(ID_TEST_COMBO));
m_ribbonPanel = DYNAMIC_DOWNCAST(MyRibbonPanel, m_RibbonSSSRules->GetParentPanel());
CString s(_T("sdcasdc"));
m_ribbonPanel->SetName(s);
This allows you to change the text without having to manually create the panel
You can try SetWindowText function.
Also you could override the drawing of the text and add you own text there.
Hope this helps.
You'll probably have to remove and re-add it. That's how some other MFC ribbon functions work.
Combining https://stackoverflow.com/a/5120994/6648895 and https://stackoverflow.com/a/25180098/6648895
is what finally worked for me in VS 2013:
MyRibbonPanel* m_ribbonPanel;
m_ribbonPanel = static_cast<MyRibbonPanel*>(m_wndRibbonBar.GetCategory(1)->GetPanel(1));
CString s(_T("sdcasdc"));
m_ribbonPanel->SetName(s);