how do i make MFC SDI Client window Non-movable? - mfc

i have re sized the client window initially and also made it non-re sizable. Only problem, i am facing right now is when i move the window ( drag title bar left or right ) , the contents in it disappears that is why i want to make it fixed at top left until termination.
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if( !CFrameWnd::PreCreateWindow(cs) )
return FALSE;
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.x=0;
cs.y=0;
// Create a window without min/max buttons or sizable border
cs.style = WS_OVERLAPPED | WS_SYSMENU | WS_BORDER;
return TRUE;
}
here is my OnDraw() :
void CblockmaniaView::OnDraw(CDC* pDC)
{
CblockmaniaDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CImage* img= new CImage();
pDC->FillSolidRect( 0, 0,810,640, RGB(128,128,128));
for (int i=0;i<16;i++)
{
for (int j=0;j<16;j++)
{
if (updateInfo[j]==true)
{
if (GetDocument()->x.map[i][j]!=NULL)
{
x=GetDocument()->x.map[i][j]->x;
y=GetDocument()->x.map[i][j]->y;
img->Load(GetDocument()->x.map[i][j]->imgPath);
img->Draw(pDC->m_hDC,40*y,40*x, 40, 40);
img->Destroy();
}
}
}
}
if (firstRun)
{
pDC->FillSolidRect( 640, 0, 10,640, RGB(0,0,0));
img->Load(L"C:\\Users\\Ad33l's\\Documents\\Visual Studio 2012\\Projects\\block mania\\block mania\\res\\score.png");
img->Draw(pDC->m_hDC,655,25,150,30);
img->Destroy();
}
for (int i=0;i<16;i++)
{
updateInfo[i]=false;
}
}

Related

How to create a CDockablePane in a CFrameWnd with C++ in MFC

At first I called the Create method of the CFrameWnd within another class.
Then I continued with the Create method of CDockablePane with the FrameWnd as the pParentWnd parameter.
The second Create was not successful, an assertion occured in the following code:
void CMFCDragFrameImpl::Init(CWnd* pDraggedWnd)
{
ASSERT_VALID(pDraggedWnd);
m_pDraggedWnd = pDraggedWnd;
CWnd* pDockSite = NULL;
if (m_pDraggedWnd->IsKindOf(RUNTIME_CLASS(CPaneFrameWnd)))
{
CPaneFrameWnd* pMiniFrame = DYNAMIC_DOWNCAST(CPaneFrameWnd, m_pDraggedWnd);
pDockSite = pMiniFrame->GetParent();
}
else if (m_pDraggedWnd->IsKindOf(RUNTIME_CLASS(CPane)))
{
CPane* pBar = DYNAMIC_DOWNCAST(CPane, m_pDraggedWnd);
ASSERT_VALID(pBar);
CPaneFrameWnd* pParentMiniFrame = pBar->GetParentMiniFrame();
if (pParentMiniFrame != NULL)
{
pDockSite = pParentMiniFrame->GetParent();
}
else
{
pDockSite = pBar->GetDockSiteFrameWnd();
}
}
m_pDockManager = afxGlobalUtils.GetDockingManager(pDockSite);
if (afxGlobalUtils.m_bDialogApp)
{
return;
}
ENSURE(m_pDockManager != NULL); <-----------------------
}
Somehow a docking manager seems to be missing. Is it possible that CFrameWnd is not suitable for CDockablePane? Or the docking manager needs to be initialized?
Thanks for your help (code snippets are welcome)!
To add a dockable pane to your project, the first step is to derive a new class from CDockablePane and you must add two message handlers for OnCreate and OnSize, and add a member child window as the main content. Your simple CTreePane class should look like this:
class CTreePane : public CDockablePane
{
DECLARE_MESSAGE_MAP()
DECLARE_DYNAMIC(CTreePane)
protected:
afx_msg int OnCreate(LPCREATESTRUCT lp);
afx_msg void OnSize(UINT nType,int cx,int cy);
private:
CTreeCtrl m_wndTree ;
};
int CTreePane::OnCreate(LPCREATESTRUCT lp)
{
if(CDockablePane::OnCreate(lp)==-1)
return -1;
DWORD style = TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT|
WS_CHILD|WS_VISIBLE|TVS_SHOWSELALWAYS | TVS_FULLROWSELECT;
CRect dump(0,0,0,0) ;
if(!m_wndTree.Create(style,dump,this,IDC_TREECTRL))
return -1;
return 0;
}
In the OnSize handler, you should size your control to fill the entire dockable pane client area.
void CTreePane::OnSize(UINT nType,int cx,int cy)
{
CDockablePane::OnSize(nType,cx,cy);
m_wndTree.SetWindowPos(NULL,0,0,cx,cy, SWP_NOACTIVATE|SWP_NOZORDER);
}
To support a dockable pane in your frame, you must first derive from the Ex family of frames (CFrameWndEx, CMDIFrameWndEx, ..) and in the OnCreate handler, you should initialize the docking manager by setting the allowable docking area, general properties, smart docking mode, …etc.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
...
CDockingManager::SetDockingMode(DT_SMART);
EnableAutoHidePanes(CBRS_ALIGN_ANY);
...
}void CMainFrame::OnTreePane()
{
if(m_treePane && m_treePane->GetSafeHwnd())
{
m_treePane->ShowPane(TRUE,FALSE,TRUE);
return ;
}
m_treePane = new CTreePane;
UINT style = WS_CHILD | CBRS_RIGHT |CBRS_FLOAT_MULTI;
CString strTitle = _T("Tree Pane");
if (!m_treePane->Create(strTitle, this,
CRect(0, 0, 200, 400),TRUE,IDC_TREE_PANE, style))
{
delete m_treePane;
m_treePane = NULL ;
return ;
}
m_treePane->EnableDocking(CBRS_ALIGN_ANY);
DockPane((CBasePane*)m_treePane,AFX_IDW_DOCKBAR_LEFT);
m_treePane->ShowPane(TRUE,FALSE,TRUE);
RecalcLayout();
}

