Splitting Child Window in MFC MDI Program - visual-c++

I am trying to split the Child Window of an MFC MDI progarm that I am working on but am having some problems. I know I have to use the CSplitterWnd class and have been trying to follow the instructions on the post here:
Create multi views in a CChildFrame using CSplitterWnd
but can't seem to get it to work, would anyone be able to offer me some advice with regard to these instructions, I have some specific questions:
Is CRightView also a CView derived class and what code should go in there if any?
Are m_pLeftView, m_pRightView, m_pPhongView and m_pPhongInfo all variables of the appropriate classes and do they have any particular type?
Where does CTreeView come from, does not seem to be a standard base class?
rc.Width in CChildFrame::OnCreateClient gives an undeclared identifier error, is there something I should be declaring somewhere here?
I would appreciate any advice about this, really struggling to get the splitter to work.
Thanks

After working on it for a couple of days I've managed to solve my own problem, I'm adding the solution here for anyone else who might have the same problem.
Declare the two view classes, in my case CElement View which is a CWnd derived class and CSampleViewer3dView which is a CView derived class.
In CChildFrame add a variable with access private, type CSplitterWnd and name m_wndSplitter.
Add an override for the OnCreateClient function in CChildFrame, this should add the code:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
to ChildFrm.h, you should also add a boolean flag m_bInitSplitter to ChildFrm.h:
BOOL m_bInitSplitter;
you also have to add:
m_bInitSplitter = false;
to the constructor of ChildFrm.cpp, the following code is added to ChildFrm.cpp when you add the variable using the wizard:
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
{
}
Put the following code into CChildFrame::OnCreateClient:
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
{
CRect cr;
GetWindowRect( &cr );
if (!m_wndSplitter.CreateStatic(this, 1, 2))
{
MessageBox("Error setting up splitter frames!", "Init Error!", MB_OK | MB_ICONERROR);
return FALSE;
}
if (!m_wndSplitter.CreateView( 0, 0, RUNTIME_CLASS(CElementView),
CSize(cr.Width()/2, cr.Height()), pContext ) )
{
MessageBox("Error setting up splitter frames!", "Init Error!", MB_OK | MB_ICONERROR);
return FALSE;
}
if (!m_wndSplitter.CreateView( 0, 1, RUNTIME_CLASS(CSampleViewer3dView),
CSize(cr.Width()/2, cr.Height()), pContext))
{
MessageBox("Error setting up splitter frames!", "Init Error!", MB_OK | MB_ICONERROR);
return FALSE;
}
m_bInitSplitter = TRUE;
return TRUE;
}
Add an override for the WM_SIZE message to CChildFrame and insert the following code:
void CChildFrame::OnSize(UINT nType, int cx, int cy)
{
CMDIChildWnd::OnSize(nType, cx, cy);
CRect cr;
GetWindowRect(&cr);
if (m_bInitSplitter && nType != SIZE_MINIMIZED)
{
m_wndSplitter.SetRowInfo( 0, cy, 0 );
m_wndSplitter.SetColumnInfo(0, cr.Width()*0.25 / 2, 50);
m_wndSplitter.SetColumnInfo(1, cr.Width()*0.75 / 2, 50);
m_wndSplitter.RecalcLayout();
}
}
You can edit the size of each window by changing the values of 0.25 and 0.75 to the required percentage of the screen that you want each view to take up.
Add header files for the two views to ChildFrm.cpp, e.g. ElementView.h and SampleViewer3dView.h.
You should then have two independent views in the child window of an MDI program.

Related

Create Uniform Buffers in Vulkan

