mfc — Visual C ++ Как перерисовать изображение в CView с помощью CPaintDC, CDC

Я использую 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 вызывается таймером правильно. Почему изображения отображаются только при изменении размера главного окна?

2

Решение

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();
}
2

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector