드디어 실버라이트2가 베일을 벗었습니다.
자, 어디서부터 시작해야 할까요?

실버라이트2 Beta1은 alpha때와는 달리 기본적인 튜토리얼과 핸즈온랩을 제공하고 있어서 전보다는 삽질을 덜 할 수 있을 것으로 기대되네요.

먼저, 모든 링크는 http://silverlight.net/Learn/ 여기에서 시작되죠.

  • How Do I? 에서는 동영상으로 강좌가 올라와 있어요.
  • Tutorials 에서는 단계적인 강좌가 올라와 있고요.
    Jesse Liberty의 Blog에서 최신 업데이트를 볼 수 있어요.
  • 단계적으로 완전한 예제를 따라할 수 있는 Hands On Lab도 벌써 올라와 있죠.

물론 이 모든 것은 아직 영문이죠.
하지만 대부분의 경우 단계적으로 따라할 수 있는 내용이니 '겁먹지 마세요!'

신고
Posted by gongdo
Silverlight 1.1 바로 시작하기

HTTP를 통한 Plain XML 수신
준비 사항
Silverlight 개발의 기초에서 개발에 필요한 도구와 기술에 대해 설명하고 있습니다.
이벤트 핸들링에서 개체의 이벤트를 처리하는 방법에 대해 설명하고 있습니다.
디버깅과 예외처리에서 Web Application 프로젝트와 Silverlight Application 프로젝트를 연동하는 방법을 소개하고 있습니다.

Silverlight에서의 HTTP Request

XML 메시지와 HTTP Request
Plain XML(이하 POX -Plain Old XML-) 메시지는 SOAP이나 WS-*등의 프로토콜을 지원하지 않는 클라이언트에서도 폭넓게 사용할 수 있습니다. 또한 HTTP 프로토콜 상에서 쉽게 전송될 수 있으며 수신된 데이터를 보다 구조적으로 분석할 수 있으므로 단순한 데이터 교환에 좋은 선택이 됩니다.

Silverlight에서 POX 메시지는 새로운 BrowserHttpWebRequest 클래스를 사용하여 요청을 전송하고 HttpWebResponse 클래스를 사용하여 데이터를 수신할 수 있습니다.
노트
BrowserHttpWebRequest 클래스는 현재 크로스도메인에 대한 접근을 허용하지 않습니다. 따라서 BrowserHttpWebRequest로 요청하는 URI는 반드시 실버라이트 페이지와 같은 서버에 존재해야 합니다.

동기적(Synchronously)으로 송수신하기
동기적인 Web Request를 사용하면 간단한 코드로 요청에 대한 응답을 수신하고 수신 받은 응답으로부터 스트림으로 데이터를 얻을 수 있습니다.
Howto : POX 메시지를 동기적으로 수신하는 방법
1. 우선 전송할 xml 파일을 작성합니다. 파일의 이름은 test.xml이라고 하고 내용은 다음과 같으며 실버라이트 페이지와 같은 경로에 작성합니다.
test.xml
<?xml version="1.0" encoding="utf-8" ?>
<List>
    <Item>
        <Name>test name1</Name>
        <Address>test address1</Address>
    </Item>
    <Item>
        <Name>test name2</Name>
        <Address>test address2</Address>
    </Item>
</List>

2. 버튼으로 사용할 TextBlock과 결과를 출력할 TextBlock을 마크업에 올려 놓습니다.
XAML
<TextBlock x:Name="btnSync" Text="Request Synchronously" />
<TextBlock x:Name="txtResult" Text="" Canvas.Top="30" />

3. 코드-비하인드에 HttpWebRequest와 HttpWebResponse 및 Stream을 위한 네임스페이스를 추가합니다.
C#
using System.Net;
using System.Windows.Browser.Net;
using System.IO;

4. 코드-비하인드에 버튼에 대한 이벤트 핸들러를 추가하고 XML 메시지를 요청하는 코드를 작성합니다.
C#
public void Page_Loaded(object o, EventArgs e)
{
    // Required to initialize variables
    InitializeComponent();
    btnSync.MouseLeftButtonDown += new MouseEventHandler(btnSync_MouseLeftButtonDown);
}

void btnSync_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    // URI Request.
    Uri uri = new Uri("test.xml", UriKind.Relative);
    BrowserHttpWebRequest request = new BrowserHttpWebRequest(uri);
}

5. 곧바로 XML 메시지를 수신하여 디버그 창에 표시하는 코드를 작성합니다. 위의 코드에서 request를 생성한 코드 아랫줄에 계속 입력합니다. 수신 내용을 표시하는 코드는 별도의 프로시저로 작성합니다.
C#
void btnSync_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    // URI Request.
    Uri uri = new Uri("test.xml", UriKind.Relative);
    BrowserHttpWebRequest request = new BrowserHttpWebRequest(uri);

    // Request로부터 Response를 수신합니다.
    HttpWebResponse response = request.GetResponse();
    // Response를 덤프하고 닫습니다.
    DumpResponse(response);
    response.Close();
}

private static void DumpResponse(HttpWebResponse response)
{
    // 수신이 완료되지 않으면 예외를 던집니다.
   
if (response.StatusCode != HttpStatusCode.OK)
    {
        throw new ApplicationException("Status not OK : " + response.StatusCode.ToString());
        return;
    }

    // response의 스트림을 문자열로 읽습니다.
    StreamReader sr = new StreamReader(response.GetResponseStream());
    string rawResponse = sr.ReadToEnd();

    // 출력합니다.
    txtResult.Text = rawResponse;
}




비동기적(Asynchronously)으로 송수신하기
동기적인 송수신은 Request를 전송하고 수신 받기 까지 실버라이트 런타임이 어떤 코드도 처리를 하지 못하게 되고 따라서 그 시간 동안 Freezing(화면 멈춤) 현상이 나타나게 됩니다. 따라서 수신 받을 데이터가 크거나 네트워크의 속도가 충분히 빠르지 않을 때에는 반드시 비동기적인 송수신을 수행해야 하며, 그렇지 않을 경우에도 가급적 비동기적인 송수신을 수행하는 것이 좋습니다.

비동기적인 송수신은 비동기 CallBack을 사용하여 구현됩니다.

Howto : POX 메시지를 비동기적으로 수신하는 방법
1. 위에서 작성한 동기적으로 송수신하기 프로젝트에서 다음과 같은 코드들을 추가합니다.

2. 버튼으로 사용할 TextBlock을 마크업에 추가합니다.
XAML
<TextBlock x:Name="btnAsync" Text="Request Asynchronously" Canvas.Left="300" />

3. 코드-비하인드에 버튼에 대한 이벤트 핸들러를 추가하고 XML 메시지를 요청하는 코드를 작성합니다. Request 개체는 비동기적으로 수행되므로 클래스의 멤버 변수로 미리 선언합니다.
C#
HttpWebRequest _asyncRequest;
 
public void Page_Loaded(object o, EventArgs e)
{
    // Required to initialize variables
    InitializeComponent();
    btnSync.MouseLeftButtonDown += new MouseEventHandler(btnSync_MouseLeftButtonDown);
    btnAsync.MouseLeftButtonDown += new MouseEventHandler(btnAsync_MouseLeftButtonDown);

}

void btnAsync_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    // URI를 생성하고 Request를 작성한 후 Request에 대한 비동기 수신자를 등록합니다.
    Uri uri = new Uri("test.xml", UriKind.Relative);
    _asyncRequest = new BrowserHttpWebRequest(uri);
    IAsyncResult iar = _asyncRequest.BeginGetResponse(new AsyncCallback(OnResponseDownload), _asyncRequest);
}

5. Response가 완료되었을 때 처리할 프로시저 OnResponseDownload를 작성합니다.
C#
// 비동기적인 송수신
private void OnResponseDownload(IAsyncResult iar)
{
    // 응답 다운로드를 종료하고 참조를 획득합니다.
    HttpWebResponse response = (iar.AsyncState as HttpWebRequest).EndGetResponse(iar);

    // Response를 덤프하고 닫습니다.

    DumpResponse(response);
    response.Close();
}


Web Application으로 로컬에서 테스트하기

HttpWebRequest/Response
HttpWebRequest와 HttpWebResponse 클래스는 그 이름이 말하는 것과 같이 HTTP 프로토콜을 통해 전달됩니다. 때문에 다른 코드와 달리 로컬에서 실행하는 실버라이트 애플리케이션에서 이 클래스를 사용할 경우 예외를 발생시킵니다.

따라서 HttpWebRequest/Response와 같은 HTTP 프로토콜을 이용하는 클래스나 코드를 테스트하기 위해서는 실제 웹서버에서 직접 테스트하거나 디버깅과 예외처리에서 소개한 것과 같이 Web Application프로젝트를 추가하여 Web 애플리케이션에서 테스트해야 합니다. 여기에서는 웹서버 없이 로컬에서 사용가능한 후자의 방법에 대해 알아보겠습니다.
Howto : Web Application을 추가하고 로컬에서 테스트하는 방법
1. 위에서 작성된 프로젝트의 솔루션에서 마우스 오른쪽 클릭을 한 후 Add-> New Project를 선택합니다.


2. 원하는 형태의 새 Web Application 프로젝트 타입을 선택하고 생성합니다.


3. 디버깅과 예외처리를 참고하여 위에서 작성된 실버라이트 프로젝트를 링크하고 디버그 할 수 있도록 설정합니다. 웹 애플리케이션의 Deafult.aspx에 링크한 실버라이트 페이지를 보여줄 수 있도록 초기화 코드를 추가하고 새 웹 애플리케이션 프로젝트를 시작 프로젝트로 설정합니다. 또한 위에서 작성한 test.xml도 미리 웹 애플리케이션의 루트에 복사해 놓습니다.

최종적으로 새 웹 애플리케이션 프로젝트는 다음과 같은 모습을 가질 것입니다.


4. 이제 웹 애플리케이션 프로젝트를 빌드하고 F5를 눌러 디버그 모드로 실행하여 테스트해봅니다.


참고

지금까지 작성한 샘플 프로젝트:
RESTXmlMessage.zip

HTTP를 통한 Plain XML 수신


Silverlight 공식 QuickStarts 참고:
http://silverlight.net/QuickStarts/Remote/UsingREST.aspx
신고
Posted by gongdo
네이버의 실버라이트 카페에 정말 알기 쉬운 튜토리얼 모음집이 올라왔습니다.
수고하신 패러다임님께 박수를... ^^
http://cafe.naver.com/mssilverlight.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=77

실버라이트를 처음 시작하시는 분이라면 위의 튜토리얼 먼저 시작하시는게 좋을 것 같네요.
위의 주소에 들어가서 파일을 다운 받으시고 압축 풀고 index.html을 열어보시면 다음과 같은 화면을 볼 수 있어요.



Silverlight 1.0 Beta의 Quickstarts를 기준으로 단계별로 학습을 진행하시면 될 것 같네요. ^^
신고
Posted by gongdo
Silverlight 1.1 바로 시작하기

HTML DOM의 사용
준비 사항
Silverlight 개발의 기초에서 개발에 필요한 도구와 기술에 대해 설명하고 있습니다.
이벤트 핸들링에서 개체의 이벤트를 처리하는 방법에 대해 설명하고 있습니다.

HTML DOM(Document Object Model)과 JavaScript에 대한 사전 지식이 필요합니다. http://www.cadvance.org/ 에 HTML부터 XML까지 아주 잘 정리되어 있으니 참고하시기 바랍니다.

예제 중간에 정규표현식이 사용되며 여기에 대한 자세한 설명은 http://msdn2.microsoft.com/ko-kr/library/hs600312(VS.80).aspx 을 참고하십시오.

이 글에서 사용된 예제는 http://gongdo.oranc.co.kr/Silverlight/Samples/HtmlDom/TestPage.html 에서 미리 테스트해볼 수 있습니다.

HTML DOM 참조

호스트 HTML의 DOM에 접근하는 방법
Silverlight는 System.Windows.Browser 네임스페이스를 통해 호스트 HTML의 DOM에 접근하고 핸들링 할 수 있는 클래스들을 제공합니다.


매니지드 코드에서 HTML DOM에 접근하는 방법을 알아보기 전에 사용자와 상호작용이 가능한 간단한 HTML 페이지를 만들어 보겠습니다. 또한 이 예제는 Silverlight내의 매니지드 코드로 HTML DOM에 접근하는 방법을 보여주기 위한 것이므로 XAML 디자인도 간소화 하였습니다.

Howto:5-1 사용자와 상호작용하는 HTML 페이지 샘플 및 XAML 페이지 디자인
1. HtmlDom이라는 이름으로 새 Silverlight 프로젝트를 생성합니다.

2. TestPage.html의 BODY태그 안에 다음 DIV 코드 블럭을 추가합니다.
HTML
<div id="userInteraction">
    <p style="font-size:20px; font-weight:bold;">매니지드 코드에서 HTML DOM 접근 예제</p>
    <span style="font-size:14px">1. HTML DOM의 이벤트 핸들링</span><br />
    예제 1) e-mail 유효성 검사 :
    <input type="text" id="email" size="50px" /><input type="button" id="emailSubmit" value="체크" /><br />
    <span style="margin-left:50px">검사 결과 : </span>
    <input type="text" id="emailValidationResult" size="70" /><br /><br />

    <span style="font-size:14px">2. 호스트 HTML 페이지의 속성 얻기</span><br />
    예제 2) 페이지 속성 정보 : 
    <input type="button" id="propertiesSubmit" value="속성 가져오기" /><br />
    <textarea id="properties" cols="70" rows="7"></textarea><br /><br />

    <span style="font-size:14px">3. HTML DOM의 메소드 호출</span><br />
    예제 3) URL 유효성 검사 및 이동 : 
    <input type="text" id="url" size="30" /><input type="button" id="urlSubmit" value="체크 & 이동" /><br /><br />
</div>

예제는 1. 간단한 이메일 유효성 검사, 2. HTML 페이지의 속성 얻기, 3. HTML DOM의 메소드 호출하는 방법 입니다.

