Неправильная отрисовка элементов CListCtrl
От: brightside90  
Дата: 15.05.15 12:58
Оценка:
Доброго времени суток всем.
У меня есть класс CListCtrlEx, который я переопределил от обычного CListCtrl. Этот список имеет стиль LVS_REPORT, LVS_OWNERDRAWFIXED и LVS_EX_GRIDLINES. Я прикрутил возможность смены шрифта для этого списка. Она вроде и работает, но есть одно но — если я меняю шрифт и перед этим не делаю скроллинг списка, то тогда все элементы списка перерисовываются правильно, но если я сначала сделаю скроллинг, а затем сменю шрифт, то получится, что элементы списка "съедут" чуть ниже своего положения в сетке списка, т. е. это будет выглядеть как текст перечёркнутый линиями сетки.

Код замены шрифта:

LRESULT CListCtrlEx::OnSetFont(WPARAM wParam, LPARAM)
{
LRESULT res = Default();

CRect rc;
GetWindowRect(&rc);

WINDOWPOS wp;
wp.hwnd = m_hWnd;
wp.cx = rc.Width();
wp.cy = rc.Height();
wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);

return res;
}

void CListCtrlEx::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
HDC hDC = ::GetDC(NULL);
CFont* pFont = GetFont();
HFONT hFontOld = (HFONT)SelectObject(hDC, pFont->GetSafeHandle());
CRect rect;

DrawText(hDC, _T(" "), 1, rect, DT_SINGLELINE | DT_CALCRECT);
lpMeasureItemStruct->itemHeight = rect.bottom — rect.top;
SelectObject(hDC, hFontOld);
::ReleaseDC(NULL, hDC);
}

Вот как это выглядит:


Может кто-то знает, куда надо копать? Неделю уже тыкаюсь, но толку — ноль.
Re: Неправильная отрисовка элементов CListCtrl
От: Pavel Dvorkin Россия  
Дата: 16.05.15 16:59
Оценка:
Здравствуйте, brightside90, Вы писали:

B>Доброго времени суток всем.

B>У меня есть класс CListCtrlEx, который я переопределил от обычного CListCtrl. Этот список имеет стиль LVS_REPORT, LVS_OWNERDRAWFIXED и LVS_EX_GRIDLINES. Я прикрутил возможность смены шрифта для этого списка. Она вроде и работает, но есть одно но — если я меняю шрифт и перед этим не делаю скроллинг списка, то тогда все элементы списка перерисовываются правильно, но если я сначала сделаю скроллинг, а затем сменю шрифт, то получится, что элементы списка "съедут" чуть ниже своего положения в сетке списка, т. е. это будет выглядеть как текст перечёркнутый линиями сетки.

B>Код замены шрифта:


<skipped>

1.Почему просто вместо этого не послать ему WM_SETFONT ?

По коду
2. В MeasureItem не нужно ничего рисовать для измерения размера высоты шрифта. На это GetTextExtentPoint32 есть.
With best regards
Pavel Dvorkin
Re[2]: Неправильная отрисовка элементов CListCtrl
От: brightside90  
Дата: 17.05.15 15:45
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, brightside90, Вы писали:


B>>Доброго времени суток всем.

B>>У меня есть класс CListCtrlEx, который я переопределил от обычного CListCtrl. Этот список имеет стиль LVS_REPORT, LVS_OWNERDRAWFIXED и LVS_EX_GRIDLINES. Я прикрутил возможность смены шрифта для этого списка. Она вроде и работает, но есть одно но — если я меняю шрифт и перед этим не делаю скроллинг списка, то тогда все элементы списка перерисовываются правильно, но если я сначала сделаю скроллинг, а затем сменю шрифт, то получится, что элементы списка "съедут" чуть ниже своего положения в сетке списка, т. е. это будет выглядеть как текст перечёркнутый линиями сетки.

B>>Код замены шрифта:


PD><skipped>


PD>1.Почему просто вместо этого не послать ему WM_SETFONT ?


PD>По коду

PD>2. В MeasureItem не нужно ничего рисовать для измерения размера высоты шрифта. На это GetTextExtentPoint32 есть.

