Displaying 1 - 3 of 18 entries.

Loading pages in IFRAME dynamically from codebehind – ASP.NET

  • Posted on 7월 1, 2011 at 8:04 오전

Most of us who develop Web Applications would have used an IFRAME during some stage of our lives. IFRAME’s are an easy way by which you can embed another page within your original page such that you can show some important information like Stock position/Weather from another site without worrying about the changes happening to that site and updating the same. The Frame can also be used to show another page from your own application.

ASP.NET also provides the option to have an IFRAME in our ASPX Pages. It can be with/without the “runat=server” attribute and does serve the purpose of embedding the page. The source for the frame can be set as follows:-

 

<IFRAME id=”frame1″ src=”SourcePage.extension / URL of the external Site” scrolling=”auto”>
</IFRAME>


However, in practical scenarios, we may want to load the page dynamically. In other words, we may want to specify the “src” attribute (the page which we want to show), dynamically. There is no straight forward way to do that and even if you add a “runat=server” attribute to it (though required in the work around provided below), you cannot access the “src” property directly.

The workaround to do that is as follows:-

1. Specify the “runat=server” attribute as follows in the ASPX Page:-

<IFRAME id=”frame1″ scrolling=”auto” runat=”server”>
</IFRAME>

2. In the codebehind, you may need to declare a HtmlGenericControl in the control declarations section as follows:-
 

C#
protected System.Web.UI.HtmlControls.HtmlGenericControl frame1; (not required if working with ASP.NET  2.0, 3.5 i.e. Visual Studio 2005, 2008)
 
VB.NET
Protected WithEvents frame1 As System.Web.UI.HtmlControls.HtmlGenericControl (not required if working with ASP.NET  2.0, 3.5 i.e. Visual Studio 2005, 2008)
 
3. Then, you need to do a findcontrol to identify the control on the page and typecast it as follows:-
 

C#
HtmlControl frame1 = (HtmlControl)this.FindControl(“frame1″); (not required if working with ASP.NET  2.0, 3.5 i.e. Visual Studio 2005, 2008)
  
VB.NET
Dim frame1 As HtmlControl = CType(Me.FindControl(“frame1″), HtmlControl) (not required if working with ASP.NET  2.0, 3.5 i.e. Visual Studio 2005, 2008)

Note: You can have different name for the Generic Control you define in the code behind, but for ease I keep both the same. The “frame1″ referred in Find Control is the ID as declared in the ASPX Page.

4. Thereafter, you will be able to access the src property as follows:-


 
 

C#
frame1.Attributes["src"] = “http://www.live.com” ;

 

VB.NET
frame1.Attributes(“src”) = “http://www.live.com” ;
  
NOTE: Thanks PhOeNiX for providing the VB.NET equivalent.  I have added the same now.

As you can see though I have hard-coded the URL you can assign it dynamically based on a condition or any other business logic.

This serves the purpose of dynamically loading the page in the IFRAME.

Cheers !!!
 

How to call JavaScript functions in a TWebBrowser from Delphi

  • Posted on 3월 31, 2011 at 4:04 오후

Introduction

On some occasions when using a TWebBrowser I’ve needed to use Delphi to call JavaScript functions contained in the current document.

This is quite easy to do. We’ll first examine the techniques then we’ll look at a case study that changes the font in an HTML document.

Finally, thanks to contributions from Christian Sciberras, we will look at how to get the return value from a JavaScript function called from Delphi.

Overview of Solution

The window object associated with an HTML document exposes a method – execScript – that enables JavaScript to be called. The first parameter of this method takes a string containing the required function call (complete with actual parameters). The method’s second parameter specifies the script language being used – in our case 'JavaScript'.

So, how do we get hold of the window object that exposes the required method? We simply use the parentWindow property of the web browser control’s document object. We need to ensure that a document object is available by first loading the required document into the web browser and waiting for it to finish loading.

Implementing the Solution

