How to format CMFCPropertyGridProperty for float/double values - mfc

I have several properties that are floats. By default, the CMFCPropertyGridProperty displays these with 6 decimal places. I want them to display with 2 decimal places as in 12.75 vs 12.750000. So I put in the following line:
CMFCPropertyGridProperty::m_strFormatFloat = "%.2f";
That makes it display correctly with 2 decimal places. But when I change the value, %.2f is not a valid scanf format, so the program blows up.
I tried overriding the FormatProperty() function like this:
class PropertyGrid2Digits : public CMFCPropertyGridProperty
{
public:
PropertyGrid2Digits(const CString& strName, const float InitialValue = 0, LPCTSTR lpszDescr = NULL, DWORD_PTR dwData = 0);
virtual ~PropertyGrid2Digits();
virtual CString FormatProperty() {
CString str;
str.Format("%.2f", GetValue().fltVal);
return str;
}
};
but my version only gets called when the property is first created. Somehow, the CMFCPropertyGridProperty::FormatProperty() function gets called whenever the property gets painted.
Does anybody know how I can fix this?
Thanks!

Related

calling MFC edit control GetWindowText member can't compile

My code:
//the auto generated stuff (by right-click on editbox add variable to control option)
CEdit edit_name;
void CSendMessageWithActualDataDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_NAME, edit_name);
}
Looks OK so far, right?
void dofoo()
{
//IDE shows error squiggle at dot "name.GetWi" with no pop-up
//details given except the object's blurb text
CString foostring= edit_name.GetWindowTextW();
//error C2661: 'CWnd::GetWindowTextW': no overloaded function takes 0 arguments
// or instead with no parentheses added:
// no IDE error indication
CString foostring= edit_name.GetWindowTextW;
//error C3867: 'CWnd::GetWindowTextW': non-standard syntax; use '&' to create a pointer to member
}
According to:
Read text from edit control in MFC and VS2010
You can then retrieve the text in the edit box like this:
CEdit m_EditCtrl;
// ....
CString filePath1 = m_EditCtrl.GetWindowText()
.
.
.
// Yes, now it works...
I swear, this is like pulling teeth.
Apparently, this is all about convenience. Since you already know how to call CWnd::GetWindowText, but find it too cumbersome to use, just wrap everything up in a free function:
CString GetWindowText(CWnd const& wnd) {
CString s;
wnd.GetWindowText(s);
return s;
}
You can call that, passing in any object that (publicly) derives from CWnd (such as the CEdit edit_name) and get a CString object you can use any which way. (C++17 introduced guaranteed copy elision, meaning that s will never need to get copied.)
If you need to pass it into other Windows API functions that expect an LPCTSTR, CString implements operator PCXSTR that implicitly converts things as needed, e.g.
AfxMessageBox(GetWindowText(edit_name));
When you add a EDIT control to a dialog, and you go to map it to a variable, you have two choices:
Control
Value
If you select Value, then you can map it to a CString:
This creates the DDX_Text entry that was referred to in the comments:
void CTestDialogDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, m_strText);
}
Or, you can use GetDlgItemText if you want to just get the value directly from the control, and avoid creating a variable. This shows both:
void CTestDialogDlg::OnBnClickedButton1()
{
// This way you get the current text value from the edit control, without even creating a CEdit control member variable
CString strText;
GetDlgItemText(IDC_EDIT1, strText);
AfxMessageBox(strText);
// This way we use our mapped CString to the EDIT control
UpdateData(TRUE);
AfxMessageBox(m_strText);
}
If you do decide to map to a CEdit (a Control instead of a Value) then as you found out:
// Get the value from the control
m_editText.GetWindowText(strText);
AfxMessageBox(strText);
I assume your linked article is incorrect as GetWindowText does not return a CString.
By the way, with Visual Assist (a third-party extension) it shows the options correctly for Intellisense:
By the way, you may find this article useful (Dialog box controls and variable types). If you look at the table and find EDIT:
The above is with the previous rows on the table stripped out. As you can see, you can map the EDIT to more types of variables than just a CString.
this suffices:
class GetWindowText
{
CString s;
public:
GetWindowText(CWnd &w){w.GetWindowText(s);}
~GetWindowText() {}
CString operator()() {return s;}
};
just call like this:
MessageBoxW(GetWindowText(edit_name)());

const char* returns true in C++ CLI