1. По-идее, я сначала делаю вызов SetFont(...), после которого происходит обработка сообщения WM_SETFONT, а как сделать это без такого вызова?

2. Я делаю всё почти как здесь http://www.codeproject.com/Articles/1401/Changing-Row-Height-in-an-owner-drawn-Control с тем лишь отличием, что вместо GetTextMetrics(hDC, &tm); решил нарисовать квадратик, чтобы вычислить высоту шрифта. Да, можно сделать это и через GetTextExtentPoint32(), но и там, судя по описанию придётся "написать" строку текста длиной хотя бы в один символ, что не критично.

Если в теле OnSetFont(...) не посылать никаких сообщений, чтобы вызвать MeasureItem, то шрифт в списке изменится, а высота строк останется прежней
Re[3]: Неправильная отрисовка элементов CListCtrl
От: Pavel Dvorkin Россия  
Дата: 17.05.15 16:19
Оценка:
Здравствуйте, brightside90, Вы писали:

Покажи OnDrawItem.
With best regards
Pavel Dvorkin
Re[4]: Неправильная отрисовка элементов CListCtrl
От: brightside90  
Дата: 17.05.15 16:32
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, brightside90, Вы писали:


PD>Покажи OnDrawItem.


Лучше я покажу вам класс полностью:

.h: http://pastebin.com/UdXYEpF7
.cpp: http://pastebin.com/2HYe5AEd
Re[5]: Неправильная отрисовка элементов CListCtrl
От: Pavel Dvorkin Россия  
Дата: 17.05.15 17:05
Оценка:
Здравствуйте, brightside90, Вы писали:

Похоже, вот в чем дело

WINDOWPOS wp;
wp.hwnd = m_hWnd;
wp.cx = rc.Width();
wp.cy = rc.Height();
wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);

WM_WINDOWPOSCHANGED вообще-то не посылают сами. Это нотификация о том, что положение окна изменилось вследствие каких-то причин. Оно придет, конечно, но изменения-то нет.

Sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function.

Попробуй заменить на SetWindowPos, а скорее просто на Invalidate.
With best regards
Pavel Dvorkin
Re[6]: Неправильная отрисовка элементов CListCtrl
От: brightside90  
Дата: 17.05.15 17:33
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, brightside90, Вы писали:


PD>Похоже, вот в чем дело


PD>WINDOWPOS wp;

PD>wp.hwnd = m_hWnd;
PD>wp.cx = rc.Width();
PD>wp.cy = rc.Height();
PD>wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
PD>SendMessage(WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp);

PD>WM_WINDOWPOSCHANGED вообще-то не посылают сами. Это нотификация о том, что положение окна изменилось вследствие каких-то причин. Оно придет, конечно, но изменения-то нет.


PD>Sent to a window whose size, position, or place in the Z order has changed as a result of a call to the SetWindowPos function or another window-management function.


PD>Попробуй заменить на SetWindowPos, а скорее просто на Invalidate.


Если я вызову просто Invalidate, то MeasureItem(...) не откликнется, да и в случае с SetWindowPos, оно тоже спит.
Делаю так:
WINDOWPOS wp;
ZeroMemory(&wp, sizeof(wp));
wp.hwnd  = m_hWnd;
wp.cx    = rc.Width();
wp.cy    = rc.Height();
wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), wp.flags);


Может быть с флагами надо что-то намудрить, но я уже запутался)
Re[7]: Неправильная отрисовка элементов CListCtrl
От: Pavel Dvorkin Россия  
Дата: 17.05.15 17:44
Оценка: 1 (1)
Здравствуйте, brightside90, Вы писали:


B>Может быть с флагами надо что-то намудрить, но я уже запутался)


Посмотри вот здесь

http://sources.ru/cpp/mfc/t9107.htm

Anyway, I added a handler
for WM_MEASUREITEM in my dialog class and called CMyListCtrl::MeasureItem()
myself and everything works.

Попробуй, может быть это и есть самое простое. А потом Invalidate.
With best regards
Pavel Dvorkin
Re[8]: Неправильная отрисовка элементов CListCtrl
От: brightside90  
Дата: 17.05.15 18:20
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, brightside90, Вы писали:



B>>Может быть с флагами надо что-то намудрить, но я уже запутался)