Very low framerate when using full-screen window while multiple other windows are open

Sorry for the long title, but I have a very specific problem that can't really be expressed any more concisely. I'm programming a game engine (GitHub link: here) and I'm trying to let the client create windows on top of the main window which the application supplies automatically.
I've completely managed to get this to work but I'm bothered with the framerate of the main window when it enters full-screen mode (either on initialization or when the user presses alt+enter). I haven't benchmarked the performance, but it is visibly bad (so probably around 20-30 FPS) and the performance only drops when the user creates another window (it doesn't even have to be showing).
Since all of the windows the user creates are children of the main window, I have to hide them before entering full-screen mode.
I have a lot of code in my window class (over 1000 lines), so giving you a minimal example will be very difficult. If you must see the code, please visit the GitHub repo (under platform/windows you will find the code I'm referencing). I wonder if this is a strange artifact of having multiple windows open in the same process, or if I'm just missing some code.
That being said, here is some actual client code:
SandboxApp.h
#pragma once
#include<Infinity.h>
class SandboxApp : public Infinity::Application
{
private:
Infinity::Window *m_popup_window;
Infinity::Rasterizer *m_rasterizer;
Infinity::OrthoCamera m_camera;
Infinity::Renderer2D m_renderer;
Infinity::Texture2D *m_texture;
float m_aspect_ratio;
public:
SandboxApp();
~SandboxApp();
void OnApplicationEntered(Infinity::ApplicationEnteredEvent *event) override;
void OnUserCreate(Infinity::UserCreateEvent *event) override;
void OnUserUpdate(Infinity::UserUpdateEvent *event) override;
void OnUserRender(Infinity::UserRenderEvent *event) override;
void OnUserDestroy(Infinity::UserDestroyEvent *event) override;
void OnWindowResized(Infinity::WindowResizedEvent *event) override;
void Exit(const char *message);
};
SanboxApp.cpp
#define INFINITY_ENTRY_POINT
#include"SandboxApp.h"
SandboxApp::SandboxApp():
m_popup_window(nullptr),
m_rasterizer(nullptr),
m_renderer(),
m_texture(),
m_aspect_ratio(),
m_camera()
{}
SandboxApp::~SandboxApp()
{}
void SandboxApp::Exit(const char *message)
{
INFINITY_CLIENT_ERROR(message);
RequestExit();
}
void SandboxApp::OnApplicationEntered(Infinity::ApplicationEnteredEvent *event)
{
Infinity::Window::WindowParams &params = event->GetMainWindowParams();
params.fullscreen = true;
params.auto_show = false;
}
void SandboxApp::OnUserCreate(Infinity::UserCreateEvent *event)
{
Infinity::Window *window = GetMainWindow();
m_popup_window = Infinity::Window::CreateWindow();
Infinity::Window::WindowParams window_params;
window_params.width = 300;
window_params.height = 300;
window_params.title = "Popup window!";
if (!m_popup_window->Init(window_params))
{
Exit("Error initializing popup window");
return;
}
// Set clear color
Infinity::Context *context = Infinity::Window::GetContext();
context->SetClearColor(0.0f, 0.0f, 0.0f, 1.0f);
m_popup_window->MakeContextCurrent();
context = Infinity::Window::GetContext();
context->SetClearColor(0.0f, 0.0f, 1.0f, 1.0f);
window->MakeContextCurrent();
// Initialize other resources
m_rasterizer = Infinity::Rasterizer::CreateRasterizer();
if (!m_rasterizer->Init(Infinity::Rasterizer::CullMode::NONE, true))
{
Exit("Error initializing rasterizer");
return;
}
m_rasterizer->Bind();
if (!m_renderer.Init())
{
Exit("Error initializing Renderer2D");
return;
}
m_texture = Infinity::Texture2D::CreateTexture();
if (!m_texture->Init("assets/image.png"))
{
Exit("Error initializing texture");
return;
}
INFINITY_CLIENT_INFO("Client created");
window->Show();
m_popup_window->Show();
event->Consume();
}
void SandboxApp::OnUserUpdate(Infinity::UserUpdateEvent *event)
{
Infinity::Window *window = GetMainWindow();
if (KeyPressed(Infinity::KeyCode::Escape))
{
if (window->CursorEnabled())
{
window->DisableCursor();
}
else
{
window->EnableCursor();
}
}
if (window->CursorEnabled())
{
event->Consume();
return;
}
float speed = (float)(3.0 * event->GetDT());
float r_speed = (float)(2.0 * event->GetDT());
float z_speed = (float)(1.0 * event->GetDT());
if (KeyDown(Infinity::KeyCode::Left)) { m_camera.MoveLeft(speed); }
if (KeyDown(Infinity::KeyCode::Right)) { m_camera.MoveRight(speed); }
if (KeyDown(Infinity::KeyCode::Down)) { m_camera.MoveBackward(speed); }
if (KeyDown(Infinity::KeyCode::Up)) { m_camera.MoveForward(speed); }
if (KeyDown(Infinity::KeyCode::W)) { m_camera.zoom += z_speed; }
if (KeyDown(Infinity::KeyCode::S)) { m_camera.zoom -= z_speed; }
if (KeyDown(Infinity::KeyCode::A)) { m_camera.roll -= r_speed; }
if (KeyDown(Infinity::KeyCode::D)) { m_camera.roll += r_speed; }
m_camera.Update(m_aspect_ratio);
event->Consume();
}
void SandboxApp::OnUserRender(Infinity::UserRenderEvent *event)
{
Infinity::Window *window = GetMainWindow();
window->MakeContextCurrent();
Infinity::Context *context = Infinity::Window::GetContext();
context->Clear();
m_renderer.StartScene(&m_camera);
Infinity::Renderer2D::QuadParams quad;
quad.position = { 0.0f, 0.0f };
quad.size = { 1.0f, 1.0f };
quad.color = { 1.0f, 0.0f, 0.0f, 1.0f };
m_renderer.DrawQuad(quad);
m_renderer.EndScene();
m_popup_window->MakeContextCurrent();
context = Infinity::Window::GetContext();
context->Clear();
window->MakeContextCurrent();
event->Consume();
}
void SandboxApp::OnUserDestroy(Infinity::UserDestroyEvent *event)
{
m_renderer.Destroy();
if (m_rasterizer)
{
m_rasterizer->Destroy();
delete m_rasterizer;
}
if (m_texture)
{
m_texture->Destroy();
delete m_texture;
}
if (m_popup_window)
{
m_popup_window->Destroy();
delete m_popup_window;
}
INFINITY_CLIENT_INFO("Client destroyed");
event->Consume();
}
void SandboxApp::OnWindowResized(Infinity::WindowResizedEvent *event)
{
if (event->GetWindow() == GetMainWindow())
{
m_aspect_ratio = (float)event->GetWidth() / (float)event->GetHeight();
m_camera.Update(m_aspect_ratio);
event->Consume();
}
}
Infinity::Application *Infinity::CreateApplication()
{
return new SandboxApp;
}
If you need any other information, please just leave a comment.
Thanks in advance! :)
Update
I tried adding my executables to the Graphics Performance options list but it didn't change the low framerate of the full-screen window.
I did some more testing and found out that that I only need to create the sub-window for these inefficiencies to occur. Even if I don't show, update or render to the window, simply creating it slows down the frame rate of my full-screen main window.
Trying to do more research, I realized that MSDN does not have any documentation on using multiple DXGI swap chains. My hunch is that setting the full-screen state of one swap chain to true somehow interferes with the other swap chain causing inefficiencies (Although my ID3D11Device debug output doesn't mention inefficiencies anywhere)
There’re 2 kinds of full-screen in Windows.
True fullscreen is probably what you’re doing, calling IDXGISwapChain::SetFullscreenState. In this mode, the windows desktop compositor, dwm.exe, is disabled, giving your app exclusive access to the monitor. However, as you found out it has complications. There’re more of them BTW: alt+tab can be slow, popups from other applications may glitch, and also it’s tricky to implement correctly, I think you have to re-create a swap chain and all render targets. On the bright side, in some rare cases it may improve FPS by a single-digit number.
Borderless windowed is what you should be doing since you want multiple Win32 windows running along with your fullscreen content. In this mode the desktop compositor is up and running, updating your window along others. Switching in and out that mode is also way easier, example below.
HRESULT WindowImpl::maximizeBorderless( bool max )
{
// https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
DWORD dwStyle = GetWindowLong( GWL_STYLE );
if( max )
{
MONITORINFO mi = { sizeof( mi ) };
if( GetWindowPlacement( &m_wpPrev ) && GetMonitorInfo( MonitorFromWindow( m_hWnd, MONITOR_DEFAULTTOPRIMARY ), &mi ) )
{
SetWindowLong( GWL_STYLE, dwStyle & ~WS_OVERLAPPEDWINDOW );
SetWindowPos( HWND_TOP,
mi.rcMonitor.left, mi.rcMonitor.top,
mi.rcMonitor.right - mi.rcMonitor.left,
mi.rcMonitor.bottom - mi.rcMonitor.top,
SWP_NOOWNERZORDER | SWP_FRAMECHANGED );
}
}
else
{
SetWindowLong( GWL_STYLE, dwStyle | WS_OVERLAPPEDWINDOW );
SetWindowPlacement( &m_wpPrev );
SetWindowPos( NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_NOOWNERZORDER | SWP_FRAMECHANGED );
}
return S_OK;
}

How to change CTabCtrl tab colors?

Hello and happy new year, (it is acceptable to say it until Thursday)
I am trying to change the color of the tabs in the CTabCtrl class. I am trying to create my own ReskinCTablCtrl so that I can just call it in separate classes and easily use it throughout my program.
Currently I am able to change the background color of the CTabCtrl but I cannot modify the tab's themselves.
I used ON_WM_ERASEBKGND() for painting the background and it worked without a problem:
BOOL ReskinCTabCtrl::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
CBrush myBrush(RGB(51, 51, 51)); // dialog background color
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SetBkColor(RGB(51, 51, 51));
pDC->FillRect(&rect, &myBrush);
return bRes;
}
However, I have been unsuccesfull at changing the tab colors themselves. They are still the default MFC colors. I have tried to implement ON_WM_PAINT() and ON_WM_DRAWITEM() without any success. I think I can get to the specific tab rect with using both OnDraw and DrawItem similar to the second link example that I have posted in the end of this question.
void ReskinCTabCtrl::OnPaint() {
...
// paint the tabs first and then the borders
int nTab = GetItemCount();
int nSel = GetCurSel();
if (!nTab) // no pages added
return;
while (nTab--)
{
if (nTab != nSel)
{
dis.itemID = nTab;
dis.itemState = 0;
VERIFY(GetItemRect(nTab, &dis.rcItem));
dis.rcItem.bottom -= 2;
DrawItem(&dis);
DrawItemBorder(&dis);
}
}
...
}
I would really appreciate at least some direction to go about this problem, perhaps some more examples or what methods I should focus on using. I don't need the tabs to be different colors, maybe there is an easy way of doing this?
I've been trying to follow some examples like the following links but I still couldn't figure out the right way to do it.
https://support.microsoft.com/en-us/help/179909/how-to-change-the-background-color-of-a-tab-control
https://www.codeproject.com/Articles/1786/Ownerdraw-Tab-Controls-Borders-and-All
Enable OwnerDraw for tab control, either in resource editor, or set TCS_OWNERDRAWFIXED in OnInitDialog
CTabCtrl has message reflection for WM_DRAWITEM therefore we don't want to override WM_DRAWITEM/OnDrawItem from parent class. Instead override in CTabCtrl::DrawItem(LPDRAWITEMSTRUCT).
Unfortunately the result is rather ugly. It's sort of like overriding DrawItem in a button.
If Visual Style is available and enabled, then you can override CTabCtrl::OnPaint and draw everything manually. Example:
void CMyTabCtrl::OnPaint()
{
CPaintDC dc(this);
dc.SelectObject(GetFont());
CPen pen, pen_active;
COLORREF color_off = RGB(240, 240, 240);
COLORREF color_active = RGB(200, 240, 240);
CBrush brush_off, brush_active;
brush_off.CreateSolidBrush(color_off);
brush_active.CreateSolidBrush(color_active);
pen.CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
pen_active.CreatePen(PS_SOLID, 1, color_active);
CRect rcitem;
GetItemRect(0, &rcitem);
CRect rc;
GetClientRect(&rc);
rc.bottom = rcitem.bottom;
dc.FillSolidRect(&rc, GetSysColor(COLOR_3DFACE));
GetClientRect(&rc);
rc.top = rcitem.bottom - 1;
dc.SelectObject(&pen);
dc.SelectObject(&brush_active);
dc.Rectangle(&rc);
for(int i = 0; i < GetItemCount(); i++)
{
dc.SelectObject(&pen);
if(i == GetCurSel())
{
dc.SelectObject(&brush_active);
dc.SetBkColor(color_active);
}
else
{
dc.SelectObject(&brush_off);
dc.SetBkColor(color_off);
}
GetItemRect(i, &rcitem);
rcitem.right++;
dc.Rectangle(&rcitem);
if(i == GetCurSel())
{
dc.SelectObject(pen_active);
dc.MoveTo(rcitem.left+1, rcitem.bottom - 1);
dc.LineTo(rcitem.right, rcitem.bottom - 1);
}
TCITEM item = { 0 };
wchar_t buf[32];
item.pszText = buf;
item.cchTextMax = 32;
item.mask = TCIF_TEXT;
GetItem(i, &item);
dc.DrawText(buf, &rcitem, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
BOOL CMyTabCtrl::OnEraseBkgnd(CDC*)
{
return TRUE;
}

How to display GIF as splash in visual studio c++

I am trying to display a GIF picture as splash as starting of my small program in visual studio. I am really getting crazy. I looked it is possible in Qt IDE but I really need it in visual studio because my other code works only with visual studio. And yes I tried to convert my code for Qt giving me too many errors.
I have seen this post.
I am using GDI+ but still dunno how to simply display it instead of play and stop. It's okay even instead of splash to display a small form that plays the GIF file, can you guys give me a small code snippet in how to do it in c++?
Thanks.
Here is an MFC window class that implements a splash screen using GDI Plus for displaying an (animated) GIF.
I've encapsulated everything inside a header file to simplify using it with your project. Save it as an .h file (maybe "SplashWnd.h"), then include it wherever you want to set up the splash. You app's InitInstance might be a good place to add it - something like this line (before any DoModal calls):
SplashWnd splash(_T("Filname.gif"));
The constructor can also take parameters for controlling the delay before auto-closing, and also specifying a function to call when the splash is closed.
The splash window has no border or caption - it appears only as the loaded image, floating on top of any other windows. Its icon doesn't appear in the taskbar, and will close when its timeout expires, or the user clicks the window or presses a key.
#pragma once
#include <functional>
#include <afxwin.h>
#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib")
inline void ManageGdiPlusInit(bool release=false) {
static int refcount = 0;
static ULONG_PTR token;
if(release) {
if(--refcount == 0) {
Gdiplus::GdiplusShutdown(token);
}
} else if(++refcount == 1) {
Gdiplus::GdiplusStartupInput startup_input;
Gdiplus::GdiplusStartup(&token, &startup_input, 0);
} }
inline void GdiPlusInit() { ManageGdiPlusInit(false); }
inline void GdiPlusRelease() { ManageGdiPlusInit(true); }
namespace {
class SplashWnd : public CWnd {
protected:
static CString WindowClass() {
static CString name;
if(name.IsEmpty()) {
name = AfxRegisterWndClass(CS_DROPSHADOW, 0, (HBRUSH)GetStockObject(GRAY_BRUSH), 0);
}
return name;
}
Gdiplus::Image *m_pImage;
UINT m_FrameCount;
unsigned char *m_FrameDelayData;
const UINT *m_FrameDelays;
UINT m_CurFrameIndex;
UINT m_AnimationTimerId;
UINT m_ExpireTimerId;
CRect m_WindowRect;
std::function<void()> m_DismissCallback;
DECLARE_MESSAGE_MAP()
afx_msg void OnLButtonDown(UINT nFlags, CPoint point) {
DestroyWindow();
}
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
DestroyWindow();
}
afx_msg void OnDestroy() {
if(m_AnimationTimerId != UINT(-1)) {
KillTimer(m_AnimationTimerId);
}
if(m_ExpireTimerId != UINT(-1)) {
KillTimer(m_ExpireTimerId);
}
if(m_DismissCallback) {
m_DismissCallback();
}
CWnd::OnDestroy();
}
afx_msg void OnTimer(UINT nIDEvent) {
if(nIDEvent == m_AnimationTimerId) {
if(++m_CurFrameIndex >= m_FrameCount) {
m_CurFrameIndex = 0;
}
DrawCurFrame();
KillTimer(m_AnimationTimerId);
m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
return;
}
if(nIDEvent == m_ExpireTimerId) {
DestroyWindow();
return;
} }
void PostNcDestroy() {
if(m_DeleteSelf) {
delete this;
}
}
void DrawCurFrame() {
Gdiplus::Graphics g(m_hWnd);
GUID dim_select_id = Gdiplus::FrameDimensionTime;
m_pImage->SelectActiveFrame(&dim_select_id, m_CurFrameIndex);
g.DrawImage(m_pImage, 0, 0, m_WindowRect.Width(), m_WindowRect.Height());
}
public:
// set m_DeleteSelf to true if a SplashWnd is created with new, and you want it to
// auto-delete itself when the window expires or is dismissed.
bool m_DeleteSelf;
// file_path the gif file path
// ExpireMs the time, in milliseconds until the window automatically closes itself
// WidthFactor the fraction of the width of the primary display to use as the splash screen's width
// HeightFactor the fraction of the height of the primary display to use as the height
// If WidthFactor or HeightFactor are 0, the original image aspect ratio is preserved
// If both are 0, the original image size, in pixels is used
SplashWnd(CString file_path, DWORD ExpireMs=2000, double WidthFactor=0.4, double HeightFactor=0) {
GdiPlusInit();
m_pImage = new Gdiplus::Image(file_path);
// Set up an array of frame times for animated images
UINT dimension_count = m_pImage->GetFrameDimensionsCount();
GUID dimension_id;
m_pImage->GetFrameDimensionsList(&dimension_id, 1);
m_FrameCount = m_pImage->GetFrameCount(&dimension_id);
UINT frame_delay_size = m_pImage->GetPropertyItemSize(PropertyTagFrameDelay);
m_FrameDelayData = new unsigned char[frame_delay_size];
Gdiplus::PropertyItem* frame_delay_item = reinterpret_cast<Gdiplus::PropertyItem*>(m_FrameDelayData);
m_pImage->GetPropertyItem(PropertyTagFrameDelay, frame_delay_size, frame_delay_item);
m_FrameDelays = reinterpret_cast<const UINT*>(frame_delay_item->value);
// Figure out the size and location of the splash window
int primary_width = GetSystemMetrics(SM_CXFULLSCREEN);
int primary_height = GetSystemMetrics(SM_CYFULLSCREEN);
int splash_width = int(primary_width * WidthFactor);
int splash_height = int(primary_height * HeightFactor);
if(splash_width == 0) {
if(splash_height == 0) {
splash_width = m_pImage->GetWidth();
splash_height = m_pImage->GetHeight();
} else {
splash_width = primary_width * splash_height / primary_height;
}
} else if(splash_height == 0) {
splash_height = primary_height * splash_width / primary_width;
}
int l = (primary_width - splash_width) / 2;
int t = (primary_height - splash_height) / 2;
int r = l + splash_width;
int b = t + splash_height;
m_WindowRect.SetRect(
(primary_width - splash_width) / 2,
(primary_height - splash_height) / 2,
(primary_width + splash_width) / 2,
(primary_height + splash_height) / 2);
// WS_EX_TOPMOST makes the window cover up other, regular windows
// WS_EX_TOOLWINDOW prevents an icon for this window in the taskbar
// WS_POPUP prevents caption and border from being drawn
CreateEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, WindowClass(), _T("Splash"), WS_VISIBLE | WS_POPUP, m_WindowRect, 0, 0);
// Show the first frame
m_CurFrameIndex = 0;
DrawCurFrame();
// Set up the frame-flipping animation timer
m_ExpireTimerId = m_AnimationTimerId = UINT(-1);
if(m_FrameCount > 1) {
m_AnimationTimerId = SetTimer(1, m_FrameDelays[m_CurFrameIndex], 0);
}
// Set up the expiration timer
if(ExpireMs != INFINITE) {
m_ExpireTimerId = SetTimer(2, ExpireMs, 0);
}
m_DeleteSelf = false;
}
// Constructor which takes a callback function which will be called when the splash window closes
template <typename F>
SplashWnd(CString file_path, DWORD ExpireMs, double WidthFactor, double HeightFactor, F DismissCallback)
: SplashWnd(file_path, ExpireMs, WidthFactor, HeightFactor)
{
m_DismissCallback = DismissCallback;
}
~SplashWnd() {
delete [] m_FrameDelayData;
delete m_pImage;
GdiPlusRelease();
}
};
// Message map, usually in an implementation file, but here encapsulated inside the header
// using an anonymous namespace to prevent possible ODR problems.
BEGIN_MESSAGE_MAP(SplashWnd, CWnd)
ON_WM_KEYDOWN()
ON_WM_LBUTTONDOWN()
ON_WM_TIMER()
ON_WM_DESTROY()
END_MESSAGE_MAP()
}

How do I prevent flickering on CListCtrl?

I'm using a CListCtrl/CListView report view (LVS_REPORT) in virtual mode (LVS_OWNERDATA) with LVS_EX_DOUBLEBUFFER enabled and I encounter ugly flickering. Double buffer have a real effect but it doesn't stop all flickering (without it very slow).
I'm not looking for switching to other controls that would require a high amount of rework (like ObjectListView)
How does the flickering behaves:
on column resize - the background is first clean using lightgray and after this is displayed the text (background is white)
on mouse scroll (animated) - for a very short time there is lightgray-bar displayed in the area where new lines are to be displayed.
It looks like it does clean the background using the default window background color (lightgray) for the area where it has to redraw.
How do I solve the flickering problem?
Try to do the following:
- Set Clip Children and Clip Sibling for paremt dialog of List Control.
- Make dirived from CListCtrl class. In this class overwrite OnEraseBkgnd. In the OnEraseBkgnd fill with background color area around of visible items of the list.
The OnEraseBkgnd can look like:
BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC)
{
CBrush br;
CRect rcCli;
CRect rcItemsRect(0, 0, 0, 0);
int nHeadHeight = 0;
int nItems = GetItemCount();
GetClientRect(&rcCli);
CHeaderCtrl* pHeadCtrl = GetHeaderCtrl();
if (pHeadCtrl)
{
CRect rcHead;
pHeadCtrl->GetWindowRect(&rcHead);
nHeadHeight = rcHead.Height();
}
rcCli.top += nHeadHeight;
if (nItems > 0)
{
CPoint ptItem;
CRect rcItem;
GetItemRect(nItems - 1, &rcItem, LVIR_BOUNDS);
GetItemPosition(nItems - 1, &ptItem);
rcItemsRect.top = rcCli.top;
rcItemsRect.left = ptItem.x;
rcItemsRect.right = rcItem.right;
rcItemsRect.bottom = rcItem.bottom;
if (GetExtendedStyle() & LVS_EX_CHECKBOXES)
rcItemsRect.left -= GetSystemMetrics(SM_CXEDGE) + 16;
}
br.CreateSolidBrush(GetBkColor());
if (rcItemsRect.IsRectEmpty())
pDC->FillRect(rcCli, &br);
else
{
if (rcItemsRect.left > rcCli.left) // fill left rectangle
pDC->FillRect(
CRect(0, rcCli.top, rcItemsRect.left, rcCli.bottom), &br);
if (rcItemsRect.bottom < rcCli.bottom) // fill bottom rectangle
pDC->FillRect(
CRect(0, rcItemsRect.bottom, rcCli.right, rcCli.bottom), &br);
if (rcItemsRect.right < rcCli.right) // fill right rectangle
pDC->FillRect(
CRect(rcItemsRect.right, rcCli.top, rcCli.right, rcCli.bottom), &br);
}
return TRUE;
}
I know only way to have flicker free is using double buffering or MemDC.
have found this article: Flicker-free-drawing-of-any-control
This article explains it well how to quickly perform Non Flickering drawing on your CListCtrl.
And it works excellent.
PS: VS 2005 doesn't have CMemDC class you will need to implement it your self, or use the following code:
//
// CMemDC.h header file
//
#pragma once
class CMemDC
{
public:
CMemDC(CDC& dc, CWnd* pWnd);
CMemDC(CDC& dc, const CRect& rect);
virtual ~CMemDC();
CDC& GetDC() { return m_bMemDC ? m_dcMem : m_dc; }
BOOL IsMemDC() const { return m_bMemDC; }
BOOL IsVistaDC() const { return m_hBufferedPaint != NULL; }
void EraseBkClip();
protected:
CDC& m_dc;
BOOL m_bMemDC;
HANDLE m_hBufferedPaint;
CDC m_dcMem;
CBitmap m_bmp;
CBitmap* m_pOldBmp;
CRect m_rect;
};
//
// CMemDC.cpp source file
//
#include "CMemDC.h"
CMemDC::CMemDC(CDC& dc, CWnd* pWnd) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL)
{
ASSERT_VALID(pWnd);
pWnd->GetClientRect(m_rect);
m_rect.right += pWnd->GetScrollPos(SB_HORZ);
m_rect.bottom += pWnd->GetScrollPos(SB_VERT);
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::CMemDC(CDC& dc, const CRect& rect) :
m_dc(dc), m_bMemDC(FALSE), m_hBufferedPaint(NULL), m_pOldBmp(NULL), m_rect(rect)
{
ASSERT(!m_rect.IsRectEmpty());
if (m_dcMem.CreateCompatibleDC(&m_dc) &&
m_bmp.CreateCompatibleBitmap(&m_dc, m_rect.Width(), m_rect.Height()))
{
m_bMemDC = TRUE;
m_pOldBmp = m_dcMem.SelectObject(&m_bmp);
}
}
CMemDC::~CMemDC()
{
if (m_bMemDC)
{
CRect rectClip;
int nClipType = m_dc.GetClipBox(rectClip);
if (nClipType != NULLREGION)
{
if (nClipType != SIMPLEREGION)
{
rectClip = m_rect;
}
m_dc.BitBlt(rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(), &m_dcMem, rectClip.left, rectClip.top, SRCCOPY);
}
m_dcMem.SelectObject(m_pOldBmp);
}
}
void CMemDC::EraseBkClip()
{
CRect clip;
m_dcMem.GetClipBox(&clip);
m_dcMem.FillSolidRect(clip, GetSysColor(COLOR_WINDOW));
}
There is an ultra simple way I found that worked for me:
Turn off redraw with m_List1.SetRedraw(false)
Reset contents with m_List1.ResetContents()
Add new strings in loop with m_List1.AddString()
Then finalize by turning back on redraw and a m_List1.UpdateWindow().