Let us assume we have a TWebBrowser control on a form into which an HTML document has been loaded. Assume also that the HTML document defines a JavaScript function named foo() that takes a string and an integer parameter. Assuming the document is fully loaded, we can call foo() with a method similar to this:

uses MSHTML; procedure TForm1.CallFoo(S: string; I: Integer); { Calls JavaScript foo() function } var Doc: IHTMLDocument2; // current HTML document HTMLWindow: IHTMLWindow2; // parent window of current HTML document JSFn: string; // stores JavaScipt function call begin // Get reference to current document Doc := WebBrowser1.Document as IHTMLDocument2; if not Assigned(Doc) then Exit; // Get parent window of current document HTMLWindow := Doc.parentWindow; if not Assigned(HTMLWindow) then Exit; // Run JavaScript try JSFn := Format(‘foo(“%s”,%d)’, [S, I]); // build function call HTMLWindow.execScript(JSFn, ‘JavaScript’); // execute function except // handle exception in case JavaScript fails to run end; end;

Listing 1

Let’s look at what’s going on here. We first check that a document is available and store a reference to in Doc. We then attempt to get a reference to the document’s parent window object and store it in HTMLWindow. Once we have a valid window object we call its execScript method, passing the call to foo(). The parameters to foo() must be literal values – we can’t pass variables. String literals must be enclosed by double or single quotes.

Escaping quotes in string literal parameters
When passing string literal parameters to JavaScript functions you need to be careful to escape any quote characters contained in the string, otherwise the quote will terminate the string prematurely. Since JavaScript can use either single or double quotes to delimit literal strings you may need to escape either of these types of quote by preceeding it with a backslash.
As an example, suppose we need to pass the string He didn't say "hello" to a JavaScript function. If we delimit the string with double quotes we pass the it as "He didn't say \"hello\"".
Alternatively we may delimit the string with single quotes and pass it as 'He didn\'t say "hello"'.

If the JavaScript function contains errors or doesn’t exist an exception will be raised. We may wish to handle such exceptions before returning.

Case Study

In this case study we will develop a small application that displays an HTML document in a browser window. The document will contain a Javascript function – SetFont() – that can change document’s default font. The application will display a combo box containing a list of all installed screen fonts. When the user selects a font from the combo box the font used in the web browser will be changed accordingly. We do this by calling SetFont() from Delphi.

Firstly, create an HTML document with any content you choose and include the following JavaScript function in the document’s <head> section:

<script type=”text/javascript”> function SetFont(fontname) { document.body.style.fontFamily = fontname; } </script>

Listing 2

This is the code that changes the font.

Now create a new delphi application and drop a TComboBox and a TWebBrowser on the main form. We will load the HTML document when the form is first shown. We will also load the screen fonts into the combo box at the same time. To do this create an OnShow event handler for the form with the following code:

procedure TForm1.FormShow(Sender: TObject); { Setup combo box and load document } begin // Store screen fonts in combo box and disabled it ComboBox1.Items.Assign(Screen.Fonts); ComboBox1.Enabled := False; // Load the HTML page WebBrowser1.Navigate(ExtractFilePath(ParamStr(0)) + ‘Test.html’); end;

Listing 3

Note that we disabled the combo box to prevent a font from being selected. We do this because to set a font we need to access the browser’s Document object – and this object is only available when the HTML document is fully loaded. TWebBrowser triggers an OnDocumentComplete event when the document has finished loading. Therefore we handle that event and enable the combo box:

procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject; const pDisp: IDispatch; var URL: OleVariant); { Document loaded: enable combo box } begin ComboBox1.Enabled := True; end;

Listing 4

We now come to the code that actually calls the JavaScript function. When the user selects a new font the combo box triggers its OnChange event. We handle this event by calling the JavaScript SetFont() function with the name of the selected font, as follows:

