пятница, 12 августа 2011 г.

Delphi Notes Splitter

Многие практикующие Delphi-программисты знают, что у стандартного TSplitter’а есть ряд недостатков (т.е. багов, если сказать откровенно). В попытках борьбы с ошибками стандартного TSplitter’а, а также желая “прикрутить” к сплиттеру кнопку (для скрытия/отображения связанной панели), я в своё время перепробовал много компонент (доступных для бесплатного скачивания), но так ни на чём конкретно и не остановился: все варианты были чем-то не тем, что нужно было мне. В итоге я решил написать свой компонент.

За основу компонента я взял обычный TSplitter (copy/paste). Первое что я сделал - поменял у него родительский класс (TGraphicControl заменил на TCustomControl), что позволило сделать нормальную обработку клавиатуры (Escape-клавиша) и мыши (без хаков и подменой обработчиков у активного контрола). Во-вторых ввёл понятие AlignControl – связанный со сплиттером компонент (у TSplitter этот компонент определяется в run-time и это иногда приводит к тому, что сплиттер теряет привязку к компоненту). Затем я добавил кнопку (алгоритм прорисовки которой я позаимствовал у другого сплиттера, к сожалению уже не помню какого именно). Ну, и на последок, компонент обзавёлся способностью связываться с Action’ами.

Итак, предлагаю Вашему вниманию VCL компонент: TdnSplitter. Это ещё один клон сплиттера, но у него есть отличительные особенности. Самая главная – TdnSplitter не является наследником от TSplitter. А следовательно он не наследует проблемы TSplitter’а. TdnSplitter создан как отдельный компонент.

Вот список отличительных свойств компонента.

  1. TdnSplitter наследован от TCustomControl (который в свою очередь наследован от TWinControl), в отличии от TSplitter, который наследован TGraphicControl.
  2. Свойство AlignControl. Это свойство задаётся явно (или определяется автоматически в Design-Time). Благодаря этому случайная потеря связанного со сплиттером компонента невозможна в принципе.
  3. Свойство IsSnapped. Узнать, скрыт ли связанный компонент можно через Boolean-свойство. Через это же свойство можно скрывать/отображать связанный компонент.
  4. Свойство ControlSize. Это свойство позволяет узнать (или изменить) ширину связанного компонента (или высоту – в зависимости от расположения).
  5. Имеется кнопка для скрывания/отображения связанного компонента. Для этой кнопки можно указать Hint и ещё несколько параметров, отвечающих за внешний вид.
  6. Свойство AllowDrag позволяет запретить изменять размер связанного компонента перетаскиванием (т.е. можно оставить только кнопку и сплиттер будет иметь всего два положения – см. свойство IsSnapped).
  7. Свойство Action. TdnSplitter можно связать с Action. Если Вы пользуетесь Аction’ами, то Вы поймёте, зачем это нужно.

Благодаря свойствам IsSnapped и ControlSize сохранять и восстанавливать состояние сплиттера и связанного с ним компонента стало проще простого.

Ссылка для скачивания: Исходник компонента + исходник демо приложения + скомпилированное демо (zip-архив 209 К).

Скриншот из реального приложения:

На картинке два сплиттера: первый “раскрыт” (видна панель “Папки” и показан хинт под курсором мыши), второй “скрыт” (панель связей в правой части окна).

Компонент написан в среде Delphi 7 и без проблем должен заработать и в более поздних версиях Delphi.

 

P.S.: В качестве развития компонента у меня была такая идея: при скрытии связанного компонента явно менять его видимость (Visible), вместо изменения его размера в 0 (как сейчас). Однако на практике это особо мне не понадобилось и пока оно осталось как есть.

P.P.S.: В Design-Time, когда вы кидаете Splitter на форму и меняете у него привязку, у сплиттера может неправильно установиться внешний размер. Не пугайтесь :). Достаточно установить/проверить свойства Align, AlignControl и Size. Так как это проявляется редко и только в Design-Time в момент настройки компонента, я не стал себя утруждать (каюсь!) исправлением этой мелкой … “ошибки” :).

P.P.S.: Несмотря на мелкую проблему с компонентом в Design-Time, я очень надеюсь что этот пост поучаствует в конкурсе delphifeeds.ru.

33 коммент.:

Анонимный комментирует...

Скриншот бы еще, или демку скомпилированную

vsmihaylovsky комментирует...

Один завалящий скриншотик - не помешал бы.
А так - на первый взгляд - очень понравилось. Может заюзаю :) Надеюсь он бесплатный?

Николай Зверев комментирует...

Скриншот приложил (для красоты я его взял из реального приложения а не из демки, состряпанной на скорую руку).
Скомпилированная демка есть в архиве с исходниками.

Компонент бесплатный, Вы можете использовать его на свой страх и риск без каких-либо ограничений и обязательств перед автором (т.е. передо мной) :).

vsmihaylovsky комментирует...

Спасибо :)

Да, демку я видел. Просто пришлось весь архив качать, только что бы посмотреть о чем речь в статье. А сейчас, со скиншотом, вообще отлично.

Unknown комментирует...

Напоминает мне он внешний, такой же из Raize =)))

Николай Зверев комментирует...

Ничего общего мой компонент с Raize не имеет:
TRzSplitter: This custom container consists of two panes separated by a splitter bar. The panes, and their contents are automatically resized when the splitter bar is moved.

А вот прорисовка кнопки... насколько мне известно, такая прорисовка (стрелки + точки с тенью) впервые появилась в Netscape навигаторе.

Андрей комментирует...

Спасибо)))
Очень полезная вещь!
Долго такую искал.