3. Page.xaml의 루트 엘리먼트인 Canvas를 300 x 50 픽셀로 설정하고 TextBlock 하나를 선언합니다. 다른 HTML 엘리먼트와 구분하기 위해 배경 색상을 따로 설정하였습니다.
XAML
<Canvas x:Name="parentCanvas"
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Loaded="Page_Loaded"
    x:Class="HtmlDom.Page;assembly=ClientBin/HtmlDom.dll"
    Width="300" Height="50" Background="#FFFF9966">
    <TextBlock Text="Access HTML DOM from managed code." />
</Canvas>

4. TestPage.html.js에서 호스팅할 Silverlight 컨트롤의 크기도 300 x 50으로 설정합니다.
Script
//...전략...
    properties: {
        width: "300",
        height: "50",
//...후략...

매니지드 코드에서 HTML DOM에 접근하기 위해서 다음과 같은 코드를 사용합니다.

C#
using System.Windows.Browser;
HtmlDocument document = HtmlPage.Document;

앞서 얘기했듯이 System.Windows.Browser 네임스페이스는 자신을 호스팅하는 HTML 페이지에 접근하기 위한 다양한 클래스와 메소드를 제공합니다. 호스트 HTML 페이지는 HtmlPage 클래스로 캡슐화되고 HtmlDocument 클래스는 HTML DOM 그 자체를 캡슐화 하며 HtmlPage의 Document 프로퍼티를 통해 호스트 HTML의 HTML DOM에 대한 참조를 얻을 수 있습니다.

HTML DOM 엘리먼트의 이벤트 핸들링
HtmlPage.Document 프로퍼트를 통해 얻은 HTML DOM에 대한 참조를 사용하여 HTML DOM 엘리먼트에 대한 이벤트 핸들러를 매니지드 코드로 작성한 함수로 지정할 수 있습니다. 여기에서는 앞에서 작성한 HTML 페이지의 1번 예제를 처리하는 방법을 제시합니다.
Howto:5-2 HTML DOM 엘리먼트의 이벤트 핸들링
1. Page.xaml.cs에 HTML 페이지 정보 처리와 정규 표현식 처리를 위한 네임스페이스를 추가하고 HTML DOM에 대한 참조를 클래스의 지역 변수에 저장합니다.
C#
// 다음 네임스페이스를 추가
using
System.Windows.Browser;

using System.Text.RegularExpressions;

// 프로젝트의 네임스페이스 선언은 생략
public partial class Page : Canvas
{
    HtmlDocument _document; // HTML DOM 참조
    public void Page_Loaded(object o, EventArgs e)
    {
        // Required to initialize variables
        InitializeComponent();
        _document = HtmlPage.Document;
    }
}

2. 예제 1의 버튼 클릭 이벤트를 받아서 처리할 이벤트 핸들러를 선언합니다. 내부 코드는 아래에서 추가하도록 하고 여기에서는 단지 이름만을 선언해 놓습니다.
C#
private void OnEmailClicked(object sender, HtmlEventArgs e)
{
}

3. 클래스의 생성자에 HTML DOM 참조로부터 실제로 이벤트를 부여할 엘리먼트의 참조를 얻고 이벤트 핸들러를 지정하는 코드를 추가합니다. 과정 2에서 미리 이벤트 핸들러를 선언했기 때문에 코드 입력 과정에서 인텔리센스의 도움을 받을 수 있습니다.
C#
// 예제1의 버튼 ID를 통해 이벤트 핸들러 지정
HtmlElement btnEmail = _document.GetElementByID("emailSubmit");
btnEmail.AttachEvent("onclick", new EventHandler<HtmlEventArgs>(this.OnEmailClicked));

4. 이제 실제 작동할 이벤트 핸들러 코드를 작성합니다.
C#
private void OnEmailClicked(object sender, HtmlEventArgs e)
{
    // 엘리먼트의 참조 얻기

    HtmlElement txtEmail = _document.GetElementByID("email");
    HtmlElement txtResult = _document.GetElementByID("emailValidationResult");

    // 엘리먼트의 값을 얻어와 정규 표현식으로 점검하고 결과 설정
    string email = txtEmail.GetAttribute("value");
    if (email == null)    // 값이 설정되지 않은 경우 null이 올 수 있으므로 주의
        email = "";
    Regex reg = new Regex(@"\w+[@]\w+\.\w+");
    if (reg.Match(email).Success)
    {
        txtResult.SetAttribute("value", " E-mail [" + email + "] .");
    }
    else
    {
        txtResult.SetAttribute("value", " E-mail [" + email + "] .");
    }
}

5. 프로젝트를 빌드하고 F5를 눌러 테스트 해봅니다.


호스트 HTML 페이지의 속성(Property) 참조
HtmlPage 클래스는 또한 현재 호스트 HTML 페이지의 유용한 속성을 제공합니다. 예제 2를 작성해 보겠습니다.
Howto:5-3 호스트 HTML 페이지의 속성 참조
1. 예제 2의 버튼 클릭 이벤트를 받아서 처리할 이벤트 핸들러를 선언합니다. 내부 코드는 아래에서 추가하도록 하고 여기에서는 단지 이름만을 선언해 놓습니다.
C#
private void OnPropertiesClicked(object sender, HtmlEventArgs e)
{
}

2. 클래스의 생성자에 HTML DOM 참조로부터 실제로 이벤트를 부여할 엘리먼트의 참조를 얻고 이벤트 핸들러를 지정하는 코드를 추가합니다.
C#
// 예제2의 버튼 ID를 통해 이벤트 핸들러 지정
HtmlElement btnProperties = _document.GetElementByID("propertiesSubmit");
btnProperties.AttachEvent("onclick", new EventHandler<HtmlEventArgs>(this.OnPropertiesClicked));

3. 이제 실제 작동할 이벤트 핸들러 코드를 작성합니다.
C#
private void OnPropertiesClicked(object sender, HtmlEventArgs e)
{
    // 엘리먼트의 참조 얻기

    HtmlElement txtProperties = _document.GetElementByID("properties");

    //
호스트 HTML 페이지의 속성들을 표시
    string
output = "";
    output = "절대 경로 : " + HtmlPage.DocumentUri.AbsolutePath;
    output += "\n플랫폼 : " + HtmlPage.BrowserInformation.Platform;
    output += "\n에이전트 : " + HtmlPage.BrowserInformation.UserAgent;
    txtProperties.SetAttribute("value", output);
}

4. 프로젝트를 빌드하고 F5를 눌러 테스트 해봅니다.

호스트 HTML DOM의 메소드 호출
HtmlPage 클래스는 또한 HTML DOM과 관계된 메소드를 제공합니다. 예제 3에서 지정된 URL로 이동하는 Navigate 메소드를 호출하는 예제를 작성해 보겠습니다.
Howto:5-4 호스트 HTML DOM의 메소드 호출
1. 예제 3의 버튼 클릭 이벤트를 받아서 처리할 이벤트 핸들러를 선언합니다. 내부 코드는 아래에서 추가하도록 하고 여기에서는 단지 이름만을 선언해 놓습니다.
C#
private void OnNavigateClicked(object sender, HtmlEventArgs e)
{
}

2. 클래스의 생성자에 HTML DOM 참조로부터 실제로 이벤트를 부여할 엘리먼트의 참조를 얻고 이벤트 핸들러를 지정하는 코드를 추가합니다.
C#
// 예제3의 버튼 ID를 통해 이벤트 핸들러 지정
HtmlElement btnNavigate = _document.GetElementByID("urlSubmit");
btnNavigate.AttachEvent("onclick", new EventHandler<HtmlEventArgs>(this.OnNavigateClicked));

3. 이제 실제 작동할 이벤트 핸들러 코드를 작성합니다.
C#
private void OnNavigateClicked(object sender, HtmlEventArgs e)
{
    // 엘리먼트의 참조 얻기
    HtmlElement txtUrl = _document.GetElementByID("url");

    // 엘리먼트의 값을 가져와 정규 표현식으로 점검하고 결과 설정
    string url = txtUrl.GetAttribute("value");
    if (url == null)
    url = "";

    // 먼저 스키마 지정(http://등등)이 없으면 http://를 추가
    Regex regPre = new Regex(@"([\w]+:)?//\w*");
    if (!regPre.Match(url).Success)
        url = "http://" + url;

    // 다시 검사(RFC URL규정)
    Regex reg = new Regex(@"(([\w]+:)?//)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,4}(:[\d]+)?(/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?");
    if (reg.Match(url).Success)
    {
        HtmlPage.Navigate(url);
    }
    else
    {
        txtUrl.SetAttribute("value", " URL.");
    }
}

4. 프로젝트를 빌드하고 F5를 눌러 테스트 해봅니다.

예제 프로젝트를 첨부하였습니다.



디버깅 팁

예제와 같이 어떤 개체의 참조를 문자열로부터 얻는 코드는 대단히 위험합니다. 컴파일러는 코드를 빌드하는 시점에서는 그 문자열에 사소한 오타나 문제점이 있어도 그것을 감지할 수 없기 때문에 런타임에 예외를 발생시킬 가능성이 매우 높으며 따라서 반드시 try~catch 구문을 사용하여 문제가 발생할 경우 예외 처리를 수행해야 합니다.

예를 들어 초기에 HTML 페이지에 'test'라는 ID의 엘리먼트를 배치하고 페이지를 작성하였고 매니지드 코드에서 이 엘리먼트에 디버깅 결과를 출력하였다고 합시다. 시간이 흘러 더 이상 test가 필요 없게 되었고 페이지에서 이 엘리먼트를 삭제했지만 깜빡하고 매니지드 코드에서는 관련 코드를 제거하지 않았다면 해당 코드가 실행될 때 더 이상 test라는 ID를 가진 엘리먼트를 찾을 수 없으므로 Silverlight 애플리케이션은 예외를 발생시키고 그 이후의 어떤 코드도 실행되지 않게 될 것입니다.

항상 런타임에 동적으로 바인딩 되는 개체를 참조할 때에는 반드시 예외 처리를 할 것을 강력하게 권장합니다.

참고

Silverlight 공식 QuickStarts 참고:
http://silverlight.net/QuickStarts/Dom/DomAccess.aspx

.NET Framework의 정규표현식 참고:
http://msdn2.microsoft.com/ko-kr/library/hs600312(VS.80).aspx

정규표현식 라이브러리:
http://regexlib.com/
신고
Posted by gongdo
Silverlight 1.1 바로 시작하기

디버깅과 예외 처리
준비 사항
Silverlight 개발의 기초에서 개발에 필요한 도구와 기술에 대해 설명하고 있습니다.
이벤트 핸들링에서 개체의 이벤트를 처리하는 방법에 대해 설명하고 있습니다.
커스텀 컨트롤에서 개발 프로젝트에 테스트 프로젝트를 추가하는 방법을 소개하고 있습니다.

기본적으로 Visual Studio IDE를 사용한 .Net 프로그래밍 및 디버깅 방법과 유사하므로 Visual Studio IDE를 사용한 보다 전문적인 디버깅은 Visual Studio의 디버깅을 참고하십시오. 이 글에서는 간단한 기법만을 소개합니다.

또한 ASP.NET 프로젝트와 IIS에 대한 언급도 나옵니다. 이 부분은 지금 곧 사용하지 않는다면 특별한 사전 지식은 필요 없습니다.

매니지드 코드 디버깅

일반적인 브레이크 포인트를 사용한 디버깅
매니지드 코드의 디버깅은 일반적인 .Net 프로그래밍에서의 그것과 동일합니다. 가장 기본적인 디버깅 방법으로 브레이크 포인트를 걸고 런타임에 해당 코드가 실행되기 직전에 코드의 실행을 멈추고 콜스택이나 멤버 변수 등의 값을 살펴볼 수 있습니다.

우선 매니지드 코드 디버깅 예제를 위해 새 프로젝트를 생성하고 일부러 틀린 코드를 넣어보겠습니다.
Howto:4-1 새 프로젝트에 잘못된 코드 작성하기
1. 새 프로젝트를 생성합니다. (프로젝트를 생성하는 방법은 Silverlight 개발의 기초를 참고하십시오.)
2. Page.xaml.cs의 Page 클래스에 다음과 같은 멤버 함수를 작성하고 Page_Loaded에서 해당 함수를 호출합니다.
C#
private void WrongMethod()
{
    TextBlock tb = new TextBlock();
    Run run = new Run();
    run.Text = "Test text";
    tb.Inlines.Add(run);
    this.Children.Add(tb);
}

위의 코드를 언뜻 보면 새 TextBlock 개체를 생성하고 TextBlock에 Run 텍스트를 하나 추가한 후 TextBlock을 페이지의 자식 개체로 추가하는 정상적인 코드처럼 보입니다. 아마도 개발자는 화면에 Test text 란 메시지가 출력되길 기대하고 있을 것입니다.

하지만 F5를 눌러 위의 코드를 디버그 모드로 실행해보면 화면에는 아무것도 나오지 않으며 다만 Output 창에는 예외가 발생했다는 메시지가 보입니다.


문제가 발생하는 시점을 찾기 위해 브레이크 포인트를 걸고 차근차근 진행해 보겠습니다.
Howto:4-2 브레이크 포인트 걸고 진행하기
1. 어디에서 문제가 생겼는지 확실치 않으므로 처음부터 브레이크 포인트를 설정합니다. 첫 문장인 TextBlock tb = new TextBlock() 코드에 커서를 맞추고 F9를 누르거나 해당 라인의 제일 왼쪽의 회색 공간을 클릭하여 브레이크 포인트를 설정합니다.


2. F5를 눌러 디버그 모드로 실행하면 다음과 같이 브레이크 포인트에서 코드 실행이 멈춥니다.


3. F10을 눌러 한 라인 단위로 진행합니다. (※노트: 함수 내부까지 들어가서 진행하려면 F11을 눌러 진행합니다.)

4. 아마도 tb.Inlines.Add(run); 문장을 진행하면 Output 창에 예외가 발생하였다는 메시지와 함께 디버깅이 중지되고 미완성된 페이지가 브라우저에 나타날 것입니다.

5. Shift + F5를 누르거나 디버깅 중인 브라우저를 닫아 디버깅 모드를 완전히 종료합니다.

6. 4번 과정을 실행하는 중 예외가 발생하였으므로 이 부분의 코드가 잘못되었다는 것을 알 수 있습니다. 또한 예외 메시지를 살펴 보면 [A first chance exception of type 'System.NullReferenceException' occurred in DebugManagedCode] 즉, 예외의 종류가 System.NullReferenceException임을 알 수 있고 자연스럽게 null을 참조하여 발생한 문제라는 것도 예상할 수 있습니다.

7. 다시 한번 디버깅을 진행하여 주의 깊게 진행하고 4번 과정에서 진행하지 않고 멈춰서 tb.Inlines 부분에 마우스 커서를 맞추면 다음과 같이 상세 정보를 볼 수 있습니다. 예상대로 tb.Inlines 멤버가 null을 참조하고 있으며 따라서 Inlines의 멤버 메소드인 .Add를 접근할 수 없음을 알 수 있습니다.
 

위의 시나리오는 비교적 간단하고 사소한 실수이지만 코드를 작성할 때 빈번하게 발생하는 문제이기도 합니다. 보통 코드를 작성하고 최초로 테스트 할 때에는 위와 같이 새로 작성한 코드에 브레이크 포인트를 설정하여 한 라인씩 진행하면서 코드가 예측한대로 작동하는지 검사하곤 합니다.

Visual Studio IDE는 디버그 모드상에서 어떤 개체에 대해 매우 직관적이고 상세한 정보를 실시간으로 볼 수 있는 인터페이스를 제공하고 있습니다. 각 개체는 자신의 멤버 뿐만 아니라 자신의 상위 개체에 대한 정보도 볼 수 있습니다.

브레이크 상태에서 코드를 직접 실행하기
브레이크가 걸린 상태에서 IDE에는 직접 실행 창(Immediate Window)이 나타납니다. 이 직접 실행 창에서는 현재 코드 상태에서 멤버의 값을 변경하거나 읽어오는 등 즉시 코드를 실행할 수 있습니다. 특히 임의의 식(expression)을 테스트 해볼 수도 있어서 디버깅에 매우 유용한 툴입니다.

Howto:4-3 직접 실행 창에서 디버깅 하기
1. 이전 예제를 디버깅 모드로 실행하고 브레이크 상태를 만든 후 문제의 코드까지 진행합니다. IDE의 하단에서 직접 실행 창(Immediate Window) 탭을 찾아 엽니다.

2. 직접 실행 창에 다음과 같이 입력하고 엔터를 눌러 tb.Inlines의 값을 확인해 봅니다.
Immediate Window
? tb.Inlines.ToString();


tb.Inlines가 null임을 확인할 수 있습니다. 여기에서 '?'는 직접 실행 창에 그 결과를 출력하라는 의미입니다.

3. 직접 실행 창에 다음과 같이 입력하고 엔터를 눌러 tb.Inlines의 새 인스턴스를 할당해 봅니다.
Immediate Window
tb.Inlines = new Inlines();



4. 개체 참조가 변경되었는지 확인하기 위해 다시 한번 tb.Inlines의 내용을 출력해봅니다.


5. F5를 눌러 코드를 계속 진행합니다. 아까와는 다르게 예외가 발생하지 않고 정상적인 출력이 된 것을 확인할 수 있습니다.


직접 실행 창을 사용하면 런타임 도중에 문제가 생겼다고 예상되는 지점에서 직접 코드를 테스트해볼 수 있습니다. 여기에서 이미 작성된 코드 뿐만 아니라 위의 예제와 같이 new 연산자 등을 통해 개체의 새 인스턴스를 생성하는 것도 가능하며 코드와 전혀 관계 없는 수식 등도 사용할 수 있습니다.

예외 처리
브레이크 포인트와 직접 실행 창을 사용한 디버깅은 개발 초기에는 문제점을 찾기에 매우 유용하지만 애플리케이션이 배포되고 난 후에는 전혀 도움이 되지 않습니다. 애플리케이션은 미처 수정하지 못한 문제점이 있기 마련이며 코드가 완벽하더라도 외부 환경적인 요인으로 인해 문제가 발생할 수도 있습니다.

예를 들어, 외부 리소스 XAML 파일의 스트림을 읽어오는 코드를 생각해보면 이 XAML 파일은 외부 컨텐츠이므로 해당 경로에 존재하지 않을 수도 있을 것입니다. 이렇게 동적으로 상태가 변경될 수 있는 가능성이 있는 코드는 반드시 예외 처리를 할 필요가 있으며 Silverlight 매니지드 코드에서는 전통적인 try~catch~finally문을 지원합니다.
Howto:4-4 예외가 발생할 수 있는 코드를 위한 try~catch 예제
1. Page.xaml.cs에 다음과 같이 System.IO 네임스페이스를 추가합니다.
C#
using System.IO;

2. Page 클래스에 다음과 같은 멤버 메소드를 작성하고 Page_Loaded에서 해당 메소드를 호출하는 코드를 추가하고 이전 예제인 WrongMethod() 호출은 주석처리 합니다.
C#
private void ExceptionalMethod()
{
    StreamReader sr = null;
    try
    {
        sr = new StreamReader("test.xaml");
        Brush dynamicBrush = (Brush)XamlReader.Load(sr.ReadToEnd());
        sr.Close();
    }
    catch (Exception ex)
    {
        string err = ex.Message;
    }
}

3. 프로젝트 폴더에 test.xaml이란 파일은 존재하지 않으므로 예외가 발생하고 catch 될 것을 예상할 수 있습니다. string err = ex.Message; 문장에 브레이크 포인트를 설정하고 F5를 눌러 프로젝트를 디버깅 모드로 실행해 봅니다.

4. IDE상의 직접 접근과 직접 실행 창 등을 통해 예외 정보 개체인 ex를 통해 예외의 정보에 접근할 수 있습니다.

코드에서 예외가 발생하면 그 다음 문장은 실행되지 않고 코드가 중지되어버리기 때문에 웹 애플리케이션의 관점에서 최악의 상황이라고 할 수 있습니다. 따라서 런타임 중 예외가 발생할 가능성이 있는 코드는 반드시 위와 같은 try~catch 문을 사용하여 예외가 발생했을 때 애플리케이션이 그냥 중지되어버리지 않도록 처리할 필요가 있습니다.

브레이크 포인트를 걸지 않고 정보 출력하기
매우 빈번하게 진행되는 코드의 디버깅을 브레이크 포인트로 한 줄씩 실행한다는 것은 너무나도 소모적인 일입니다. 보다 효율적인 디버깅을 위해 Silverlight는 System.Diagnostics 네임스페이스에 포함된 Debug 클래스를 제공합니다.


이 클래스는 디버깅 도중 브레이크 포인트를 걸지 않고도 Output 창에 문자열을 출력할 수 있는 WriteLine 메소드를 지원하며 WriteLine 메소드는 콘솔 애플리케이션의 Console.WriteLine 메소드와 동일한 사용법을 가졌습니다.
노트
아쉽게도, 매우 강력한 디버깅 도구인 Debug.Assert는 현재 제대로 작동하지 않는 것으로 보입니다. Assert는 코드의 전제 조건을 강제하고 조건이 실패하는 경우 코드의 실행을 멈추고 지정한 메시지를 출력하는 역할을 수행하는 메소드입니다. 하지만 Silverlight 1.1 Alpha(2007.5 CTP) 현재는 Assert에 어떤 조건을 걸어도 별도의 리액션이 발생하지 않습니다.

Howto:4-5 Debug.WriteLine 메소드
1. Page.xaml.cs에 다음과 같이 System.Diagnostics 네임스페이스를 추가합니다.
C#
using System.Diagnostics;

2. Page 클래스에 다음과 같은 멤버 메소드를 작성하고 Page_Loaded에서 해당 메소드를 호출하는 코드를 추가하고 이전의 두 예제 메소드는 주석처리 합니다.
C#
private void OutputMethod()
{
    int sum = 0;
    for (int i = 1; i <= 100; ++i)
    {
        sum += i;
        Debug.WriteLine("i = {0}, sum = {1}", i, sum);
    }
    Debug.WriteLine("Total = {0}", sum);
}

3. F5를 눌러 실행합니다. IDE의 Output 창에 다음과 같이 출력되었음을 확인할 수 있습니다.

지금까지 테스트했던 프로젝트를 첨부하였습니다.
DebugManagedCode.zip

매니지드 코드 디버깅



웹 프로젝트와 Silverlight 프로젝트를 함께 디버깅

Silverlight는 JavaScript를 사용하여 HTML 페이지에 호스트되는 클라이언트 사이드 애플리케이션이기 때문에 서버 사이드는 어떤 언어를 사용하여도 상관 없습니다. 하지만 Silverlight 컨트롤을 ASP.NET 페이지에 호스팅하도록 개발한다면 하나의 개발환경과 CLR을 통해 제공되는 일관된 언어 사용의 장점을 얻을 수 있을 것입니다.

이때 Silverlight 컨트롤이 자신을 호스팅하는 ASP.NET 페이지와 상호작용이 많다면 ASP.NET 페이지와 Silverlight 컨트롤의 코드를 함께 디버깅할 수 있으면 편리할 뿐더러 생산성도 대폭 향상될 것입니다.

이 절에서는 ASP.NET 웹 프로젝트에서 Silverlight 프로젝트를 추가하여 함께 디버깅하는 방법을 알아보겠습니다.

먼저 아주 간단하게 Hello Silverlight!를 출력하는 Silverlight 프로젝트를 하나 작성합니다. 이제 프로젝트 작성에 익숙해졌으리라 믿고 생성하는 방법은 생략하겠습니다. 프로젝트 이름은 HelloSilverlight라고 하겠습니다.
Howto:4-6 웹 프로젝트와 Silverlight 프로젝트를 링크하고 디버깅하는 방법
1. Visual Studio를 실행하고 메뉴에서 File->New->Project를 선택하거나 Ctrl+Shift+N을 눌러 새 프로젝트를 선택합니다.

2. Project Type에서 Visual C#->Web을 선택하고 Templates에서 ASP.NET Web Application을 선택한 후 프로젝트 이름을 MixedModeDebug라고 적고 [OK]를 누릅니다.
사용자 삽입 이미지


3. 솔루션 익스플로러의 솔루션 아이템에서 마우스 오른쪽 버튼을 누르고 Add->Existing Project를 선택합니다.


4. 미리 만들어둔 HelloSilverlight 프로젝트를 선택합니다. 솔루션 익스플로러에 이제 두개의 프로젝트가 포함될 것입니다.

5. 솔루션 익스플로러의 MixedModeDebug 웹 프로젝트에서 마우스 오른쪽 버튼을 누르고 Add Silverlight Link를 선택합니다.


6. 목록에 HelloSilverlight가 있을 것입니다. 선택하고 [OK]를 누릅니다.


7. 이 프로젝트를 위한 Silverlight 디버깅을 활성화 하겠냐고 묻는 창이 나옵니다. [예]를 누릅니다.
사용자 삽입 이미지

중요
이 설정은 스크립트 디버깅을 비활성화 상태로 설정합니다. Mixed-mode 디버깅을 사용하는 상태에서 매니지드 코드와 언매니지드 코드(스크립트)를 동시에 디버깅 할 수 없습니다.

8. 다음과 같이 MixedModeDebug 웹 프로젝트에 ClientBin과 Page.xaml 파일이 복사되었음을 확인할 수 있습니다.


9. Page.xaml을 ASP 페이지로 로드하기 위해 Silverlight.js와 createSilverlight 함수를 프로젝트로 추가합니다. 실제 프로젝트에서 Silverlight.js는 아마 웹서버의 공용 include 폴더에 위치할 것이며 createSilverlight 함수는 각 XAML 파일마다 하나씩 존재할 것입니다. 여기에서는 빠른 진행을 위해 HelloSilverlight 프로젝트의 Silverlight.js와 createSilverlight 함수가 포함된 TestPage.html.js 파일을 프로젝트로 추가하고 MixedModeDebug 웹 프로젝트의 Default.aspx에 위 스크립트 파일들을 include하고 Page.xaml을 로드할 것입니다.

솔루션 익스플로러의 MixedModeDebug 웹 프로젝트에서 마우스 오른쪽 버튼을 누르고 Add Existing Item을 선택하고 HelloSilverlight 프로젝트의 Silverlight.js 파일과 TestPage.html.js 파일을 선택합니다.



10. 다음과 같이 MixedModeDebug 웹 프로젝트에 해당 파일이 복사되었음을 확인할 수 있습니다.


11. HelloSilverlight 프로젝트의 TestPage.html 파일을 열어 스크립트를 include 하는 부분과 XAML을 로드하는 HTML 코드를 복사하여 MixedModeDebug 웹 프로젝트의 Default.aspx파일에 붙여넣습니다.
사용자 삽입 이미지


12. 양쪽 프로젝트에 모두 브레이크 포인트가 잡히는지 확인하기 위해 MixedModeDebug 웹 프로젝트의 Default.aspx.cs 파일에서 Page_Load 메소드에 브레이크 포인트를 설정하고 HelloSilverlight 프로젝트의 Page.xaml.cs 파일에 Page_Loaded 메소드에 브레이크 포인트를 설정합니다.


13. Ctrl+Shift+B 또는 F6을 눌러 솔루션을 빌드하고 F5를 눌러 디버깅 모드로 실행해 봅니다.
이때 Web.config 파일에 디버깅이 활성화되지 않았다는 메시지가 나올 것입니다. [Modify Web.config file to enable debugging.]을 선택하고 [OK]를 누릅니다.


14. 정상적인 경우 MixedModeDebug 웹 프로젝트의 Default.aspx.cs 파일에 먼저 브레이크가 걸리고 계속 진행하면 HelloSilverlight 프로젝트의 Page.xaml.cs 파일에 브레이크가 걸릴 것입니다.

웹 프로젝트에서 JavaScript 디버깅

앞서 소개한 디버깅 기법으로 웹 프로젝트와 Silverlight를 통합된 환경에서 디버깅할 수 있지만 이렇게 할 경우 다른 JavaScript 등의 스크립트 디버깅 기능은 사용할 수 없게 됩니다. 이 절에서는 이 전 절의 솔루션 상태에서 Silverlight 디버깅을 제거하고 JavaScript 디버깅을 하는 방법에 대해 알아보겠습니다.
Howto:4-7 웹 프로젝트에서 JavaScript 디버깅하는 방법
1. 먼저 인터넷 익스플로러를 디버깅 가능한 상태로 설정해야 합니다.
인터넷 익스플로러의 도구 메뉴에서 인터넷 옵션을 선택합니다.

2. 고급 탭에서 '스크립트 디버깅 사용 안 함(Internet Explorer)'의 체크를 해제합니다. 만약 다른 브라우저로도 디버깅하려면 '스크립트 디버깅 사용 안 함(기타)'의 체크도 해제합니다.

3. '모든 스크립트 오류에 관련된 알림 표시'를 체크합니다.

4. 'HTTP 오류 메시지 표시'의 체크를 해제합니다.

5. MixedModeDebug 웹 프로젝트의 Default.aspx의 body 태그에 테스트를 위해 다음과 같은 JavaScript 코드를 추가합니다.
JavaScript
<script type="text/javascript">
    var today = Date();
    alert(today.toString());
</script>

6. 솔루션 익스플로러의 MixedModeDebug 웹 프로젝트에서 마우스 오른쪽 버튼을 누르고 Properties를 선택하거나 Properties 항목을 더블 클릭합니다.

7. Web 탭의 Debuggers 섹션에서 Silverlight의 체크를 해제합니다.
사용자 삽입 이미지


8. 속성 패널을 닫고 5번 과정에서 추가한 JavaScript 코드에 브레이크 포인트를 추가합니다.

9. 솔루션을 빌드하고 F5를 눌러 디버깅 모드로 실행합니다.

10. 다음과 같이 JavaScript에서 브레이크가 걸리고 줄 단위 혹은 코드 단위 진행이 가능합니다.

여기까지 작성된 프로젝트를 첨부합니다.
MixedModeDebug.zip

MixedMode 디버깅과 JavaScript 디버깅



IIS를 사용한 디버깅

기본적으로 ASP.NET 페이지 프로젝트는 Visual Studio의 가상 웹서버를 통해 호스팅됩니다. 실제 프로젝트에서는 IIS와 연동을 통한 테스트를 직접 수행할 수 있습니다.
Howto:4-8 IIS를 사용한 디버깅
1. MixedModeDebug 웹 프로젝트에서 마우스 오른쪽 버튼을 누르고 Properties를 선택하거나 Properties 항목을 더블 클릭합니다.

2. Web 탭의 Servers 섹션에서 Use IIS Server를 체크합니다. 이 옵션은 반드시 IIS 서버가 사용 가능한 상태여야 합니다.

3. Create New Virtual Directory를 클릭하고 새로 생성할 디렉토리의 정보를 입력합니다.

4. 속성 패널을 닫고 모두 저장합니다.
이제 실제 IIS 서버를 사용하여 디버깅을 할 수 있습니다.

참고

Silverlight 공식 QuickStarts 참고:
http://silverlight.net/QuickStarts/Start/Debug.aspx

Visual Studio 디버깅 참고:
http://msdn2.microsoft.com/ko-kr/library/sc65sadd(VS.80).aspx
신고
Posted by gongdo
Silverlight 1.1 바로 시작하기

매니지드 코드의 기초
준비 사항
Silverlight 개발의 기초에서 개발에 필요한 도구와 기술에 대해 설명하고 있습니다.
이벤트 핸들링에서 개체의 이벤트를 처리하는 방법에 대해 설명하고 있습니다.

Silverlight 프로그래밍 모델

프로그래밍 모델 소개
Silverlight 프로그래밍은 크게 Silverlight 1.0 Beta로 릴리즈되어 JavaScript를 사용하는 언매니지드 코드 API와 Silverlight 1.1 Alpha로 릴리즈되어 C#또는 VB.NET을 사용하는 매니지드 코드 API를 사용하는 모델로 구분할 수 있습니다. 1.1 Alpha 릴리즈에서는 DLR(Dynamic Language Runtime)을 통해 파이선이나 JScript를 사용할 수도 있습니다.

어느쪽이든 Silverlight 컨트롤(또는 페이지)는 HTML 파일 안에 호스팅되는 웹 애플리케이션으로써 JavaScript를 통해 초기화되고 HTML 페이지에 로드되며 이에 따라 매니지드 코드와 언매니지드 코드를 섞어서 사용할 수도 있습니다.

현재 1.1 Alpha에 대한 정확한 문서화는 이루어져있지 않지만 대다수의 API 모델과 사용 방법이 1.0 Beta의 그것과 동일합니다. Silverlight 1.1 Alpha에서의 매니지드 코드 작성은 VS의 IDE에서 제공하는 인텔리센스, 코드 자동완성 기능과 개체 브라우저를 활용하여 직관적인 코드 작성이 가능하며, 개체의 구체적인 기능과 사용 방법은 1.0 Beta의 레퍼런스를 참고할 수 있겠습니다.

이 글에서는 직접적인 사용 방법 보다는 Silverlight 프로그래밍에 빈번히 사용되는 기초적인 개체의 기초적인 사항과 기반 지식을 간략하게 소개합니다. Silverlight 1.1 Alpha API는 성능이나 기능 개선 등의 이유로 언제든지 변경될 수 있으니 세부적인 사항은 항상 MSDN을 참고하시기 바랍니다.

매니지드 클래스 계층 구조
Silverlight의 컨트롤과 UI 기능을 제공하는 많은 클래스는 기본적으로 다음과 같은 계층 구조로 이루어집니다.


WPF 프로그래밍을 해보셨다면 WPF의 엘리먼트 계층 구조와 유사하다는 것을 알 수 있을 것입니다. 실제로 몇 가지 기능적인 제약을 제외하고는 기본적인 계층 구조의 각 클래스의 역할이 WPF와 같습니다. Silverlight에서는 FrameworkElement를 상속받는 클래스를 일반적인 엘리먼트 클래스라고 생각할 수 있고 이것은 UI를 구성하는데 기본적으로 필요한 유용한 속성과 메서드 및 이벤트를 제공하며 XAML 마크업에서 UI의 한 부분으로 선언할 수 있습니다.

하지만 XAML에서 사용하는 모든 요소가 FrameworkElement는 아닙니다. 특수한 UI 컴포넌트나 이벤트를 처리하기 위한 컨트롤들은 그들만의 고유한 속성을 가지고 있습니다. 예를 들어 Animation이나 Storyboard에서 파생되는 클래스들이 있는데, 이들은 UI 엘리먼트의 타겟 속성으로 사용될 것입니다.

개체 트리와 개체 찾기
이 절에 대한 내용은 앞서 포스팅한 이벤트 핸들링에서 자세히 설명하고 있습니다. 여기에서는 간략하게 요약하겠습니다.

Silverlight에서 UI 구성은 XAML 마크업을 통한 계층 구조로 이루어지며 마크업의 루트 엘리먼트는 x:Class를 정의하고 여기에서 정의된 클래스가 코드-비하인드에서 매니지드 코드를 사용하여 제어할 수 있게 됩니다.

이 때문에 코드-비하인드내에 정의된 같은 이름의 클래스에서 this 키워드는 마크업의 루트 엘리먼트를 의미합니다.
마크업에서 정의된 하위 엘리먼트들을 접근하는 방법은 루트 엘리먼트가 Panel에서 파생되는 Canvas 등의 개체일 경우 Panel.Children 컬렉션을 통해 인덱스 또는 반복자를 사용하거나 코드-비하인드에서 접근할 엘리먼트에 x:Name 어트리뷰트를 지정하고 코드에서 DependencyObject.FindName 메소드를 사용하여 해당 이름으로 개체의 참조를 얻을 수 있습니다.

각 개체들은 FrameworkElement.Parent 속성을 통해 자신의 부모 개체를 참조할 수 있으며 루트 엘리먼트에서 이 속성은 null을 가리킵니다.

주요 개체와 속성들

너비(Width)와 높이(Height)
너비와 높이는 FrameworkElement가 제공하는 대표적인 UI 엘리먼트를 위한 속성입니다. FrameworkElement에서 파생된 Canvas, TextBlock 또 Shape와 같은 개체들의 너비와 높이를 이 속성으로 설정하거나 얻을 수 있습니다.

TextBlock과 같은 어떤 엘리먼트들은 ActualWidth와 ActualHeight이라는 동적으로 계산되는 읽기 전용 속성을 제공하기도 합니다. TextBlock의 ActualWidth와 ActualHeight은 사용자가 지정한 Width와 Height에 관계없이 실제 표시될 Text에 해당하는 실제 너비와 높이를 의미합니다.

반대로 얘기하자면 어떤 엘리먼트들은 Width와 Height의 설정이 실제로 표시되는 내용의 너비와 높이와 정확히 일치하지 않을 수도 있습니다.

첨부된 속성(Attached Properties) 설정 및 얻기
첨부된 속성은 심지어 코드상의 멤버 목록에 없는 속성일지라도 마크업에서 다른 속성과 같이 어트리뷰트로 정의할 수 있는 속성을 말합니다. 예를 들어, Canvas는 자신의 자식 엘리먼트들에게 그들의 레이아웃 좌표를 결정할 수 있게 하는 속성을 제공합니다. 이 속성은 Canvas.Left와 Canvas.Top 그리고 Canvas.ZIndex입니다. XAML에서 이러한 첨부된 속성을 사용하려면 반드시 그 속성의 Owner.Property와 같이 전체 경로를 지정해야 합니다.

XAML에서는 간단하게 Owner.Property 구분으로 설정이 가능하지만 코드에서는 다른 방법을 사용해야 합니다. 왜냐하면 코드에서 Owner.Property = 값; 이런식으로는 단 하나의 개체에 대한 속성만 지정할 수 있기 때문입니다.

매니지드 코드에서 첨부된 속성은 DependencyObject.GetValue와 SetValue로 읽고 쓸 수 있습니다. 첨부된 속성은 DependencyProperty라는 Object 형태로 표현되며 따라서 읽고 쓸 때 해당 DependencyProperty 타입과 실제 타입으로 캐스팅이 필요합니다.
XAML 첨부된 속성 설정의 예
<Canvas Width="500" Height=200">
    <TextBlock x:Name="myText" Canvas.Left="200", Canvas.Top="10" Text="test" />
</Canvas>

C# DependencyProperty를 사용하는 방법
// 가져오기
double currentLeft = (double) myText.GetValue(Canvas.LeftProperty);

// 설정하기
myText.SetValue(Canvas.LeftProperty, 100);

DependencyProperty로 캐스팅하는 첨부된 속성의 설정과 읽기는 캐스팅 과정 때문에 성능상의 불이익이 있으므로 첨부된 속성이 아닌 일반 속성들은 GetValue나 SetValue를 사용하지 않고 해당 속성을 직접 설정하는 것이 유리합니다.

TextBlock과 Text API
TextBlock은 연속적인 텍스트를 표현하는데 효과적인 수단을 제공합니다. 현재는 중/동아시아 지역 언어를 제대로 표현하지 못하고 있지만 정식 버전이 나올 때에는 해결될 것입니다. 이 예제에서는 정상적으로 표현되는 영문 폰트만을 사용합니다.

TextBlock은 단순한 한줄 짜리 텍스트에서 여러 줄에 걸친 다양한 표현까지 출력하기 위한 수단을 제공합니다.
XAML 멀티 라인을 지원하는 TextBlock 예제
<TextBlock FontSize="30">
    <Run FontFamily="Arial" FontSize="20" Text="Arial, first line" Foreground="red" />
    <LineBreak />
    <Run FontFamily="Comic Sans MS" Text="Comic Sans MS, second line" />
</TextBlock>


위의 예제에서 TextBlock은 일종의 텍스트 컨테이너 루트라고 볼 수 있고 그 안에 Run이란 엘리먼트로 독립적인 폰트 속성의 텍스트를 추가할 수 있고 LineBreak란 엘리먼트로 폰트 사이즈에 맞춰 다음 줄로 넘어갈 수 있습니다. 매니지드 코드로 위의 예제를 표현하자면 다음과 같습니다.
C# 매니지드 코드로 작성한 TextBlock
TextBlock tb = new TextBlock();
tb.FontSize = 30;

Run
run = new Run();
run.FontFamily = "Arial";
run.FontSize = 20;
run.Foreground = new SolidColorBrush(Color.FromRgb(0xFF, 0x00, 0x00));
run.Text = "Arial, first line";
tb.Inlines = new Inlines();
tb.Inlines.Add(run);

tb.Inlines.Add(
new LineBreak());

run =
new Run();
run.FontFamily = "Comic Sans MS";
run.Text = "Comic Sans MS, second line";
tb.Inlines.Add(run);

XAML의 엘리먼트와 매니지드 코드의 클래스가 1:1로 매치되고 있어서 쉽게 접근할 수 있습니다. TextBlock의 상세한 속성과 기능은 MSDN을 참고하십시오.

컬러와 브러쉬
XAML에서 색상은 약 256가지의 일반적인 색상의 이름(Red, Blue)을 사용하거나 #를 붙인 RGB 표기 또는 ARGB 표기를 사용할 수 있습니다.
RGB는 Red, Green, Blue 색상을 순서대로 16진수로 표현하고 ARGB는 불투명도를 의미하는 Alpha를 앞에 붙입니다.

매니지드 코드에서 RGB 색상과 ARGB 색상은 Color.FromRGB와 Color.FromScRgb 및 Color.FromArgb 메소드를 통해 얻을 수 있습니다. 이 색상 값은 보통 어떤 컨트롤을 단일 색상 브러쉬로 칠할 때 사용됩니다. 사용 예제는 앞서 소개한 TextBlock 예제를 참고하십시오.

브러쉬는 어떤 영역을 칠하는 방법을 나타내는 클래스로 단일 색상을 의미하는 SolidColorBrush, 직선형 그래디언트를 의미하는 LinerGradientBrush, 원형 그래디언트를 의미하는 RadialGradientBrush를 비롯해 색상이 아닌 이미지나 심지어 비디오를 소스로 사용할 수도 있습니다. 자세한 것은 역시 MSDN을 참조하십시오.

자주 사용되는 복잡한 형태의 그래디언트나 이미지 브러쉬들을 그때그때 XAML이나 코드로 작성하여 사용한다면 유지보수가 굉장히 힘들어지고 메모리도 많이 차지하게 될 것입니다. 브러쉬 등의 개체는 build action을 embedded resource로 설정한 XAML을 사용하여 리소스의 형태로 사용할 수 있으며 이렇게 리소스화 된 개체는 다른 매니지드 코드에서 XamlReader 클래스를 사용하여 로드할 수 있습니다.

복잡한 형태의 드로잉을 가지는 개체는 Expression Blend와 같은 디자인 툴을 사용하여 XAML로 작성하고 이것을 코드에서 로드하여 사용하는 형태가 이상적일 것입니다.

마우스와 마우스 포인터의 위치
Silverlight UI는 HTML 상에 하나의 컨트롤로써 로드되기 때문에 마우스 포인터의 위치를 항상 얻을 수 있는 건 아닙니다. Silverlight에서 마우스 포인터를 획득하는 것은 마우스 포인터가 루트 컨트롤의 영역 내에 있을 때만 가능하며, 마우스 포인터의 위치는 오직 마우스 관련 이벤트를 통해서만 얻을 수 있습니다.

일반적인 마우스 관련 이벤트는 UIElement 클래스에서 제공하고 있습니다. 대표적인 마우스 이벤트인 MouseMove는 마우스 포인터가 해당 컨트롤 위에서 움직일 때 발생하며 MouseMove 이벤트 핸들러를 작성하여 마우스 포인터의 위치를 얻을 수 있습니다.
C# 마우스 포인터의 위치 얻기
void Page_MouseMove(object sender, MouseEventArgs e)
{
    Point pos = e.GetPosition((UIElement)sender);
    double x = pos.X; // X 좌표
    double y = pos.Y; // Y 좌표
}

마우스 관련 이벤트는 보통 MouseEventArgs 클래스를 통해 전달됩니다. Silverlight는 마우스 포인터의 위치를 직접 X, Y로 전달하는 대신 GetPosition 메소드를 통해 X, Y 좌표를 나타내는 Point 개체를 획득하도록 되어 있습니다.

약간 번거러워 보이는 이 메소드는 대신 인수로 좌표의 기준이 되는 UIElement의 참조를 전달할 수 있으며 null을 전달할 경우 Silverlight 컨트롤 전체에서의 좌표를 반환합니다. 심지어 이벤트와 관계 없는 다른 UIElement의 참조를 전달하여 해당 엘리먼트로부터의 좌표를 구할 수도 있지만 보통은 이벤트가 일어난 개체를 기준으로 할 것이며 이 때 이벤트가 발생한 개체를 의미하는 sender 개체를 UIElement로 캐스팅하여 전달하면 됩니다.

현재 Silverlight는 윈도우즈뿐만 아니라 MAC도 고려하는 크로스 플랫폼으로 개발되고 있기 때문에 윈도우즈 UI 프로그래밍에서 당연하다고 생각되는 몇 가지 속성이나 이벤트가 빠져 있습니다. 예를 들어 마우스 오른쪽 버튼과 휠 버튼이라던가 Alt 키가 눌렸는지 여부 등 윈도우즈에서는 여러모로 유용한 정보들이 있습니다.

이것은 기술적인 문제보다는 정책적인 문제가 고려되는 사항으로 앞으로의 지원 계획이 정확히 어떻게 될지는 확실하지 않습니다.

XamlReader 클래스와 스트림
JavaScript에서는 생성자가 없어서 엘리먼트의 새 인스턴스를 생성하여 개체 트리에 추가할 수 없습니다. 그래서 동적으로 표현할 UI 요소들을 DHTML 기법에서 그랬던 것처럼 숨겨진 엘리먼트를 미리 만들어두고 시나리오에 따라 보여주거나 움직이는 방법을 사용했습니다. 또는 Control.content.createFromXAML 메소드를 이용하여 문자열로부터 엘리먼트를 동적으로 생성하여 개체 트리에 추가하는 방법을 사용하기도 했습니다.

매니지드 코드에서는 개체를 선언하는 것으로 생성자에 접근할 수 있습니다. 때문에 대부분의 경우 XamlReader.Load(createFromXAML과 같은 역할을 하는 메소드) 메소드로 XAML 텍스트를 직접 코드로 로드하는 것은 불필요합니다. 왜냐면 매니지드 코드는 개체 모델에서 새로운 개체를 생성하는 것이 전혀 어렵지 않기 때문입니다.

더 구체적으로 디자인이 동적으로 복잡하게 변경될 수 있는 경우를 생각해 볼 수 있습니다. 분명히 코드에서 개체를 생성하는 것 자체는 어렵지 않지만 복잡한 디자인을 그때그때 코드로 작성한다는 것은 굉장한 낭비입니다. 이런 경우 앞서 얘기한 리소스의 경우처럼 디자이너가 필요한 UI 디자인이나 컨셉을 XAML로 작성하고 개발자는 코드에서 그 XAML 파일을 동적으로 로드하여 시나리오에 맞춰 보여주는 게 더 유리합니다. 그러면 디자이너와 개발자가 서로 격리되어 작업이 가능하며 디자이너와 개발자는 서로 정해진 네임스페이스와 개체 이름만을 공유하여 결과물인 XAML 파일만 다시 로드하면 될 것입니다.

이러한 동작은 System.IO.StreamReader를 사용하여 효율적으로 구현할 수 있습니다. StreamReader는 여러가지 상황에 맞춰 다양한 생성자를 오버로딩하고 있으며 크게 다른 Stream 개체에서 초기화하는 방법과 파일로부터 초기화하는 방법이 있습니다.

어떤 리소스를 XAML파일로부터 동적으로 로드하고 싶다면 두 가지 상황을 고려할 수 있습니다.
하나는 완성된 XAML이 정적일 경우 프로젝트의 embedded resource로 추가하여 어셈블리 안에 리소스로 함께 컴파일 되도록 하고 Assembly.GetManifestResourceStream 메소드를 통해 리소스의 스트림을 획득하는 방법이 있고 다른 하나는 XAML 파일이 런타임에 동적으로 변경되는 경우 StreamReader 클래스를 생성할 때 해당 XAML의 위치를 직접 지정해주는 방법입니다.

전자의 경우 XAML 파일의 경로는 반드시 네임스페이스를 포함한 전체 경로를 지정해야 하며, 후자의 경우 해당 XAML파일의 존재 여부를 런타임에 보장할 수 없으므로 반드시 예외 처리가 필요할 것입니다.
C# 동적으로 XAML을 로드하는 방법
// embedded resource로 추가된 XAML에서 스트림을 얻고 로드하는 방법
Stream s = this.GetType().Assembly.GetManifestResourceStream("TestProejct.test.xaml");
StreamReader sr = new StreamReader(s);
Brush dynamicBrush = XamlReader.Load(sr.ReadToEnd());
sr.Close();
s.Close();

// XAML 파일에서 직접 스트림을 얻고 로드하는 방법
StreamReader sr = new StreamReader("/Resources/test.xaml");
Brush dynamicBrush = XamlReader.Load(sr.ReadToEnd());
sr.Close();

Downloader 클래스
Silverlight는 웹 애플리케이션입니다. 보통은 웹 서버에서 호스팅되며 따라서 모든 컨텐츠는 일단 웹 서버에 존재하게 됩니다.
코드에서 동적으로 이런 컨텐츠를 접근하려고 하면 HTTP특성상 접근하는데 시간도 걸리고 서버도 부하가 늘어나게 될 것입니다. 빠른 반응과 효율적인 서버 운영을 위해서는 어떤 페이지에서 필요한 리소스들은 가급적 한번에 다운로드하고 같은 컨텐츠를 불필요하게 다시 요청하지 않는 게 좋을 것입니다.

Downloader 클래스는 서버에 있는 컨텐츠를 현재 실행중인 클라이언트로 다운로드할 수 있게 해줍니다. 이렇게 다운로드 받은 컨텐츠들은 Downloader 클래스의 지원 메소드들을 통해 스트림으로 얻거나 폰트 소스 설정 등으로 활용됩니다.

Downloader는 일반적으로 새 인스턴스를 생성한 후 DownloadProgressChanged, DownloadFailed, Completed 이벤트 핸들러를 추가한 후 Open 메소드로 읽어올 파일의 URI를 지정하고 Send 메소드를 호출하여 다운로드를 시작합니다. 만약 URI 지정이 잘못되었거나 문제가 있을 경우 DownloadFailed 이벤트가 발생하며 다운로드가 시작되면 진행 상황을 DownloadProgressChanged 이벤트를 통해 알 수 있고 다운로드가 완료되면 Completed 이벤트가 발생합니다.

아직 Downloader 클래스의 구체적인 활용 방법은 문서화되어 있지 않습니다. 현재 시점에서 가장 많이 활용되는 것은 TextBlock에 표시할 폰트를 다운로드 받고 변경하는 코드이며 자세한 활용 방법은 MSDN을 참고하십시오.
C# Downloader 사용 예제
public void Page_Loaded(object o, EventArgs e)
{
    // ... 생략 ...
    Downloader dn = new Downloader();
    dn.Completed += new EventHandler(dn_Completed);
    dn.DownloadFailed += new ErrorEventHandler(dn_DownloadFailed);
    dn.DownloadProgressChanged += new EventHandler(dn_DownloadProgressChanged);
}

void dn_DownloadProgressChanged(object sender, EventArgs e)
{
    // 다운로드 진행률 얻기
    double progressPercent = ((Downloader)sender).DownloadProgress * 100;
}

void dn_DownloadFailed(object sender, ErrorEventArgs e)
{
    // 다운로드 실패
}

void dn_Completed(object sender, EventArgs e)
{
    // 다운로드 완료
}

호스팅 브라우저에서 이벤트와 정보 얻기
Silverlight 컨트롤은 자신을 호스팅하는 HTML DOM에 접근할 수 있고 이를 통해 HTML 엘리먼트의 이벤트나 속성을 Silverlight 컨트롤의 코드에서 컨트롤 할 수 있습니다. 하지만 이런 방법으로 호스트 브라우저의 크기나 이벤트를 얻기는 상당히 어렵고 번거롭습니다.

BrowserHost 클래스는 호스트 브라우저에 대한 실제 크기 및 풀 스크린 모드 여부를 제공하는 편리한 이벤트와 속성을 제공하며 화면을 꽉 채우거나 풀 스크린을 사용하는 Silverlight 컨트롤에서 특히 유용하게 쓸 수 있습니다.  BrowserHost 클래스를 사용하려면 System.Windows.Interop 네임스페이스를 추가해야 합니다.
C# BrowserHost 클래스 사용 예제
public void Page_Loaded(object o, EventArgs e)
{
    // ...생략...
    BrowserHost.Resize += new EventHandler(BrowserHost_Resize);
    BrowserHost.FullScreenChange += new EventHandler(BrowserHost_FullScreenChange);
}

void BrowserHost_Resize(object sender, EventArgs e)
{
    // 호스트 브라우저의 크기가 변경되었을 때 이벤트와 실제 크기 얻기
    double hostWidth = BrowserHost.ActualWidth;
    double hostHeight = BrowserHost.ActualHeight;
}

void BrowserHost_FullScreenChange(object sender, EventArgs e)
{
    // 호스트 브라우저의 풀스크린 모드 변경과 상태 얻기
    bool hostFullScreen = BrowserHost.IsFullScreen;
}

호스트 브라우저의 HTML DOM에 직접 접근하는 것은 System.Windows.Browser 네임스페이스와 그 하위 클래스들로부터 지원됩니다. 자세한 사항은 다른 글에서 자세히 다룰 것입니다.

참고

Silverlight 공식 QuickStarts 참고:
http://silverlight.net/QuickStarts/BuildUi/CallJavascript.aspx

신고
Posted by gongdo
Silverlight 1.1 바로 시작하기

커스텀 컨트롤
준비 사항
Silverlight 개발의 기초에서 개발에 필요한 도구와 기술에 대해 설명하고 있습니다.
이벤트 핸들링에서 개체의 이벤트를 처리하는 방법에 대해 설명하고 있습니다.

커스텀 컨트롤 만들기

컨트롤의 기본 구성
Silverlight에서 컨트롤은 UI를 구성하는 XAML 마크업과 개체 모델을 구성하는 코드-비하인드로 구분할 수 있습니다. 특히 Silverlight와 같이 풍부한 사용자 인터페이스가 강조되는 프로그래밍에서 UI의 미려함은 매우 중요하므로 디자이너는 미적 감각을 충분히 살려 디자인된 컨트롤을 마크업으로 전달하고 개발자는 코드-비하인드에서 컨트롤을 모델링하고 제어하여 다른 XAML 페이지에서 재사용할 수 있습니다.

이렇게 작성된 XAML과 코드는 하나의 라이브러리 파일(.dll)로 컴파일 되며 다른 프로젝트에서는 이 라이브러리 파일의 어셈블리를 참조하여 사용이 가능합니다.

여기에서는 커스텀 컨트롤의 개요를 살펴보기 위해 MyButton이라는 간단한 버튼을 만들어보겠습니다.
이 버튼은 아주 단순하게 캡션과 너비 및 높이 속성을 가지며 클릭 이벤트를 노출하여 버튼이 클릭되었을 때 핸들링 할 수 있도록 할 것입니다. 또한 속성을 변경하면 캡션이 현재 너비 및 높이의 한 가운데에 표시될 수 있도록 하겠습니다.

Silverlight 컨트롤 프로젝트 만들기
기본적으로 커스텀 컨트롤은 XAML이나 코드를 포함하지 않는 라이브러리의 형태로 배포될 것입니다. 때문에 Silverlight 컨트롤은 일반적으로 Silverlight 클래스 라이브러리 프로젝트를 기반으로 작성합니다.

Howto:3-1 Silverlight 컨트롤 작성을 위한 클래스 라이브러리 프로젝트 만들기
1. Visual Studio를 실행하고 메뉴에서 File->New->Project를 선택하거나 Ctrl+Shift+N를 눌러 새 프로젝트를 선택합니다.
2. 새 프로젝트 다이얼로그 박스에서 Project types를 Visual C#->Silverlight로 선택하고 Templates에서 Silverlight Class Library를 선택한 후 프로젝트 이름을 MyButton이라고 입력하고 [OK]를 누릅니다.
사용자 삽입 이미지

3. 기본으로 생성되어 있는 Class1.cs는 필요 없으므로 솔루션 익스플로러에서 Class1.cs를 선택하고 마우스 오른쪽 버튼을 누른 후 Delete를 선택하여 삭제합니다.
4. 메뉴에서 Project->Add New Item을 선택하거나 Ctrl+Shift+A를 눌러 새 아이템을 선택합니다.
5. 새 아이템 다이얼로그 박스에서 Silverlight User Control을 선택하고 클래스 이름을 MyButton이라고 입력하고 [OK]를 누릅니다.
사용자 삽입 이미지

노트
다시 설명하겠지만, Silverlight User Control은 하나의 라이브러리 파일로 배포하기 위해 XAML파일의 build action 속성을 embedded resource 형태로 설정하며 따라서 컴파일시 XAML 파일은 어셈블리에 포함되게 됩니다.


UI 디자인하기
실제 작성해야 할 컨트롤은 미려한 디자인과 기능성 그리고 인터페이스 접근성을 고려해야 할 것입니다. 그러기 위해 UI의 디자인은 Expression Blend나 Design 등 XAML 코드를 작성할 수 있는 디자인 툴을 사용하는 것이 좋습니다.

여기에서는 단순히 TextBlock을 하나 만드는 걸로 만족하겠습니다.
Howto:3-2 XAML로 기본적인 UI 디자인 하기
1. MyButton.xaml을 열고 다음 코드를 루트 Canvas 엘리먼트 안에 넣습니다.
XAML
<TextBlock x:Name="text"></TextBlock>

2. 이 TextBlock은 버튼의 캡션을 표시하는 역할을 수행할 것입니다.
코드에서 제어할 필요가 있는 UI 요소는 반드시 x:Name으로 이름을 지정하여 코드에서 참조할 수 있도록 해야 합니다.

개체 참조 얻기
디자인이 완료되었으면 코드-비하인드에서는 UI 모델을 구현하기 위해 제어할 UI 개체의 참조를 획득해야 합니다.
MyButton.xaml.cs를 보면 다음과 같이 Control 클래스를 상속하며 기본 생성자가 미리 만들어져 있음을 확인할 수 있습니다.
C# 기본 생성자
namespace MyButton
{
    public class MyButton : Control
    {
        public MyButton()
        {

            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("MyButton.MyButton.xaml");
            this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());
        }
    }
}