procedure TForm1.ComboBox1Change(Sender: TObject); { Make browser use selected font } var Doc: IHTMLDocument2; // current HTML document HTMLWindow: IHTMLWindow2; // parent window of current HTML document JSFn: string; // stores JavaScipt function call begin // Get reference to current document Doc := WebBrowser1.Document as IHTMLDocument2; if not Assigned(Doc) then Exit; // Get parent window of current document HTMLWindow := Doc.parentWindow; if not Assigned(HTMLWindow) then Exit; // Run JavaScript try JSFn := ‘SetFont(”’ + ComboBox1.Text + ”’)'; HTMLWindow.execScript(JSFn, ‘JavaScript’); except // handle exception in case JavaScript fails to run ShowMessage(‘Error running JavaScript’); end; end;

Listing 5

This method is very similar to that described in the previous section. SetFont() is called with the name of the font selected in the combo box. If an exception is raised when the JavaScript is called we display a message.

We have now developed all the code. All that remains is to add the following uses clause to the implementation section of the unit to enable the program to compile:

uses SysUtils, MSHTML, Dialogs;

Listing 6

Getting the Return Value

All this is very well, but how can we grab the return value of a JavaScript function called from Delphi? The short answer is that we can’t, because the execScript() function doesn’t ever return a useful value. Here’s what the Microsoft SDK Help says about it (my emphasis):

Syntax
HRESULT execScript(
  BSTR code,
  BSTR language,
  VARIANT *pvarRet
);
Parameters
code
[in] BSTR that specifies the code to be executed.
language
[in] BSTR that specifies the language in which the code is executed. The language defaults to Microsoft JScript.
pvarRet
[out, retval] Address of a VARIANT of type VT_EMPTY. This method always returns VT_EMPTY.
Return Value
Returns S_OK if successful, or an error value otherwise.
Huh?
What’s the point of a function result that’s always empty?
Search me!

Because Delphi uses the safecall convention for the definition of execScript in the MSHTML unit, the return value from execScript is that of the pvarRet parameter (flagged [retval]). (Delphi uses the documented return value to generate an exception if it is not S_OK). Notice that the help entry for pvarRet says it always returns VT_EMPTY. This means that execScript on Delphi always returns and empty variant, not the JavaScript function return value that we may have expected.

However, Christian Sciberras has come up with a clever work-around, providing you can modify the document’s HTML and the JavaScript source code.

Taking our original JavaScript foo() function, let us assume it returns a value we want to capture.

First of all modify your HTML document’s body so that it contains a hidden input field with an id attribute of ‘result’, i.e.:

<input type=’hidden’ id=’result’ value=” />

Listing 7

Assume that foo() was originally coded like this:

function foo(str, int) { var result; /* do something with str and int and store return value in result */ return result; }

Listing 8

We modify it to write its result into the hidden input field’s value attribute like so:

function foo(str, int) { var result; /* do something with str and int and store return value in result */ document.getElementById(‘result’).value = result; return result; }

Listing 9

Notice we’ve retained the return statement so that any other code that calls foo() still works. It remains to grab the value of the hidden input field from Delphi. Listing 10 shows a generic function written by Christian that can get a named attribute from an HTML tag with a specified id.

function GetElementIdValue(WebBrowser: TWebBrowser; TagName, TagId, TagAttrib: string):string; var Document: IHTMLDocument2; Body: IHTMLElement2; Tags: IHTMLElementCollection; Tag: IHTMLElement; I: Integer; begin Result:=”; if not Supports(WebBrowser.Document, 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’); Tags := Body.getElementsByTagName(UpperCase(TagName)); for I := 0 to Pred(Tags.length) do begin Tag:=Tags.item(I, EmptyParam) as IHTMLElement; if Tag.id=TagId then Result := Tag.getAttribute(TagAttrib, 0); end; end;

Listing 10

The function first gets a reference to the <body> tag in the loaded HTML document. It then gets hold of a collection of all the contained tags named TagName and searches the collection looking for the tag with the required id (TagId). When the correct tag is found the function returns the value of its TagAtrrib attribute. If the tag or attribute is not found the empty string is returned.

Finally we just need to adapt the code from Listing 1 that calls foo() to gets its return value. Listing 11 shows the revised code.

