...

суббота, 22 ноября 2014 г.

Использование TTreeView в Firemonkey приложениях

На днях мне пришлось столкнуться с компонентом TTreeView. Заказчик настаивал, на привычном ему компоненте — “Дереве”, и хотел что бы приложение выглядело так же как он привык, в VCL.


Поэтому , в этой статье я хотел бы рассказать о компоненте TTreeView(ветка дерева) и его использовании в Firemonkey приложениях, а также рассмотреть в чем различия между VCL и FireMonkey реализацие.


В VCL, каждой ветке добавить свою картинку было не сложно. Всё что для этого требуется — это добавить компонент TImageList, “загрузить в него” картинки и назначить этот список свойству TreeView.Images:= ImageList;


Теперь 2 раза кликнем на дереве, и добавляем ветки. Каждой указываем порядковый номер картинки которую хотим отобразить на ветке.


После компиляции, получим такой результат:


В FireMonkey дерево слегка изменилось. Во-первых, в FMX нет класса TTreeNode. Во-вторых, нет свойства Images. Ну и в третьих, в классе TTreeViewItem, нету никаких свойств ответственных за использование картинок.


Интернет на запрос — how to add image to treeviewitem firemonkey. Предлагает воспользоваться довольно стандартным, как мне кажется, способом менять стандартные компоненты расширяя их за счет изменения стиля. http://ift.tt/1uQYiQB

Пример имеет полное право на жизнь и вполне необходим когда вы хотите видеть сразу результаты изменений стиля, в том числе в IDE. Но есть и другой способ, особенно если все манипуляции вы делаете в Run-Time.


Способ основывается на особенностях архитектуры FireMonkey. Если мы заглянем в документацию, то увидим такую строчку:

FireMonkey Controls Have Owners, Parents, and Children


Что означает что каждый компонент может при необходимости выступить в роли контейнера для ”любых” других компонентов (TfmxObject). Чем я и воспользуюсь.


Первым делом унаследуем новый класс ветки, и слегка “расширим” его:



type
TNode = class(TTreeViewItem)
strict private
FImage: TImage;
private
procedure SetImage(const aValue: TImage);
public
constructor Create(Owner: TComponent; const aText: String;
const aImageFileName: String); reintroduce;
destructor Destroy; override;
published
property Image: TImage Read FImage Write SetImage;
end;

{ TTestNode }

constructor TNode.Create(Owner: TComponent; const aText: String;
const aImageFileName: String);
begin
inherited Create(Owner);
Self.Text := aText;

// Создаем картинку
FImage := TImage.Create(Owner);
// Особая магия FireMonkey, интересующимся - советую заглянуть в код этого метода
Self.AddObject(FImage);
// Позиционируем картинку
FImage.Align := TAlignLayout.Right;
//Загружаем из файла
FImage.Bitmap.LoadFromFile(aImageFileName);
// Делаем картинку фоном
FImage.SendToBack;
end;

destructor TNode.Destroy;
begin
Image.FreeOnRelease;
inherited;
end;

procedure TNode.SetImage(const aValue: TImage);
begin
FImage := aValue;
end;


Следующим шагом разместим на форме несколько компонентов:

TTreeView

TImage

2 TButton

TOpenDialog

я ещё добавил компонент TStyleBook, однако он не обязателен, а лишь меняет стандартный стиль, на один из стилей “из коробки”, которые Embarcadero предоставляет вместе с IDE.


После выполнения предыдущих операций мое IDE приобрело следующий вид:


Добавим код обработки нажатия кнопок, и событие TreeChange, для дерева:



procedure TMainForm.AddNodeClick(Sender: TObject);
var
Node : TNode;
begin
// Устанавливаем параметры открытия файлов
OpenImage.Options := OpenImage.Options - [TOpenOption.ofAllowMultiSelect];

if OpenImage.Execute then
begin
// Показываем картинку на форме
MainImage.Bitmap.LoadFromFile(OpenImage.Files[0]);
// Создаем новую ветку с нужными нам параметрами
Node := TNode.Create(MainTree, ExtractFileName(OpenImage.Files[0]), OpenImage.Files[0]);

// Если ничего не выбрано, добавляем ветку дереву
if MainTree.Selected = nil then
MainTree.AddObject(Node)
else
// Добавляем ветку той которая выбрана, способ аналогичен тому который описан выше
MainTree.Selected.AddObject(Node);
end;
end;

// В этом методе всё аналогично предыдущему, кроме того что здесь есть возможность выбрать несколько картинок сразу
procedure TMainForm.AddImageListClick(Sender: TObject);
var
ImageFileName: string;
Node : TNode;
begin
OpenImage.Options := OpenImage.Options + [TOpenOption.ofAllowMultiSelect];
if OpenImage.Execute then
begin
for ImageFileName in OpenImage.Files do
begin
Node := TNode.Create(MainTree, ExtractFileName(ImageFileName), ImageFileName);
MainTree.AddObject(Node);
end;

MainImage.Bitmap.LoadFromFile(OpenImage.Files[0]);
end;
end;

procedure TMainForm.MainTreeChange(Sender: TObject);
begin
// Если выбрали ветку, то изменим картинку на картинку ветки
if MainTree.Selected is TNode then
MainImage.Bitmap := (MainTree.Selected as TNode).Image.Bitmap;
end;


После запуска приложение приняло такой вид:


На этом можно был бы заканчивать, однако я хотел обратить особое внимание читателей на особенности TFMXObject. А именно на метод AddObject который позволяет нам дорабатывать наши компоненты на лету.


Давайте, теперь добавим нашей ветви, например кнопку. Для этого аналогично примеру расширим наш класс, и добавим в конструктор инициализацию кнопки:



type
TNode = class(TTreeViewItem)
strict private
FImage: TImage;
FButton: TButton;
private
procedure SetImage(const aValue: TImage);
procedure TreeButtonClick(Sender: TObject);
procedure SetButton(const Value: TButton);
public
constructor Create(Owner: TComponent; const aText: String;
const aImageFileName: String); reintroduce;
destructor Destroy; override;
published
property Image: TImage Read FImage Write SetImage;
property Button: TButton Read FButton Write SetButton;
end;

constructor TNode.Create(Owner: TComponent; const aText: String;
const aImageFileName: String);
begin
inherited Create(Owner);
Self.Text := aText;

// Создаем картинку
FImage := TImage.Create(Owner);
// Особая магия FireMonkey, интересующимся - советую заглянуть в код этого метода
Self.AddObject(FImage);
// Позиционируем картинку
FImage.Align := TAlignLayout.Right;
//Загружаем из файла
FImage.Bitmap.LoadFromFile(aImageFileName);
// Делаем картинку фоном
FImage.SendToBack;

// Всё по сути аналогично, кроме события OnClick
FButton := TButton.Create(Owner);
FButton.Text := 'Hi World';
Self.AddObject(FButton);
FButton.Align := TAlignLayout.Center;
FButton.SendToBack;
FButton.OnClick := TreeButtonClick;
end;

procedure TNode.TreeButtonClick(Sender: TObject);
begin
// Тут можно сделать обработку того какая кнопка была нажата
ShowMessage('Hello World');
end;


Откомпилируем приложение:


Таким же образом добавим поле ввода.


Код.



{ TTestNode }

constructor TNode.Create(Owner: TComponent; const aText: String;
const aImageFileName: String);
begin
inherited Create(Owner);
Self.Text := aText;

FButton := TButton.Create(Owner);
FButton.Text := 'Send';
Self.AddObject(FButton);
FButton.Align := TAlignLayout.Center;
FButton.SendToBack;
FButton.OnClick := TreeButtonClick;

// Добавим TEdit
FEdit:= TEdit.Create(Owner);
Self.AddObject(FEdit);
FEdit.Position.X := 150;
FEdit.Position.Y := 25;
FEdit.SendToBack;

FImage := TImage.Create(Owner);
Self.AddObject(FImage);
FImage.Align := TAlignLayout.Right;
FImage.Bitmap.LoadFromFile(aImageFileName);
FImage.SendToBack;
end;


Вот так вот просто в Run-Time расширять собственные компоненты. И благодаря FireMonkey наше приложение получиться ещё и кросс-платформенное.


Спасибо, всем кто прочитал эту статью. Жду Ваших замечаний, и комментариев.


Ссылка на репозиторий с примером.

http://ift.tt/1p94vI9


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

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