이 코드는 MyButton 클래스의 어셈블리로부터 XAML 파일을 리소스 스트림으로 읽어와서 그 스트림으로부터 초기화를 수행합니다. 이를 위해 MyButton.xaml 파일의 속성을 보면 build action이 embedded resource로 설정되어 있을 것입니다.

이 특수한 형태의 초기화는 Control 클래스의 InitializeFromXaml 메소드를 통해 구현됩니다.

미리 생성된 코드에서 InitializeFromXaml 메소드를 호출만 하지만 이 메소드는 XAML 스트림을 읽어서 초기화한 후 이 XAML의 루트 개체에 대한 참조를 반환합니다. 이 참조를 클래스의 지역 변수로 저장하여 UI 개체를 코드로 제어할 수 있습니다.

또한 루트 개체에 대한 참조를 얻으면 XAML에서 x:Name을 지정한 개체에 대한 참조를 FindName 메소드를 사용하여 얻을 수 있고 이런 식으로 XAML내의 모든 개체를 코드에서 제어할 수 있습니다.
Howto:3-3 XAML에서 디자인된 개체의 참조를 얻기
1. MyButton.xaml.cs를 엽니다.
2. 클래스의 private 지역 변수로 FrameworkElement 타입의 implementationRoot를 선언하고 InitializeFromXaml의 반환값을 implementationRoot에 대입합니다.
3. TextBlock에 대한 참조를 보유하기 위해 클래스의 private 지역 변수로 TextBlock 타입의 text를 선언하고 implementationRoot.FindName("text") 메소드를 사용하여 참조를 얻습니다. 이렇게 완성된 코드는 다음과 같습니다.

