На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
  
> FireMonkey, FireDAC , Программирование БД в действии
    Начинаю серию постов написания демо-приложения БД с использованием новейшей технологии FireMonkey!
    Подобное я уже писал на MFC, ща буду щупать FMX и FireDAC! :D
    Ну держитесь! :D

    В демо я буду юзать готовую БД, знаете какую? Chinook!
    А в качестве движка SQLite!

    Потом это демо запустим на гавнофоне! мля думаю буде интресно посмотреть как оно работает! :D

    ПС. Разработку веду на Delphi 10.3 Rio, ссылки на сайт Chinook
    https://archive.codeplex.com/?p=chinookdatabase
    и тьюториал, где также есть сама БД и диаграмма таблиц
    http://www.sqlitetutorial.net/sqlite-sample-database/
    Сообщение отредактировано: Cfon -
      Итак начнем, что мы имеем? мы имеем готовую БД, которая хорошо подходит для демо приложения.
      Что это за приложение будет? Это будет класическое приложение БД, которое мы постепенно нарастим функционалом и также постепено доведем до совершенства! :D
      Я уже смотрел схему этой БД, в ней 8 таблиц, которые связаны между собой. Для начала надо разобраться в данными хранящимися в них. Давайте напишем первый прототип приложения, с учетом того что оно будет запускаться на смартфоне. Мы будем отображать каждую таблицу в TListView. Говорят он хорошо заходит для этого. А каждый листвью будет располагаться на отдельной вкладке компонент TTabControl. Говорят он хорошо подходит для дизайна под смартфон, вот и проверим :D
      Сообщение отредактировано: Cfon -
        Амиго, тебе бы в такой форме на хабр писать или в блог. Форум все же другой формат подразумевает
        Сообщение отредактировано: Fr0sT -
          Цитата Fr0sT @
          Амиго, тебе бы в такой форме на хабр писать или в блог. Форум все же другой формат подразумевает

          Да я ж учусь, блоги пока ран мне, да и могут возникнуть вопросы по теме :D

          Продолжаю вещать :D
          Когда делал прототип меня поразило то что в Делфи почти все можно сделать без написания кода!
          Чтобы отобразить данные таблицы Artists из Chinook.db вот что мне понадобилось:
          - Компоненты TListView, TBindSourceDB, TBindingsList на главной форме
          - TFDConnection, TFDQuery на дата форме
          - настроил TFDConnection на подключение к Chinook.db через SQLite, настоил select запрос в TFDQuery.
          - настроил TListView, через ItemAppearance -> Item добавил два итема ArtistID и Name.
          - заюзал LiveBindings Designer, чтобы связать TBindSourceDB, TBindingsList и TFDQuery. Там все визуально!
          ВСЕ! MVVM в действии никаких тебе заморочек с кодом! :crazy:

          В архиве код ручного кодирования, но без MVVM :jokingly:
          Прикреплённый файлПрикреплённый файлVersion1.zip (55,34 Кбайт, скачиваний: 105)
          Сообщение отредактировано: Cfon -
            Давайте продолжим кодирование нашего примера, оставим пока LiveBinding, визуальное кодирование удобно, но не всегда гибко.
            Итак у есть метод обновления TListView UpdateArtistListView, произведем его рефакторинг. Почему так скоро? Потому что потом будет поздно :D
            Основной принцип успешного кодирования это пишем гавнокод потом его рефакторинг, только так можно получить из *авна конфетку :crazy:
            Вы спросите а из любого гавнокода можно конфетку сделать? Да у меня всегда получалось это, но я опытный рефакторщик :jokingly:

            Вот исходный код UpdateArtistsListView:
            ExpandedWrap disabled
              procedure TChinookMainForm.UpdateArtistsListView;
              begin
                var LQuery:= ChinookDataModule.ArtistSelectQuery;
                ArtistsListView.BeginUpdate;
                try
                  LQuery.Open;
                  try
                    while not LQuery.Eof do
                    begin
                      var item:= ArtistsListView.Items.Add;
                      item.Tag:= LQuery.FieldByName('ArtistID').AsInteger;
                      item.Objects.FindObjectT<TListItemText>('ArtistID').Text:=
                        LQuery.FieldByName('ArtistID').AsString;
                      item.Objects.FindObjectT<TListItemText>('Name').Text:=
                        LQuery.FieldByName('Name').AsString;
                      LQuery.Next;
                    end;
                  finally
                    LQuery.Close;
                  end;
                finally
                  ArtistsListView.EndUpdate;
                end;
              end;

            Что в нем не так? Все так он рабочий, но вот не гибкий, поскольку жестко привязан к данным, нам надо развязать :D
            Что мы делаем, выносим все ссылки на данные это TListView, TFDQuery:
            ExpandedWrap disabled
              procedure TChinookMainForm.UpdateArtistsListViewV1(AListView: TListView;
                AQuery: TFDQuery);
              begin
                AListView.BeginUpdate;
                try
                  AQuery.Open;
                  try
                    while not AQuery.Eof do
                    begin
                      var item:= AListView.Items.Add; ////<-- не выношу за цикл для оптимизации
                      
                      // VVV
                      item.Tag:= AQuery.FieldByName('ArtistID').AsInteger;
                      item.Objects.FindObjectT<TListItemText>('ArtistID').Text:=
                        AQuery.FieldByName('ArtistID').AsString;
                      item.Objects.FindObjectT<TListItemText>('Name').Text:=
                        AQuery.FieldByName('Name').AsString;        
                      // ^^^ этот блок кода надо изменять
               
                      AQuery.Next;
                    end;
                  finally
                    AQuery.Close;
                  end;
                finally
                  AListView.EndUpdate;
                end;
              end;

            но еще не все там есть кусок кода, который надо тоже менять (я его отметил комментом), поскольку поля то у разных запросов тоже разные! Как быть? Идеи?

            По путно отмечу, что мы не оптимизируем код, а меняем структуру кода, например в случае с переменной item что находится внутри цикла, ее можно конечно вынести из цикла и возможно это увеличит производительность, но не на много и да преждевременная оптимизация вредна! Ее надо делать тока если код реально тормозной. Я ее не вынес наружу ибо так лучше смотрится и не отвлекается :D
            Сообщение отредактировано: Cfon -
              Решение простое юзаем фунциональщину! :D
              Наша функция переходит в разряд высших функций! Зерте чо я делаю:
              ExpandedWrap disabled
                procedure TChinookMainForm.UpdateListViewV2(AListView: TListView;
                  AQuery: TFDQuery; InitItemProc: TProc<TListViewItem>);
                begin
                  AListView.BeginUpdate;
                  try
                    AQuery.Open;
                    try
                      while not AQuery.Eof do
                      begin
                        var item:= AListView.Items.Add;
                        InitItemProc(item); //<-- вызов иницилизации
                        AQuery.Next;
                      end;
                    finally
                      AQuery.Close;
                    end;
                  finally
                    AListView.EndUpdate;
                  end;
                end;

              и вызов
              ExpandedWrap disabled
                UpdateListViewV2(ArtistsListView, ChinookDataModule.ArtistSelectQuery,
                    procedure (item: TListViewItem)
                    begin
                      var query:= ChinookDataModule.ArtistSelectQuery;
                      item.Tag:= query.FieldByName('ArtistID').AsInteger;
                      item.Objects.FindObjectT<TListItemText>('ArtistID').Text:=
                        query.FieldByName('ArtistID').AsString;
                      item.Objects.FindObjectT<TListItemText>('Name').Text:=
                        query.FieldByName('Name').AsString;
                    end
                  );

              Тут есть одна особенность, не стал передавать в функцию InitItemProc ссылку на запрос TFDQuery, а внутри функции просто юзается внешняя ссылка ChinookDataModule.ArtistSelectQuery.
              Вот так уже гибче, теперь мы можем создать еще листвьюшек и добавить туда запросы на остальные таблицы. Например вот как может выглядеть вызов для Album:
              ExpandedWrap disabled
                UpdateListViewV2(AlbumsListView, ChinookDataModule.AlbumSelectQuery,
                    procedure (item: TListViewItem)
                    begin
                      var query:= ChinookDataModule.AlbumSelectQuery;
                      item.Tag:= query.FieldByName('AlbumId').AsInteger;
                      item.Text:= query.FieldByName('Title').AsString;
                    end
                  );

              ну и так далее.
              Другой варик юзать один и тот же листвью для отображения разных запросов, но я не стал так делать. Почему? Потому что тогда придется еще в коде писать настройки каждой вьюшки :D
              Сообщение отредактировано: Cfon -
                Вынес длинющие вызовы FindObjectT с отдельный метод SetListItemText чтобы уменьшить объем кода анонимок:
                ExpandedWrap disabled
                  procedure TChinookMainForm.SetListItemText(AItem: TListViewItem; AQuery: TFDQuery;
                    itemName, fieldName: string);
                  begin
                    if fieldName = '' then fieldName:= itemName;
                    AItem.Objects.FindObjectT<TListItemText>(itemName).Text:=
                      AQuery.FieldByName(fieldName).AsString;
                  end;
                   
                  procedure TChinookMainForm.UpdateArtistsListViewActionExecute(Sender: TObject);
                  begin
                    UpdateListView(ArtistsListView, ChinookDataModule.ArtistSelectQuery,
                      procedure (item: TListViewItem; query: TFDQuery)
                      begin
                        item.Tag:= query.FieldByName('ArtistId').AsInteger;
                        SetListItemText(item, query, 'ArtistId'); //<-- тут
                        SetListItemText(item, query, 'Name');  //<-- и тут
                      end
                    );
                  end;

                Еще как оказалось названия итемов листвьюшек различают строчные и прописные буквы! Поэтому надо передавать их имена в соотвествии с регистром букв, что не совсем типично для Делфи. Случайно напоролся на эту фичу, но не сразу понял в чем дело :wacko:
                Попутно обернул вызовы UpdateListView в действия, для удобства их вызова :D
                Сообщение отредактировано: Cfon -
                  Так вроде все красиво и работает :D
                  Но есть одно но, мы отвязали данные только функции UpdateListView, но что если нам потом потребуется получать данные не от SQLite, а например из облака?

                  Итак отделяем морду от тела :D
                  Пишем модель данных, что представляют содержимое наших таблиц. Для начала берем таблицу Artists:
                  ExpandedWrap disabled
                    unit uChinookModel;
                     
                    interface
                     
                    uses System.Generics.Collections;
                     
                    type
                      TArtist = record
                        ArtistId: integer;
                        Name: string;
                        constructor Create(AArtistId: integer; AName: string);
                      end;
                     
                      TArtistList = TList<TArtist>;
                     
                      IArtistData = interface
                        function GetArtistList: TArtistList;
                      end;
                     
                    implementation
                     
                    { TArtist }
                     
                    constructor TArtist.Create(AArtistId: integer; AName: string);
                    begin
                      ArtistId:= AArtistId;
                      Name:= AName;
                    end;
                     
                    end.

                  далее в модуле TChinookDataModule, определяем ArtistData и реализуем IArtistData:
                  ExpandedWrap disabled
                    type
                      TChinookDataModule = class(TDataModule)
                      ...
                      private
                        FArtistData: IArtistData;
                      public
                        property ArtistData: IArtistData read FArtistData;
                      end;
                     
                    ....
                     
                    implementation
                    type
                      TArtistData = class(TInterfacedObject, IArtistData)
                      private
                        FDataModule: TChinookDataModule;
                        function GetArtistList: TArtistList;
                      public
                        constructor Create(ADataModule: TChinookDataModule);
                      end;
                     
                    { TArtistData }
                     
                    constructor TArtistData.Create(ADataModule: TChinookDataModule);
                    begin
                      FDataModule:= ADataModule;
                    end;
                     
                    function TArtistData.GetArtistList: TArtistList;
                    var artist: TArtist;
                    begin
                      Result := TList<TArtist>.Create;
                      var LQuery := FDataModule.ArtistSelectQuery;
                      LQuery.Open;
                      try
                        while not LQuery.Eof do
                        begin
                          artist.ArtistId := LQuery.FieldByName('ArtistId').AsInteger;
                          artist.Name := LQuery.FieldByName('Name').AsString;
                          Result.Add(artist);
                          LQuery.Next;
                        end;
                      finally
                        LQuery.Close;
                      end;
                    end;
                     
                    procedure TChinookDataModule.DataModuleCreate(Sender: TObject);
                    begin
                      FArtistData:= TArtistData.Create(self);
                    end;

                  Теперь надо изменить код UpdateListView морды
                  ExpandedWrap disabled
                    procedure TChinookMainForm.UpdateListView(AListView: TListView;
                      AArtistData: IArtistData);
                    begin
                      AListView.BeginUpdate;
                      try
                        for var artist: TArtist in AArtistData.GetArtistList do
                        begin
                          var item: TListViewItem := AListView.Items.Add;
                          item.Tag:= artist.ArtistId;
                          item.Objects.FindObjectT<TListItemText>('ArtistId')
                            .Text:=artist.ArtistId.ToString;
                          item.Objects.FindObjectT<TListItemText>('Name').Text:=artist.Name;
                        end;
                      finally
                        AListView.EndUpdate;
                      end;
                    end;

                  Я ее не стал делать через колбек, этот рефакторинг мы уже умеем :D
                  Как видим кода много, но зато теперь если нам надо подключиться как я уже говорил к облаку, то мы просто пишем модуль реализации IArtistData и ВСЕ! :D
                  Морду не надо будет уже править, ибо она получает интерфейc IArtistData.

                  Цикл while not LQuery.Eof do переместился в модуль TChinookDataModule где ему и место, а в UpdateListView идет цикл по списку TArtistList, т.е. по обобщеным данным. Сам же список формируется за пределами морды и получается через свойство ArtistData модуля TChinookDataModule.
                  По аналогии делаем все тоже для остальных таблиц.
                  Сообщение отредактировано: Cfon -
                    Возникла проблема у нас UpdateListView снова зависит от данных, в даном случае ему можно передавать тока IArtistData. Как быть? Идеи?

                    Ответ :D
                    Надо передавать не интерфейс, а сам список. Также надо типизировать UpdateListView. Пример позже запушу.
                    Сообщение отредактировано: Cfon -
                      Вот что получается
                      uChinookMainForm:
                      ExpandedWrap disabled
                        type
                          TChinookMainForm = class(TForm)
                            ToolBar1: TToolBar;
                            Label1: TLabel;
                            TabControl1: TTabControl;
                            ArtistsTabItem: TTabItem;
                            ArtistsListView: TListView;
                            AlbumsTabItem: TTabItem;
                            AlbumsListView: TListView;    
                            ActionList1: TActionList;
                            UpdateArtistsListViewAction: TAction;
                            UpdateAlbumsListViewAction: TAction;
                            procedure FormCreate(Sender: TObject);
                            procedure UpdateArtistsListViewActionExecute(Sender: TObject);    
                            procedure UpdateAlbumsListViewActionExecute(Sender: TObject);
                            procedure ArtistsTabItemClick(Sender: TObject);
                            procedure AlbumsTabItemClick(Sender: TObject);    
                          private
                            procedure SetListItemText(AItem: TListViewItem; AItemName, AValue: string);
                          public
                            procedure UpdateListView<T>(AListView: TListView; AList: TList<T>;
                              InitItemProc: TProc<TListViewItem, T>);
                          end;
                         
                        var
                          ChinookMainForm: TChinookMainForm;
                         
                        implementation
                         
                        {$R *.fmx}
                        {$R *.NmXhdpiPh.fmx ANDROID}
                         
                        uses
                          uChinookDataModule
                        {$IFDEF MSWINDOWS}
                        , Winapi.Windows
                        {$ENDIF}
                        ;
                         
                        procedure TChinookMainForm.SetListItemText(AItem: TListViewItem; AItemName,
                          AValue: string);
                        begin
                          AItem.Objects.FindObjectT<TListItemText>(AItemName).Text:= AValue;
                        end;
                         
                        procedure TChinookMainForm.UpdateListView<T>(AListView: TListView;
                          AList: TList<T>; InitItemProc: TProc<TListViewItem, T>);
                        begin
                          AListView.BeginUpdate;
                          try
                            AListView.Items.Clear;
                            for var elem in AList do
                            begin
                              var item := AListView.Items.Add;
                              InitItemProc(item, elem);
                            end;
                          finally
                            AListView.EndUpdate;
                          end;
                        end;

                      Модуль uChinookModel:
                      ExpandedWrap disabled
                        type
                          TArtist = record
                            ArtistId: integer;
                            Name: string;    
                          end;
                         
                          TAlbum = record
                            AlbumId: integer;
                            Title: string;
                            ArtistId: integer;
                          end;
                         
                          TArtistList = TList<TArtist>;
                          TAlbumList = TList<TAlbum>;
                          
                          IArtistData = interface
                            function GetArtistList: TArtistList;
                          end;
                         
                          IAlbumData = interface
                            function GetAlbumList: TAlbumList;
                          end;

                      uChinookDataModule:
                      ExpandedWrap disabled
                        type
                          TChinookDataModule = class(TDataModule)
                            ChinookConnection: TFDConnection;
                            ArtistSelectQuery: TFDQuery;
                            AlbumSelectQuery: TFDQuery;    
                            procedure ChinookConnectionBeforeConnect(Sender: TObject);
                            procedure DataModuleCreate(Sender: TObject);
                          private
                            FArtistData: IArtistData;
                            FAlbumData: IAlbumData;
                          public
                            property ArtistData: IArtistData read FArtistData;
                            property AlbumData: IAlbumData read FAlbumData;
                          end;
                         
                        implementation
                         
                        {%CLASSGROUP 'FMX.Controls.TControl'}
                         
                        {$R *.dfm}
                         
                        uses
                          System.IOUtils;
                         
                        type
                          TBaseData = class abstract(TInterfacedObject)
                          private
                            FDataModule: TChinookDataModule;
                          public
                            constructor Create(ADataModule: TChinookDataModule);
                            function ToList<T>(AQuery: TFDQuery; AFunc: TFunc<TFDQuery, T>): TList<T>;
                          end;
                         
                          TArtistData = class(TBaseData, IArtistData)
                          private
                            function GetArtistList: TArtistList;
                          end;
                         
                          TAlbumData = class(TBaseData, IAlbumData)
                          private
                            function GetAlbumList: TAlbumList;
                          end;
                         
                        procedure TChinookDataModule.ChinookConnectionBeforeConnect(Sender: TObject);
                        begin
                          ChinookConnection.Params.Values['Database']:= TPath.Combine(
                            TPath.GetDocumentsPath, 'chinook.db');
                        end;
                         
                        procedure TChinookDataModule.DataModuleCreate(Sender: TObject);
                        begin
                          FArtistData:= TArtistData.Create(self);
                          FAlbumData:= TAlbumData.Create(self);
                        end;
                         
                        { TArtistData }
                         
                        function TArtistData.GetArtistList: TArtistList;
                        begin
                          Result := ToList<TArtist>(FDataModule.ArtistSelectQuery,
                            function(query: TFDQuery): TArtist
                            begin
                              Result.ArtistId := query.FieldByName('ArtistId').AsInteger;
                              Result.Name := query.FieldByName('Name').AsString;
                            end
                          );
                        end;
                         
                        { TAlbumData }
                         
                        function TAlbumData.GetAlbumList: TAlbumList;
                        begin
                          Result := ToList<TAlbum>(FDataModule.AlbumSelectQuery,
                            function(query: TFDQuery): TAlbum
                            begin
                              Result.AlbumId := query.FieldByName('AlbumId').AsInteger;
                              Result.Title := query.FieldByName('Title').AsString;
                              Result.ArtistId:= query.FieldByName('ArtistId').AsInteger;
                            end
                          );
                        end;
                         
                        { TBaseData }
                         
                        constructor TBaseData.Create(ADataModule: TChinookDataModule);
                        begin
                          FDataModule:= ADataModule;
                        end;
                         
                        function TBaseData.ToList<T>(AQuery: TFDQuery;
                          AFunc: TFunc<TFDQuery, T>): TList<T>;
                        begin
                          Result := TList<T>.Create;
                          AQuery.Open;
                          try
                            while not AQuery.Eof do
                            begin
                              Result.Add(AFunc(AQuery));
                              AQuery.Next;
                            end;
                          finally
                            AQuery.Close;
                          end;
                        end;

                      Кроме метода UpdateListView изменил реализацию модели, создал дополнительный
                      класс TBaseData, чтобы вынести некоторый общий код см обобщеный метод ToList.

                      Прикреплённый файлПрикреплённый файлVersion2.zip (60,94 Кбайт, скачиваний: 105)

                      Добавлено
                      Теперь все пучком, но! :D
                      Ща у нас тейблы один в один отображаются во вьюшках, а что если нам надо данные миксовать! Ну например отобразить AlbumName и ArtistName или TrackName, AlbumName, ArtistName ну и тд.

                      Ответ :D
                      Меняем морду согласно требованию, и добавляем след код:
                      ExpandedWrap disabled
                        procedure TChinookMainForm.UpdateAlbumsListViewActionExecute(Sender: TObject);
                        begin
                          var ArtistList:=ChinookDataModule.ArtistData.GetArtistList;
                         
                          UpdateListView<TAlbum>(AlbumsListView, ChinookDataModule.AlbumData.GetAlbumList,
                            procedure (item: TListViewItem; album: TAlbum)
                            begin
                              item.Tag:= album.AlbumId;
                              SetListItemText(item, 'AlbumId', album.AlbumId.ToString);
                              SetListItemText(item, 'Title', album.Title);
                         
                              // поиск album.ArtistId
                              //   VVVV
                              for var artist: TArtist in ArtistList do
                              begin
                                if artist.ArtistId = album.ArtistId then
                                begin
                                  SetListItemText(item, 'ArtistName', artist.Name);          
                                  Break;
                                end;
                              end;
                            end
                          );
                        end;

                      Варик создать запрос и уже его обрабатывать, но тут я думаю будет больше гемора.
                      Заметьте я тут не юзаю Spring4D. Почему?! Да просто не хотел подключать в демке другие библы :D Возможно в следущей версии подключу, а то циклами не удобно :jokingly: хотя и я изрядно попотел над длинным синтаксисом Spring :D
                      Да еще я вынес обращение к GetArtistList наружу для оптимизации. Вот тут как раз она нужна. Если оставить внутри, то на каждую строчку альбома будет выполняться запрос всего списка артистов снова и снова.
                      Тут мы видим во всей красе силу функционального стиля програмирования. Внутри анонимки мы обращемся ко списку ArtistList. Это называется замыкание. Гибко однако. :blush:
                      Сообщение отредактировано: Cfon -
                        складно пишешь, ток никуя не понятно)
                          Цитата vasya2019 @
                          складно пишешь, ток никуя не понятно)

                          Дядка главное что я понел :D

                          Возвращаясь к демо. Все пашет, но! :D
                          Что если.. нам надо ввести например ArtistName и число альбомов выпущеных этим артистом? Как быть? Идеи?

                          Ответ :D
                          Все как и раньше меняем морду и пишем код:
                          ExpandedWrap disabled
                            procedure TChinookMainForm.UpdateArtistsListViewActionExecute(Sender: TObject);
                            begin
                              var AlbumList:= ChinookDataModule.AlbumData.GetAlbumList;
                             
                              UpdateListView<TArtist>(ArtistsListView, ChinookDataModule.ArtistData.GetArtistList,
                                procedure (item: TListViewItem; artist: TArtist)
                                begin
                                  item.Tag:= artist.ArtistId;
                                  SetListItemText(item, 'ArtistId', artist.ArtistId.ToString);
                                  SetListItemText(item, 'Name', artist.Name);
                             
                                  // подсчет числа альбомов у артиста
                                  //   VVV
                                  var albumCount:= 0;
                                  for var album in AlbumList do
                                  begin
                                    if album.ArtistId = artist.ArtistId then
                                      Inc(albumCount);
                                  end;
                                  SetListItemText(item, 'AlbumCount', albumCount.ToString);
                                end
                              );
                            end;

                          походу тут уже у нас не будет проблем с модификацией морды, че б я не придумал все изи решается.

                          О! что если нам надо ваще другую морду делать? Как быть? Идеи? :fool: :lool:
                          Объясняю. К примеру нам нужен график вместо листвью! :blink:
                          Сообщение отредактировано: Cfon -
                            Ответ :D
                            Отделяем кожу от морды :crazy:
                            Короче надо выносить всю логику в отдельный модуль так называемый ViewModel.
                            У нас будет реализован шаблон MVVM (модель-вью-вьюмодель). Это один из паттернов програмирования таких как MVC, MVP.
                            Чем они отличаются? Да хз я запуталси :D, вроде как толщиной промежуточного слоя между View и Model :D
                            В одном случае он называется контролер, а с другом презентер. Кто такой презентер не знаю :D

                            Где тут логика?
                            Например в двух предыдуших примерах я вычислял значения полей для листвьюшек, и чем дальше тем больше будет логики! Мы еще не писали код редактирования данных :D
                            Сообщение отредактировано: Cfon -
                              Кто знает как настроить TListView, чтобы были заголовки у него? :wall:
                                Просмотрел кучу мобильных приложений и понял что не правильно делал его морду. Исходно я делал в десктоп стиле. В следующей версии я предоствлю новый дизайн. Век живи век учись :D
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0545 ]   [ 18 queries used ]   [ Generated: 19.03.24, 04:50 GMT ]