2014-08-27 6 views
6

웹 페이지의 활성 요소가 변경되면 감지 할 수있는 이벤트가 있습니까? 예를 들어, 사용자가 편집 상자를 포커스하는 경우.TWebBrowser 문서의 활성 요소가 변경 될 때 감지

나는 타이머의 활성 요소를 확인할 수 있지만 가능하면 이것을 피하고 싶습니다.

+0

당신은 당신의 통제 아래에있는 HTML 문서를해야합니까? – whosrdaddy

+0

@whosdaddy - 예, IHTMLDocumentx에 액세스 할 수 있습니다. – norgepaul

+0

아니요, 실제 웹 페이지입니까? – whosrdaddy

답변

10

이것은 q에 대한 완전한 대답은 아니지만, 거기에있는 대부분의 방법을 얻을 수 있기를 바랍니다. 비슷한 질문을 통해 여기에 도착 미래의 독자

(:.

  • 당신이 SHDOCVW, MSHTML 같은 자동화/컴 서버 또는 MS 워드의 하나에 대한 형식 라이브러리 가져 오기 장치가 있다고 가정 때때로, Delphi의 형식 라이브러리 임포터는 TWebBrowser, OnNavigateComplete 등의 이벤트와 같이 생성 된 Delphi TObject- 자손 래퍼에 이벤트 지원을 추가합니다. 델파이 래퍼 클래스를 생성 할 수 없거나 생성하지 않을 수도 있지만, 서버 객체의 이벤트와 델파이 코드의 이벤트 핸들러를 연결하는 아래처럼 EventObject를 생성하는 등 여러 가지 방법 중 하나를 사용하여 서버 객체 이벤트를 구현할 수 있습니다.

  • 핸들링 인터페이스 이벤트는 기본적으로 IDispatch 인터페이스를 구현하는 Delphi 클래스를 정의한 다음 해당 인터페이스를 알림 대상 이벤트가있는 Ole 또는 COM 개체에 연결합니다. 그런 다음 Ole/COM의 "뒤"인터페이스에서 이벤트가 발생하면 동일한 방식으로 IDispatch를 호출합니다. 이벤트 알림으로하는 일은 전적으로 귀하에게 달려 있습니다. 아래의 코드는 그것들을 TForm1의 메소드에 전달합니다. http://www.djpate.freeserve.co.uk/Automation.htm) - )

아래은 EventObject 밀접 TeamB의 데보라 페이트 (그녀는 델파이를 사용하여 자동화에 대한 그녀의 웹 사이트에 정말 좋은 섹션이에 의해 2003 년 11 월 볼랜드 NGS에 게시 한을 기반으로합니다. 이 객체는 특정 Ole/COM 객체의 이벤트 처리에만 국한되지 않는다는 점에서 매우 일반적입니다.

// The following code is intended to illustrate methods of detecting that the 
// active element in an Html page has changed. See the comments in the AnEvent 
// procedure about how exactly to detect such a change. 
// 
// The code also illustrates how to handle a single event, e.g. onbeforeeditfocus 
// of an Events objects such as HtmlDocumentEvents or HtmlDocumentEvents2 (see MSHTML.Pas) 
// or all the events the events interface contains. 


type 

    TInvokeEvent = procedure(Sender : TObject; DispIP : Integer) of Object; 

    TEventObject = class(TInterfacedObject, IDispatch) 
    private 
    FOnEvent: TInvokeEvent; 
    FSinkAllEvents : Boolean; 
    protected 
    function GetTypeInfoCount(out Count: Integer): HResult; stdcall; 
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall; 
    function GetIDsOfNames(const IID: TGUID; Names: Pointer; 
     NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall; 
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; 
     Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall; 
    public 
    constructor Create(const AnEvent : TInvokeEvent; SinkAll : Boolean); 
    property OnEvent: TInvokeEvent read FOnEvent write FOnEvent; 
    property SinkAllEvents: Boolean read FSinkAllEvents; 
    end; 

type 
    TForm1 = class(TForm) 
    [ ... ] 
    private 
    { Private declarations } 
    procedure AnEvent(Sender : TObject; DispID : Integer); 
    procedure AnotherEvent(Sender : TObject; DispID : Integer); 
    public 
    { Public declarations } 
    Doc : IHtmlDocument3; 
    DocEvent, 
    DocEvent2: OleVariant; 
    Cookie : Longint; 
    CPC : IConnectionPointContainer; 
    Sink : IConnectionPoint; 
    PrvActiveElement : IHTMLElement; 
    Events : Integer; 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

constructor TEventObject.Create(const AnEvent: TInvokeEvent; SinkAll : Boolean); 
begin 
    inherited Create; 
    FOnEvent := AnEvent; 
    FSinkAllEvents := SinkAll; 
end; 

function TEventObject.GetIDsOfNames(const IID: TGUID; Names: Pointer; 
    NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; 
begin 
    Result := E_NOTIMPL; 
end; 

function TEventObject.GetTypeInfo(Index, LocaleID: Integer; 
    out TypeInfo): HResult; 
begin 
    Result := E_NOTIMPL; 
end; 

function TEventObject.GetTypeInfoCount(out Count: Integer): HResult; 
begin 
    Result := E_NOTIMPL; 
end; 

function TEventObject.Invoke(DispID: Integer; const IID: TGUID; 
    LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, 
    ArgErr: Pointer): HResult; 
begin 
    if SinkAllEvents then begin 
    if Assigned(FOnEvent) then 
     FOnEvent(Self, DispID); 
    Result := S_OK; 
    end 
    else begin 
    if (Dispid = DISPID_VALUE) then begin 
     if Assigned(FOnEvent) then 
     FOnEvent(Self, DispID); 
     Result := S_OK; 
    end 
    else Result := E_NOTIMPL; 
    end; 
end; 

procedure TForm1.AnEvent(Sender : TObject; DispID : Integer); 
var 
    Doc2 : IHTMLDocument2; 
    E : IHTMLElement; 
begin 
    Inc(Events); 
    Doc.QueryInterface(IHTMLDocument2, Doc2); 
    E := Doc2.activeElement; 

    // NB: When an <INPUT> text edit is receiving focus, the following code is triggered twice 
    // or more with different values of Pointer(Doc2.activeElement). So, "(E <> PrvActiveElement)" 
    // doesn't seem a very effective test that the active element has changed. However, 
    // testing E's Name, ID, etc should provide a useful test. 

    if (E <> Nil) and (E <> PrvActiveElement) and E.isTextEdit then begin 
    if PrvActiveElement <> Nil then 
     PrvActiveElement := E; 
     Caption := Format('Something happened: Element Tagname: %s, Name: %s, %d, %d, %p', 
     [E.TagName, E.GetAttribute('Name', 0), DispID, Events, Pointer(Doc2.activeElement)]); 
    end; 
end; 

procedure TForm1.AnotherEvent(Sender : TObject; DispID : Integer); 
begin 
    Caption := Format('Something else happened: %d', [DispID]); 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    Memo1.Lines.LoadFromFile('D:\aaad7\html\postdata.htm'); 
end; 

procedure TForm1.btnLoadClick(Sender: TObject); 
var 
    V : OleVariant; 
    Doc2 : IHtmlDocument2; 
begin 
    WebBrowser1.Navigate('about:blank'); 
    Doc := WebBrowser1.Document as IHTMLDocument3; 
    Doc.QueryInterface(IHTMLDocument2, Doc2); 
    V := VarArrayCreate([0, 0], varVariant); 
    V[0] := Memo1.Lines.Text; 
    try 
    Doc2.Write(PSafeArray(TVarData(v).VArray)); 
    finally 
    Doc2.Close; 
    end; 

    DocEvent := TEventObject.Create(Self.AnEvent, cbSinkAll.Checked) as IDispatch; 

    if cbsinkAll.Checked then begin 
    CPC := Doc2 as IConnectionPointContainer; 
    Assert(CPC <> Nil); 
    OleCheck(CPC.FindConnectionPoint(HTMLDocumentEvents, Sink)); 
    OleCheck((Sink as IConnectionPoint).Advise(DocEvent, Cookie)); 
    end 
    else 
    Doc.onbeforeeditfocus := DocEvent; 
end; 

TForm1.AnEvent의 주석에 유의하십시오. cbSinkAll 체크 박스 을 체크하고 많은 INPUT 박스가있는 페이지에서 코드를 실행하면, 과 동일한 INPUT 박스에 AnEvent가 여러 번 실행되고 매번 Doc2.ActiveElement의 값이 다른 것을 볼 수 있습니다 . 그 이유는 확실하지 않지만 Doc2.ActiveElement 속성의 현재 값을 이전 값과 비교하면 Html 페이지의 포커스가 인 것을 감지하는 데 효과적이지 않습니다. 그러나 요소의 속성 (예 : 그 이름이나 ID는 신뢰할 수있는 검사를 제공하는 것 같습니다.

두 가지주의 사항 : 데보라 페이트의 원래 코드에서

  • , 그녀는 나중에 복원 할 수있는 있도록 OleVariant에에 이전 이벤트 핸들러 (있는 경우)를 저장합니다.
  • 연속해서 여러 HTML 페이지의 이벤트에 연결하려면 사이에 EventObject를 놓아야합니다.

[MSHTML.Pas에서 추출]

HTMLDocumentEvents = dispinterface 
    ['{3050F260-98B5-11CF-BB82-00AA00BDCE0B}'] 
    function onhelp: WordBool; dispid -2147418102; 
    [...] 
    procedure onbeforeeditfocus; dispid 1027; 
    end; 
+0

단일 요소 (전체 문서가 아닌)에 대한 이벤트를 처리하려면 이벤트 싱크 (여기에 'TEventObject' 개체)를 할당하십시오. 주어진 엘리먼트 이벤트 ('HTMLElementEvents2' 인터페이스에 의해 제공됨). [ '이 장] (http://msdn.microsoft.com/en-us/library/bb508508%28v=vs.85%29.aspx#Sink)에 설명되어있는 모든 것. 추신 나는 개인적으로 싱크 객체를'OleVariant' 변수에 저장하지 않을 것입니다. 나는'IDispatch'를 직접 사용할 것입니다. [+ 1ed] – TLama

+0

@TLama : 실제로 (단일 이벤트에 관한). 귀하의 현자 의견/조언 주셔서 감사합니다. 당신이 짐작 했겠지만, 저는 오늘 자동화 이벤트를 시도하고 이해할 수있는 날이라고 결정했습니다. (Bihn Ly가 D2/D3 시대에이 모든 것들을 파악한 것에 놀랐습니다.) 나는 OleVariant를 우회하는 것에 동의하지만 많은 질문자 (여기서 OP를 의미하지는 않음)는 늦은 바인딩을 사용하고 가능한 경우 인터페이스를 사용하지 않는 것처럼 보입니다. – MartynA

+0

나는 ConnectionPoint의 사용에 초점을 맞추기 위해 응답을 다시 업데이트했습니다. ConnectionPoint를 사용하면 ConnectionPoint를 훨씬 짧게 만들 수 있습니다. – MartynA