C# 개체 참조를 보유하기 위한 코드 수정
namespace MyButton
{
    public class MyButton : Control
    {
        private FrameworkElement implementationRoot;
        private TextBlock text;

        public MyButton()
        {

            System.IO.Stream s = this.GetType().Assembly.GetManifestResourceStream("MyButton.MyButton.xaml");
            implementationRoot =
this.InitializeFromXaml(new System.IO.StreamReader(s).ReadToEnd());

            text = implementationRoot.FindName("text") as TextBlock;
        }
    }
}

속성 및 이벤트 추가하기
컨트롤에서 가장 중요한 것은 적절한 속성(Property)과 이벤트(Event)를 정의하는 것입니다.
속성과 이벤트는 이 컨트롤을 사용하는 마크업에서 어트리뷰트의 형식으로 선언하고 설정할 수 있으며 이 컨트롤 개체에 대한 참조를 획득한 코드에서도 접근할 수 있습니다. 앞서 얘기한 것처럼 여기에서는 캡션(Caption), 너비(Width), 높이(Height) 속성과 Click 이벤트를 추가하도록 하겠습니다.

Control 클래스는 FrameworkElement를 상속하고 있으며 기본적인 너비(Width), 높이(Height), 클리핑 영역(Clip), 커서(Cursor), 불투명도(Opacity) 등 많은 기본적인 속성을 그대로 받아 옵니다. 따라서 우리가 추가하려는 속성인 너비와 높이는 FrameworkElement에서 이미 선언이 되어 있으며 이 속성은 virtual이 아니므로 오버라이딩이 불가능하고 만약 똑같이 Width라는 이름의 속성을 만든다면 컴파일러는 이 속성이 FrameworkElement.Width 속성에 의해 숨겨질 것이라는 경고를 내릴 것입니다.

