Ошибка прокрутки TListView при использовании стилей VCL — Delphi XE8

Я попытался установить направление управления TListView на RTL с помощью следующей функции:

procedure RTL_LV(lv:TListView);
const
  LVM_FIRST = $1000;
  LVM_GETHEADER = LVM_FIRST + 31;
var
  header: THandle;
begin
  header:= SendMessage (lv.Handle, LVM_GETHEADER, 0, 0);
  SetWindowLong (header, GWL_EXSTYLE,
                 GetWindowLong (header, GWL_EXSTYLE)  or
                 WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);

  SetWindowLong (lv.Handle, GWL_EXSTYLE,
                 GetWindowLong (lv.Handle, GWL_EXSTYLE)  or
                 WS_EX_LAYOUTRTL or WS_EX_NOINHERITLAYOUT);
  lv.invalidate;
end;

Но у меня есть две проблемы, когда в проекте используется VCL Styles:

1: Vertical scrollbar не появляется без нажатия на него.

введите здесь описание изображения

2: Когда я изменяю размер столбцов ListView и нажимаю horizontal scrollbar, отображается следующее сообщение об ошибке:

введите здесь описание изображения

Источник исключения: Vcl.ComCtrls.TListViewStyleHook.WMMouseMove

введите здесь описание изображения

procedure TListViewStyleHook.WMMouseMove(var Message: TWMMouse);
var
  SF: TScrollInfo;
  SPos: Integer;
  R: TRect;
begin
  if VertSliderState = tsThumbBtnVertPressed then
  begin
    SF.fMask := SIF_ALL;
    SF.cbSize := SizeOf(SF);
    GetScrollInfo(Handle, SB_VERT, SF);
    ScrollPos := ScrollPos + (SF.nMax - SF.nMin) * ((Mouse.CursorPos.Y - PrevScrollPos) / VertTrackRect.Height);

    PrevScrollPos := Mouse.CursorPos.Y;

    if Control is TCustomListView then
    begin
      PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_THUMBTRACK, Round(ScrollPos))), 0);
      if TCustomListView(Control).ViewStyle = vsReport then
      begin
        if (Abs(ScrollPos - ListPos) >= 1) or
        ((ScrollPos = SF.nMin) and (ListPos <> ScrollPos)) or
        ((ScrollPos = SF.nMax) and (ListPos <> ScrollPos)) then
        begin
          if TCustomListView(Control).GroupView then
          begin
            SPos := Round(ScrollPos - ListPos);
            if SF.nPos + SPos < 0 then SPos := -SF.nPos;
          end
          else
            begin
              ListView_GetItemRect(Handle, 0, R, LVIR_BOUNDS);
              SPos := Round((ScrollPos - ListPos) * R.Height);
            end;
          ListView_Scroll(Handle, 0, SPos);
          ListPos := ScrollPos;
        end;
      end
      else
      begin
        if Abs(ScrollPos - ListPos) >= 1 then
        begin
          ListView_Scroll(Handle, 0, Round((ScrollPos - ListPos)));
          ListPos := ScrollPos;
        end;
      end;
    end
    else
      PostMessage(Handle, WM_VSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Round(ScrollPos))), 0);
    PaintScroll;
    Handled := True;
    Exit;
  end;

  if HorzSliderState = tsThumbBtnHorzPressed then
  begin
    SF.fMask := SIF_ALL;
    SF.cbSize := SizeOf(SF);
    GeTScrollInfo(Handle, SB_HORZ, SF);
    ScrollPos := ScrollPos + (SF.nMax - SF.nMin) * ((Mouse.CursorPos.X - PrevScrollPos) / HorzTrackRect.Width);
    if ScrollPos < SF.nMin then
      ScrollPos := SF.nMin;
    if ScrollPos > SF.nMax then
      ScrollPos := SF.nMax;

    PrevScrollPos := Mouse.CursorPos.X;

    if Control is TCustomListView then
    begin
      if TCustomListView(Control).ViewStyle = vsReport then
      begin
        if Abs(ScrollPos - ListPos) >= 1 then
        begin
          ListView_Scroll(Handle, Round((ScrollPos - ListPos)), 0);
          ListPos := ScrollPos;
        end;
      end
      else
      begin
        if Abs(ScrollPos - ListPos) >= 0.5 then
        begin
          ListView_Scroll(Handle, Round((ScrollPos - ListPos)), 0);
          ListPos := ScrollPos;
        end;
      end;
    end
    else
      PostMessage(Handle, WM_HSCROLL, Integer(SmallPoint(SB_THUMBPOSITION, Round(ScrollPos))), 0);
    PaintScroll;
    Handled := True;
    Exit;
  end;

  if (HorzSliderState <> tsThumbBtnHorzPressed) and (HorzSliderState = tsThumbBtnHorzHot) then
  begin
    HorzSliderState := tsThumbBtnHorzNormal;
    PaintScroll;
  end;

  if (VertSliderState <> tsThumbBtnVertPressed) and (VertSliderState = tsThumbBtnVertHot) then
  begin
    VertSliderState := tsThumbBtnVertNormal;
    PaintScroll;
  end;

  if (HorzUpState <> tsArrowBtnLeftPressed) and (HorzUpState = tsArrowBtnLeftHot) then
  begin
    HorzUpState := tsArrowBtnLeftNormal;
    PaintScroll;
  end;

  if (HorzDownState <> tsArrowBtnRightPressed) and (HorzDownState =tsArrowBtnRightHot) then
  begin
    HorzDownState := tsArrowBtnRightNormal;
    PaintScroll;
  end;

  if (VertUpState <> tsArrowBtnUpPressed) and (VertUpState = tsArrowBtnUpHot) then
  begin
    VertUpState := tsArrowBtnUpNormal;
    PaintScroll;
  end;

  if (VertDownState <> tsArrowBtnDownPressed) and (VertDownState = tsArrowBtnDownHot) then
  begin
    VertDownState := tsArrowBtnDownNormal;
    PaintScroll;
  end;

  CallDefaultProc(TMessage(Message));
  if LeftButtonDown then
    PaintScroll;
  Handled := True;