PD>Посмотри вот здесь


PD>http://sources.ru/cpp/mfc/t9107.htm


PD>Anyway, I added a handler

PD>for WM_MEASUREITEM in my dialog class and called CMyListCtrl::MeasureItem()
PD>myself and everything works.

PD>Попробуй, может быть это и есть самое простое. А потом Invalidate.


Хе, осталось понять только как он инициализировал LPMEASUREITEMSTRUCT, чтобы вызвать MeasureItem(), а способ с двумя подряд вызовами SetWindowPos() не прокатывает.
Re[7]: Неправильная отрисовка элементов CListCtrl
От: Carc Россия https://vk.com/gosha_mazov
Дата: 17.05.15 23:18
Оценка:
PD>>Попробуй заменить на SetWindowPos, а скорее просто на Invalidate.

B>Если я вызову просто Invalidate, то MeasureItem(...) не откликнется, да и в случае с SetWindowPos, оно тоже спит.

B>Делаю так:
B>
B>WINDOWPOS wp;
B>ZeroMemory(&wp, sizeof(wp));
B>wp.hwnd  = m_hWnd;
B>wp.cx    = rc.Width();
B>wp.cy    = rc.Height();
B>wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
B>SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), wp.flags);
B>


B>Может быть с флагами надо что-то намудрить, но я уже запутался)

Нужно
SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), wp.flags | SWP_FRAMECHANGED );
Aml Pages Home
Re[8]: Неправильная отрисовка элементов CListCtrl
От: brightside90  
Дата: 18.05.15 08:03
Оценка:
Здравствуйте, Carc, Вы писали:

PD>>>Попробуй заменить на SetWindowPos, а скорее просто на Invalidate.


B>>Если я вызову просто Invalidate, то MeasureItem(...) не откликнется, да и в случае с SetWindowPos, оно тоже спит.

B>>Делаю так:
B>>
B>>WINDOWPOS wp;
B>>ZeroMemory(&wp, sizeof(wp));
B>>wp.hwnd  = m_hWnd;
B>>wp.cx    = rc.Width();
B>>wp.cy    = rc.Height();
B>>wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
B>>SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), wp.flags);
B>>


B>>Может быть с флагами надо что-то намудрить, но я уже запутался)

C>Нужно
C>
C>SetWindowPos(NULL, 0, 0, rc.Width(), rc.Height(), wp.flags | SWP_FRAMECHANGED );
C>


Это тоже не срабатывает, причём, если оставить в таком виде параметры SetWindowPos, то MeasureItem не вызовется, а вот если добавить к ширине или высоте прямоугольника хотя бы 1, то MeasureItem тут же просыпается Но, тем не менее, это не помогает.

Пока сделал так:
раз уж если вертикальный скролл стоит в своём начальном положении и при этом шрифт хорошо изменяется, то если мы поменяли его положение, нужно сначала его откатить после создания шрифта, но до его установки:
int nTopIndex = GetTopIndex();
if (nTopIndex != 0)
{
    SetRedraw(FALSE);
    RECT rect;
    if (GetItemRect(nTopIndex, &rect, LVIR_LABEL))
    {
        CSize size;
        size.cx = 0;
        size.cy = -(nTopIndex * rect.bottom - rect.top);
        Scroll(size);
    }
}


А после возврата из MeasureItem вызвать
EnsureVisible(m_nItem, FALSE);
SetRedraw(TRUE);

чтобы откатить положение скролла к последнему выделенному элементу списка (хотя бы к нему, даже если его положение было не обязательно последним положением соответствовавшим положению скролла до смены шрифта).
Есть опасение, что, если в списке будет содержаться большое количество записей, то такие фокусы будут дорого стоить.
Отредактировано 18.05.2015 8:11 brightside90 . Предыдущая версия .
Re[9]: Неправильная отрисовка элементов CListCtrl
От: Pavel Dvorkin Россия  
Дата: 18.05.15 12:26
Оценка: +1
Здравствуйте, brightside90, Вы писали:

B>Есть опасение, что, если в списке будет содержаться большое количество записей, то такие фокусы будут дорого стоить.


В этом случае стоит перейти на virtual listview
With best regards
Pavel Dvorkin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.