이렇게 부모 클래스에서 미리 선언된 일반적인 이름의 속성을 재정의 하고 싶을 때에는 속성의 선언에 new 키워드를 추가해야 합니다.
Howto:3-4 속성 추가하기
1. Width 속성을 추가합니다. Width는 implementationRoot 즉, 컨테이너 역할을 하는 루트 캔버스 개체의 너비와 싱크되며 이 값을 수정하면 텍스트의 위치를 가운데 정렬하기 위해 레이아웃 계산을 다시 수행합니다. Width라는 이름은 이 컨트롤이 상속하는 FrameworkElement의 Width와 중복되어 이 속성이 숨겨지므로 new 키워드를 사용하여 재정의 하고 이 속성을 다른 클래스로 파생할 수 있도록 virtual 키워드도 추가하였습니다.
C# Width 속성 정의(클래스에 추가할 것)
public virtual new double Width
{
    get { return implementationRoot.Width; }
    set
    {
        implementationRoot.Width = value;
        UpdateLayout();
    }
}

UpdateLayout 메소드는 텍스트의 위치를 중앙 정렬하는 기능을 수행하며 일반적으로 컨트롤의 속성이 변경되어 레이아웃이나 화면 출력을 변경할 필요가 있을 때 내부적으로 사용됩니다. UpdateLayout 메소드의 정의는 아래에 있습니다.