end;

Как должны решаться эти проблемы?

Спасибо.


person smartiz    schedule 20.10.2015    source источник
comment
Вы пробовали это без использования стиля VCL? Очевидно, что исключение связано со стилями, поэтому первое, что нужно сделать, это изолировать его, посмотрев, что происходит, когда вы его не используете. Кроме того, ваш снимок экрана отсекает часть математики, которая выполняет деление, поэтому никто не может посмотреть на него и увидеть, в чем ошибка.   -  person Brandon Staggs    schedule 20.10.2015
comment
Пожалуйста, покажите код вместо скриншотов.   -  person LU RD    schedule 20.10.2015
comment
@BrandonStaggs да, после моего поста у меня возникают проблемы только тогда, когда я использую VCL-Style, и без VCL-Style проблем нет.   -  person smartiz    schedule 20.10.2015
comment
@LURD это тестовый проект, и весь мой код вызывает функцию RTL_LV,   -  person smartiz    schedule 20.10.2015
comment
@BrandonStaggs Что касается математической части, этот код находится в Vcl.ComCtrls, поэтому, если это действительно ошибка, это будет ошибка Delphi, которую должны воспроизвести многие другие. Но все же согласился, что никогда не следует публиковать скриншот кода.   -  person Jerry Dodge    schedule 20.10.2015
comment
Я отредактировал свой пост.   -  person smartiz    schedule 20.10.2015
comment
Таким образом, исключение возникает, когда хук стиля использует нулевую высоту или ширину прямоугольника в качестве делителя. Вопрос в том, почему это происходит, и я думаю, что это связано с тем, что вы выполняете обход элемента управления и напрямую устанавливаете стили с помощью API. Смотрите мой ответ для правильного подхода, который не вызовет этой проблемы.   -  person Brandon Staggs    schedule 20.10.2015


Ответы (1)


Есть несколько проблем с вашим подходом. Быстрый ответ:

Не делайте этого. Вместо этого задайте для свойства BiDiMode элементов управления значение bdRightToLeft. Если я что-то не упустил, это обеспечит вам нужное поведение, и я проверил это сейчас, с прокруткой тем таким образом проблем не возникнет.

Есть две большие проблемы с тем, как вы это делаете:

  1. Вы не можете гарантировать, что элемент управления сохранит настройки, которые вы в него ввели. В первый раз, когда VCL потребуется воссоздать окно для элемента управления, ваши настройки будут стерты.

  2. Вы предполагаете, что VCL не нужно каким-то образом учитывать этот параметр. Ясно, что это так, потому что вы получаете плохое поведение при попытке подорвать VCL и отправить стиль непосредственно в окно. Если вы действительно хотите напрямую управлять стилем окна, вам нужно создать свой собственный потомок класса управления и обрабатывать все, что связано, в нужных местах — вы не можете просто выбрать любое старое время, которое хотите. изменить элемент управления на RTL с помощью вызова Windows API (а не свойств элемента управления) и ожидать, что элемент управления будет продолжать работать правильно.

person Brandon Staggs    schedule 20.10.2015
comment
BiDiMode=bdRightToLeft Не работает на TListView и TTreeView правильно управляет. - person smartiz; 21.10.2015
comment
это ошибка в VCL-Styles. Спасибо. - person smartiz; 21.10.2015
comment
Я согласен с тем, что хук стиля никогда не должен делить на ноль, но проблема в том, что вы меняете стили окна, не позволяя элементу управления делать что-либо для его учета. Как я уже сказал в своем ответе, это не работает, независимо от стиля. VCL часто требуется пересоздавать окна, и в первый раз, когда это происходит, ваши настройки, которые вы применили через вызовы API, исчезнут. Если элемент управления VCL имеет ошибочную реализацию BiDiMode, вам нужно будет создать свой собственный элемент управления с исправленным поведением (или опубликовать отчет об ошибке QA и подождать, пока они исправят его). - person Brandon Staggs; 21.10.2015