uses MSHTML; function TForm1.CallFoo(S: string; I: Integer): string; { Calls JavaScript foo() function and returns its return value } var Doc: IHTMLDocument2; // current HTML document HTMLWindow: IHTMLWindow2; // parent window of current HTML document JSFn: string; // stores JavaScipt function call begin Result := ”; // Get reference to current document Doc := WebBrowser1.Document as IHTMLDocument2; if not Assigned(Doc) then Exit; // Get parent window of current document HTMLWindow := Doc.parentWindow; if not Assigned(HTMLWindow) then Exit; // Run JavaScript try JSFn := Format(‘foo(“%s”,%d)’, [S, I]); // build function call HTMLWindow.execScript(JSFn, ‘JavaScript’); // execute function // get result Result := GetElementIdValue(WebBrowser1, ‘input’, ‘result’, ‘value’) except // handle exception in case JavaScript fails to run end; end;

Listing 11

There are only three changes to the code:

  1. CallFoo is changed from a procedure to a function that returns a string value.
  2. A default return value of the empty string is set in case of error.
  3. The return value of the function is set by calling the GetElementIdValue function from Listing 10.

Demo code

Demo code to accompany this article is available for download (see below). The demo includes the following:

  • An implementation of the case study.
  • A demo by Christian Sciberras that illustrates getting the return value a JavaScript function call. This demo lets you enter code that will be evaluated and displayed in a “console” window.

The source code was tested using Delphi 7 Professional.

This source code is merely a proof of concept and is intended only to illustrate this article. It is not designed for use in its current form in finished applications. The code is provided on an “AS IS” basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. The demo is open-source. See license.txt included in the download for license details.

This article is copyright © Peter Johnson 2005-2009

맥 OS X 파인더 사용 기본 팁

  • Posted on 3월 2, 2011 at 3:33 오후

맥 초보를 위한 맥 OS X 파인더 사용 기본 팁 – 이것만 알면 됨

맥이 어렵게 느껴지는 이유?
윈도우만 써온 맥 초심자는 맥이 윈도우에 비해 ‘비직관적이고 어렵다’라고 말한다.
그 가장 큰 이유는 파인더가 윈도우의 파일 익스플로러와 다르다는 점인데 그것은 크게 두가지로

  1. 상위 폴더로 올라가는 버튼이 없다
  2. 파일 주소창이 없다.

두가지로 요약된다. 우선 디렉토리 상하 이동에 대해 정리해보도록 하자.

I. 폴더 상하 이동
1. 디렉토리 구조 아래로 이동

화살표 키를 사용하는 방법

Command + Down Arrow 로 하위 폴더 열기

O키(Open)를 사용하는 방법

Command + O 로 하위 폴더 열기
Command + Control + O 로 별도의 창으로 하위 폴더 열기

마우스를 사용하는 방법

Command + double click 으로 별도의 창으로 폴더 열기

2. 디렉토리 구조 위로 이동

화살표 키를 사용하는 방법

Command + Up 으로 상위 폴더 열기
Command + Option + Up 으로 별도의 창으로 상위 폴더 열기

마우스를 사용하는 방법

주소를 마우스로 Command + 클릭 또는 오른클릭하면 상위 폴더들이 나오는데 그것을 클릭하면 된다.

3. 새 창 띄우기

N 키를 사용하는 방법

Command + N 으로 창 하나 더 띄우기

II. 파인더의 전체 패스 문제
이번에는 파인더에 주소창이 없는 생소함을 해결해보도록 하자.
파일 전체 패스를 파인더에 표시하는 방법은 다음의 방법을 보고 그대로 하면 된다.
맥 OS X 파인더에 전체 패스 표시하는 방법

맥에서 폴더의 전체 패스를 표시하는 주소창이 없는 것은, 모든 것이 drag and drop으로 가능하기 때문이다. 왜냐하면 Open Dialog Box 나 Save Dialog Box 에도 자유자재로 파일과 폴더를 끌어다 넣을 수 있는데 그렇게 하면 패스가 지정이 된다. (윈도우에서는 보통, 주소창에서 전체 패스를 카피한 후 그것을 오픈다이얼로그 박스에 paste하는 식으로 패스를 지정할 것이다)