since yesterday I have been struggling to turn text into label with code from another class, I came to this:
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
TestApp::UI_Error form("test", "test");
Application::Run(% form);
Using the above code, i display a winapi form that receives "test", "test" as 2x const char* on loading, the problem appears when im trying to set the text in labels using these variables
The code looks like this:
public:
UI_Error(const char* errorText, const char* errorCode)
{
InitializeComponent();
this->testLabel->Text = System::Convert::ToString(errorText);
}
For some reason, each time the return value shown in the win forms window is "true", although it should be "test" here, does anyone know the solution?
I tried to use std::string instead of const char*, unfortunately for some reason i get the error code that a static variable is required :(
Maybe I am wrong here, but System::Convert::ToString() seems not to have a method that accepts a const char* pointer. It looks like it gets cast to something else.
Try this instead:
Text = gcnew System::String(errorText);

CDialog update input box value

I am working with CDialogs and mfc. My dialog loads, and there are two input boxes and a button. When i click the button I want to have the values in the input boxes change. My current attempt is this
void
CInstanceNumberDlg::updateLeftRange(int i) {
GetDlgItem(IDC_2NDDERIV_WT)->SetWindowText((LPCTSTR)i);
UpdateData(false);
UpdateWindow();
}
I have also tried with other combinations of including UpdateData() and UpdateWindow(). This method is being reached with a valid integer, and that ID should be valid.
How can i get the value in the input box to be modified?
Thank you
Your problem is this line:
GetDlgItem(IDC_2NDDERIV_WT)->SetWindowText((LPCTSTR)i);
i is not a pointer to a string and so you should not be casting it to an LPCTSTR. What you need to do convert the value of i to a string and then pass a pointer to the resulting string to SetWindowText(). And I don't believe you need UpdateData() or UpdateWindow(). For example:
void CInstanceNumberDlg::updateLeftRange(int i)
{
TCHAR tszValue[32];
StringCchPrintf(tszValue, _countof(tszValue), TEXT("%d"), i);
GetDlgItem(IDC_2NDDERIV_WT)->SetWindowText(tszValue);
}

Win32 C++ DLL function gets garbage values in parameters

I have a Win32 C++ dll (A) that calls another Win32 C++ dll (B). (B) is loaded using LoadLibrary and contains a method:
Draw(HDC hDC, LPRECT lpRect, LPBUFFER buffer, LPOPTIONS options)
Buffer structure is defined as:
struct Buffer
{
char* pData;
long Length;
TCHAR FileName[MAX_PATH];
Extension Extension;
};
typedef Buffer BUFFER, *LPBUFFER;
(A) fills BUFFER with filename, length etc and calls the Draw function. The Draw function then uses the values from BUFFER. It all works fine when DLLs are compiled as 64-bit but if I compile them as 32-bit then I start getting garbage values in BUFFER fields in (B). Logs shows that the values are good in (A) but turn into garbage when they reach (B).
I tried changing the Structure Alignment Option /ZpX and calling convention for Draw method (__cdecl, __stdcall) but none helped. I think it is related to calling convention because if I change Draw function syntax and put BUFFER as first param then (B) gets correct values. What's going on here?
Function pointer type:
typedef bool (__cdecl *DrawFunc)(HDC hDC, LPRECT lpRect, LPBUFFER buffer, LPOPTIONS options);
Then in InitInstance:
pDrawFunc = (DrawFunc)GetProcAddress(dllHandle, "Draw");
UPDATE
1. As mentioned above, if I put BUFFER as first param then it receives correct values.
2. HDC being a single numeric value always receives correct value
3. RECT gets incorrect values, very large ones
I believe the problem has something to do with structs. Only structs get incorrect values.
UPDATE 2
OK I found out my own silly mistake, the declaration for Draw method had LPRECT whereas the implementation had RECT. My bad, sorry about that.
But I am still not sure why:
1. Other parameters were showing garbage values?
2. Why it worked in 64-bit?
Ok, I create a solution with 3 projects: library B, that contains Draw(), library A, that has Test(), that loads library B and call Draw() with some Buffer* and application test, that links with library A and calls Test(). Everything works fine, both for 32 bit and 64. Small snippet of Test():
#include "stdafx.h"
#include "A.h"
#include "../B/B.h"
namespace {
LPBUFFER CreateBuffer(const char* const data, LPCTSTR const name)
{
if(!data || !name)
return NULL;
LPBUFFER buffer = new BUFFER();
buffer->Length = static_cast<long>(strlen(data) + 1);
buffer->pData = new char[buffer->Length];
strcpy_s(buffer->pData, buffer->Length * sizeof(char), data);
buffer->Extension = 0;
::ZeroMemory(buffer->FileName, _countof(buffer->FileName) * sizeof(TCHAR));
_tcscpy_s(buffer->FileName, name);
return buffer;
}
void DestroyBuffer(LPBUFFER buffer)
{
delete [] buffer->pData;
buffer->Length = 0;
buffer->pData = NULL;
buffer->Extension = 0;
::ZeroMemory(buffer->FileName, _countof(buffer->FileName) * sizeof(TCHAR));
delete buffer;
}
} // namespace
A_API void Test()
{
HMODULE b_lib = ::LoadLibrary(_T("B.dll"));
if(!b_lib)
{
::OutputDebugString(_T("Can't load library\n"));
return;
}
typedef bool (*DrawFunction)(HDC hDC, LPRECT lpRect, LPBUFFER buffer, LPOPTIONS options);
DrawFunction draw = reinterpret_cast<DrawFunction>(::GetProcAddress(b_lib, "Draw"));
if(!draw)
{
::OutputDebugString(_T("Can't get address of Draw()"));
goto FINISH_LABEL;
}
LPBUFFER buffer = CreateBuffer("test", _T("path"));
draw(NULL, NULL, buffer, NULL);
DestroyBuffer(buffer);
FINISH_LABEL:
::FreeLibrary(b_lib);
b_lib = NULL;
}
And a whole solution: https://www.dropbox.com/s/5ei6ros9e8s94e2/B.zip

Why does the original CString get overwritten when passing a copy to the DrawText function with the DT_MODIFYSTRING option?

I've already found a workaround to this problem, but was just wondering if anyone knew what was actually happening to cause the problem I was seeing. My guess is that it has something to do with mutability of strings, but I thought the CString object accounted for that in the copy constructor.
The following code causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return mFileName;}
private:
CString mFileName;
};
class FileContainer {
private: File* mFile;
public:
FileContainer() {
mFile = new File("C:\temp.txt");
}
GetFilename(CString& fileName) {
fileName = mFile->GetFileName();
}
}
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt.GetFilename(filePath);
...
::DrawText(hDC, filePath, -1, &destRect, DT_PATH_ELLIPSIS | DT_MODIFYSTRING | DT_CALCRECT);
}
What happens is that the first time UpdateText is called, GetFilename returns C:\temp.txt. Assuming that the bounding rect caused the text to be truncated to "...\temp.txt" on the first call, "...\temp.txt" is what is returned from GetFilename on the second call to UpdateText.
Even more perplexing is that this didn't cause mFileName to be changed:
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt->GetFilename(filePath);
filePath = L"TEST";
}
GetFilename always returned C:\temp.txt. So it would seem that the DrawText function is somehow finding the original CString and modifying it. But how?
UPDATE: I figured I'd throw another odd chunk of code that also causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return CString(mFileName);}
private:
CString mFileName;
};
That seems like it should create a new object and return that new object. Yet, somehow, DrawText still overwrites mFileName.
If I change the code to the following, I don't have any issues:
class File {
public:
...
CString GetFilename() {return CString(mFileName.GetBuffer());}
private:
CString mFileName;
};
The only thing that seems to solve the problem is to construct a new CString the way I showed in the workaround. What is DrawText doing when I pass the DT_MODIFYSTRING option?
First, note that CString can be used as a raw string pointer in two ways:
operator LPCSTR - gives a pointer which should never be modified.
GetBuffer - gives a pointer to memory specifically for the purpose of modifying the string.
Now, DrawText is declared to accept a LPCSTR. So when you pass a CString object directly as in your code, it implicitly uses operator LPCSTR to give the function what it says it wants, a constant string pointer.
However, DT_MODIFYSTRING says that DrawText can modify the string it was given. So internally, DrawText must be throwing away the constness of the pointer and modifying the string anyway.
This combination is a bad thing. But the fault is mainly in the implmentation of DrawText which is violating its own declaration.
As for why this modifies other CString objects: Apparently when a CString object is copied, it delays copying the internal string memory until something tries to modify the string through a CString member function. But until that happens, the operator LPCSTR of each CString object would still point to the same shared internal memory. This is normally fine, as long as any code using it is obeying the rules of const-correctness. However, as we've already seen, DrawText with DT_MODIFYSTRING is not playing by the rules. Thus, it is overwriting memory shared by multiple CString objects.
So to fix this problem, you either need to stop using DT_MODIFYSTRING if you don't actually need the modified text. Or else you need to pass the string to DrawText using filePath.GetBuffer() and then call filePath.ReleaseBuffer() afterwards.
Well there are some discrepancies in the code that you posted:
In 'class File':
GetFileName() {return mFileName;}
There is no return type here? Also in the FileContainer class you define the stored 'File' object as a pointer, but in the GetFileName function you access it as if it was not a pointer?
File* mFile;
...
mFile.GetFileName();
As far as why this is happening well right now I can't really tell. Another work around however would be to change the GetFileName function to return a const ref, this should ensure that the returned value can never be changed.
const CString& GetFileName() { return mFileName; }