Олег комментирует...

Отличнейший интуитивно понятный и простой компонент! Как раз нашел у вас то что было нужно и разобрался за 1 мин. даже не открывая демо!
Благодарю!

Николай Зверев комментирует...

Спасибо, что не ленитесь писать комментарии!

Пользуйтесь на здоровье!

ivk комментирует...

Николай!

Большое спасибо за компонент. Тоже давно не устраивает стандартный.
Единственное, что потребовалось поправить, это поведение кнопки. Интуитивно пробовал тянуть за середину, чтобы изменить размер, а срабатывала кнопка. Перенес снаппинг в MouseUp, сделал проверку на +-2 точки и получил то, что "ждал многие годы".
Спасибо.

Николай Зверев комментирует...

Кстати, да! Почему бы за саму кнопку путём перетягивания не изменять размер? Хорошая мысль!

ivk, не могли бы Вы мне прислать изменения в исходнике, я бы его выложил заново? Контакты в профиле.

ivk комментирует...

Отправил на gmail

Николай Зверев комментирует...

Кому интересно - обновил исходник

mli2805 комментирует...

Никогда не добавлял сторонние компоненты. Где можно глянуть алгоритм действий? У меня embarcadero RAD 2010

mli2805 комментирует...

Delphi package я проинсталил. А компонента не добавилась. Что еще надо сделать?

Николай Зверев комментирует...

В файле dnSplitter.pas есть процедура Register, которая выглядит так:
procedure Register;
begin
RegisterComponents('Delphi Notes.RU', [TdnSplitter]);
end;

Это означает, что при установке компонента TdnSplitter, он появится на вкладке 'Delphi Notes.RU' в панели компонент (сама вкладка добавится, наверное, в конец списка вкладок).
А в Delphi 2010 есть ещё возможность поиска компонент - там специальное поле ввода - попробуйте туда ввести TdnSplitter

Николай Зверев комментирует...

mli2805, не забудьте также добавить каталог с исходником в меню Tools \ Options -> Environment Options \ Delphi Options \ Library - Win32 в список Library Path

Анонимный комментирует...

Не работает в ХЕ2 криво встает.... не компилит dnSplitter.pas

Кто решал проблему? может скинуть норм компонент

Николай Зверев комментирует...

Анонимный, у меня сейчас к сожалению нет XE2. Почитайте комментарий, может поможет:
http://www.delphinotes.ru/2012/05/delphi-notes-splitter.html?showComment=1336816317935#c2699450887164990205

А вообще, исходник открытый, если вам удастся исправить в нём ошибки совместимости с XE2 (3?), не поленитесь, пришлите, пожалуйста, его мне.

Контакты в профиле.

Анонимный комментирует...

После раскрытия нажатие Ctrl приводит к Stack Overflow

Николай Зверев комментирует...

> Анонимный
Да-да-да... и не только Ctrl... и не только после раскрытия, а по разным причинам это может произойти.

Я это пофиксил, но ещё не выложил исходник, выложу в ближайшее время - отпишусь отдельной заметкой

Анонимный комментирует...

А в С++ Builder можно установить? Спасибо.

Николай Зверев комментирует...

> А в С++ Builder можно установить?
Попробуйте... Я всего однажды устанавливал себе С++ Builder (чисто поглядеть), насколько помню, он прекрасно понимает pas-файлы, правда, возможно, чтобы установить компонент, придётся немного пошаманить. Поисковик вам в помощь.

Анонимный комментирует...

Компонент нравится.
Ctrl - просто отключил onKeyDown. Но есть ещё пара нюансов. На форме 2 панели с горизонтальным сплиттером. На верхней 3 мемо с двумя вертикальными. Чтобы вертикальные сплиттеры не слиплись при запуске, приходится задавать в onCreate MemoX.Align и dnSplitterX.AlignControl, по списку.
Если у горизонтального сплиттера якорь зацеплен за низ, то при уменьшении высоты формы, если он доходит до верхней границе, то прилипает к ней, правда потом, после пары щелчков возвращается.

Николай Зверев комментирует...

> Ctrl - просто отключил onKeyDown
Ошибка исправлена, архив по ссылке обновлён как раз вчера.

> Но есть ещё пара нюансов
Предлагаю из комментариев перейти к прямому общению - email/ICQ в профиле. Может вышлете текст dfm-формы?

Анонимный комментирует...

Николай, а вы не обновляли компонент? На XE4 не работает, ругается на FAction, на несовпадение типов Edges (TBevelEdges).

Николай Зверев комментирует...

Анонимный
Не обновлял и в ближайшее время не планирую. Если вдруг случится чудо - напишу отдельной заметкой.

Анонимный комментирует...

Жаль.

Анонимный комментирует...

Спасибо автору за компонент!
Hint кнопки у меня не работает. Заполнил в design time св-во Hint. Искал ShowHint - не нашёл.
Delphi версии 7.0

Николай Зверев комментирует...

Ошибку с FAction исправил - http://www.delphinotes.ru/2015/09/delphi-notes-splitter-v109.html
Компонент без проблем работает в XE7

Евгений комментирует...

Установил на 10.1 Berlin Starter. Встал без проблем.
Спасибо, Николай.

Анонимный комментирует...

Демка сплиттера по ссылке в статье не грузится с дроп-бокса (

Анонимный комментирует...

Добавил в C++Builder Tokyo 10.2, требуются минимальные правки:
к именам модулей в Uses добавить префиксы "Winapi.", "System.", "Vcl.".
Не забываем про зависимости, иначе ругается на отсутствие OBJ.
Отличный компонент!

Отправить комментарий

.

.