2. Width와 마찬가지로 Height 속성을 추가합니다.
C# Height 속성 정의(클래스에 추가할 것)
public virtual new double Height
{
    get { return implementationRoot.Height; }
    set
    {
        implementationRoot.Height= value;
        UpdateLayout();
    }
}

3. 텍스트 내용을 나타내는 Caption 속성을 추가합니다. Caption은 새로 정의하는 속성이므로 일반적인 속성 선언과 같이 하면 됩니다.
C# Caption 속성 정의(클래스에 추가할 것)
public string Caption
{
    get { return text.Text; }
    set
    {
        text.Text = value;
        UpdateLayout();
    }
}

4. 속성이 변경될 때 텍스트 위치 계산을 위한 UpdateLayout 메소드를 구현합니다. UpdateLayout은 내부적으로만 사용되므로 private으로 선언합니다. TextBlock은 현재 자신의 텍스트 내용에 해당하는 너비와 높이를 반환하는 ActualWidth와 ActualHeight이라는 편리한 속성을 제공합니다.
C# UpdateLayout 메소드 정의(클래스에 추가할 것)
private void UpdateLayout()
{
    text.SetValue(Canvas.LeftProperty, (implementationRoot.Width - text.ActualWidth) / 2);
    text.SetValue(Canvas.TopProperty, (implementationRoot.Height - text.ActualHeight) / 2);
}

이제 이벤트를 추가합니다.
우리가 추가할 이벤트는 버튼 클릭이 완료된 시점입니다. 클릭 동작은 마우스 버튼이 눌렸다가(down) 뗀(up) 그 순간을 의미하며 이런 이벤트는 별도로 정의되어 있지 않습니다.

주의해야 할 점은 버튼 위에서 마우스 버튼을 눌렀다가 떼지 않고 버튼 바깥으로 이동할 수도 있다는 겁니다. 따라서 클릭 이벤트를 구현하기 위해서는 마우스 버튼이 눌린 상태를 기억하는 변수를 하나 만들고 버튼 바깥으로 마우스가 나간 경우 그 상태를 클리어 해줄 필요가 있습니다.
Howto:3-5 이벤트 핸들링 및 외부로 노출할 이벤트 정의
1. 마우스의 눌린 상태를 기억하기 위한 멤버 변수를 클래스의 private으로 선언합니다.
C# 마우스 버튼 상태 멤버 변수 추가(클래스에 추가할 것)
private bool mouseDowned;

2. 외부로 노출할 이벤트를 선언합니다. 이벤트는 delegate를 정의하여 전달할 파라미터의 타입을 명시해야 하는데 우리가 추가할 Click 이벤트는 특별히 전달해야 할 파라미터가 없으므로 미리 선언된 delegate인 EventHandler를 사용하면 됩니다. 클래스에 다음의 이벤트 선언을 추가합니다. 만약 사용자가 특정한 파라미터를 전달하고 싶다면 EventArgs 클래스를 상속하는 파라미터 전달용 클래스를 정의하고 delegate를 따로 선언해야 합니다.
 
C# 이벤트 선언 추가(클래스에 추가할 것)
public event EventHandler Clicked;

3. 루트 개체의 마우스 이벤트를 처리할 이벤트 핸들러를 추가합니다. 여기에서 필요한 이벤트는 MouseLeftButtonDown, MouseLeftButtonUp 그리고 MouseLeave 입니다. MyButton 생성자 코드에 다음 코드를 추가합니다.
C# 이벤트 핸들러 추가(생성자에 추가할 것)
implementationRoot.MouseLeftButtonDown += new MouseEventHandler(implementationRoot_MouseLeftButtonDown);
implementationRoot.MouseLeftButtonUp += new MouseEventHandler(implementationRoot_MouseLeftButtonUp);
implementationRoot.MouseLeave += new EventHandler(implementationRoot_MouseLeave);

기본적으로 이벤트 핸들러는 코드 자동완성 기능을 사용하는게 편리합니다. 예를 들어 MouseLeftButtonDown 이벤트는 MouseLeftButtonDown += 까지 입력하고 탭을 두번 누르면 골격 코드가 자동으로 생성됩니다.

4. 자동으로 생성된 코드를 수정하여 마우스 버튼이 눌렸을 때 mouseDowned를 true로 설정하고 마우스 버튼이 들렸을 때 mouseDowned가 true일 경우 Clicked 이벤트를 발생시키고 마우스 커서가 빠져나갔을 때 mouseDowned를 false로 설정합니다.
C# 이벤트 핸들러 구현
void implementationRoot_MouseLeftButtonDown(object sender, MouseEventArgs e)
{
    mouseDowned = true;
}

void implementationRoot_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
    if (mouseDowned == true && Clicked != null)
    {
        Clicked(this, null);
        mouseDowned = false;
    }
}

void implementationRoot_MouseLeave(object sender, EventArgs e)
{
    mouseDowned = false;
}

주의할 점은 Clicked 이벤트가 null인지 여부를 반드시 확인해야 합니다. 또 여기서 Clicked 이벤트의 EventArgs를 null로 전달하였기 때문에 사용하는 쪽에서 이 파라미터를 null 체크 없이 접근할 경우 억세스 위반이 일어날 것입니다.

여기까지 작성된 소스코드 및 프로젝트를 첨부하였습니다. 직접 작성한 것과 비교해보세요 :)
MyButton1.zip

첫번째 예제



컨트롤 테스트 하기

테스트를 위한 Silverlight 페이지 프로젝트 추가
작성된 컨트롤은 라이브러리일 뿐 실제 UI가 아닙니다. 이 컨트롤을 테스트하려면 실제로 이 컨트롤을 Silverlight 페이지에 로드하여 표시해야 합니다.

Howto:3-6 테스트 프로젝트 추가하기
1. MyButton 프로젝트를 그대로 열어놓은 상태에서 메뉴의 File->Add->New Project를 선택하고 Silverlight Project 템플릿을 선택한 후 이름을 MyButtonTester라고 설정하고 [OK]를 누릅니다. 솔루션 익스플로러에서 다음과 같은 모습을 확인할 수 있습니다.


2. 메뉴의 Project->Add Reference를 선택하거나 솔루션 익스플로러에서 MyButtonTester 프로젝트의 Reference 항목에서 마우스 오른쪽 버튼을 클릭한 후 Add Reference를 선택합니다. 참조 추가 창에서 [Project] 탭을 보면 작성된 MyButton 프로젝트 항목이 있을 것입니다. MyButton을 선택하고 [OK]를 누릅니다.


3. 솔루션 익스플로러의 MyButtonTester 프로젝트에서 마우스 오른쪽 버튼을 클릭한 후 Set as Startup Project를 선택하고 TestPage.html에서 마우스 오른쪽 버튼을 클릭한 후 Set as Startup Page를 선택하여 테스트 프로젝트로 디버깅 할 수 있게 합니다.