i hava a problem with drawing meshes in Vulkan.
I want to bind a UniformBufferObject in the following form to a Object.
void mainLoop() {
..
vulkanDrawing.Draw();
plane.UpdateUniformBuffers();
..
}
To get the currentImage, I created a method SetCurrentImage(uint32_t currentImage).
SetCurrentImage is set from VulkanDrawing::Draw() Method.
This current image is used in the UpdateUniformBuffers().
I get only a black screen if I run this application.
Since, I want to see a square.
In the past, I called the UpdateUniformBuffers Method with an imageIndex parameter in VulkanDrawing::Draw().
I think it could be a problem with the fences or semaphores. But I don't know how I shall fix it.
Does I use eventually a wrong Architecture?
I have attached important Methods:
void CVulkanDrawing::Draw()
{
vkWaitForFences(m_LogicalDevice.getDevice(), 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(m_LogicalDevice.getDevice(), 1,inFlightFences[currentFrame]);
uint32_t imageIndex;
vkAcquireNextImageKHR(m_LogicalDevice.getDevice(), m_Presentation.GetSwapChain(), std::numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
for(unsigned int i = 0; i < m_VulkanMesh.size(); i++)
{
//m_VulkanMesh.at(i).UpdateUniformBuffers(imageIndex);
m_VulkanMesh.at(i).SetCurrentImage(imageIndex);
}
VkSubmitInfo submitInfo = {};
...
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
void CVulkanMesh::UpdateUniformBuffers()
{
...
vkMapMemory(m_LogicalDevice.getDevice(), uniformBuffersMemory[this->m_CurrentImage], 0, sizeof(ubo), 0, &data);
memcpy(data, &ubo, sizeof(ubo));
vkUnmapMemory(m_LogicalDevice.getDevice(), uniformBuffersMemory[this->m_CurrentImage]);
}
void CVulkanMesh::SetCurrentImage(uint32_t currentImage)
{
this->m_CurrentImage = currentImage;
}
I have additionally created a branch named: https://github.com/dekorlp/VulkanWrapper/tree/VulkanTest
I hope you can help me :)
Best regards
Pixma

MFC dialog form freezes

I have simple MFC dialog type window that calls external dll function and one of parameters is callback function. Callback function creates another dialog window if it is not created and updates label with information from function parameter:
int userNotify ( int iNotificationType, char* pcNotificationText )
{
if(statusDlg)
{
if ( !(statusDlg->IsWindowVisible()) )
{
statusDlg->ShowWindow(SW_SHOW);
}
statusDlg->showNotification(iNotificationType,pcNotificationText);
} else
{
statusDlg = new StatusDlg(NULL);
statusDlg->Create(StatusDlg::IDD,CWnd::GetDesktopWindow());
statusDlg->ShowWindow(SW_SHOW);
statusDlg->showNotification(iNotificationType,pcNotificationText);
}
return 0;
}
statusDlg is global variable and is very simple MFC dialog form with one static label. And it has one feature - it is placed on topmost.
BOOL StatusDlg::OnInitDialog()
{
staticStatus = (CStatic*)GetDlgItem(IDC_STATIC_TRN_STATUS_DIALOG);
...
SetWindowPos(&this->wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
return TRUE; // return TRUE unless you set the focus to a control
}
Dialog form is shown during callback and label shows required information. But if I try to move form with mouse it becomes like frozen like in picture below and information on label is not updated anymore. Why this happens? How to solve this problem?
When you create StatusDlg you give it a parent of the Desktop. That is very likely wrong and leads to your later issues. The parent of your second dialog should be the main dialog that invokes it.
int userNotify ( CWnd *pParentWnd, int iNotificationType, char* pcNotificationText )
{
...
statusDlg->Create(StatusDlg::IDD, pParentWnd);
...
}
The parent window pointer will simply be the this pointer when you call userNotify.

Unity Vuforia Google VR - Can't make onPointerEnter to GameObject change material for itself

I have two 3d buttons in my scene and when I gaze into any of the buttons it will invoke OnPointerEnter callback and saving the object the pointer gazed to.
Upon pressing Fire1 on the Gamepad I apply materials taken from Resources folder.
My problem started when I gazed into the second button, and pressing Fire1 button will awkwardly changed both buttons at the same time.
This is the script I attached to both of the buttons
using UnityEngine;
using UnityEngine.EventSystems;
using Vuforia;
using System.Collections;
public class TriggerMethods : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
Material _mat;
GameObject targetObject;
Renderer rend;
int i = 0;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
TukarMat();
}
public void OnPointerEnter(PointerEventData eventData)
{
targetObject = ExecuteEvents.GetEventHandler<IPointerEnterHandler>(eventData.pointerEnter);
}
public void OnPointerExit(PointerEventData eventData)
{
targetObject = null;
}
public void TukarMat()
{
Debug.Log("Value i = " + i);
if (i == 0)
{
ApplyTexture(i);
i++;
}
else if (i == 1)
{
ApplyTexture(i);
i++;
}
else if (i == 2)
{
ApplyTexture(i);
i = 0;
}
}
void ApplyTexture(int i)
{
rend = targetObject.GetComponent<Renderer>();
rend.enabled = true;
switch (i)
{
case 0:
_mat = Resources.Load("Balut", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 1:
_mat = Resources.Load("Khasiat", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 2:
_mat = Resources.Load("Alma", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
default:
break;
}
}
I sensed some logic error and tried making another class to only manage object the pointer gazed to but I was getting more confused.
Hope getting some helps
Thank you
TukarMat() is beeing called on both buttons when you press Fire1. If targetObject is really becoming null this should give an error on first button since it's trying to get component from a null object. Else, it'll change both as you said. Make sure OnPointerExit is beeing called.
Also, it seems you are changing the shared material.
The documentation suggests:
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
So, try changing the material property instead of sharedMaterial since it'll change the material for that object only.

Starting Doc/View application hidden

Using Visual studio 2010 and MFC Doc/View Applications I want my SDI application to start up completely hidden, and after sometime or with receiving some message from tray icon it shows the mainframe, view and so on. I change the line m_pMainWnd->ShowWindow(SW_NORMAL); to m_pMainWnd->ShowWindow(SW_HIDE); in BOOL CMyApp::InitInstance() but the main frame just flickers after executing the application and then goes hiiden what should I do inorder to avoid this problem and keep the showing capability of main frame when ever I want.
Here is the solution for SDI/MDI app: The new MFC (with VC2010) overrides the m_nCmdShow value with a setting stored in the system registry. To change this behaviour, simply override the LoadWindowPlacement virtual function in the application class.
BOOL CAdVisuoApp::LoadWindowPlacement(CRect& rectNormalPosition, int& nFflags, int& nShowCmd)
{
BOOL b = CWinAppEx::LoadWindowPlacement(rectNormalPosition, nFflags, nShowCmd);
nShowCmd = SW_HIDE;
return b;
}
Normally if you have VC2005 or earlier the following will do:
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
m_nCmdShow = SW_HIDE;
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it
m_pMainWnd->ShowWindow( m_nCmdShow);
m_pMainWnd->UpdateWindow();
Note that m_nCmdShow should be set to SW_HIDE before ProcessShallCommand for the flicker not to occur.
It looks like there might be a bug in VC2010 though. Since I have done this before it intrigued me and tried a fresh VC2010 project but it was not working. I noticed the problem was deep in the following MFC function.
BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle,
CWnd* pParentWnd, CCreateContext* pContext)
{
// only do this once
ASSERT_VALID_IDR(nIDResource);
ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE)
CString strFullString;
if (strFullString.LoadString(nIDResource))
AfxExtractSubString(m_strTitle, strFullString, 0); // first sub-string
VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
// attempt to create the window
LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource);
CString strTitle = m_strTitle;
if (!Create(lpszClass, strTitle, dwDefaultStyle, rectDefault,
pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext))
{
return FALSE; // will self destruct on failure normally
}
// save the default menu handle
ASSERT(m_hWnd != NULL);
m_hMenuDefault = m_dwMenuBarState == AFX_MBS_VISIBLE ? ::GetMenu(m_hWnd) : m_hMenu;
// load accelerator resource
LoadAccelTable(ATL_MAKEINTRESOURCE(nIDResource));
if (pContext == NULL) // send initial update
SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE);
return TRUE;
}
m_nCmdShow is still SW_HIDE when this function executes but it changes to SW_SHOWNORMAL when if (!Create(lpszClass... line executes. I don't know why this happens in VC2010 project only, sounds like a bug to me.
My sample project was SDI.
This comes from a dialog based application but you should be able to convert it to a Doc/View app as well. You need to handle the OnWindowPosChanging event. The key line is the the one inside the if statement. This allows my application to start completely hidden from view.
void CIPViewerDlg::OnWindowPosChanging( WINDOWPOS FAR* lpWindowPosition )
{
if( !m_bVisible )
{
lpWindowPosition->flags &= ~SWP_SHOWWINDOW;
}
CDialog::OnWindowPosChanging( lpWindowPosition );
}
Make sure that you are correctly turning off the WS_VISIBLE bit in CMainFrame::PreCreateWindow(CREATESTRUCT& cs). Something like this should worK:
cs.style &= ~WS_VISIBLE;
We had simply been negating the bit instead of turning it off, and we got away with it in VS 6.0 because this function was called only once. It is called twice in newer versions of Visual Studio, so in the second call we were flipping it right back on again. :-O
I tried all for Visual Studio 2010 and finished up with:
class CMainFrame : public CFrameWndEx
{
// ...
// Attributes
public:
BOOL m_bForceHidden;
// ...
// Overrides
public:
virtual void ActivateFrame(int nCmdShow = -1);
//...
};
CMainFrame::CMainFrame() : m_bForceHidden(TRUE)
{
// ...
}
void CMainFrame::ActivateFrame(int nCmdShow)
{
if(m_bForceHidden)
{
nCmdShow = SW_HIDE;
m_bForceHidden = FALSE;
}
CFrameWndEx::ActivateFrame(nCmdShow);
}
Other tricks did not work for me.
Found solution at:
http://forums.codeguru.com/showthread.php?478882-RESOLVED-Can-a-Doc-view-be-hidden-at-startup
I found in VS2017 (using BCGControlBar Pro which is what MFC Feature Pack was based on) that you have to handle things in two places:
BOOL CMainFrame::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)
{
if (!__super::LoadFrame(nIDResource, dwDefaultStyle, pParentWnd, pContext))
{
return FALSE;
}
// undo what __super::LoadFrame() does where it will set it to SW_NORMAL if not SW_MAXIMIZED
AfxGetApp()->m_nCmdShow = SW_HIDE;
}
BOOL CTheApp::LoadWindowPlacement(CRect& rectNormalPosition, int& nFflags, int& nShowCmd)
{
BOOL b = __super::LoadWindowPlacement(rectNormalPosition, nFflags, nShowCmd);
nShowCmd = SW_HIDE;
return b;
}

Getting edit box text from a modal MFC dialog after it is closed

From a modal MFC dialog, I want to extract text from an edit box after the dialog is closed. I attempted this:
CPreparationDlg Dlg;
CString m_str;
m_pMainWnd = &Dlg;
Dlg.DoModal();
CWnd *pMyDialog=AfxGetMainWnd();
CWnd *pWnd=pMyDialog->GetDlgItem(IDC_EDIT1);
pWnd->SetWindowText("huha max");
return TRUE;
It does not work.
The dialog and its controls is not created until you call DoModal() and as already pointed, is destroyed already by the time DoModal() returns. Because of that you cannot call GetDlgItem() neither before, nor after DoModal(). The solution to pass or retrieve data to a control, is to use a variable in the class. You can set it when you create the class instance, before the call to DoModal(). In OnInitDialog() you put in the control the value of the variable. Then, when the window is destroyed, you get the value from the control and put it into the variable. Then you read the variable from the calling context.
Something like this (notice I typed it directly in the browser, so there might be errors):
class CMyDialog : CDialog
{
CString m_value;
public:
CString GetValue() const {return m_value;}
void SetValue(const CString& value) {m_value = value;}
virtual BOOL OnInitDialog();
virtual BOOL DestroyWindow( );
}
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
SetDlgItemText(IDC_EDIT1, m_value);
return TRUE;
}
BOOL CMyDialog::DestroyWindow()
{
GetDlgItemText(IDC_EDIT1, m_value);
return CDialog::DestroyWindow();
}
Then you can use it like this:
CMyDialog dlg;
dlg.SetValue("stackoverflow");
dlg.DoModal();
CString response = dlg.GetValue();
Open your dialog resource, right-click on the textbox and choose "Add variable", pick value-type and CString
In the dialog-class: before closing, call UpdateData(TRUE)
Outside the dialog:
CPreparationDlg dlg(AfxGetMainWnd());
dlg.m_myVariableName = "my Value";
dlg.DoModal();
// the new value is still in dlg.m_myVariableName
DoModal() destroys the dialog box before it returns and so the value is no longer available.
It's hard to tell why you are setting m_pMainWnd to your dialog. To be honest, I'm not really sure what you are trying to do there. That's bound to cause problems as now AfxGetMainWnd() is broken.
Either way, you can't get the dialog box's control values after the dialog has been destroyed.
I often use
D_SOHINH dsohinh = new D_SOHINH();
dsohinh.vd_kichthuoc=v_kichthuocDOC;
dsohinh.vd_sohinh=v_soluongDOC;
if(dsohinh.DoModal()==IDOK)
{
v_soluongDOC=dsohinh.vd_sohinh;
v_kichthuocDOC=dsohinh.vd_kichthuoc;
}
SetModifiedFlag(true);
UpdateAllViews(NULL);
With dsohinh is Dialog form that you want to get data to mainform .
After get data then call SetModifiedFlag(true) to set view data updated.
call UpdateAllViews(NULL) to Set data to mainform
This solution may seem long, meaning that so much code has been written for this seemingly small task.
But when we have a list or tree inside the child window where all the items are created in the child window
and the items have to be moved to the parent window,
then it makes sense.
This source code can easily create a window and transfer information from the window before closing to the parents.
//copy the two functions in your code
//1- bool peek_and_pump(void)
// template<class T,class THISCLASS>
//2- void TshowWindow(int id,T *&pVar,THISCLASS *ths)
//and make two member variable
// bool do_exit;
// bool do_cancel;
//in child dialog class.
//set true value in do_exit in child dialog for exit
CchildDialog *dlg;
template<class T,class THISCLASS>
void TshowWindow(int id,T *&pVar,THISCLASS *ths)
{
T *p=pVar;
if(!p)
p= new T;
if(p->m_hWnd)
{
p->SetForegroundWindow();
}
else
{
delete p;
p= new T;
if(!(p->m_hWnd && IsWindow(p->m_hWnd)))
{
p->Create(id,ths);
if(IsWindow(p->m_hWnd))
p->ShowWindow(TRUE);
}
}
pVar=p;
}
bool peek_and_pump(void)
{
MSG msg;
#if defined(_AFX) || defined(_AFXDLL)
while(::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))
{
if(!AfxGetApp()->PumpMessage())
{
::PostQuitMessage(0);
return false;
}
}
long lIdle = 0;
while(AfxGetApp()->OnIdle(lIdle++))
;
#else
if(::PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
#endif
return true;
}
void CparentPage::OnBnClick1()
{
if(dlg)
{
dlg->DestroyWindow();
}
TshowWindow<CchildDialog,CparentPage>(IDD_DIALOG_child,dlg,this);
dlg->GetDlgItem(IDC_EDIT_1)->SetWindowText("");
dlg->m_temp_window.EnableWindow(FALSE);//enable or disable controls.
dlg->UpdateData(false);//for to be done enable of disable or else.
dlg->do_exit=false;
dlg->do_cancel=false;
while(dlg->do_exit==false)
{
peek_and_pump();//wait for dlg->do_exit set true
}
if( dlg->do_cancel==false )
{
CString str1;
dlg->GetDlgItem(IDC_EDIT_1)->GetWindowText(str1);
//or other member variale of CchildDialog
//after finish all work with dlg then destroy its.
}
dlg->DestroyWindow();
}
void CchildDialog::OnBnClickedOk()
{
UpdateData();
OnOK();
do_exit=true;
do_cancel=false;
}
void CchildDialog::OnBnClickedCancel()
{
OnCancel();
do_exit=true;
do_cancel=true;
}

Resources