将 STYLE 插入 TWebBrowser
Insert STYLEs into TWebBrowser
我正在使用 TWebBrowser 作为用户的编辑器 GUI。我希望能够将 Web 控件插入到文档中。一个简单的例子是复选框。 (如果需要,我可以详细说明原因)。当我首先 assemble HTML 文档(及其 STYLE 和 SCRIPTS 部分)然后将其整块传递给 TWebBrowser 时,我已经完成了所有这些工作。但现在我希望能够将我的元素插入到现有文档中。
我有下面这段代码,但它会导致 OLE 错误(请参阅代码注释):
procedure THTMLTemplateDocument.EnsureStylesInWebDOM;
var StyleBlock : IHTMLElement;
StyleText: string;
begin
StyleBlock := FWebBrowser.GetDocStyle;
if not assigned(StyleBlock) then
raise Exception.Create('Unable to access <STYLE> block in web document');
StyleText := FCumulativeStyleCodes.Text;
StyleBlock.InnerText := StyleText; <--- generates "OLE ERROR 800A0258"
end;
以上代码调用的函数如下:
function THtmlObj.GetDocStyle: IHTMLElement;
//Return pointer to <STYLE> block, creating this if it was not already present.
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
Elements: IHTMLElementCollection; // all tags in document body
AElement: IHTMLElement; // a tag in document body
Style, Head: IHTMLElement;
I: Integer; // loops thru Elements in document body
begin
Result := nil;
if not Supports(Doc, IHTMLDocument2, Document) then
raise Exception.Create('Invalid HTML document');
Elements := Document.all;
for I := 0 to Pred(Elements.length) do begin
AElement := Elements.item(I, EmptyParam) as IHTMLElement;
if UpperCase(AElement.tagName) <> 'STYLE' then continue;
result := AElement;
break;
end;
if not assigned(Result) then begin
Head := GetDocHead;
if assigned(Head) then begin
Style := Document.CreateElement('STYLE');
(Head as IHTMLDOMNode).AppendChild(Style as IHTMLDOMNode);
Result := Style;
end;
end;
end;
和
function THtmlObj.GetDocHead: IHTMLElement;
//Return pointer to <HEAD> block, creating this if it was not already present.
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
Elements: IHTMLElementCollection; // all tags in document body
AElement: IHTMLElement; // a tag in document body
Body: IHTMLElement2; // document body element
Head: IHTMLElement;
I: Integer; // loops thru Elements in document body
begin
Result := nil;
if not Supports(Doc, IHTMLDocument2, Document) then
raise Exception.Create('Invalid HTML document');
if not Supports(Document.body, IHTMLElement2, Body) then
raise Exception.Create('Can''t find <body> element');
Elements := Document.all;
for I := 0 to Pred(Elements.length) do begin
AElement := Elements.item(I, EmptyParam) as IHTMLElement;
if UpperCase(AElement.tagName) <> 'HEAD' then continue;
Result := AElement;
break;
end;
if not assigned(Result) then begin
Head := Document.CreateElement('HEAD');
(Body as IHTMLDOMNode).insertBefore(Head as IHTMLDOMNode, Body as IHTMLDOMNode);
//now look for it again
Elements := Document.all;
for I := 0 to Pred(Elements.length) do begin
AElement := Elements.item(I, EmptyParam) as IHTMLElement;
if UpperCase(AElement.tagName) <> 'HEAD' then continue;
Result := AElement;
break;
end;
end;
end;
当我运行这个的时候,StyleText=
'.selected {'#$D#$A' font-weight : bold;'#$D#$A' //background-color : yellow;'#$D#$A'}'#$D#$ A'.unselected {'#$D#$A' font-weight : normal;'#$D#$A' //background-color : white;'#$D#$A'}'#$D#$A#$D#$ A
但我尝试将 StyleText 设计得像 'hello' 一样简单,但它仍然崩溃了。
Google 对 "OLE ERROR 800A0258" 的搜索显示其他几个人也遇到过类似的问题,例如 here and here -- this later user seems to indicate he fixed the problem by using .OuterHTML, but I tried this with similar error generated. This 线程似乎表明 .InnerText 是只读的。但是在接口声明中(见下文),好像有设置的方法(即非只读)。
// *********************************************************************//
// Interface: IHTMLElement
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}
// *********************************************************************//
IHTMLElement = interface(IDispatch)
['{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}']
...
procedure Set_innerHTML(const p: WideString); safecall;
function Get_innerHTML: WideString; safecall;
procedure Set_innerText(const p: WideString); safecall;
function Get_innerText: WideString; safecall;
procedure Set_outerHTML(const p: WideString); safecall;
function Get_outerHTML: WideString; safecall;
procedure Set_outerText(const p: WideString); safecall;
function Get_outerText: WideString; safecall;
...
property innerHTML: WideString read Get_innerHTML write Set_innerHTML;
property innerText: WideString read Get_innerText write Set_innerText;
property outerHTML: WideString read Get_outerHTML write Set_outerHTML;
property outerText: WideString read Get_outerText write Set_outerText;
...
end;
任何人都可以帮助弄清楚如何在 TWebBrowser 中现有 HTML 文档的 <STYLE>
部分设置 STYLES 吗?
如果您有有效的 IHTMLDocument2,那么您可以调用它 createStyleSheet()。它将 return IHTMLStyleSheet 实例。您可以使用它的 cssText
属性 来设置样式。
确保考虑文档的字符编码。
根据@Zamrony P. Juhara 的指导,我想出了以下代码。我发帖以防将来它能帮助其他人。
procedure THtmlObj.AddStylesToExistingStyleSheet(StyleSheet: IHTMLStyleSheet; SelectorSL, CSSLineSL : TStringList);
//NOTE: There must be a 1:1 correlation between SelectorSL and CSSLineSL
// The first SL will contain the selector text
// the second SL will contain all the CSS in one line (divided by ";"'s)
var
SLIdx, RuleIdx, p: integer;
SelectorText, CSSText, OneCSSEntry : string;
begin
if not assigned(StyleSheet) then begin
raise Exception.Create('Invalid StyleSheet');
end;
for SLIdx := 0 to SelectorSL.Count - 1 do begin
SelectorText := SelectorSL.Strings[SLIdx];
if SlIdx > (CSSLineSL.Count - 1) then break;
CSSText := CSSLineSL.Strings[SLIdx];
while CSSText <> '' do begin
p := Pos(';', CSSText);
if p > 0 then begin
OneCSSEntry := MidStr(CSSText, 1, p);
CSSText := MidStr(CSSText, p+1, Length(CSSText));
end else begin
OneCSSEntry := CSSText;
CSSText := '';
end;
RuleIdx := StyleSheet.Rules.length;
StyleSheet.addRule(SelectorText, OneCSSEntry, RuleIdx);
end;
end;
end;
function THtmlObj.AddStyles(SelectorSL, CSSLineSL : TStringList) : IHTMLStyleSheet;
//NOTE: There must be a 1:1 correlation between SelectorSL and CSSLineSL
// The first SL will contain the selector text
// the second SL will contain all the CSS in one line (divided by ";"'s)
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
StyleSheets: IHTMLStyleSheetsCollection; // document's style sheets
StyleSheet: IHTMLStyleSheet; // reference to a style sheet
OVStyleSheet: OleVariant; // variant ref to style sheet
Idx: integer;
begin
Result := nil;
if not Supports(Doc, IHTMLDocument2, Document) then begin
raise Exception.Create('Invalid HTML document');
end;
StyleSheets := Document.styleSheets;
Idx := Document.StyleSheets.length;
OVStyleSheet := Document.createStyleSheet('',Idx);
if not VarSupports(OVStyleSheet, IHTMLStyleSheet, StyleSheet) then begin
raise Exception.Create('Unable to create valid style sheet');
end;
Result := StyleSheet;
AddStylesToExistingStyleSheet(StyleSheet, SelectorSL, CSSLineSL);
end; //AddStyles
我正在使用 TWebBrowser 作为用户的编辑器 GUI。我希望能够将 Web 控件插入到文档中。一个简单的例子是复选框。 (如果需要,我可以详细说明原因)。当我首先 assemble HTML 文档(及其 STYLE 和 SCRIPTS 部分)然后将其整块传递给 TWebBrowser 时,我已经完成了所有这些工作。但现在我希望能够将我的元素插入到现有文档中。
我有下面这段代码,但它会导致 OLE 错误(请参阅代码注释):
procedure THTMLTemplateDocument.EnsureStylesInWebDOM;
var StyleBlock : IHTMLElement;
StyleText: string;
begin
StyleBlock := FWebBrowser.GetDocStyle;
if not assigned(StyleBlock) then
raise Exception.Create('Unable to access <STYLE> block in web document');
StyleText := FCumulativeStyleCodes.Text;
StyleBlock.InnerText := StyleText; <--- generates "OLE ERROR 800A0258"
end;
以上代码调用的函数如下:
function THtmlObj.GetDocStyle: IHTMLElement;
//Return pointer to <STYLE> block, creating this if it was not already present.
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
Elements: IHTMLElementCollection; // all tags in document body
AElement: IHTMLElement; // a tag in document body
Style, Head: IHTMLElement;
I: Integer; // loops thru Elements in document body
begin
Result := nil;
if not Supports(Doc, IHTMLDocument2, Document) then
raise Exception.Create('Invalid HTML document');
Elements := Document.all;
for I := 0 to Pred(Elements.length) do begin
AElement := Elements.item(I, EmptyParam) as IHTMLElement;
if UpperCase(AElement.tagName) <> 'STYLE' then continue;
result := AElement;
break;
end;
if not assigned(Result) then begin
Head := GetDocHead;
if assigned(Head) then begin
Style := Document.CreateElement('STYLE');
(Head as IHTMLDOMNode).AppendChild(Style as IHTMLDOMNode);
Result := Style;
end;
end;
end;
和
function THtmlObj.GetDocHead: IHTMLElement;
//Return pointer to <HEAD> block, creating this if it was not already present.
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
Elements: IHTMLElementCollection; // all tags in document body
AElement: IHTMLElement; // a tag in document body
Body: IHTMLElement2; // document body element
Head: IHTMLElement;
I: Integer; // loops thru Elements in document body
begin
Result := nil;
if not Supports(Doc, IHTMLDocument2, Document) then
raise Exception.Create('Invalid HTML document');
if not Supports(Document.body, IHTMLElement2, Body) then
raise Exception.Create('Can''t find <body> element');
Elements := Document.all;
for I := 0 to Pred(Elements.length) do begin
AElement := Elements.item(I, EmptyParam) as IHTMLElement;
if UpperCase(AElement.tagName) <> 'HEAD' then continue;
Result := AElement;
break;
end;
if not assigned(Result) then begin
Head := Document.CreateElement('HEAD');
(Body as IHTMLDOMNode).insertBefore(Head as IHTMLDOMNode, Body as IHTMLDOMNode);
//now look for it again
Elements := Document.all;
for I := 0 to Pred(Elements.length) do begin
AElement := Elements.item(I, EmptyParam) as IHTMLElement;
if UpperCase(AElement.tagName) <> 'HEAD' then continue;
Result := AElement;
break;
end;
end;
end;
当我运行这个的时候,StyleText=
'.selected {'#$D#$A' font-weight : bold;'#$D#$A' //background-color : yellow;'#$D#$A'}'#$D#$ A'.unselected {'#$D#$A' font-weight : normal;'#$D#$A' //background-color : white;'#$D#$A'}'#$D#$A#$D#$ A
但我尝试将 StyleText 设计得像 'hello' 一样简单,但它仍然崩溃了。
Google 对 "OLE ERROR 800A0258" 的搜索显示其他几个人也遇到过类似的问题,例如 here and here -- this later user seems to indicate he fixed the problem by using .OuterHTML, but I tried this with similar error generated. This 线程似乎表明 .InnerText 是只读的。但是在接口声明中(见下文),好像有设置的方法(即非只读)。
// *********************************************************************//
// Interface: IHTMLElement
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}
// *********************************************************************//
IHTMLElement = interface(IDispatch)
['{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}']
...
procedure Set_innerHTML(const p: WideString); safecall;
function Get_innerHTML: WideString; safecall;
procedure Set_innerText(const p: WideString); safecall;
function Get_innerText: WideString; safecall;
procedure Set_outerHTML(const p: WideString); safecall;
function Get_outerHTML: WideString; safecall;
procedure Set_outerText(const p: WideString); safecall;
function Get_outerText: WideString; safecall;
...
property innerHTML: WideString read Get_innerHTML write Set_innerHTML;
property innerText: WideString read Get_innerText write Set_innerText;
property outerHTML: WideString read Get_outerHTML write Set_outerHTML;
property outerText: WideString read Get_outerText write Set_outerText;
...
end;
任何人都可以帮助弄清楚如何在 TWebBrowser 中现有 HTML 文档的 <STYLE>
部分设置 STYLES 吗?
如果您有有效的 IHTMLDocument2,那么您可以调用它 createStyleSheet()。它将 return IHTMLStyleSheet 实例。您可以使用它的 cssText
属性 来设置样式。
确保考虑文档的字符编码。
根据@Zamrony P. Juhara 的指导,我想出了以下代码。我发帖以防将来它能帮助其他人。
procedure THtmlObj.AddStylesToExistingStyleSheet(StyleSheet: IHTMLStyleSheet; SelectorSL, CSSLineSL : TStringList);
//NOTE: There must be a 1:1 correlation between SelectorSL and CSSLineSL
// The first SL will contain the selector text
// the second SL will contain all the CSS in one line (divided by ";"'s)
var
SLIdx, RuleIdx, p: integer;
SelectorText, CSSText, OneCSSEntry : string;
begin
if not assigned(StyleSheet) then begin
raise Exception.Create('Invalid StyleSheet');
end;
for SLIdx := 0 to SelectorSL.Count - 1 do begin
SelectorText := SelectorSL.Strings[SLIdx];
if SlIdx > (CSSLineSL.Count - 1) then break;
CSSText := CSSLineSL.Strings[SLIdx];
while CSSText <> '' do begin
p := Pos(';', CSSText);
if p > 0 then begin
OneCSSEntry := MidStr(CSSText, 1, p);
CSSText := MidStr(CSSText, p+1, Length(CSSText));
end else begin
OneCSSEntry := CSSText;
CSSText := '';
end;
RuleIdx := StyleSheet.Rules.length;
StyleSheet.addRule(SelectorText, OneCSSEntry, RuleIdx);
end;
end;
end;
function THtmlObj.AddStyles(SelectorSL, CSSLineSL : TStringList) : IHTMLStyleSheet;
//NOTE: There must be a 1:1 correlation between SelectorSL and CSSLineSL
// The first SL will contain the selector text
// the second SL will contain all the CSS in one line (divided by ";"'s)
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
StyleSheets: IHTMLStyleSheetsCollection; // document's style sheets
StyleSheet: IHTMLStyleSheet; // reference to a style sheet
OVStyleSheet: OleVariant; // variant ref to style sheet
Idx: integer;
begin
Result := nil;
if not Supports(Doc, IHTMLDocument2, Document) then begin
raise Exception.Create('Invalid HTML document');
end;
StyleSheets := Document.styleSheets;
Idx := Document.StyleSheets.length;
OVStyleSheet := Document.createStyleSheet('',Idx);
if not VarSupports(OVStyleSheet, IHTMLStyleSheet, StyleSheet) then begin
raise Exception.Create('Unable to create valid style sheet');
end;
Result := StyleSheet;
AddStylesToExistingStyleSheet(StyleSheet, SelectorSL, CSSLineSL);
end; //AddStyles