4. 테스트할 페이지 즉, Page.xaml의 루트 엘리먼트에 사용할 컨트롤의 네임스페이스를 선언하고 어셈블리를 지정합니다.
XAML 네임스페이스 선언 및 어셈블리 지정
<Canvas x:Name="parentCanvas"
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Loaded="Page_Loaded"
    x:Class="MyButtonTester.Page;assembly=ClientBin/MyButtonTester.dll"
    xmlns:my="clr-namespace:MyButton;assembly=ClientBin/MyButton.dll"
    Width="640"
    Height="480"
    Background="White">
</Canvas>

외부 라이브러리를 사용할 때에는 위와 같이 루트 엘리먼트에서 "xmlns:이름" 구문으로 네임스페이스를 지정하고 clr-namespace를 "컨트롤의 네임스페이스명" 으로 지정하며 assembly를 라이브러리 파일(.dll)의 경로로 지정해주면 됩니다.

5. 루트 캔버스에 컨트롤을 추가합니다.
XAML 컨트롤 추가
<Canvas ... 생략 ...>
    <my:MyButton x:Name="testButton" Width="100" Height="50" Caption="button test" />

</Canvas>

6. 파일을 저장하고 Clicked 이벤트를 처리하기 위해 코드-비하인드에 Clicked 이벤트 핸들러를 추가합니다. 앞서서 x:Name으로 testButton을 지정했으므로 코드-비하인드에서는 자동 생성된 코드에 의해 같은 이름으로 개체를 참조할 수 있습니다.
C# MyButton의 Clicked 이벤트 핸들러 추가
public void Page_Loaded(object o, EventArgs e)
{
    // Required to initialize variables
    InitializeComponent();
    testButton.Clicked += new EventHandler(testButton_Clicked);
}

void testButton_Clicked(object sender, EventArgs e)
{
    testButton.Caption = "Clicked!";
}

7. F6을 눌러 빌드하고 F5를 눌러 디버그 모드로 실행하여 클릭했을 때 글자가 바뀌는지 확인합니다.


여기까지 진행해보면 분명히 클릭 이벤트는 잘 작동하지만 우리가 의도한대로 텍스트가 버튼의 한가운데로 정렬되지 않았음을 알 수 있습니다. 왜냐하면 마크업에서 MyButton의 어트리뷰트에 Width=100 Height=50을 지정하였는데 이대로라면 텍스트가 해당 위치를 벗어나지 않는 다는 것을 의미합니다. 그런데 출력된 텍스트는 한참 멀리 떨어져 있고 자세히 보면 640, 480의 한가운데로 정렬되어 있음을 알 수 있습니다.

이 문제는 Silverlight의 버그인지 사용 방법상의 차이인지 확실치 않습니다만 다음 절에서 얘기할 약간의 추가 작업을 통해 수정할 수 있습니다.
우선 여기까지의 프로젝트를 첨부하였습니다.
MyButton2.zip

두번째 예제



특수한 문제점과 해결

Silverlight 페이지 즉, MyButtonTester에서 MyButton을 사용할 때의 모습을 간략하게 도식화하면 다음과 같습니다.


MyButtonTester의 XAML코드에서 MyButton을 하나 선언하였고 여기에서 Width, Height, Caption속성을 설정(set)하였습니다. 따라서 이 프로젝트를 디버깅 모드로 실행했을 때 MyButton의 Width, Height, Caption 속성의 'set' 부분의 코드가 실행될 것을 기대할 수 있습니다.
MyButton.xaml.cs에서 Width, Height, Caption 속성의 'set' 코드에 F9를 눌러 브레이크 포인트를 각각 설정하고 F5를 눌러 디버깅 모드로 실행해보시기 바랍니다.

이상하게도 Caption 속성에는 브레이크가 걸리지만 Width와 Height에는 걸리지 않는 걸 확인할 수 있습니다.
혹시 Width와 Height이 정상적으로 작동하지 않은지 확인하기 위해 MyButtonTester의 코드-비하인드에서 코드로 직접 Width와 Height을 설정해보면 그 코드에 대해서는 브레이크가 작동하며 코드 자체는 정상적이라는 걸 알 수 있습니다.

이 문제는 Control에서 상속받은 모든 기본 속성에서 공통적으로 나타납니다. 따라서 Control 및 그 상위 클래스들에서 상속받는 속성과 같은 이름의 속성을 재정의했을 때에는 이들 속성에 대한 초기화를 별도로 해줄 필요가 있습니다.

Howto:3-7 상위 클래스에서 상속받은 속성과 같은 이름을 가지는 속성을 재정의 할 때의 추가 작업
1. MyButton 클래스의 생성자에 컨트롤이 로드 완료되었을 때 처리할 이벤트 핸들러를 추가합니다. 마찬가지로 코드 자동 생성기능을 활용합니다.
C# 컨트롤 로드 완료시 처리 이벤트 핸들러 추가
public MyButton()
{
    // ... 생략
    implementationRoot.Loaded += new EventHandler(implementationRoot_Loaded);
}

2. 추가된 이벤트 핸들러에서 상위 클래스(base)와 중복되는 이름의 속성을 하나씩 가져옵니다.
C# 이벤트 핸들러 작성
// 클라이언트 측에서 이 컨트롤의 로드를 완료하였을 때 처리
void implementationRoot_Loaded(object sender, EventArgs e)
{
    // 로드가 완료되었을 때 상위 클래스의 속성으로부터 미완료된 속성을 수작업으로 설정해야 함
    implementationRoot.Width = base.Width;
    implementationRoot.Height = base.Height;
    UpdateLayout();
}

3. F6을 눌러 빌드하고 F5를 눌러 다시 실행하여 정상적으로 Width와 Height 속성이 적용되었는지 확인합니다.

여기까지 완성된 프로젝트입니다.
MyButton3.zip

완성된 컨트롤 프로젝트


이 외에도 커스텀으로 작성한 컨트롤을 올린 페이지는 Expression Blend를 사용할 수 없게 되며 VS의 IDE에서도 XAML 코드의 인텔리센스를 지원하지 않는 등 외부적인 문제도 아직 남아있습니다. 하지만 이 문제는 아마도 정식판이 나오면서 해결되지 않을까 기대를 해봅니다.

참고

Silverlight 공식 QuickStarts 참고:
http://silverlight.net/QuickStarts/BuildUi/CustomControl.aspx

Silverlight 샘플 컨트롤 참고: Silverlight 1.1 SDK에서 SilverlightControls 폴더를 참고합니다.

개발자들이 공개하는 커스텀 컨트롤:
http://blogs.msdn.com/devdave/archive/2007/05/17/silverlight-1-1-alpha-layout-system-and-controls-framework.aspx (Grid, StackPanel 등의 컨테이너 샘플)
신고
Posted by gongdo
Silverlight 1.1 바로 시작하기

Silverlight 개발의 기초
준비 사항

Silverlight 개발 환경
먼저 개발에 필요한 환경을 갖춰야겠죠?
현재 Silverlight은 자바스크립트만으로 컨트롤이 가능한 1.0 beta 버전과 매니지드 코드 즉, CLR환경과 통합되는 1.1 alpha 버전으로 나누어져 있습니다. 여기에서는 1.1 alpha를 기준하겠습니다.

필요한 모든 파일은 http://msdn2.microsoft.com/en-us/asp.net/bb187452.aspx 여기에서 다운받을 수 있어요.

먼저 실제 개발에 필요한 툴. 다음으로 디자인에 필요한 툴. 마지막으로 Silverlight SDK인데요, QuickStarts에 있는 내용과 샘플이 대부분입니다. 개발시 최소한 모든 개발 툴과 Expression Blend는 있어야 편합니다.
그 외에 illustrator나 플래쉬 SWF파일을 XAML로 변환해주는 툴들도 있으니 참고하세요.

Silverlight를 시작하기 전에
먼저 Silverlight가 도대체 뭐 하는 넘인가부터 알아야겠죠? 하지만 이 주제는 너무나 일반적이고 다른 곳에서 더 잘 정리되어 있으니 굳이 여기에서 다시 얘기하지 않는 게 좋을 것 같습니다.

Silverlight 개발을 시작하기 전에 먼저 아래의 내용들을 둘러보세요.
  • Silverlight 및 XAML에 대한 전반적인 소개
    http://msdn.microsoft.com/msdnmag/issues/07/06/Silverlight/Default.aspx?loc=ko
    여기에서 Silverlight와 XAML에 대한 소개 부분만 읽어보시면 돼요.
  • WPF 및 XAML 다루는 예제
    http://hoons.kr, http://visumuri.net 같은 커뮤니티의 WPF 강좌에서 XAML을 다루는 방법을 익혀두시는 게 좋아요. Silverlight에서 지원하는 엘리먼트가 적긴 하지만 기본적인 사용방법은 크게 차이 나지 않으니 도움이 될 거에요.
  • Silverlight 공식 QuickStarts 페이지
    http://silverlight.net/quickstarts/managed.aspx
    이 글의 기반이 되는 공식 페이지입니다. 미리 얘기 드리자면 이 글은 이 페이지의 내용을 기반으로 제가 나름대로 편집하고 추가한 거에요. 대부분 HowTo형식으로 작성되어 있으니 따라 하는데 별다른 어려움은 없으니 이 페이지를 닫고 이걸로 공부하시는 것도 좋은 방법이죠.
  • Silverlight MSDN 페이지
    http://msdn2.microsoft.com/en-us/library/bb188743.aspx
    MS 기술 개발자라면 자연스럽게 사용해야 할 MSDN입니다. 아직 Silverlight 1.1(with .NET)은 개체 레퍼런스가 나와있지 않은데요 기본적으로 Javascript 버전과 개체의 이름과 내용은 거의 동일하므로 Javascript 버전을 참고해야 할거에요.
사전 지식
Silverlight는 워낙 고수준의 추상화가 잘 되어 있는데다가 Visual Studio의 막강한 IDE덕분에 쉽게 접근할 수 있다고 생각해요.
하지만 기본적으로 HTML/Javascript/XML에 대해 개념 정도는 파악하고 있어야겠고요, 매니지드 코드를 다루기 위해 C#(혹은 VB)를 어느 정도는 다룰 수 있어야 해요. 이 글에서는 말 그대로 곧바로 시작하기 위해 이런 기반 기술에 대한 설명은 최대한 줄일 생각이에요.

Silverlight 1.1 프로젝트 구성

기본 프로젝트 템플릿 구성
앞서 말한 개발 환경을 갖추고 Visual Studio codename "Orcas"(이하 Orcas)를 실행하고 새 프로젝트를 선택해보면 Silverlight 프로젝트 템플릿이 보입니다. 현재 Silverlight 1.1에서 지원하는 CLR은 C#과 VB.NET이 있는데요, 앞으로의 모든 내용은 C#을 기준으로 합니다. 긴말 할 필요 없이 만들어 볼까요?

Howto:1-1. 새 Silverlight 프로젝트 만들기
1. Orcas를 실행합니다.
2. 메뉴에서 File->New->Project를 선택하거나 Ctrl+Shift+N를 눌러 새 프로젝트를 선택합니다.
3. 새 프로젝트 다이얼로그 박스에서 Project types를 Visual C#->Silverlight로 선택하고 Templates에서 Silverlight Project를 선택한 후 [OK]를 누릅니다.
사용자 삽입 이미지


프로젝트를 생성한 후 우측 상단에 있는 솔루션 익스플로러를 보면 다음과 같은 파일로 구성되어 있습니다.
 
  • TestPage.html
    Silverlight 애플리케이션을 테스트하기 위한 HTML 파일로 브라우저에서 최초로 실행되는 엔트리 포인트의 역할을 합니다. 일반적으로 Silverlight 페이지를 호스팅할 HTML 파일은 Silverlight.js와 Silverlight 페이지를 생성하는 자바 스크립트 파일을 포함하며 Silverlight 페이지는 보통 임의의 DIV 태그 안에 호스팅됩니다.
  • TestPage.html.js
    Silverlight 페이지를 생성하기 위한 createSilverlight 메소드를 제공하는 자바 스크립트 파일입니다. 내부적으로 Silverlight.js에서 제공하는 createObject 혹은 createObjectEx 메소드를 사용하여 지정된 XAML 파일 및 설정으로 Silverlight 페이지를 생성합니다. 이 메소드의 내용은 애플리케이션마다 변경될 수 있으므로 각 Silverlight 애플리케이션마다 고유한 스크립트 파일을 생성합니다.
  • Silverlight.js
    Silverlight 컨트롤을 생성하여 HTML 페이지로 호스팅하기 위한 자바 스크립트 파일입니다. 이 파일은 모든 프로젝트에서 공통으로 사용되므로 사이트의 특정 위치에 놓고 include 하여 사용하는 게 좋습니다.
  • Page.xaml
    createSilverlight 메소드 호출을 통해 HTML 페이지에 호스팅될 Silverlight 컨텐츠입니다. 이 마크업의 루트 엘리먼트는 반드시 xmlns 어트리뷰트로 올바른 네임스페이스를 지정해줘야 합니다. 일반적으로 Silverlight 페이지의 루트 엘리먼트는 다른 개체를 담을 수 있는 Canvas를 사용합니다. Silverlight 애플리케이션이 매니지드 코드를 사용하므로 루트 엘리먼트는 반드시 x:Class 어트리뷰트를 포함해야 합니다. 나중에 설명하겠지만 x:Class 어트리뷰트는 코드-비하인드에서 사용될 클래스 명을 식별하는데 사용됩니다.
Silverlight 1.1은 매니지드 코드를 사용하며 위의 XAML 파일을 핸들링하는 코드-비하인드 파일을 포함합니다.
  • Page.xaml.cs (또는 .vb)
    이 파일은 런타임에 이벤트를 핸들링하는 매니지드 코드를 제공하며 반드시 Page.xaml에서 정의된 x:Class와 동일한 이름의 partial 클래스를 정의해야 합니다. 이 매니지드 코드는 외부로 배포할 필요가 없으며 마크업에서 정의된 assembly만 배포하면 됩니다.
