Certainly not production code but it worked fine on a test grid. Note the export is slightly changed (it exports attributes on the Column tag) since the previous post:
CODE
procedure TForm1.ExportToXml(const GridView: TGridView; const fn: string);
var
r, c : Integer;
sl : TStringList;
function EscapeValue(const value: string): string;
begin
//Not implemented yet.
//Change all characters outside the letters, numbers and crlf to
//< format where 3C is the hex value of the Characters Ord.
Result := value;
end;
begin
sl := TStringList.Create;
sl.Add('<?xml version="1.0"?>');
sl.Add(Format('<GridView version = "%s">', ['2.4.1'])); //Should be stVersion!
sl.Add(' <Columns>');
for c := 0 to Pred(GridView.Columns.Count) do
begin
//Use Attributes to Export Column Dependend settings like min/max values.
sl.Add(Format(' <Column Name="%s" ColumnClass="%s">', [GridView.Columns[c].Name, GridView.Columns[c].ClassName]));
sl.Add(' ' + EscapeValue(GridView.Columns[c].Header.Caption));
sl.Add(' </Column>');
end;
sl.Add(' </Columns>');
sl.Add(' <Rows>');
for r := 0 to Pred(GridView.RowCount) do
begin
sl.Add(' <Row>');
for c := 0 to Pred(GridView.Columns.Count) do
begin
sl.Add(' <Cell>');
{if not (GridView.Columns[c].ClassNameIs('TGraphicColumn') or
GridView.Columns[c].ClassNameIs('TImageColumn')) then}
sl.Add(' ' + EscapeValue(GridView.Cell[c, r].AsString));
sl.Add(' </Cell>');
end;
sl.Add(' </Row>');
end;
sl.Add(' </Rows>');
sl.Add('</GridView>');
sl.SaveToFile(fn);
sl.Free;
end;
var
r, c : Integer;
sl : TStringList;
function EscapeValue(const value: string): string;
begin
//Not implemented yet.
//Change all characters outside the letters, numbers and crlf to
//< format where 3C is the hex value of the Characters Ord.
Result := value;
end;
begin
sl := TStringList.Create;
sl.Add('<?xml version="1.0"?>');
sl.Add(Format('<GridView version = "%s">', ['2.4.1'])); //Should be stVersion!
sl.Add(' <Columns>');
for c := 0 to Pred(GridView.Columns.Count) do
begin
//Use Attributes to Export Column Dependend settings like min/max values.
sl.Add(Format(' <Column Name="%s" ColumnClass="%s">', [GridView.Columns[c].Name, GridView.Columns[c].ClassName]));
sl.Add(' ' + EscapeValue(GridView.Columns[c].Header.Caption));
sl.Add(' </Column>');
end;
sl.Add(' </Columns>');
sl.Add(' <Rows>');
for r := 0 to Pred(GridView.RowCount) do
begin
sl.Add(' <Row>');
for c := 0 to Pred(GridView.Columns.Count) do
begin
sl.Add(' <Cell>');
{if not (GridView.Columns[c].ClassNameIs('TGraphicColumn') or
GridView.Columns[c].ClassNameIs('TImageColumn')) then}
sl.Add(' ' + EscapeValue(GridView.Cell[c, r].AsString));
sl.Add(' </Cell>');
end;
sl.Add(' </Row>');
end;
sl.Add(' </Rows>');
sl.Add('</GridView>');
sl.SaveToFile(fn);
sl.Free;
end;
And the import:
CODE
procedure TForm1.ImportFromXml(const GridView: TGridView; const fn: string);
var
sl : TStringList;
function FetchAttributes(tag, value: string; var a: TStringList): string;
var
i : Integer;
s : string;
begin
//Fetch the attributes into a TStringList.
Result := value;
Delete(Result, 1, Length(Format('<%s ', [tag])));
Delete(Result, Pos('>', Result), Length(Result));
a.Text := Trim(Result);
//Return the input value without the Attributes.
Result := value;
Delete(Result, 1, Pos('>', Result) + 1);
Result := Format('<%s>', [tag]) + Result;
//Remove Surrounding Quotes from Value.
for i := 0 to Pred(a.Count) do
begin
s := a.Values[a.Names[i]];
if (s[1] = '"') and (s[Length(s)] = '"') then
begin
Delete(s, 1, 1);
Delete(s, Length(s), 1);
end;
a.Values[a.Names[i]] := s;
end;
end;
function FetchInnerText(tag, value: string): string;
begin
//Fetch the Text between a start and end tag.
Result := value;
Delete(Result, 1, Length(Format('<%s>', [tag])));
while not (AnsiEndsText(Format('</%s>', [tag]), Result)) do
begin
Result := Result + Trim(sl[0]);
sl.Delete(0);
end;
Delete(Result, Pos(Format('</%s>', [tag]), Result), Length(Result));
Result := Trim(Result);
end;
type
states = (xmlNone, xmlHeader, xmlGridView, xmlColumns, xmlColumn, xmlRows, xmlRow, xmlCells, xmlCell);
var
r, c : Integer;
i : Integer;
s, value : string;
state : states;
a : TStringList;
begin
sl := TStringList.Create;
GridView.ClearRows;
for i := 0 to Pred(GridView1.Columns.Count) do
GridView1.Columns[i].Header.Caption := '';
GridView1.Update;
sl.LoadFromFile(fn);
r := 0;
c := 0;
state := xmlNone;
repeat
s := Trim(sl[0]);
sl.Delete(0);
if (CompareText(s, '<?xml version="1.0"?>') = 0) then //Process XML Version
begin
if (state <> xmlNone) then
begin
ShowMessage('Xml header not found.');
Break;
end
else
state := xmlHeader;
end
else if (CompareText(s, '<GridView version = "2.4.1">') = 0) then //Process GridView Tag
state := xmlGridView
else if (CompareText(s, '<Columns>') = 0) then //Process Columns Tag
begin
//sl.Add(Format(' <Column Name="%s" ColumnClass="%s">', [GridView.Columns[c].Name, GridView.Columns[c].ClassName]));
//sl.Add(' ' + EscapeValue(GridView.Columns[c].Header.Caption));
c := 0;
State := xmlColumns;
end
else if AnsiStartsText('<Column ', s) then //Process Column Tag
begin
State := xmlColumn;
a := TStringList.Create();
s := FetchAttributes('Column', s, a);
s := FetchInnerText('Column', s);
GridView.Columns[c].Header.Caption := s;
//Use StringList 'a' here to restore Column Class Dependend Values (like min/max).
//This information can also be cached to match <Cell> tags to the correct column
//so importing data after changing the order of the columns is also supported.
Inc(c);
a.Free;
State := xmlColumns;
end
else if (CompareText(s, '</Columns>') = 0) then //Finished Processing Columns Tag
begin
c := 0;
State := xmlGridView;
end
else if (CompareText(s, '<Rows>') = 0) then //Process Rows Tag
begin
State := xmlRows;
end
else if (AnsiStartsText(s, '<Row>')) then //Process Row Tag
begin
Inc(r);
GridView.AddRow();
c := 0;
State := xmlRows;
end
else if AnsiStartsText('<Cell>', s) then //Process Cell Tag
begin
State := xmlCell;
GridView.Cell[c, Pred(GridView.RowCount)].AsString := FetchInnerText('Cell', s);
Inc(c);
State := xmlCells;
end
else if (CompareText(s, '</Row>') = 0) then //Finished Processing Row Tag
begin
State := xmlRows;
end
else if (CompareText(s, '</Rows>') = 0) then //Finished Processing Rows Tag
begin
State := xmlGridView;
end
else if (CompareText(s, '</GridView>') = 0) then //Finished Processing GridView Tag
state := xmlNone;
until sl.Count = 0;
sl.Free;
end;
var
sl : TStringList;
function FetchAttributes(tag, value: string; var a: TStringList): string;
var
i : Integer;
s : string;
begin
//Fetch the attributes into a TStringList.
Result := value;
Delete(Result, 1, Length(Format('<%s ', [tag])));
Delete(Result, Pos('>', Result), Length(Result));
a.Text := Trim(Result);
//Return the input value without the Attributes.
Result := value;
Delete(Result, 1, Pos('>', Result) + 1);
Result := Format('<%s>', [tag]) + Result;
//Remove Surrounding Quotes from Value.
for i := 0 to Pred(a.Count) do
begin
s := a.Values[a.Names[i]];
if (s[1] = '"') and (s[Length(s)] = '"') then
begin
Delete(s, 1, 1);
Delete(s, Length(s), 1);
end;
a.Values[a.Names[i]] := s;
end;
end;
function FetchInnerText(tag, value: string): string;
begin
//Fetch the Text between a start and end tag.
Result := value;
Delete(Result, 1, Length(Format('<%s>', [tag])));
while not (AnsiEndsText(Format('</%s>', [tag]), Result)) do
begin
Result := Result + Trim(sl[0]);
sl.Delete(0);
end;
Delete(Result, Pos(Format('</%s>', [tag]), Result), Length(Result));
Result := Trim(Result);
end;
type
states = (xmlNone, xmlHeader, xmlGridView, xmlColumns, xmlColumn, xmlRows, xmlRow, xmlCells, xmlCell);
var
r, c : Integer;
i : Integer;
s, value : string;
state : states;
a : TStringList;
begin
sl := TStringList.Create;
GridView.ClearRows;
for i := 0 to Pred(GridView1.Columns.Count) do
GridView1.Columns[i].Header.Caption := '';
GridView1.Update;
sl.LoadFromFile(fn);
r := 0;
c := 0;
state := xmlNone;
repeat
s := Trim(sl[0]);
sl.Delete(0);
if (CompareText(s, '<?xml version="1.0"?>') = 0) then //Process XML Version
begin
if (state <> xmlNone) then
begin
ShowMessage('Xml header not found.');
Break;
end
else
state := xmlHeader;
end
else if (CompareText(s, '<GridView version = "2.4.1">') = 0) then //Process GridView Tag
state := xmlGridView
else if (CompareText(s, '<Columns>') = 0) then //Process Columns Tag
begin
//sl.Add(Format(' <Column Name="%s" ColumnClass="%s">', [GridView.Columns[c].Name, GridView.Columns[c].ClassName]));
//sl.Add(' ' + EscapeValue(GridView.Columns[c].Header.Caption));
c := 0;
State := xmlColumns;
end
else if AnsiStartsText('<Column ', s) then //Process Column Tag
begin
State := xmlColumn;
a := TStringList.Create();
s := FetchAttributes('Column', s, a);
s := FetchInnerText('Column', s);
GridView.Columns[c].Header.Caption := s;
//Use StringList 'a' here to restore Column Class Dependend Values (like min/max).
//This information can also be cached to match <Cell> tags to the correct column
//so importing data after changing the order of the columns is also supported.
Inc(c);
a.Free;
State := xmlColumns;
end
else if (CompareText(s, '</Columns>') = 0) then //Finished Processing Columns Tag
begin
c := 0;
State := xmlGridView;
end
else if (CompareText(s, '<Rows>') = 0) then //Process Rows Tag
begin
State := xmlRows;
end
else if (AnsiStartsText(s, '<Row>')) then //Process Row Tag
begin
Inc(r);
GridView.AddRow();
c := 0;
State := xmlRows;
end
else if AnsiStartsText('<Cell>', s) then //Process Cell Tag
begin
State := xmlCell;
GridView.Cell[c, Pred(GridView.RowCount)].AsString := FetchInnerText('Cell', s);
Inc(c);
State := xmlCells;
end
else if (CompareText(s, '</Row>') = 0) then //Finished Processing Row Tag
begin
State := xmlRows;
end
else if (CompareText(s, '</Rows>') = 0) then //Finished Processing Rows Tag
begin
State := xmlGridView;
end
else if (CompareText(s, '</GridView>') = 0) then //Finished Processing GridView Tag
state := xmlNone;
until sl.Count = 0;
sl.Free;
end;
Tested on TTextualColumn, TNumericColumn, TCheckBoxColumn and TPorgressColumn. Guess all non image/html ones will work if there are no strange symbols in the text as there isn't any escaping implemented yet.