맥에서 전체 패스가 필요한 경우는, 다른 사람에게 특정 패스를 알려주는 경우 정도이다.
전체 패스를 텍스트로 얻어내는 방법은 다음과 같다(출처).

(1) 파일 전체패스 얻어내기
Text Edit 또는 Terminal 에 파일 아이콘을 끌어 넣으면 된다.

(2) 폴더 패스로 바로 이동하기
Command + Shift + G 를 누르고 폴더 전체패스를 입력하면 된다.

III. 데스크탑 검색기능
그러면 이번에는 윈도우에 없고 맥 파인더에만 있는 편리한 기능을 이용해보자.
그 대표적인 것이 Spotlight 이다.

맥 OS X 에서는 Spotlight 라는 검색툴을 이용해 어플리케이션이나 파일들을 빠르게
검색할 수 있게 해준다. Spotlight 는 크게 두 군데에서 액세스 가능한데

  1. 데스크탑 메뉴 맨 오른쪽 위 확대경 아이콘 (기본 핫키 Command + Space)
  2. 파인더 맨 오른쪽 위 검색창 (기본 핫키 Command + Option + Space)

이다. 아쉽게도 Command + Space 는 한영 전환 키와 겹친다.
따라서 System Preferences 에 가서 Spotlight 핫키를 다음과 같이 바꿔주는 것이 좋다.

  1. 데스크탑 메뉴 맨 오른쪽 위 확대경 아이콘 (Option + Space)
  2. 파인더 맨 오른쪽 위 검색창 (Control + Option + Space)
뒤에서는 이렇게 수정한 핫키를 기준으로 설명할 것이다.

Spotlight 이용한 파일 검색
파인더 오른쪽 위 검색창에다가 파일명이나 파일 내부에 포함된 단어를 입력해보자.
그러면 실시간으로 검색 결과가 나온다.
(Control + Option + Space 를 이용하면 더 빠르다)

이를테면 홍길동이라는 사람의 전화번호를 어떤 엑셀 파일에 저장해두었는데
그게 어디있는지 모르겠다고 하자. 그러면 Option + Space 누르고 검색창에
“홍길” 까지만 입력하면, 그 파일이 바로 밑에 검색되어 보일 것이다.

또는 친구에게 “웃기는사진.jpg”이라는 것을 받았는데 그 파일을 어디 받아놓았는지 모르겠다고 하자.
그러면 Option + Space 누르고 검색창에 “웃기” 까지만 입력하는 시점에서
그 파일이 바로 검색되어 보이고 있을 것이다.
Spotlight 의 검색 기능은 그처럼 강력하다.

검색된 결과 폴더 열기
검색된 결과의 파일을 직접 열수도 있지만, 대부분은 파인더에서 그 폴더를 열기를 원할 것이다.
그러려면 오른클릭하고 Open Enclosing Folder 를 선택하면 된다.
또는, Command + R 하면 된다. Command + R 은 검색결과나 alias 의 원본을 찾아가는 핫키이다.

Spotlight 이용한 어플리케이션 검색
데스크탑 맨 오른쪽 위의 확대경 아이콘을 클릭하고 거기 어플리케이션 이름을
앞부분만 입력해보자. 그러면 어플리케이션 이름이 밑에 뜬다.
즉 Dock 을 이용하지 않고 바로 어플리케이션을 빠르게 띄울 수 있다.
(Option + Space 를 이용하면 더 빠르다)

예를 들어 Disk Utility 를 빠르게 띄우고자 할 때 어플리케이션 폴더에 들어가
Utilities 폴더에 들어가 거기서 disk utility 를 찾는 것보다는
Option + Space 한 후 검색창에 di 까지만 치면 바로 disk utilities 가 뜨게 될 것이다.

