Дублекат Вкладок В Tabcontrol После Обновления Listview

Shouldercannon

Well-Known Member
25.05.2010
128
0
#1
Доброго времени суток!
Пытаюсь реализовать работу с вкладками TabControl
Код:
unit frm_Main;

...

var
FormMain: TFormMain;

implementation

uses frm_2;

{$R *.dfm}

procedure TFormMain.Button1Click(Sender: TObject);
var
i: Integer;
Item: TListItem;
begin
LVUsers.Clear;

for i := 0 to 4 do
begin
Item := LVUsers.Items.Add;
Item.Caption := '';
Item.SubItems.Add(Format('Name: %d', [i])); // Name
Item.SubItems.Add(Format('30%d', [i])); // Room
Item.SubItems.Add(IntToStr(i)); // ID
Item.SubItems.Add('0'); // Icon
Item.ImageIndex := 0;
end;
end;

procedure TFormMain.LVUsersDblClick(Sender: TObject);
begin
if LVUsers.Selected = nil then Exit;
Form2.ProcAddTab(LVUsers.Items[LVUsers.ItemIndex], True, True);
end

end.



unit frm_2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, StdCtrls, ComCtrls, ToolWin, ExtCtrls, Contnrs;

type
TTabData = class(TObject)
public
UserID: Integer;
Nick: string;
TempMessage: string;
end;

TTabDataList = class(TObjectList)
private
function GetTab(Index: Integer): TTabData;
public
function AddTab(UserID: Integer; Nick, TempMessage: string): TTabData;
property Tabs[Index: Integer]: TTabData read GetTab; default;
end;

type
TForm2 = class(TForm)
...
private
{ Private declarations }
public
{ Public declarations }
procedure ProcAddTab(Usr: TListItem; ChangeTab, ShowForm: Boolean);
end;

var
Form2: TForm2;
TabList: TTabDataList;
tbInd: Integer;
UserData: TTabData;

implementation

uses frm_Main;

{$R *.dfm}

function TTabDataList.AddTab(UserID: Integer; Nick, TempMessage: string): TTabData;
begin
Result := TTabData.Create;
Result.UserID := UserID;
Result.Nick := Nick;
Result.TempMessage := TempMessage;
inherited Add(Result);
end;

function TTabDataList.GetTab(Index: Integer): TTabData;
begin
Result := inherited Items[Index] as TTabData;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
TabList := TTabDataList.Create(True); // TabList сам уничтожтит элемент при удалении
end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
FreeAndNil(TabList);
end;

procedure TForm2.ProcAddTab(Usr: TListItem; ChangeTab, ShowForm: Boolean);
var
UsrData: TTabData;
i: Integer;
begin
UsrData := Usr.Data;

if (UsrData = nil) then // Нет такого пользователя
begin
// Создаем, добавляем в список TabList
UsrData := TabList.AddTab(StrToInt(Usr.SubItems[2]), Usr.SubItems[0], '');
// Связываем с элементом в LVUsers
Usr.Data := UsrData;
end;
// Ищем пользователя на вкладках
i := TabControl1.Tabs.IndexOfObject(UsrData);

if (i < 0) then // Нет вкладки с таким пользователем, добавляем новую вкдадку - здесь дыра
begin
i := TabControl1.Tabs.Add(UsrData.Nick);
// Связываем c конкретной вкладкой
TabControl1.Tabs.Objects[i] := UsrData;
end;

if ChangeTab then
begin
// Сделаем вкладку активной
TabControl1.TabIndex := i;
end;
// Покажем форму если разрешено
if ShowForm then
begin
Self.WindowState := wsNormal;
Self.Show;
end;
end;

end.
Нашёл один недостаток. Если пересоздать список пользователей в ListView и кликать по этим записям, то в TabControl появляться дубликаты уже имеющихся/открытых вкладок.
Тут
Код:
 // Ищем пользователя на вкладках
i := TabControl1.Tabs.IndexOfObject(UsrData);
отладчик показывает одинаковые данные до обновления и после обновления списка пользователей при проверке выбранной записи
(0, 'Name: 0', '')
(0, 'Name: 0', '')

В чём проблема? Это можно исправить?
 

Shouldercannon

Well-Known Member
25.05.2010
128
0
#5
В TForm2.ProcAddTab передается TListItem, взятое из TListView.Items. Там же потом извлекается Data. Так вот, что же храниться в Data? А там храниться ссылка на область памяти. Той самой памяти, которая заполняется в AddTab и отдается потом в TabControl1.Tabs.Objects. При нажатии Button1 срабатывает LVUsers.Clear; TListView очищается и, соответственно, очищаются все Data. При последующем срабатывании ProcAddTab данных в Data нет. А раз так, то выполняется условие (UsrData = nil), заново выделяется и заполняется данными память в AddTab, идет сравнение по IndexOfObject и оно не срабатывает т.к. хоть данные и одинаковые, но это не те же данные, а поэтому IndexOfObject возвращает -1. Это очень хорошо видно, если под отладчиком в ProcAddTab вызвать Pointer(UsrData) и посмотреть что там будет для каждой записи при первом заполнении LVUsers и потом при перезаполнении LVUsers.

Короче, IndexOfObject не срабатывает потому, что мы сравниваем разные экземпляры класса TTabData. И хоть они заполнены одинаковыми данными, это все-равно разные экземпляры.
 

-master-

Well-Known Member
14.01.2012
616
20
#6
в этой строке происходит та самая проверка

поэтому надо вам посмотреть в книгу, где и как она делается