Конференция "Компоненты" » компонент наследник TStringGrid [D7]
 
  • OnCreate © (19.10.10 23:30) [0]
    Здравствуйте товарищи. Слёзно прошу вас помочь мне написать компонент, наследник TStringGrid. Этот компонент должен уметь читать RSS ленту новостей, используя парсер от Microsoft. Когда мы ставим компонент на форму, мы указываем в свойстве адрес ленты и при активации свойства Active например, он нам читает нововсти и заполняет их в свои ячейки. У меня получилось сделать обыкновенное приложение, не компонент, в этом приложении все делается с TMemo. А вот как это всё реализовать в компоненте, да тем более наследнике TStringGrid не представляю.
    Вот код моего приложения:

    procedure TForm1.Button1Click(Sender: TObject);
    Var rss_doc: IXMLDOMDocument;
       node1: IXMLDOMNode;
       i:Integer;
    begin
     If not (OpenDialog1.Execute) then Exit;
     rss_doc:=CoDOMDocument.Create;
     rss_doc.async:=False;
     rss_doc.load(OpenDialog1.FileName);

     if rss_doc.parseError.errorCode<>0 then
        begin
     ShowMessage('При загрузке файл произошла ошибка!'+#13#10+
                 'Код ошибки: '+IntToStr(rss_doc.parseError.errorCode)+#13#10+
                 'Текст ошибки: '+rss_doc.parseError.reason+#13#10+
                 'Строка с ошибкой: '+IntToStr(rss_doc.parseError.line)+#13#10+
                 'Символ в строке с ошибкой: '+IntToStr(rss_doc.parseError.linepos));
     CoUnInitialize;
     Exit;
    end;
     node1:= rss_doc.selectSingleNode('//rss');
     For i:=0 to node1.selectNodes('//item').length-1 do
       begin
         Memo1.Lines.Add('Заголовок');
         Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('title').Text);
         Memo1.Lines.Add('Ссылка');
         Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('link').Text);
         Memo1.Lines.Add('Описание');
         Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('description').Text);
         If not (node1.selectnodes('//item').item[i].selectSingleNode('PubDate') = nil) then
         begin
         Memo1.Lines.Add('Дата публикации');
         Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('PubDate').Text)
         end;
         If not (node1.selectnodes('//item').item[i].selectSingleNode('Author') = nil) then
         begin
         Memo1.Lines.Add('Автор');
         Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('Author').Text);
         end;
         If not (node1.selectnodes('//item').item[i].selectSingleNode('lastBuildDate') = nil) then
         begin
         Memo1.Lines.Add('lastBuildDate');
         Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('lastBuildDate').Text);
         end;
       end;
    end;

    end.
  • DimaBr © (20.10.10 08:27) [1]
    1. Очевидно, что наследник должен иметь свойство FileName: string, с которого и будет происходить чтение.
    2. Наверняка необходимо иметь свойство Active: boolean,  по измененнию которого и начнётся заполнение грида.

    Ну и практически весь код перенести в SetActive
  • OnCreate © (20.10.10 13:31) [2]
    А какими методами переносить новости потом в грид? Как переопределять при этом компонент?
  • Медвежонок Пятачок © (20.10.10 17:55) [3]
    какой ужос.....

    стрингрид, я так понимаю, здесь только потому что "оно будет в таблице со строчками и колонками" ?
  • OnCreate © (20.10.10 20:12) [4]
    Оно в смысле RSS? Да оно должно заполняться в ячейки stringgrid.
  • Медвежонок Пятачок © (21.10.10 09:04) [5]
    зачем в ячейки?
    в смысле зачем в ячейки - это понятно.
    но зачем в ячейки стринггрида?
  • DimaBr © (21.10.10 12:26) [6]
    Вы сначала сделаете предложенные два свойства FileName,Active

    TMyStringGrid = class(TCustomDrawGrid)
     published
        property FileName: string read fFileName write fFileName;
        property Active: boolean read fActive write SetActive;
    end;


    Далее, в конструкторе определим количество строк и столбцов в контроле

    constructor Create;
    begin
     inherited Create;
     ColCount := 5;
     RowCount := 1;
    end;


    Далее в момент активации заполняем грид

    procedure SetActive(Value: boolean);
    begin
     if Value = fActive then Exit;
     fActive := Value;
     if fActive then begin
       // здась приблизительный ваш код из Button1Click(Sender: TObject);
     end;
    end;

  • Медвежонок Пятачок © (21.10.10 12:29) [7]
    не проще ли написать к этому rss (и не только к этому кстати) файл трансформации и выполнить единственный метод transformnode и вывести результ в twebbrowser?

    в табличке, с бантиками и шашечками.
  • DimaBr © (21.10.10 13:00) [8]
    Если человек хочет в гриде - не будем его переубеждать
  • OnCreate © (21.10.10 14:55) [9]
    Медвежолнок Пятачок, я бы может и рад был сделать так как ты предлагаешь, но у меня задание для стринггрида (препод настаивает). DimaBr спасибо за понимание и код, буду пробовать. Если что не получится приду опять))) Это единственный форум на котором ответили.
  • Медвежонок Пятачок © (21.10.10 17:53) [10]
    var s : TStrings;
    begin
    s := TStringList.Create;
    s.Add('Мама');
    s.Add('мыла');
    s.Add('раму.');
    s.Add('Рама');
    s.Add('чистая.');
    StringGrid1.Rows[1].Assign(s);
    .....
    s.Free;
  • OnCreate © (21.10.10 20:57) [11]
    на счёт метода s.Free (освобождение памяти), его нужно вызывать сразу после того как я все новости загоню?
  • OnCreate © (21.10.10 21:48) [12]
    Ещё закрался такой вопрос, в мемо всё передавалось в цикле:
    For i:=0 to node1.selectNodes('//item').length-1 do
      begin
        Memo1.Lines.Add('Заголовок');
        Memo1.Lines.Add(node1.selectnodes('//item').item[i].selectSingleNode('title').Text);



    А как тоже самое передать в стринггрид через свойство Cells[i,j]? Чё-то уже лыжи не едут)
  • Медвежонок Пятачок © (21.10.10 22:43) [13]
    Код у тебя шикарный. Слишком.
    Сначала для получения границ for делается selectnodes
    затем на каждой итерации снова селект и взятие айтема по индексу.
    и разумеется никогда не вспоминаем, что нил в природе встречается.

    Не пробовал писать менее экстремально и не столь гламурно?
  • OnCreate © (21.10.10 23:07) [14]
    хех, я согласен с тобой, сам пока ещё не осознаю насколько влип))
  • Медвежонок Пятачок © (21.10.10 23:25) [15]
    1. делаем селектнодес для получения списка элементов ленты. результат сохраняем в ixmldomnodelist
    2. делаем цикл от нуля до Pred(длина списка)
    3. на каждой итерации цикла получаем различные атрибуты элемента списка. Через селектноде от текущего элемента списка.
    4. получив полное описание элемента, рассовываем его в ячейки следующей строки грида (предварительно создав новую строку, если свободные строки грида уже закончились)
  • OnCreate © (24.10.10 12:36) [16]
    Получился следующий код (не без налёта гламура):
    unit RSStringGrid;

    interface

    uses
     SysUtils, Classes, Controls, Grids, MSXML, ActiveX, Dialogs;

    type
     TRSStringGrid = class(TStringGrid)
     private
       fActiveRSS: boolean;
       fFileNameRSS: String;
       //procedure FileNameRSS(const Value:String);
       procedure SetActiveRSS(const Value: Boolean);
       { Private declarations }
     protected
       { Protected declarations }
     public
       { Public declarations }
       procedure ClearGrid(sg: TRSStringGrid);
       procedure Clear;
       constructor Create(AOwner: TComponent); override;
     published
       { Published declarations }
       property FileNameRSS: String read fFileNameRSS write fFileNameRSS;
       property ActiveRSS: boolean read fActiveRSS write SetActiveRSS default False;
     end;

    procedure Register;

    implementation

    uses Variants;

    procedure Register;
    begin
     RegisterComponents('BSP', [TRSStringGrid]);
    end;

    { TRSStringGrid }

    procedure TRSStringGrid.Clear;
    Var i:Longint;
    begin
     for i:=0 to ColCount-1 do Cols[i].Clear;
    end;

    procedure TRSStringGrid.ClearGrid(sg: TRSStringGrid);
    Var i,j: integer;
    begin
     For i:=1 to sg.RowCount do
     For j:=1 to sg.ColCount do
     begin
     sg.Cells[j,i]:='';
     end;
     sg.RowCount:=2;
     sg.FixedRows:=1;
    end;

    constructor TRSStringGrid.Create(AOwner: TComponent);
    begin
     inherited Create(AOwner);
     ColCount:=5;
     RowCount:=2;
     Height:=200;
     Width:=500;
    end;

    procedure TRSStringGrid.SetActiveRSS(const Value: boolean);
    Var rss_doc:IXMLDOMDocument;
       Nodes:IXMLDOMNodeList;
       node1:IXMLDOMNode;
       i:Integer;

    begin
     Cells[0,0]:='RSS';
     Cells[1,0]:='Заголовок';
     Cells[2,0]:='Ссылка';
     Cells[3,0]:='Описание';
     Cells[4,0]:='Дата';
     If Value = fActiveRSS then Exit;
     fActiveRSS := Value;
     If fActiveRSS then
       begin
       rss_doc:=CoDOMDocument.Create;
       rss_doc.async:=False;
       rss_doc.load(fFileNameRSS);

       node1:=rss_doc.selectSingleNode('//rss');
         For i:=0 to node1.selectNodes('//item').length-1 do
           begin
           RowCount:=i+2;
           Cells[1,i+1]:=node1.selectnodes('//item').item[i].selectSingleNode('title').text;
           Cells[2,i+1]:=node1.selectnodes('//item').item[i].selectSingleNode('link').text;
           Cells[3,i+1]:=node1.selectnodes('//item').item[i].selectSingleNode('description').text;
           If not (node1.selectnodes('//item').item[i].selectSingleNode('pubDate') = nil) then
           begin
           Cells[4,i+1]:=node1.selectnodes('//item').item[i].selectSingleNode('pubDate').text;
           end;
           end;
       rss_doc:=nil;
       
       end;
         If fActiveRSS=False then Clear;
    end;

    end.

    Пробовал делать со списком ixmldomnodelist, но там некоторые элементы читает по 2 раза. Например заголовок "РБС.Этот безумный мир" добавляет 2 раза. Т.е. с selectnodes поточнее получилось. Как лучше теперь реализовать свойство FileName, что-бы в нём по умолчанию был адрес сохранён, и там проверялось например если оно пусто (пользователь стёр адрес по умолчанию и ничего не добавил) выводилось например сообщение, а свойство ActiveRSS оставалось в False?
  • DimaBr © (24.10.10 13:37) [17]

    procedure TRSStringGrid.SetActiveRSS(const Value: boolean);
    begin
    ...
    If Value = fActiveRSS then Exit;
    if Value and (fFileNameRSS = '') then begin
     raise;
    end;
    if Value and not FileExists(fFileNameRSS) then begin
     raise;
    end;
    fActiveRSS := Value;
    .....

  • OnCreate © (26.10.10 21:16) [18]
    Спасибо вам, выручили меня, очень вам благодарен. Тему можно закрывать.
 
Конференция "Компоненты" » компонент наследник TStringGrid [D7]
Есть новые Нет новых   [134466   +3][b:0][p:0.001]