Почему поле со списком меняет свой текст на текст элемента при изменении шрифта?

Очевидно, это ошибка, но я не могу понять, почему это происходит. Вот минималистичный код для воспроизведения. Просто поместите поле со списком и кнопку в форме и напишите следующие обработчики событий:

procedure TForm1.FormCreate(Sender: TObject);
begin
  ComboBox1.Items.Add('A Item');
  ComboBox1.Items.Add('B Item');
  ComboBox1.Items.Add('C Item');
  ComboBox1.Style := csDropDown;
  ComboBox1.AutoComplete := False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ComboBox1.Text := 'B';
  ComboBox1.Font.Color := clRed;
  ShowMessage(IntToStr(ComboBox1.ItemIndex));
end;

Когда вы нажмете кнопку в первый раз, вы увидите в комбинированном редактировании полностью выделенный текст второго элемента, но в окне сообщения будет показано, что индекс элемента равен -1. Когда вы его выпадаете, кажется, что второй элемент выбран. Второй щелчок установит правильный текст, но остальное будет таким же, как и при первом щелчке. Итак, поле со списком в этом случае ведет себя так, как если бы было включено какое-то странное автозаполнение.

Я отследил это до EditWndProc, где после изменения шрифта приходит WM_SETTEXT сообщение с текстом второго элемента, но я не знаю, откуда оно взялось и почему с текстом второго элемента.

Итак, мой вопрос довольно конкретен - какой (какой метод) отправляет WM_SETTEXT при смене шрифта и как он узнает о совпадении текста второго элемента, когда автозаполнение отключено?

Пока я мог воспроизвести это в Delphi 2009 и Delphi XE3 в 64-разрядной версии Windows 7 Home Premium с установленными последними обновлениями.


person TLama    schedule 09.10.2012    source источник
comment
Такое же поведение подтверждено в delphi 2007, думаю, это может быть ошибка Windows API, в какой версии Windows вы тестировали?   -  person Mike Taylor    schedule 10.10.2012
comment
Я включу это в вопрос, в Windows 7.   -  person TLama    schedule 10.10.2012
comment
@whosrdaddy, да, но в этом случае может пригодиться более конкретная информация об ОС ;-)   -  person TLama    schedule 10.10.2012
comment
Насколько я понимаю, «Автозаполнение» TComboBox - это исключительно удобство VCL (по крайней мере, в D2007) и имеет какое-либо влияние только при нажатии клавиши. Полагаю, VCL тут ни при чем.   -  person Sertac Akyuz    schedule 10.10.2012


Ответы (3)


Я не думаю, что это проблема VCL, глядя на стек вызовов, кажется, что сообщение обрабатывается через comctl32.dll. Решить проблему можно, установив цвет шрифта перед установкой текста:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ComboBox1.Font.Color := clRed;
  ComboBox1.Text := 'B';
  ShowMessage(IntToStr(ComboBox1.ItemIndex));
end;
person whosrdaddy    schedule 09.10.2012
comment
Я тоже винду потихоньку подозреваю. У меня доступна только Windows 7, поэтому я не могу проверить это на других версиях, где вы это пробовали? - person TLama; 10.10.2012
comment
Delphi XE / W7. Я могу завтра попробовать это на XP, если хочешь - person whosrdaddy; 10.10.2012
comment
что-то вроде ReCreateWnd вызывается при смене шрифта? - person Arioch 'The; 10.10.2012
comment
@TLama: XP ведет себя точно так же (чего и следовало ожидать) - person whosrdaddy; 10.10.2012

Вы могли бы отследить это самостоятельно за несколько секунд, просто включив Debug DCU, а затем войдя в средство установки свойств Font.Color.

Когда Font изменяется по какой-либо причине, запускается событие TFont.OnChange. Для TControl назначен обработчик событий, даже если он может отправлять себе CM_FONTCHANGED сообщение, чтобы классы-потомки могли реагировать на изменение. Когда TWinControl получает это сообщение, он отправляет WM_SETFONT сообщение самому себе, которое затем запускает ComCtl32 для отправки сообщения WM_SETTEXT, которое вы видите.

person Remy Lebeau    schedule 09.10.2012
comment
только что нашел это, поэтому причина, по которой он работает правильно при втором щелчке, заключается в том, что шрифт уже красный. - person Mike Taylor; 10.10.2012
comment
Конечно, я использовал debug dcus; Я не смог бы отследить это до EditWndProc метода и сообщения, которое вызывает изменение. Еще меня интересовало, как этот отправитель узнает о совпадающем элементе, но даже ваш ответ подразумевает, что это сообщение отправляет comctl32. - person TLama; 10.10.2012
comment
Я не понимаю, как это ответить, почему текст меняется на текст элемента. - person Sertac Akyuz; 10.10.2012
comment
@TLama: с включенными DCU отладки вы можете перейти к назначению свойства Font.Color и следовать логике с отладчиком до тех пор, пока EditWndProc() не будет вызван с сообщением WM_SETTEXT, когда TWinControl отправит WM_SETICON сообщение самому себе. @SertacAkyuk верен, поведение автозаполнения ComboBox не имеет ничего общего со свойством AutoComplete, которое используется только во время обработки нажатия клавиш. - person Remy Lebeau; 10.10.2012

Мои эксперименты с Delphi XE8, по-видимому, показывают, что этого может быть достаточно, чтобы принудительно запросить изменение шрифта (скажем, просто установив цвет на clBlack, даже если он уже есть), как только вы впервые начнете использовать TComboBox и до того, как вы впервые напишете текст к нему. Мне кажется, что WM_SETTEXT, выбирающий неправильный текст, происходит только ПЕРВЫЙ раз, когда цвет шрифта (или другие атрибуты шрифта) записывается. После этого все ведет себя нормально. Я не удосужился выяснить, является ли это ошибкой в ​​Windows или Delphi, поскольку этот трюк решил проблему для меня. :) Я подозреваю, однако, что это еще один случай «действия перед инициализацией» в том смысле, что кодировщики не учли тот факт, что вещи не всегда вызываются в удобном порядке, когда вы предоставляете своим пользователям много пост-конструирования свойства конфигурации (например, изменение шрифтов и текста в еще неиспользуемом TCombobox). Если это окажется «панацеей», то, возможно, нам следует убедить команду Delphi поместить его в конструктор TCombobox (или предка) за нас. Между прочим, эта же «ошибка» приводит к изменению SelLength с нуля - это очень раздражает, потому что в конечном итоге текстовое поле окрашивается в синий цвет, подразумевая фокус, в то время как в фокусе это не так! Так что, если в вашей форме много комбинированных списков, все они отображаются синим цветом и утверждают, что у них есть фокус - это тоже источник этой конкретной головной боли!

Между прочим, я поднял эту проблему с Embarcadero и предложил решение, с помощью которого вышеуказанный трюк встроен в базовый конструктор. Они передали его кодировщикам, но еще неизвестно, будут ли новые версии Delphi включать необходимое исправление.

person Alex T    schedule 28.08.2016