페이지 추가하기
필요에 따라 한 HTML 페이지 내에 여러 개의 Silverlight 컨트롤을 호스팅하거나 호스팅된 Silverlight 페이지내에서 다른 Silverlight 페이지로 내비게이팅할 수 있을 것입니다.
Howto:1-2. 새 Silverlight 페이지 추가하기
1. 메뉴에서 Project->Add New Item을 선택하거나 Ctrl+Shift+A를 눌러 새 아이템을 선택합니다.
2. 새 아이템 다이얼로그 박스에서 Silverlight Page를 선택하고 [OK]를 누릅니다.
사용자 삽입 이미지

추가된 Silverlight 페이지는 마찬가지로 마크업과 코드-비하인드로 구성되어 있으며 마크업의 루트 엘리먼트에서 정의한 x:Class 어트리뷰트와 동일한 이름의 클래스가 코드-비하인드에 정의되어 있습니다.

이렇게 추가된 Silverlight 페이지는 하나의 HTML에서 관리할 수도 있지만 다른 HTML 페이지를 생성하여 HTML레벨에서 내비게이팅할 수도 있습니다. 만약 HTML 페이지를 임의로 추가한다면 템플릿이 만들어준 .html과 .js처럼 적절한 코드를 사용하여 Silverlight 페이지를 로드해야 하겠죠.

Hello Silverlight!

시작부터 동영상 플레이어!?
보통 어떤 언어를 시작할 땐 항상 Hello, world 류의 텍스트 출력을 해보기 마련인데요, Silverlight이 얼마나 직관적으로 코드를 작성할 수 있는지 보여드리기 위해 곧바로 간단한 웹 동영상 플레이어를 만들어 보겠습니다. 흔히 백문이 불여일타라는 말을 많이 쓰지만 Silverlight은 정말로 그 효과가 크지요. 지금 바로 해보세요!

Howto:1-3. 간단한 웹 동영상 플레이어 예제
1. 새 Silverlight 프로젝트 생성합니다.
2. 아래의 코드를 각각 입력합니다. (Page.xaml과 Page.xaml.cs)
3. Ctrl+Shift+B 또는 F6을 눌러 빌드하고 F5를 눌러 실행합니다.

Code:Page.xaml
< Canvas x:Name="parentCanvas"
   
xmlns="http://schemas.microsoft.com/client/2007"
   
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   
Loaded="Page_Loaded"
   
x:Class="SilverlightProject1.Page;assembly=ClientBin/SilverlightProject1.dll"
   
Width="800"
   
Height="600"
   
Background="White"
   
>
    <
TextBlock Text="Hello Silverlight!" FontSize="30" />
    <
MediaElement x:Name="moviePlayer" Width="800" Height="600" Source="Silverlight.wmv" AutoPlay="False" MediaEnded="OnMediaEnded" />
    <
TextBlock Canvas.Left="0" Canvas.Top="620" Text="Play" MouseLeftButtonDown="OnPlay" />
    <
TextBlock Canvas.Left="50" Canvas.Top="620" Text="Stop" MouseLeftButtonDown="OnStop" />
    <
TextBlock Canvas.Left="100" Canvas.Top="620" Text="Pause" MouseLeftButtonDown="OnPause" />
</
Canvas>

Code:Page.xaml.cs (코드 길이상 using 구문은 표시하지 않았습니다.)
namespace SilverlightProject1
{
    public partial class Page : Canvas
   
{
   
    public void Page_Loaded(object o, EventArgs e)
        {
           
// Required to initialize variables
           
InitializeComponent();
        }

       
void OnMediaEnded(object sender, EventArgs e)
        {
           
// 미디어 플레이가 끝났을 때 Stop을 눌러 처음부터 재생할 수 있도록 합니다.
           
moviePlayer.Stop();
        }

       
void OnPlay(object sender, MouseEventArgs e)
        {
            moviePlayer.Play();
        }

       
void OnStop(object sender, MouseEventArgs e)
        {
            moviePlayer.Stop();
        }

       
void OnPause(object sender, MouseEventArgs e)
        {
            moviePlayer.Pause();
        }
    }
}

주요 코드의 구성
상세한 코드를 보기 전에 마크업인 XAML과 코드-비하인드인 CS 코드 파일의 관계를 살펴보죠. XAML은 HTML과 같이 구성 요소 즉, 엘리먼트 들을 태그로 표현하고 태그 내의 어트리뷰트로 구성 요소들의 속성을 나타냅니다. 템플릿 구성에서 언급했던 것처럼 마크업의 루트 엘리먼트(여기에서는 Canvas)는 반드시 x:Class 어트리뷰트를 포함해야 하며, 코드-비하인드에서 바인딩할 클래스와 네임스페이스 및 어셈블리를 정의합니다.

코드-비하인드를 살펴보면 x:Class 어트리뷰트에서 정의한대로 SilverlightProject1 이란 네임스페이스내에 Page 클래스를 정의하고 있습니다. x:Class 어트리뷰트의 정의 중에 assembly=ClientBin/SilverlightProject1.dll 이란 구문으로 어셈블리 경로를 지정하는데요, 여기서 지정된 어셈블리는 코드-비하인드가 컴파일 된 결과물입니다. 이것은 웹 사이트에 소스 코드를 배포하지 않고 오직 컴파일된 바이너리 파일만 있으면 된다는 걸 말합니다. 즉, 마크업에는 일반적인 UI 요소를 구성하고 공개되지만 내부적인 비지니스 로직은 코드-비하인드로 말 그대로 숨길 수 있다는 것을 의미합니다. 이 부분에 대해서는 아래에서 다시 얘기하겠습니다.

상세한 코드를 보죠.
루트 엘리먼트인 Canvas는 일종의 컨테이너로 다른 엘리먼트들을 포함합니다. Canvas내에는 TextBlock 엘리먼트를 사용하여 화면에 표시할 문자열을 지정하였고 MediaElement 엘리먼트를 사용해 동영상을 재생할 영역을 지정하였습니다. Orcas의 IDE에서 이 코드들을 입력할 경우 현재 상황에서 입력이 가능한 요소들만 인텔리센스로 표시되기 때문에 매우 경쾌하게 코딩할 수 있습니다.

각 엘리먼트의 어트리뷰트들도 매우 직관적인 이름을 가지고 있어서 이 예제에서 사용된 것들은 굳이 설명이 필요 없을 정도입니다. 몇 가지 중요한 어트리뷰트들만 짚어보죠.

모든 엘리먼트에 공통적으로 사용가능한 x:Name은 코드-비하인드에서 직접 사용할 수 있는 개체의 이름을 정의합니다. 엘리먼트의 이름을 지정함으로써 x:Class에서 정의한 클래스 내에서 멤버 변수처럼 사용할 수 있습니다. 다음 그림을 보면 마크업에서 정의한 x:Name으로 코드-비하인드에서 마치 멤버 변수인 것처럼 심지어 인텔리센스도 지원한다는 걸 확인할 수 있습니다.


어트리뷰트로 이벤트 핸들러를 지정할 수도 있습니다. 예제에서 MediaElement의 MediaEnded와 TextBlock의 MouseLeftButtonDown이 바로 해당 이벤트에 대한 핸들러의 이름을 지정하고 있습니다. 이벤트 역시 매우 직관적인 이름인데요, MediaEnded는 미디어 재생이 끝났을 때를 의미하고 MouseLeftButtonDown은 마우스 왼쪽 버튼이 눌렸을 때를 말한다는 걸 설명할 필요는 없을 겁니다.

이렇게 마크업에서 지정한 이벤트 핸들러는 코드-비하인드에서 반드시 구현해야 합니다. Silverlight에서 모든 엘리먼트의 이벤트는 두개의 파라미터로 전달되는데 여기에 대한 설명은 따로 좀 더 상세하게 알아보겠습니다.

우선 예제를 살펴보면 어트리뷰트에서 지정한 이름과 일치하는 멤버 함수들을 볼 수 있습니다. 여기에서 해당 이벤트에 대한 비지니스 로직 코드를 구현하면 되는 거죠. 각 코드들 역시 따로 주석이 필요치 않을 정도로 간단하고 직관적입니다.
주의
MediaElement는 현재 동영상으로 WMV 포맷과 음악으로 MP3, WAV 포맷만을 지원합니다. 따라서 서비스 전에 필요한 파일을 인코딩할 필요가 있는데요, Microsoft는 이러한 인코딩을 위한 툴로 Expression MediaEncoder를 내놓았습니다. 현재 Preview 상태로 평가판을 180일간 사용할 수 있으니 미리 테스트 해보시는 것도 좋을 것 같네요. 이 외에도 전문적인 인코딩을 위한 서버 제품들도 준비되어 있는데 http://uxkorea.net/blog_post_54.aspx 이곳을 참고하시기 바랍니다.



자동으로 생성되는 코드
아마 예리하신 분이라면 Page 클래스 첫줄에 있는 Page_Loaded 멤버 함수와 InitializeComponent 메소드 호출을 눈여겨 봤을 겁니다. Page_Loaded는 Silverlight 페이지가 HTML에 로드될 때 발생하는 이벤트 핸들러로써 Silverlight.js에 의해 호출됩니다. 여기에서 이 클래스의 멤버 변수 등을 초기화할 수 있습니다.

그런데, InitializeComponent 메소드는 어떤 역할일까요? 결론부터 말하자면 말 그대로 XAML 페이지에 있는 컴포넌트들을 초기화하는 메소드입니다. 그럼 이 메소드는 어디에서 정의되어 있을까요? 분명히 이 메소드는 프로젝트 내의 어디에도 보이지 않습니다. 혹시나 하고 부모 클래스인 Canvas 클래스를 뒤져봐도 이런 메소드는 정의되어있지 않습니다. 바로 이 메소드는 컴파일러가 자동으로 생성해주는 코드에서 구현되고 있습니다. 컴파일러는 마크업의 x:Class로 정의된 엘리먼트를 코드-비하인드에서 사용될 개체로 자동으로 생성하여 개발자의 귀찮은 코딩을 덜어주는 거죠. 이 코드는 obj\Release(또는 Debug) 폴더아래에 파일명.g.cs(또는 .vb)라는 이름으로 자동으로 생성됩니다. 이것은 XAML과 매니지드 코드와의 컴파일 과정에서 일어나는 일인데 전에 포스팅한 http://gongdo.tistory.com/74을 참고하세요. 이 글에서는 WPF의 컴파일에 대해 언급하지만 WPF와 Silverlight는 기본적인 구동 방식이 같으므로 이해하는데 문제가 없을 거라 생각합니다.

배포하기

Silverlight는 웹 기반 기술입니다. 물론 테스트는 로컬에서 하겠지만 제대로 하려면 웹서버 등을 갖춰야 하죠. 어쨌든 웹 상에서 작성한 Silverlight 애플리케이션의 배포는 단순히 .xaml.cs 등의 매니지드 코드로 작성한 소스 코드는 제외하고 컴파일된 어셈블리만 포함하면 됩니다. 위의 예제의 경우 예를 들어 http://gongdosoft.com/example 이란 URL로 배포하고 싶다면 다음과 같은 구조를 갖는게 일반적일 것입니다.
Silverlight 배포 예제
+WebRoot


+Include


-Silverlight.js


+Example


+ClientBin


-SilverlightProject1.dll


-Page.xaml


-TestPage.html


-TestPage.html.js


-Silverlight인트로.wmv



예제 코드에서는 Silverlight.js를 모든 다른 파일과 같은 장소에 둔 걸로 가정하였지만 위의 배포 예제에서는 좀 더 일반적인 방법으로 Silverlight.js를 최상위 Include 경로로 옮겨봤습니다. 물론 코드는 약간 수정을 해야겠죠.

중요한 것은 중요한 비지니스 로직이 포함된 .cs 파일 즉, 코드-비하인드 파일은 포함하지 말고 컴파일된 어셈블리만을 포함하는 것입니다. 코드-비하인드는 말 그대로 숨겨두세요.

참고

개체 브라우저
아직 Silverlight 1.1에 대한 MSDN 레퍼런스가 나와있지 않아서 어떤 클래스에 어떤 멤버들이 있는지 한눈에 보기 어렵습니다. 하지만 Silverlight의 개체들은 그 이름이 매우 설명적이기 때문에 개체 브라우저(Object Browser)에서 그 구성도를 보는 것 만으로도 클래스의 역할을 이해하는데 도움이 됩니다.
림}
Howto:1-4. 개체 브라우저 보는 방법
1. 프로젝트를 연 상태에서 메뉴에서 View->Object Browser를 선택하거나 Ctrl+W,J를 누릅니다.
2. 또는 우측의 솔루션 익스플로러의 Reference 항목에서 원하는 어셈블리를 더블 클릭합니다.

레퍼런스 및 정보 찾기
앞서 말한 것처럼 아직 Silverlight 1.1의 레퍼런스가 없으므로 Javascript 버전의 레퍼런스를 참고해야 합니다. 예를 들어 TextBlock에 대한 레퍼런스는 http://msdn2.microsoft.com/en-us/library/bb188394.aspx 에서 확인할 수 있습니다.

비록 영문이고 Javascript 버전이긴 하지만 Silverlight에 대한 기초를 더 공부하고 싶다면 http://msdn2.microsoft.com/en-us/library/bb404770.aspx 여기를 참고하시길 바랍니다.

역시 영문이지만 Silverlight 포럼도 도움이 된....다고 얘기드리고 싶지만 여긴 아직 검색도 안되네요. http://silverlight.net/forums/Default.aspx 네, 검색이 안되니 필요한 정보를 찾기가 좀 어렵습니다. 차라리 구글에서 검색하는게 훨씬 빠를지도 몰라요.

계속해서 영문이지만(...) http://silverlight.net/blogs/silverlightcommunity/ 여기에 개발자들의 포스팅이 트랙백되고 있습니다.

그리고! 바로 http://gongto.tistory.com 에서도 질문과 의견을 환영합니다!

참고로 위의 예제 프로젝트를 첨부했으니 한번 테스트해보세요. 아무리 간단하더라도 남이 만든걸 테스트하는 것도 중요한 것 같아요.
마지막으로 간단하게 만들어본 Silverlight 인트로 영상. 역시 못써먹을 것 같아서 올립니다. -_-


http://gongdo.tistory.com/attachment/cfile23.uf@25763B455878F5540B571D.avi 여기서 다운로드도 가능해요.
신고
Posted by gongdo


티스토리 툴바