Я использую Visual Studio 2015, работаю с MFC Multiple Document Application (стиль ленты).
Я пытаюсь добавить PNG изображения в CView и сделать слайд-шоу с помощью WM_TIMER.
Сначала я сделал диалоговое приложение с той же целью, оно отлично работает. Разница между этими приложениями заключается в том, что изображения рисуются в первом приложении в диалоговом окне в PictureControl (CStatic), добавляя с помощью панели инструментов. А во втором приложении я пытаюсь точно так же добавить изображение в CStatic в CView. Но с CView он не перерисовывается правильно. Только когда я изменяю размер окна (растягиваю, увеличиваю его), изображение png изменяется, но когда я прекращаю изменять размер окна, изображение снова замирает.
Создание CStatic control.
void CCardioAppView::OnInitialUpdate()
{
CView::OnInitialUpdate();
CRect rect;
GetClientRect(rect);
BOOL b = m_ctrlImage.Create(_T(""), WS_CHILD | WS_VISIBLE, rect,this,2);
m_ctrlImage.ModifyStyle(0, SS_BITMAP, SWP_NOSIZE);
}
Перерисовка по таймеру и OnSize ()
void CCardioAppView::OnTimer(UINT_PTR nIDEvent)
{
if (ShowImageTimer == nIDEvent)
{
auto bmp_iter = theApp.FullBmpMap.begin();
int sz = theApp.FullBmpMap.size();
CRect ImageRect;
GetClientRect(&ImageRect);
if (m_iCurrentImage < sz)
{
m_iCurrentImage++;
InvalidateRect(ImageRect, false);
}
else
{
m_iCurrentImage = 1;
}
}
CView::OnTimer(nIDEvent);
}
void CCardioAppView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
CRect rect;
if (m_ctrlImage.GetSafeHwnd())
{
GetClientRect(rect);
m_ctrlImage.DestroyWindow();
BOOL b = m_ctrlImage.Create(_T(""), WS_CHILD | WS_VISIBLE, rect, this, 2);
m_ctrlImage.ModifyStyle(0, SS_BITMAP);
}
}
Перерисовка OnPaint ()
void CCardioAppView::OnPaint()
{
CPaintDC view_dc(this); // device context for painting
CBitmap bmp;
CRect rect, scaleRect;
BITMAP b;
auto bmp_iter = theApp.FullBmpMap.find(m_iCurrentImage);
GetClientRect(&rect);
if (bmp_iter == theApp.FullBmpMap.end()) return;
bmp.Attach((*bmp_iter).second);
bmp.GetObject(sizeof(BITMAP), &b);
CPaintDC dc(&m_ctrlImage);
CDC memdc;
memdc.CreateCompatibleDC(&dc);
memdc.SelectObject(&bmp);
if (rect.Height() <= b.bmHeight) //scaling image
{
scaleRect = rect;
scaleRect.right = rect.left + ((b.bmWidth*rect.Height())/ b.bmHeight);
}
dc.FillSolidRect(rect, RGB(255, 255, 255));
dc.StretchBlt(0, 0, scaleRect.Width(), scaleRect.Height(), &memdc,
0, 0, b.bmWidth, b.bmHeight, SRCCOPY);
//dc.MoveTo(0, 0);
(*bmp_iter).second.Detach();
(*bmp_iter).second.Attach(bmp);
bmp.Detach();
}
OnPaint вызывается таймером правильно. Почему изображения отображаются только при изменении размера главного окна?
void CCardioAppView::OnPaint()
{
CPaintDC view_dc(this);
...
CPaintDC dc(&m_ctrlImage); //<== wrong place
...
}
CPaintDC
это обертка для BeginPaint/EndPaint
в ответ на WM_PAINT
сообщение. Его нельзя использовать для получения контекста устройства из другого окна.
Чтобы нарисовать статический элемент управления, вы должны сделать его владельцем. Но это не обязательно здесь. Вы можете просто использовать управление изображением и просто позвонить CStatic::SetBitmap
void CCardioAppView::OnTimer(UINT_PTR nIDEvent)
{
CView::OnTimer(nIDEvent);
if (ShowImageTimer == nIDEvent)
{
m_iCurrentImage++;
if (m_iCurrentImage >= theApp.FullBmpMap.size())
m_iCurrentImage = 0;
//get HBITMAP: ???
auto bmp_iter = theApp.FullBmpMap.find(m_iCurrentImage);
HBITAMP hbitmap = bmp_iter->second; //???
m_ctrlImage.SetBitmap(hbitmap);
}
}
Не переопределять OnPaint()
а также OnSize()
с этим методом.
Второй вариант:
CStatic
не будет растягивать растровое изображение. Ты можешь использовать CPictureHolder
растянуть растровое изображение. Используйте пример ниже (не создавайте m_ctrlImage
/CStatic
контроль)
void OnPaint()
{
CPaintDC dc(this);
auto bmp_iter = theApp.FullBmpMap.find(m_iCurrentImage);
HBITAMP hbitmap = bmp_iter->second; //???
CPictureHolder pic;
pic.CreateFromBitmap(hbitmap);
CRect rect;
GetClientRect(&rect);
pic.Render(&dc, rect, rect);
}
void CCardioAppView::OnTimer(UINT_PTR nIDEvent)
{
CView::OnTimer(nIDEvent);
if (nIDEvent == ShowImageTimer)
{
m_iCurrentImage++;
if (m_iCurrentImage >= theApp.FullBmpMap.size())
m_iCurrentImage = 0;
Invalidate(TRUE);
}
}
Третий вариант:
Если вы хотите сделать свою собственную картину, то используйте собственный DC окна. Пример:
void CCardioAppView::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
auto bmp_iter = theApp.FullBmpMap.find(m_iCurrentImage);
HBITAMP hbitmap = bmp_iter->second; //???
CBitmap bmp;
bmp.Attach(hbitmap);
CDC memdc;
memdc.CreateCompatibleDC(&dc);
memdc.SelectObject(&bmp);
BITMAP b;
bmp.GetObject(sizeof(BITMAP), &b);
CRect scaleRect = rect;
if (rect.Height() <= b.bmHeight)
{
scaleRect = rect;
scaleRect.right = rect.left + ((b.bmWidth*rect.Height()) / b.bmHeight);
}
dc.FillSolidRect(rect, RGB(255, 255, 255));
dc.StretchBlt(0, 0, scaleRect.Width(), scaleRect.Height(), &memdc,
0, 0, b.bmWidth, b.bmHeight, SRCCOPY);
bmp.Detach();
}
Других решений пока нет …