또한, Terminal 을 빠르게 띄우고자 할 때 어플리케이션 폴더에 들어가
Utilities 폴더에 들어가 거기서 Terminal 을 찾는 것보다는
Option + Space 한 후 검색창에 te 까지만 치면 바로 terminal 이 뜨게 될 것이다.

물론 여기서는 파일도 검색되지만 오른클릭으로 Open Enclosing Folder 여는 것은 안된다.
따라서 여기서는 Command + R 로 원본 폴더를 열어야 한다.

폴더의 트리 구조 통째로 text 로 가져오기
Text Wrangler 를 열어서 text file 을 하나 생성한 후
거기다가 파인더에서 폴더 아이콘을 끌어넣어보자.
그러면 텍스트 파일에 그 폴더의 전체 트리 구조가 들어가 있을 것이다.
(반면, 기본 텍스트 에디터인 TextEdit 에다가 끌어넣으면 그냥 전체 패스가 들어가게 된다.
따라서 전체 패스를 얻고자 하면 Text Wrangler를 쓰도록 하자.)

이 텍스트 파일은 백업 정리에 매우 유용하게 사용될 수 있다.
예를 들어 옛날부터 자료를 모으다보니 총 10개의 외장하드가 있다고 하자.
이런 상황에서 어떤 외장하드에 어떤 파일이 있는지 기억한다는 것은 매우 어렵다.
따라서 모든 외장하드마다 트리구조를 텍스트 파일로 저장해 둔 후
Spotlight 에서 검색하면 어떤 파일이 어떤 외장하드에 들어 있는지
파일명으로 바로 검색이 가능하다.

요즘은 특히 개인 데이터의 용량이 점점 커지고 있기 때문에 Spotlight 의 기능은
자신의 데이터를 관리하는데 대단히 큰 도움이 될 것이다.

IV. 기타 쾌적한 사용을 위한 팁
Open Dialog Box 와 파인더 함께 사용하기
오픈 다이얼로그 창은 열었는데 어떤 파일을 열어야 할 지 잘 생각이 안나는 경우가 있다.
그럴 때 다음과 같은 방식을 쓰면 좋다.
파일 오픈 할때 패스 쉽게 지정하는 법

Cut and Paste
맥에서는 파일의 cut and paste 가 안된다. 유저에게 파일을 cut 하게 허용하면 실수로 엉뚱한 곳에 paste 할 염려가 있기 때문에 금지한 것이다. OS에서 파일의 cut and paste 를 허용하느냐 마느냐는 편이성과 안전성 사이의 문제이고 맥에서는 안전성을 선택한 것으로 풀이된다.

그러나 이는 윈도우를 사용하던 사용자에게는 매우 불편할 수 있다. 왜햐하면 윈도우에서는 cut and paste가 있으므로 탐색기 창을 하나만 띄워놓고도 파일을 move하는 것이 가능하기 때문이다. 하지만 맥에서 파일을 move하려면 항상 drag and drop 을 써야 한다.

따라서 맨 앞에서 설명한,
상위 폴더를 별도의 창으로 여는 command + control + Up Arrow 라던가
하위 폴더를 별도의 창으로 여는 command + control + O (또는 command + 더블클릭)
등을 쓸 줄 아는 것은 매우 중요하다.

그런데 맥에서는 창을 두개 안띄우고도 파일을 move 할 수 있는 방법을 제공한다.
바로 Spring loaded folders 라는 것이 그것인데, 어떤 파일을 잡아서
옮기고자 하는 폴더 위에다가 대고 잠시 있어보자. 그러면 그 폴더가 별도의 창으로 열릴 것이다.
이런 식으로 파일을 move 할 수 있게 한 것은 spring loaded folders 라고 한다.
파일을 폴더 아이콘 상에 얼마나 대고 있어야 폴더가 열리느냐는 파인더의 preferences에서 조절가능하다.
파일을 폴더 아이콘 위에 대고 잠시 있는것보다 바로 열고 싶다면, space 를 누르면 된다.