298 Results for 'Silverlight'

  1. 2009.02.25 [HOL#06] RenderTransform 기초
  2. 2009.02.25 [HOL#05] Bubbling Events - 이벤트의 라우팅 (2)
  3. 2009.02.25 [HOL#03] VisualTreeHelper (2)
  4. 2009.02.25 실버라이트에서 사용 가능한 crossdomain.xml의 조건 (4)
  5. 2009.02.19 Live Mesh 좀 짱인듯? (4)
  6. 2009.02.19 실버라이트 2.0.40115.0 업데이트[추가] (5)
  7. 2009.02.19 Prism 2.0 for WPF & Silverlight
  8. 2009.02.13 [TIP] 불필요한 이미지 리소스 제거하기 (2)
  9. 2009.02.12 Moonlight 1.0 정식 출시
  10. 2009.02.11 Silverlight Streaming에 동영상 올리기#2
  11. 2009.02.10 Silverlight Streaming에 동영상 올리기#1
  12. 2009.02.10 [HOL#09] MediaElement
  13. 2009.02.10 [HOL#04] Event Handling (2)
  14. 2009.02.10 [HOL#02] Built-in Container(Layout)
  15. 2009.02.10 [HOL#01] Hello Silverlight
  16. 2009.02.05 앗 이럴쑤가! Mixtify 10k 등록 되었어요 >_< (3)
  17. 2009.02.02 My Mixtify 10k entry - Ink 10k (10)
  18. 2009.02.01 [Tip] Blend에서 실행중인지 아닌지 구분하는 방법 (1)
  19. 2009.01.31 환상의 궁합! MVVM+Command Pattern+Dependency Injection #2 (5)
  20. 2009.01.31 환상의 궁합! MVVM+Command Pattern+Dependency Injection #1 (4)
  21. 2009.01.30 라이브러리 문서화의 중요성 (2)
  22. 2009.01.17 오바마 대통령 취임식과 실버라이트 (1)
  23. 2009.01.16 커스텀 컨트롤의 DefaultStyle을 여러 xaml로 나누기
  24. 2009.01.16 Blend 2 SP1에서 인텔리센스를! (6)
  25. 2009.01.06 SBS드라마 가문의 영광 홈페이지 오픈 (1)
  26. 2008.12.11 Why not Silverlight? (3)
  27. 2008.11.20 스토리보드 거꾸로 재생하기 (1)
  28. 2008.11.18 컨텍스트 메뉴에 Rexap 등록하기 (1)
  29. 2008.11.17 실버라이트 3 떡밥 (5)
  30. 2008.11.06 XAP사이즈, 압축하지 않겠는가? (7)
RenderTransform은 모든 UIElement가 가지고 있는 속성으로 해당 오브젝트의 레이아웃(위치, 크기 등의 속성)이 확정된 이후 그 상태에서 확대, 기울기, 회전, 이동 등의 추가적인 변형(transform)을 설정해요. 좀 더 구체적으로 말하자면, 실버라이트 렌더링 엔진은 어떤 UIElement를 화면에 렌더링 할 때 먼저 모든 오브젝트의 레이아웃을 정리하고 그 후 각 오브젝트별로 RenderTransform을 적용하여 최종적으로 화면에 보여주죠.
RenderTransform속성을 수정해도 레이아웃에 영향을 주지 않아서 비용이 많이 들어가는 레이아웃 정리 작업을 수행하지 않으므로 특히 애니메이션 등의 고속 처리에 매우 유용해요.

Posted by gongdo
몇몇 이벤트는 이벤트가 발생한 자식에서 부모 방향으로 VisualTree를 타고 올라가는 성격을 가지고 있죠. 대표적으로 MouseLeftButtonDown과 같은 이벤트인데요, 이런 이벤트를 '버블링'된다 라고 말해요. 마치 버블을 타고 올라가는 것 처럼 말이죠.

바로 이렇게!

여튼 백문이 불여일타! HOL을 보고 직접 느껴보세요.

Posted by gongdo
후우.. 오랫만이네요. 늘 그렇듯이 바쁘죠. 바빠.
아주 짧은 HOL이에요. VisualTree에 대한 간략한 설명과 함께 VisualTreeHelper를 설명하고 있죠.


Posted by gongdo

훈스닷넷의 스터디 모임에서 크로스 도메인 접근에 대한 라이브 데모를 하다가 황당한 상황을 겪었는데요, 올바른 것으로 보이는 crossdomain.xml 파일이 있어도 실버라이트가 접근에 실패하는 경우가 있어서 포스팅합니다. 그나저나 라이브 데모는 항상 자제해야 해요. 역시 –_-;

여러 차례 블로깅했던 것 같지만, 실버라이트에서 크로스 도메인에 있는 리소스에 접근하기 위해서는 해당 서버가 실버라이트를 위한 clientaccesspolicy.xml 또는 플래시에서 사용하는 crossdomain.xml을 제공해야 하죠. MSDN의 도메인 영역을 넘을 수 있는 서비스 만들기 문서에 잘 설명이 되어 있어요.

그러나 실버라이트는 crossdomain.xml이 제공하는 모든 기능을 다 사용할 수는 없고 다만 모든 도메인에 대해 허용하는 설정만을 처리할 수 있죠. 그래서 보통 다음과 같은 설정만을 허용해요.

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="*" secure="true" />
</cross-domain-policy>

여기서 가장 중요한 부분은 allow-access-from 엘리먼트에 domain="*" 설정이 반드시 존재해야 하고 secure 어트리뷰트가 true이어야 한다는 점이죠. secure 어트리뷰트는 옵셔널이고 없을 경우 true이기 때문에 없어도 무방해요. 또한 다음과 같이 다른 allow-access-from 엘리먼트와 함께 있는 것도 상관 없어요. 예를 들어, openapi.naver.com/crossdomain.xml을 보면…

<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="static.campaign.naver.com" secure="false" />
    <allow-access-from domain="flashdev.naver.com" secure="false" />
    <allow-access-from domain="*" secure="true" />
</cross-domain-policy>

allow-access-from 엘리먼트가 여러 개 있는 걸 볼 수있죠. 그렇지만 여전히 domain 어트리뷰트와 secure 어트리뷰트가 적절하게 설정되어 있어서 실버라이트에서 접근할 수 있어요.

문제는, MSDN에서는 정확히 문서화되어 있지 않지만 아무리 allow-access-from 엘리먼트에 domain 어트리뷰트와 secure 어트리뷰트가 적절하게 설정되어 있다고 해도 그 외에 다른 어트리뷰트가 정의되어 있을 경우 해당 설정은 실버라이트에서 접근할 수 없는 것으로 판단해버려요. 예를 들어, gongdosoft.tistory.com/crossdomain.xml을 보면…

<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>

언뜻보면 될 것 같지만 to-ports라는 실버라이트가 인식할 수 없는 어트리뷰트가 붙어 있어서 여기에 대한 요청은 실패해요.

여러 가지로 테스트 해 봤는데 결국 실버라이트가 허용하는 crossdomain.xml의 조건은 다음과 같이 정리할 수 있어요.

  • allow-access-from 엘리먼트에 반드시 domain="*" 어트리뷰트가 있어야 한다.
  • secure 어트리뷰트는 반드시 true이어야 하고 생략해도 된다.
  • 위의 두 어트리뷰트 외에 어떠한 어트리뷰트도 들어가서는 안된다.

아직까지 실버라이트를 위한 clientaccesspolicy.xml을 지원하는 웹사이트는 그리 많지 않아요. 그래서 crossdomain.xml과의 공생은 당분간 필요하죠. 혹시 crossdomain.xml이 있는데도 불구하고 크로스 도메인 접근이 되지 않는다면 위의 조건에 맞는지 점검해 보세요. 그리고 제대로 되어 있지 않다면 해당 서비스 제공 업체에 수정을 요청할 수도 있겠죠. 저도 당장 티스토리에 요청해야겠어요!

Posted by gongdo

아까 간단하게 가입하고 폴더 동기화까지 해봤는데요, 원격제어도 가능하군요! 물론 이 원격제어는 화면을 서로 공유하면서 보는 스타일은 아니고 원격 데스크탑과 같은 기능으로 보이지만 여튼 이런 것 까지 준비되어있을 줄은 몰랐네요 :D

image

Connect하면…

ActiveX 설치하고 원격 데스크탑 연결!
아직 별 다른 기능은 없지만 조만간 더 쓸만하게 될 것 같다는 예감이 들지 않나요? :D

Posted by gongdo

18일, 실버라이트 2의 업데이트가 조용히 릴리즈 되었네요.
실버라이트 SDK가 깔려 있을 경우에는 http://go2.microsoft.com/fwlink/?LinkID=119972 여기에서 받으세요. 최종 사용자용과 다른 점은 디버깅 지원을 위한 몇 가지 어셈블리가 포함되어 있다는 것 외에는 똑같아요.
(최종 사용자용 런타임은 기존과 마찬가지로 http://go.microsoft.com/fwlink/?LinkID=107362 여기)

참고로 GDR은 General Distribution Release의 약자로 말 그대로 일반 배포용 릴리즈를 말하죠.
GDR 1로 표시된 걸로 봐서 GDR 2도 나올 가능성도? ^^

  • McAfee가 스캐닝할 때 실버라이트에서 발생하는 문제 수정.
  • UI Automation 안정성 수정
    • .Net Framework 3.0또는 3.5가 설치되어 있지 않을 때 몇몇 기능을 사용하면 실패하는 문제
    • 태블릿 지원 향상
  • Mac유저가 환경 설정을 커스터마이징하여 Arial과 Verdana 폰트를 제거했을 때 발생하는 문제 수정.
  • 몇몇 언어에서 Isolated Storage의 IncreaseQuotaTo 메서드를 사용할 때 발생하는 문제 수정.

예… 뭐 특별할 건 없고요. 아마 윈도 업데이트 등으로 업데이트가 자동으로 배포될 듯하고 딱히 개발 환경에 영향주는 일은 없겠네요.

저 처럼 괜히 설레이지 마세요 ㅋ

[추가]
밝힐 수 없는 소스에 의하면(ㅋㅋ 이런 말 해보고 싶었어..) 위의 요소 외에도 몇 가지 업데이트가 있었다고 해요.
뭐 그래도 딱히 중요한 요소는 별로 없지만 다른 OS에서의 호환성이나 이런 면이 나아졌다고 하니 업데이트 해보세요.


한 가지 작은 수정 점. 마우스 오른쪽 클릭했을 때 나오는 컨텍스트 메뉴에서 Configuration이 빠졌어요. 한글 윈도에서는 Silverlight 구성으로 나오다가 Silverlight만 표시되죠. 근데 어딘가 썰렁해 보이는 건 좀... -_-


인증샷.ㅋ

Posted by gongdo

[추가]
앗! 아니나 다를까 킹크랩에 잘 소개가 되어 있었군요. 필독!
http://kingcrap.com/entry/Composite-Application-Guidance-for-WPF-June-2008-Release
[수정]
앗! 이건 작년에 나왔던 WPF 전용 버전에 대한 내용이었네요;; 날짜를 잘 못봤습니...
그래도 기본 골격은 같으니 참고;;
--------------------------------------------------------------------------------------

Pattern & Practice 팀에서 다시 한번 유용한 라이브러리를 공개했네요. Prism이란 이름으로 개발되던 라이브러리인데 버전 2.0이 되면서 'Composite Application Guidance for WPF & Silverlight'라는 무시무시한 이름으로 공개를 했네요. 걍 프리즘이라고 계속  부르면 안되나 –_-;

Composite Application Guidance(이하 프리즘 2–_-)는 WPF와 실버라이트 애플리케이션을 보다 모듈화 된 형태로 쉽게 만들기 위해 디자인되었다고 해요. Guidance라는 거창한 이름이 붙은건 이 배포 패키지가 단순히 라이브러리 뿐만 아니라 즉시 실행해 볼 수 있는 핸즈온랩과 XAML 및 디자인 패턴 가이드를 포함하고 있기 때문이에요.

프리즘 2는 사실 엔터프라이즈 비즈니스 영역에 포커스를 주고 있는데요, 많은 패턴이나 라이브러리/프레임워크가 그렇듯이 애플리케이션에 따라 이 라이브러리를 도입했을 때 장점이 클 수도 그렇지 않을 수도 있겠죠. 그러나 만약 비슷한 패턴이 필요로 할 때 이렇게 검증된 라이브러리를 쓰는 것이 직접 개발하는 것에 비해 훨씬 큰 이득을 얻을 수 있을 거에요.

요즘은 너무 바빠서 자세히는 못봤고, 링크 모음:

http://blogs.msdn.com/blaine/archive/2009/02/18/prism-2-0-is-live.aspx

http://msdn.microsoft.com/en-us/library/dd458809.aspx

Posted by gongdo

XAP 사이즈, 압축하지 않겠는가?에서 얘기한 것 처럼 실버라이트 배포에 가장 중요한 점 중 하나는 바로 용량을 줄이는거죠. 그래서 배경화면이나 복잡한 디자인 등에 사용하는 이미지 파일은 프로젝트에 포함하지 않고 웹 서버쪽에 두는 것이 정석이에요. 그런데 이렇게 하면 개발자들이야 어차피 디자인을 보면서 작업하는 일이 많지 않아서 별로 문제가 안되지만, 디자이너들에겐 치명적인 문제가 되죠. 생각해보세요 배경화면을 보면서 디자인을 해야 하는데 이미지가 프로젝트에 없어서 보이지 않는다니!

요컨대, 다음과 같이 배경 이미지 한장을 보여주는 아주 간단한 샘플을 놓고 보죠.

image image

최초로 Images 폴더에 ducks.jpg를 추가하면 기본적으로 Build Action이 Resource가 되어 프로젝트를 빌드할 때 어셈블리에 리소스로 포함되고 이 이미지의 용량은 고스란히 XAP파일의 용량이 증가하는 결과로 이어지죠. 그렇다고 이미지를 삭제하고 웹서버에만 놓기엔 디자이너가 볼 수가 없어서 애매하죠.

이럴 때는 간단하게 해당 이미지 파일을 프로젝트에 그대로 놓은 상태에서 Build Action만 None으로 설정하세요.

image

이렇게 하면 비주얼 스튜디오의 디자인 뷰에는 나오지 않지만 블렌드로 열어보면…

image

잘 보이죠? 그러면서도 배포 사이즈는 이미지의 크기가 빠지게 되죠.

별거 없지만 첨부된 프로젝트를 보면서 참고하세요.


Posted by gongdo

많은 실버라이트 블로그에서 Moonlight 1.0의 정식 출시를 축하하고 있네요.
대표로 스캇 형님과 Miguel의 블로그의 링크만 달아둡니다.

http://weblogs.asp.net/scottgu/archive/2009/02/11/moonlight-1-0-release.aspx
http://tirania.org/blog/archive/2009/Feb-11.html

문라이트는 일반적인 리눅스와 Unix/X11 기반의 OS에서 실버라이트를 구동하기 위한 플러그인으로, 현재 파이어 폭스 2와 3에서 동작한다고 해요. 문라이트는 리눅스에서 닷넷 프레임워크를 구현하는 Mono 프로젝트의 서브 프로젝트로 출발하여, 현재 Novell사와 마이크로소프트사가 협력하여 만들고 있어서 사실상 공식 지원이 된다고 얘기할 수 있어요.

실버라이트 3가 나오는 마당에 1.0이 웬말이냐…라고 하실지도 모르겠지만 실버라이트의 런타임은 하위 버전의 애플리케이션도 동작시킬 수 있어야 하니까 순서라는게 있는거죠. 문라이트팀이 실버라이트의 릴리즈를 따라잡길 바래요.

전 리눅스 안써본지도 몇 년이 되어서 리뷰는 패스 :D

Posted by gongdo

Expression Encoder는 다양한 미디어 파일을 인코딩하여 서버로 배포하기 위한 다양한 기능을 제공하는데요, 특히 실버라이트를 위해 인코딩된 결과물을 곧바로 Silverlight Streaming 서버에 올릴 수 있는 플러그인이 마련되어 있어요.

먼저 Expression Encoder 2Encoder Service Pack 1을 설치한 후 Silverlight Streaming Publishing Plug-In for Expression Encoder v2가 설치되어 있어야 해요. 그리고 당연하겠지만 Silverlight Streaming에 동영상 올리기#1에서 설명했던 것처럼 live 계정으로 streaming에 가입되어 있어야 하고요.

1. 우선 http://streaming.live.com/에 가서 Manage Account 메뉴를 클릭하면 자신의 Account ID와 Key를 볼 수 있어요. 이걸 잘 복사해 놓으시고 다음을 진행하세요.

image

2. 인코더를 실행하고 Import 버튼을 클릭해서 인코딩할 동영상 파일을 선택하거나 탐색기에서 드래그&드랍하여 인코딩할 동영상 파일을 Items에 올려놓으세요. 이 글은 인코더의 기능을 설명하고자 하는게 아니라서 다른 탭에 있는 여러가지 옵션과 기능들은 설명하지 않아요.

image

(인코딩 데모때마다 혹사당하는 Bear.wmv^^)

3. Silverlight Streaming에 게시하기 위해서는 먼저 출력 템플릿을 정해줘야 해요. 템플릿은 인코딩된 결과물로 자동으로 재생 가능한 실버라이트 미디어 플레이어 프로젝트와 웹 애플리케이션을 생성해주죠. 템플릿은 반드시 Silverlight 2에 있는 것 중 하나를 선택하세요.

image

템플릿을 선택하면 템플릿의 preview를 볼 수 있어요.

image

4. Output 탭의 하단에 있는 Publish 패널에서 Publish To를 Silverlight Streaming으로 선택하세요. 만약 Silverlight Streaming이 나오지 않는다면 플러그인을 제대로 설치하지 않아서 그렇겠죠?

image

5. Silverlight Streaming을 선택하면 다음과 같은 옵션들이 나올거에요. Publish after encode에 체크하고, 앞에서 얻었던 Account ID와 Account Key를 입력하세요. 접혀있는 패널을 열어보면 해당 계정의 남은 공간과 상태를 확인할 수 있어요.

image

6. 이제 Encode를 눌러 인코딩을 진행해보죠. 인코딩이 끝나면 다음과 같이 업로드가 시작돼요.

image

업로드가 완료되면 다음과 같이 자동으로 가상 서버가 하나 뜨면서 선택한 템플릿의 실버라이트 미디어 플레이어 애플리케이션을 볼 수 있죠.

image

7. 그럼 Silverlight Streaming에 어떻게 올라갔는지 볼까요? Streaming 페이지에서 Manage Account를 클릭하면 앞에서 인코딩할 때 설정했던 이름으로 애플리케이션이 올라가 있는 것을 볼 수 있어요.

image

클릭해 보면… 여러가지 옵션을 확인할 수 있죠. Launch Application Test Page를 클릭해서 업로드된 미디어 플레이어 및 동영상 파일을 확인해 보세요.

8. 이렇게 올린 애플리케이션은 두 가지 방법으로 퍼갈 수 있는데요, 아쉽게도 <object>로 곧바로 올리는 방법은 아직 지원되지 않고 iframe을 사용하거나 LiveControl을 이용해서 올리는 두 가지 방법만 지원해요. 그리고 한가지 또 아쉬운 점은 동영상 파일의 절대 URL을 알 수 없다는 점이죠.

Silverlight Streaming은 나름 활용할 여지가 많은데요, 기회가 되면 다른 활용 방법을 소개하도록 하지요.

Posted by gongdo

실버라이트로 미디어 플레이어를 만들다보면 가끔 소스를 올려놓을 적당한 곳이 없을 때가 있는데요, 이럴 때 Silverlight Streaming이라는 좋은 서비스를 잘 활용해 보세요.

Silverlight Streaming은 live.com에서 제공하는 서비스 중 하나로 개인 live계정에 대해 무료로 제공해줘요. 구체적으로,

  • 10GB의 저장 공간
  • 한달에 5TB의 트래픽
  • 실버라이트와 호환되는 WMV/VC-1 코덱의 동영상 하나당 104M까지
  • 또는 하나당 최대 1.4Mbps의 프레임 레이트와 최대 10분 길이의 동영상

이 정도면 꽤나 멋진 조건이죠. 특히 WMV 또는 VC-1 코덱으로 인코딩된 영상은 사이즈 제한 등이 없어서 쓸만해요. 게다가 Expression Encoder에서 곧바로 Silverlight Streaming에 인코딩된 영상을 올리는 기능을 지원해서 편리하죠. 이 외에도 광고 등의 조건에 따라 몇 가지 옵션이 더 있는데 여기에서는 가장 기본적인 계정만 만들어보도록 하죠.

http://streaming.live.com/에 가서 다음 순서에 따라 등록해 보세요.

 

1. live 계정으로 sign-in. 당연하겠지만 없으면 만들어야죠?^^

image

2. Sign-in 했으면 왼쪽에 Manage Videos를 클릭하고 Upload a video를 클릭. 오른쪽 아래에 보면 사용 가능한 공간도 나와 있죠.

image 

3. 파일을 선택하고 업로드. 이 때 "The video is a Silverlight-compliant WMV file."을 체크하면 업로드하는 파일이 실버라이트에서 재생 가능한 형식일 경우 별도의 인코딩 작업 없이 올라가요. 제목은 나중에 URL에서 접근할 때 사용하니 적당히 이름 지으면 되고요.

image

Upload를 클릭하면 파일이 업로드 되는데, 물론 시간대에 따라 다르지만 생각보다 업로드 속도가 괜찮아요. 지금 14MB에 2분25초라고 표시되어 있지만 실제로는 2분이 안걸렸어요. 처음 이 서비스가 나왔을 때에는 진짜 모뎀에 필적하는 속도였는데 이 정도면 별 불편없이 쓸 수 있을 듯.

image

4. 업로드가 끝나면 다음과 같이 업로드된 동영상의 목록과 재생된 회수, 그리고 남은 Streaming 공간 등이 표시돼요.

image

5. 업로드한 동영상 제목을 클릭해 보면 다음과 같이 해당 영상을 곧바로 재생할 수 있는 기본 미디어 플레이어가 하나 제공되고 그 플레이어를 퍼갈 수 있는 <iframe>태그와 동영상을 직접 링크할 수 있는 URL도 제공돼요.

사실 이 외에도 내가 만든 실버라이트 애플리케이션도 올리는 서비스도 있는데 그건 다음 기회에.^^

Posted by gongdo
사실 실버라이트로 할 수 있는 가장 효과적인 애플리케이션은 미디어 플레이어죠. 미디어 플레이어를 만들기 전에 먼저 미디어 파일에 접근하고 재생하며 정보를 얻는 기능을 제공하는 MediaElement에 대해 알아보는 핸즈온랩이에요.


미디어 플레이어 만들 때 동영상 올리기 힘들죠?
Silverlight Streaming을 써보세요. 가장 중요한 건 무료라는거~~
http://gongdosoft.com/374 여기 참고.
Posted by gongdo

사실 이 부분은 C#을 해보셨다면 아주 쉽게 넘어갈 수 있어요. 간단하게 어떤 오브젝트에 대해 이벤트 핸들러를 붙여 코드에서 제어하는 방법을 소개하고 있어요. 실버라이트에서 이벤트 핸들러를 선언하고 연결하는 방법은 크게 세 가지죠. 한번 확인해 보세요. :D


Posted by gongdo

핸즈온랩의 두 번째 시간인데요, 이 실습을 하기전에 먼저 Expression Blend를 열어서 그냥 눈에 익을 때까지 막 만져보세요. 정확히 뭐가 어떻게 돌아가는지 어떤 패널과 어떤 탭이 어떤 역할을 하는지 몰라도 돼요. 우선은 그냥 보고 느끼는거죠. 그리고 나서 이 실습을 진행하는 게 효과적일거에요.


Posted by gongdo

이제는 너무 많이 봐서 지겨울 수도 있지만, 역시 시작이 반이죠. Visual Studio를 설치하고 실버라이트 개발 환경을 갖췄다면 그냥 따라하면서 시작하세요. 개발 환경에 익숙치 않다면 의문이 많이 생기겠지만 지금은 우선 따라하는게 중요해요. 글쎄, 일단 해보시라니까요? :D


Posted by gongdo

http://2009.visitmix.com/MIXtify/TenKDisplay.aspx?SubmissionID=0113

올리고 5일이 지나도 등록이 안되길래 실패한줄 알았더니… 올라왔네요 아이고 좋아라 >ㅅ<

자 남은 건 여러분의 도움뿐! 점수좀 줍쇼~ 굽신굽신.

Posted by gongdo

My 10k entry has been accepted to 10k gallery!
Click here and check it out. :D

"Ink 10k" looks like simple painting application. But it draws "TEXT" whereas ordiniry applications draw just line or curve.
All text you draw is coming from Twitter. Yeah! It uses Twitter search API and dynamically grabs messages.
It recongnizes pressure of pen, if you use a pen tablet as well.
And also it has a undo/redo functionality by pushing 'Ctrl+Z' or 'Ctrl+Y'.

Here's my paint.

I know, I'm horrible at painting ;~>
Why don't you try better one?

Posted by gongdo

IsolatedStorage를 사용하면 Blend에서 디자인이 안보인다?에서 DesignerProperties 클래스를 활용하여 Blend에서 실행중인지 여부를 구분할 수 있다고 했는데요, DesignerProperties를 보다 정확하게 활용할 수 있는 간단한 방법을 소개합니다.

기본적으로 DesignerProperties는 다음과 같이 사용하면 되죠.

if (DesignersProperties.GetIsInDesignMode(AnyUIElementInVisualTree) == true)
    // 디자인 모드
else
    // Web 모드

간단하죠? 그런데 한 가지 문제는 반드시 인자로 VisualTree에 올라가 있는 UIElement를 필요로 한다는 점이에요. 보통 UserControl이나 Control 안에 코드를 작성할 때에는 단순히 GetIsDesignerMode(this)를 해도 무방하지만 Model과 같이 따로 UIElement를 확정할 수 없는 코드에서는 난감하죠. 이럴 때에는 다음과 같이 해결할 수 있어요.

if (DesignersProperties.GetIsInDesignMode(Application.Current.RootVisual) == true)
    // 디자인 모드
else
    // Web 모드

Application이 지원하는 RootVisual은 전역에서 일관적으로 접근할 수 있는데다가 비주얼 트리VisualTree의 최상위 오브젝트를 의미하니까 완벽해 보여요. 그렇지만 여기에도 문제는 있는데요, 바로 Application이 아직 RootVisual을 결정하지 못한 상태일 때에 이 검사는 실패한다는 점이죠. 일반적인 실버라이트 애플리케이션의 App.xaml.cs를 보죠.

public App()
{
    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
}

private void Application_Startup(object sender, StartupEventArgs e)
{
    this.RootVisual = new Page();
}

7행의 InitializeComponent();가 실행되기 직전에는 Application.Current.RootVisual이 실버라이트 애플리케이션을 다운로드 받는 동안 보이는 로딩 화면Splash에 대한 비주얼 트리를 담고 있죠.

image

그 후 애플리케이션이 초기화 되고 로딩 화면이 사라진 후 Application_Startup 이벤트 핸들러가 호출되는데요, 12행의 RootVisual을 new Page로 설정하기 직전까지는 Application.Current.RootVisual이 null 값을 가지고 있어요.

즉, 애플리케이션이 초기화 된 직후부터 RootVisual을 설정하기 전까지 Application.Current.RootVisual이 null이므로 이 사이에는 DesignerProperties.GetIsDesignMode(Application.Current.RootVisual)이란 코드는 안전하지 않게 되는거죠. 한 가지 다행인 점은 앞서 본 것 처럼 App 클래스의 생성자에서 RootVisual이 null이 아니기 때문에 이를 이용하여 다음과 같이 안전한 코드를 작성할 수 있어요.

// 초기값은 블렌드에서 실행중이라고 가정합니다.
private bool _isBlend = true;
/// 
/// 블렌드에서 실행중인지 여부를 반환합니다.
/// 
public bool IsBlend
{
    get { return _isBlend; }
    private set { _isBlend = value; }
}

public App()
{
    // 블렌드에서 실행중인지 여부를 검사합니다.
    IsBlend = DesignerProperties.GetIsInDesignMode(RootVisual);

    this.Startup += this.Application_Startup;
    this.Exit += this.Application_Exit;
    this.UnhandledException += this.Application_UnhandledException;

    InitializeComponent();
}

여기서 중요한 점은 최초에는 블렌드에서 실행중이라고 가정한다는 거에요. 그 이유는 내부적으로 블렌드에서 실행될 때에 분명히 Application의 생성자가 호출되지만 우리가 그것을 확인할 수 없기 떄문에 실행 코드의 ‘모호성’을 줄이기 위해서에요. 반면 웹 페이지로 동작하고 있다면 이 코드는 100% 동작한다고 확신할 수 있으니 초기 값을 안전하기 가져가는 것이 좋겠죠.

그리고 코드를 애플리케이션 전역에서 공유할 수 있도록 App 클래스의 static 멤버로 정의했기 때문에 애플리케이션의 어떤 코드에서도 안전하게 가져갈 수 있겠죠.

만약 라이브러리에서도 블렌드에서 실행중인지 여부를 검사해야 한다면 여기에서 한 단계 더 나아가 위의 코드를 래핑하는 전역 클래스Static Class를 하나 만들 수도 있고요. 이 부분은 각자 프로젝트에 맞춰서 해보세요.

Posted by gongdo

MVVM+Command Pattern+Dependency Injection #1에 이어지는 Ninject를 사용한 Dependency Injection편이에요 :D

Ninject는 ‘번개처럼 빠른 인젝션’을 모토로 한 DI 프레임워크죠. Unity랑 비교해 보면 Unity가 전통적인 개발자에게 친숙한 인터페이스와 네이밍을 가진 반면 Ninject는 모던하고 약간은 팬시한 문법과 네이밍을 가졌고 다양한 조건에서 좀 더 자동화된 인젝션을 지원한다는 느낌이 들어요. 여튼 이 전에 만든 프로젝트를 정말로 약간만 수정하면 Unity를 Ninject로 바꿀 수 있어요. 이것이 바로 모듈화된 프로그래밍의 장점이죠 ;~)

우선 프로젝트 먼저 받아가시고요~


1. Ninject 라이브러리 참조

당연하겠지만 제일 먼저 라이브러리를 교체해야겠죠?

image

Ninject는 현재 5개의 어셈블리로 이루어져 있고 3개는 Extension, 2개는 거의 필수로 사용되는 어셈블리죠. 그 중 필수인 Ninject.Core와 Ninject.Conditions를 참조하면 돼요.
※Ninject.Condition 역시 사실 필수는 아니지만 프로젝트가 조금만 커져도 거의 필수로 사용됩니다.

2. ServiceConfiguration 수정

/// 
/// 서비스에서 사용할 인젝션 매핑을 설정하는 클래스
/// 
public class ServiceConfiguration : StandardModule
{
    public override void Load()
    {
        bool isBlend = App.IsBlend;
        bool isWeb = !isBlend;

        // IPhotoSerivce에 대한 바인딩 설정
        // 블렌드에서 실행할 때 바인딩만 다르게 표현
        if (isBlend)
        {
            Bind< IPhotoService>().To< MockPhotoService>();
        }
        else
        {
            Bind< IPhotoService>().To< LivePhotoService>().Only(When.Context.Variable("ServiceName").IsNotDefined);
            Bind< IPhotoService>().To< LivePhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Live"));
            Bind< IPhotoService>().To< FlickrPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Flickr"));
            Bind< IPhotoService>().To< DaumPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Daum"));
            Bind< IPhotoService>().To< NaverPhotoService>().Only(When.Context.Variable("ServiceName").EqualTo("Naver"));
        }

        // 공통으로 사용할 바인딩
        // 서비스 제공자의 목록을 싱글턴으로 바인딩
        Bind< PhotoServiceProviderList>().ToConstant< PhotoServiceProviderList>(
            new PhotoServiceProviderList
            {
                new PhotoServiceProvider { Name = "Live", DisplayName = "Live", LogoSource = "/MashupImageSearch;component/Resource/WindowsLive2_20.png" },
                new PhotoServiceProvider { Name = "Flickr", DisplayName = "Flickr", LogoSource = "/MashupImageSearch;component/Resource/flickr2_20.png" },
                new PhotoServiceProvider { Name = "Daum", DisplayName = "Daum", LogoSource = "/MashupImageSearch;component/Resource/daum_20.png" },
                new PhotoServiceProvider { Name = "Naver", DisplayName = "Naver", LogoSource = "/MashupImageSearch;component/Resource/naver_20.png" },
            }
        ).Using< SingletonBehavior>();
    }
}

Unity가 UnityContainer를 제공했던 것과 유사하게 Ninject는 StandardModule을 제공해요. Ninject에서 Module이란 하나의 서비스를 구성하는 DI들의 바인딩 구성을 정의하죠.

바인딩(매핑)을 등록하는 문장도 굉장히 다른데요, RegisterType()이라는 하나의 메서드로 인자의 위치나 배열에 따라 순서를 정했던 Unity와 달리 Ninject는 Bind<타입>().To<타입>().조건… 과 같은 명시적인 형식을 가지고 있어요. 마치 LINQ 표현식의 발전 과정을 보는 듯하죠.

동적 조건 식은 조금 복잡한데요, Unity가 단순히 이름만 가지고 바인딩을 결정했던 것과는 달리 Ninject는 굉장히 다양한 시나리오와 커스터마이징이 가능한 파라미터들로 바인딩을 결정할 수 있어요. Only(When.Context.Variable(“키”).조건…과 같이 “오직 컨텍스트에 특정 변수가 조건을 만족할 때만 바인딩”으로 글을 읽듯이 읽을 수 있죠. 굉장히 가독성이 좋은 문법이라고 봐요. 다만 지네릭과 람다 익스프레션 문법을 굉장히 많이 활용하기 때문에 익숙하지 않다면 어려울 수 있겠네요.

마지막으로 Unity의 RegisterInstance()메서드는 Ninject는 Bind<타입>().ToConstant<타입>() 으로 표현하는 걸 볼 수 있고 인젝션할 대상의 라이프 타임 관리를 .Using<라이프타임 동작>() 과 같이 할 수있어요. 여기에서 서비스 제공자의 목록은 항상 같으므로 단순한 SingletonBehavior로 지정했죠.

3. ServiceLocator 수정

/// 
/// 서비스에서 사용할 설정 모듈에서 타입을 제공하는 클래스
/// 
public class ServiceLocator
{
    #region Singlton 컨테이너
    private static IKernel _kernel;
    /// 
    /// 클래스 초기화.
    /// 서비스에서 사용할 DI 컨테이너를 초기화합니다.
    /// 
    static ServiceLocator()
    {
        if (_kernel == null)
        {
            _kernel = new StandardKernel(new ServiceConfiguration());
        }
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 인젝션이 처리된 결과
    public static T Get< T>()
    {
        return _kernel.Get< T>();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 파라미터 이름
    /// 인젝션이 처리된 결과
    public static T Get< T>(string name)
    {
        return _kernel.Get< T>(With.Parameters.Variable("ServiceName", name));
    }
    #endregion Singlton 컨테이너

    /// 
    /// 설정에 따라 이미지 검색 뷰모델의 인스턴스를 반환합니다.
    /// 
    public ImageSearchViewModel ImageSearchViewModel
    {
        get { return Get< ImageSearchViewModel>(); }
    }

    /// 
    /// 설정에 따라 포토 서비스 제공자의 목록을 반환합니다.
    /// 
    public PhotoServiceProviderList PhotoServiceProviders
    {
        get { return Get< PhotoServiceProviderList>(); }
    }
}		

Unity에서는 UnityContainer가 인젝션 바인딩(매핑) 정보를 보관하고 관리하는 역할을 모두 수행하지만 Ninject는 Kernel이 관리하는 Module에서 인젝션 바인딩 정보를 이용하여 인젝션을 관리해요. 특별히 다른 점은 없고 다만 파라미터를 줄 때도 Kernel.Get<타입>(With.Parameters.Variable(“변수이름”, 변수값) 같은 식으로 명시적이고 가독성 있는 문법을 지원해요.

이 정도 수정만으로 Unity를 Ninject로 대체하는 마이그레이션이 끝났어요. 실행 결과는 당연히 동일하고요. 간단하죠?

DI 자체에 대해서는 언제 한번 자세히 다루고 싶지만 아직 내공이 딸려서 정확한 설명은 힘드네요. 시간도 부족하고요. 우선은 지금까지 했던 프로젝트를 기반으로 지식을 확장해가면서 경험을 쌓아도 충분할거라고 봐요.

Posted by gongdo

매쉬업 이미지 검색 + MVVM모델 + CommandPattern 예제에서 길버트님이 MVVM과 Command Pattern을 활용하여 화면 표시를 담당하는 View와 데이터 및 비즈니스 로직을 담당하는 Model, 그리고 View에 표시될 데이터와 View의 흐름 제어를 담당할 ViewModel이 적용될 수 있는 멋진 데모를 보여주셨죠.

사용된 패턴과 용어 정리

MVVM은 MVC 패밀리(?)의 일족으로 MVP(Model View Presentation)과 매우 유사해요. 사실 전 MVC/MVP/MVVM은 모두 같은 원리이고 구현의 관점이 약간씩 다를 뿐이라고 생각해요. 여튼 중요한 건 뷰와 데이터를 어떻게 분리한 상태에서 컨트롤 할 것이냐죠.

Commnad Pattern은 주로 애플리케이션 전역에서 동일하게 받을 수 있는 사용자의 제어 명령에 대한 이벤트를 효과적이고 표준적으로 받기 위한 구현 방법이에요. 커맨드 패턴은 특히 MVVM처럼 뷰와 모델이 분리된 환경에서 매우 효과적으로 코드를 짤 수 있게 도와주는 역할을 하죠.

Dependency Injection(DI) Pattern은 클래스의 독립성을 극한까지 끌어올리기 위해 고안된 방법 중 하나에요. 인젝션의 핵심은 어떤 클래스가 참조하거나 이용하는 인스턴스를 그 클래스가 생성하지 않고 외부에서 생성자, 프로퍼티 또는 메서드를 사용하여 ‘주입’inject시켜 보다 투명하고 분리된 클래스를 작성하는 데 있어요.

이 글에서는 DI에대한 설명은 접고 그냥 어떻게 활용되었는지를 보도록 하고 자세한 정보는 다음 링크들을 체크해 보세요.

길버트님의 예제 리모델링!

백문이 불여일타! 일단 샘플 받아서 열어보시고요~


동작은 길버트님의 예제와 완벽하게 동일하므로 동작 완구는 생략했어요. 리모델링 과정을 알아보죠.
※보통 프로그래밍에서 어떤 코드를 좀 더 적절하게 나누고 최적화하는 과정을 리팩터링이라고 합니다. 여기에서는 리모델링으로 표현했음에 주의하세요.

1. 문제점 분석

우선 기존 프로젝트에서 어떤 부분을 왜 고쳐야 하는지 생각해보죠. 기존 프로젝트에서는 각 포토 서비스를 IPhotoService라는 인터페이스만 상속받으면 되는 독립 클래스로 만들수 있었어요. 한 가지 문제는 이렇게 새 서비스를 만들어도 View와 ViewModel이 추가된 서비스를 알 수 없기 때문에 View와 ViewModel 모두를 고쳐야 한다는 점이죠. 이는 말하자면 Model의 독립성은 확보했지만 View와 ViewModel이 Model에 종속되어 있는 그림이 되죠. 특히 View는 ComboBox 컨트롤에 하드코딩으로 목록이 들어있는 형태라서 만약 디자이너 프로세스와 개발 프로세스가 나뉘어 있다면 Model이 추가될 때마다 디자이너가 View를 만져줘야 하는 상황이 발생하겠죠.

이를 해결하려면 View는 Model에 대해 전혀 알 필요가 없어야 하고 ViewModel은 Model의 추가/변경/삭제에 대해 코드의 수정 없이 대처할 수 있어야 해요.

2. View를 독립시키기

OOP의 기본 중에 기본이죠. 하나의 클래스는 그 이름으로 대표되는 최소한의 일만 수행하도록 쪼개고 또 쪼개는 것. 기존 프로젝트에서 콤보 박스에 들어가는 데이터를 생각해보면 각 서비스 제공자의 이름(Flickr, Live…)과 로고 이미지로 구성되어 있었죠? 그리고 이 부분이 새 Model이 추가될 때마다 함께 수정해야 할 부분이었고요. 이 부분을 분리해보죠.

image

포토 서비스 제공자의 이름과 로고를 처리하기 위해 IPhotoServiceProvider와 PhotoServiceProvider 클래스를 추가했고 내용은…

/// 
/// 기본적인 포토 서비스 제공자의 메타 정보를 관리하기 위한 클래스
/// 
public class PhotoServiceProvider : IPhotoServiceProvider
{
    /// 
    /// 서비스 제공자의 이름을 설정하거나 반환합니다.
    /// 
    public string Name { get; set; }

    /// 
    /// 화면에 표시할 이름을 설정하거나 반환합니다.
    /// 
    public string DisplayName { get; set; }

    /// 
    /// 서비스 제공자의 로고 이미지 소스를 설정하거나 반환합니다.
    /// 
    public string LogoSource { get; set; }
}

public class PhotoServiceProviderList : List< IPhotoServiceProvider>
{
}

간단하죠? 그리고 이 데이터는 하나가 아닌 여럿으로 구성될 것이므로 지네릭 타입의 List도 하나 선언해줬어요.

이제 View에서 데이터 템플릿을 사용하여 하드 코딩된 디자인을 걷어내보죠.

    
        
            
                
                
            
        
    

아직 데이터 바인딩의 소스를 설정하지 않았지만 DataTemplate을 사용해서 기존 디자인 형식을 그대로 가져왔고 대신 Image의 Source와 TextBlock의 Text 속성을 위에서 만든 IPhotoServiceProvider가 제공하는 속성으로 바인딩을 해줬어요. 데이터 바인딩의 소스는 나중에 추가하기로 하죠.

자 이제 View는 Model에 변경이 있더라도 영향을 받지 않도록 준비가 되었어요. 그러나 아직 데이터 소스를 어디에서 가져와서 언제 어떻게 설정할지는 미지수인데요, 이를 위해 드디어 Dependency Injection기법을 사용하여 몇 가지를 준비해보죠.

3. Dependency Injection 프레임워크 선택

DI는 개념이 그렇게까지 복잡한 것은 아니라 물론 직접 구현할 수도 있겠죠. 그러나 언제나 그렇지만 이런 표준적인 라이브러리는 다른 사람이 만든 훌륭한 구현체가 있기 마련이죠. 바로 제가 라이브러리 문서화의 중요성에서 언급한 Unity Application Block이나 Ninject처럼 말이죠. 이 중에서 좀 더 보편적이고 무엇보다 마이크로소프트가 운영하는 Pattern&Practice 프로젝트에서 만들어진 Unity를 사용할께요. 기회가 되면 Ninject도 사용해 볼 거에요.

imageimage

Unity Application Block for Silverlight를 다운로드 받으면 Microsoft.Practices.Unity.dll이란 어셈블리를 찾을 수 있는데요, 이걸 프로젝트에서 참조하면 돼요. 테스트 할 수 있도록 프로젝트의 DLL폴더에 어셈블리 파일을 포함시켜뒀으니 이걸 사용하셔도 되고요.

4. DI 설정 및 전역 서비스 제공 클래스 작성

DI는 외부에서 작성되어 애플리케이션 전역 혹은 일부에 영향을 주게 돼요. 저는 YouCard라는 짱멋진 프로젝트에 영향을 받아 DI 바인딩(또는 매핑) 정보를 저장하고 관리하는 ServiceConfiguration 클래스와 이 설정으로부터 애플리케이션이 사용할 인스턴스화된 오브젝트를 애플리케이션 전역에 제공할 ServiceLocator 클래스를 만들었어요.

먼저 ServiceConfiguration을 보면…

/// 
/// 서비스에서 사용할 인젝션 매핑을 설정하는 클래스
/// 
public class ServiceConfiguration : UnityContainer
{
    /// 
    /// 생성자.
    /// 여기에서 애플리케이션이 공통으로 사용할 컨테이너를 설정합니다.
    /// 
    public ServiceConfiguration()
    {
        // 디자인일 때와 그렇지 않을 때 서비스 바인딩을 달리 합니다.
        if (App.IsBlend)
        {
            RegisterType< IPhotoService, MockPhotoService>();
        }
        else
        {
            // 일반일 때에는 각 서비스의 바인딩을 이름으로 구분하여 등록합니다.
            RegisterType<  IPhotoService,  LivePhotoService>();    // 이름이 없을 때는 라이브를 사용
            RegisterType<  IPhotoService,  LivePhotoService>("Live");
            RegisterType<  IPhotoService,  FlickrPhotoService>("Flickr");
            RegisterType<  IPhotoService,  DaumPhotoService>("Daum");
            RegisterType<  IPhotoService,  NaverPhotoService>("Naver");
        }

        // 공통으로 사용할 바인딩
        // 서비스 제공자의 목록
        PhotoServiceProviderList providers = new PhotoServiceProviderList
        {
            new PhotoServiceProvider { Name = "Live", DisplayName = "Live", LogoSource = "/MashupImageSearch;component/Resource/WindowsLive2_20.png" },
            new PhotoServiceProvider { Name = "Flickr", DisplayName = "Flickr", LogoSource = "/MashupImageSearch;component/Resource/flickr2_20.png" },
            new PhotoServiceProvider { Name = "Daum", DisplayName = "Daum", LogoSource = "/MashupImageSearch;component/Resource/daum_20.png" },
            new PhotoServiceProvider { Name = "Naver", DisplayName = "Naver", LogoSource = "/MashupImageSearch;component/Resource/naver_20.png" },
        };
        // 인스턴스 자체를 등록
        RegisterInstance< PhotoServiceProviderList>(providers);
    }
}

가장 핵심적인 건 ServiceConfiguration 클래스가 UnityContainer를 상속받고 있다는 점이죠. UnityContainer는 바로 Unity에서 DI에 대한 매핑 정보를 저장하고 관리하는 기본 클래스에요. 기본적으로 RegisterType 메서드로 FromT 타입에 대해 ToT 타입으로 주입Injection하도록 매핑하고 또한 RegisterInstance 메서드로 FromT타입에 대해 등록한 인스턴스를 주입하도록 매핑하죠. 여기에서는 이 정도로만 설명하고 DI에 대한 건 기회가 되면 다른 글에서 소개할께요.

여기서 한 가지 센스있는 코드가 있는데요^^, 바로 실버라이트가 디자인 모드(블렌드)에서 동작중인지 아니면 정상적으로 실행되고 있는지에 따라 타입 매핑을 다르게 했어요. 이를 통해 디자이너가 블렌드에서도 여기에서 주어진 샘플 데이터를 볼 수 있죠.

이제 새로운 Model이 추가된다면 새 Model 클래스를 작성하고 ServiceConfiguration에 새로 만들어진 Model 타입을 매핑하고 새 Model을 설명하는 PhotoServiceProvider를 추가해주기만 하면 돼요. WOW~!

다음으로 ServiceLocator를 보죠.

/// 
/// 서비스에서 사용할 컨테이너를 초기화하고
/// 인젝션에 필요한 정보를 담고 있는 싱글턴 클래스
/// 
public class ServiceLocator
{
    #region Singlton 컨테이너
    private static ServiceConfiguration _container;
    /// 
    /// 클래스 초기화.
    /// 서비스에서 사용할 DI 컨테이너를 초기화합니다.
    /// 
    static ServiceLocator()
    {
        _container = new ServiceConfiguration();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 인젝션이 처리된 결과
    public static T Get< T>()
    {
        return _container.Resolve< T>();
    }

    /// 
    /// 주어진 타입에 대한 인젝션을 수행한 참조를 반환합니다.
    /// 
    /// 인젝션을 처리할 대상
    /// 옵셔널 문자열
    /// 인젝션이 처리된 결과
    public static T Get< T>(string name)
    {
        return _container.Resolve< T>(name);
    }
    #endregion Singlton 컨테이너

    /// 
    /// 생성자.
    /// 
    public ServiceLocator()
    {
    }

    /// 
    /// 설정에 따라 이미지 검색 뷰모델의 인스턴스를 반환합니다.
    /// 
    public ImageSearchViewModel ImageSearchViewModel
    {
        get { return Get< ImageSearchViewModel >(); }
    }

    /// 
    /// 설정에 따라 포토 서비스 제공자의 목록을 반환합니다.
    /// 
    public PhotoServiceProviderList PhotoServiceProviders
    {
        get { return Get< PhotoServiceProviders>(); }
    }
}

ServiceLocator는 ServiceConfiguration에 대한 싱글턴 인스턴스를 하나 가지고 있으면서 애플리케이션에서 사용될 인젝션 대상 클래스의 인스턴스를 얻을 수(Get) 있도록 도와주죠. 그리고 바인딩의 편의를 위해 ImageSearchModelView와 PhotoServiceProviders 속성은 별도로 노출하고 있어요. 이 ServiceLocator는 말 그대로 애플리케이션 전역에서 접근할 수 있는 DI 컨테이너를 서비스하는 클래스이죠.

5. 애플리케이션 전역에 DI 컨테이너를 제공할 수 있도록 준비

애플리케이션 전역에서 ServiceLocator를 접근할 수 있도록 ServiceLocator의 인스턴스를 하나 만들어야죠. 여기에서 App.xaml의 진가가 나오는데요, 바로 App.xaml의 Resources에 ServiceLocator의 새 인스턴스를 하나 등록해두면 애플리케이션 전역에서 StaticResource로 접근할 수 있게 돼요. App.xaml을 보면…

    
        
    

View의 DataContext의 Source를 전역 리소스인 ServiceLocator로 지정했고 데이터의 경로Path는 ImageSearchViewModel로 지정했어요. 즉, ServiceLocator 클래스의 인스턴스에서 ImageSearchViewModel 속성을 읽어 오는거죠.

아주 간단하게 전역 리소스를 만들었죠? 다음으로 넘어가죠.

6. 전역 리소스에서 View에 데이터 바인딩

앞서 우리는 ServiceLocator를 전역에서 접근할 수 있도록 리소스로 만들었는데요, 이걸로 각 View의 ViewModel을 안전하게 접근하여 데이터 바인딩 할 수 있어요.

 

즉, ServiceLocator 클래스의 인스턴스에서 ImageSearchViewModel 속성으로 데이터 바인딩을 하는 거죠.

아직 하나 더 해줘야 할 게 있는데요, 앞서 DataTemplate만 설정하고 실제 데이터 소스를 설정하지 않은 ComboBox죠.

    
        
            
                
                
            
        
    

자, 이제 ViewModel을 독립시킬 차례에요.

7. ViewModel 리모델링

ViewModel은 View가 가질 데이터와 View를 표시하는 로직을 담고 Model로부터 데이터를 얻거나 넘겨주죠. 먼저 기존 ViewModel에서는 제공될 Model에 대한 정보를 이미 가지고 있었어요. 여러 개의 서비스를 사용할 수 있도록 각 서비스가 IPhotoService 인터페이스로 추상화가 되어있지만 결국 실체화된 인스턴스를 만들기 위해 Model의 Concrete 타입을 사용해요. 요컨대 ServiceTypes enum이 있었고 ComboBox에서 서비스를 변경하면 이 enum값에 따라 new LivePhotoService(); new FlickrPhotoService();와 같이 Model의 확정된 타입을 ‘생성’했었죠.

세부적인 코드는 첨부한 파일을 참고하시길 바라고 가장 핵심적으로 수정된 부분을 볼께요. 먼저 생성자 부분.

/// 
/// 기본 생성자
/// 
public ImageSearchViewModel(IPhotoService service)
{
    InitProperties(service);
    SearchCommands.Search.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(Search_Executed);
    SearchCommands.Navigate.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(Navigate_Executed);
    SearchCommands.ShowLargeImage.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(ShowLargeImage_Executed);
    SearchCommands.ChangeService.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(ChangeService_Executed);
    SearchCommands.NavigateOriginalImage.Executed += new EventHandler< HugeFlow.CommandPattern.ExecutedEventArgs>(NavigateOriginalImage_Executed);
}

생성자에서는 파라미터로 IPhotoService를 받도록 했고 InitProperties로 전달하고 InitProperties에서는 클래스의 멤버 변수인 _photoService에 전달 받은 IPhotoService의 인스턴스를 ‘주입’하게 되죠. 그리고 만약 블렌드에서 동작중이라면 타이머를 돌려 검색을 시뮬레이션 하고 있고요.

다음으로 ComboBox에서 서비스를 변경했을 때 처리를 보죠.

void ChangeService_Executed(object sender, HugeFlow.CommandPattern.ExecutedEventArgs e)
{
    PhotoServiceProvider provider = e.Parameter as PhotoServiceProvider;
    _photoService = ServiceLocator.Get< IPhotoService>(provider.Name);   // 컨테이너에서 서비스의 참조 얻기

    if (_photoService != null)
    {
        _photoService.SearchImageCompleted += new PhotoSearchEventHandler(_photoService_SearchImageCompleted);
    }

    InitSearch();
}

전에는 각 서비스 모델 별로 직접 생성을 했지만 이제는 ServiceLocator로부터 주어진 PhotoServiceProvider의 이름으로 생성해야 할 타입을 ServiceLocator가 유추하고 생성하여 반환하도록 했어요. 이로써 ViewModel은 외부 Model의 어떤 Concrete타입에도 의존하지 않고 단지 Model들의 추상화된 인터페이스만으로도 완벽하게 동작할 수 있게 되었어요.

꽤 길어졌네요. DI란게 짧은 글 하나로 완벽하게 정리가 될 만한 내용도 아니고 언제 어떻게 적용해야 하는지에 대한 것도 경험이 필요한거죠. 저역시 이제 막 이런 패턴들을 시도하고 익히고 있는 중이에요. 뭔가 설명할 수 있을 만큼 익히면 다시 정리하여 포스팅하기로 할께요. 그리고 다음 포스팅은 똑같은 프로젝트를 Unity가 아닌 Ninject를 사용하여 구현해보도록 하죠.

Posted by gongdo

요즘 실버라이트용 DI(Dependency Injection) 프레임워크로 Pattern&Practice 팀에서 제공하는 Unity Application Block과 독특한 특징을 가진 Ninject, 이 둘을 보고 있죠. 사실 DI의 원리나 구성은 대략 파악했다고 생각하는데 실제 구현은 참 난감한 부분이 많은 것 같아요. 그래서 우리는 이런 공개된 프레임워크의 덕분에 좀 더 쉬운 삶-바로 제가 추구하는 그것!-을 살 수 있어요.

여튼 이 둘은 서로 스타일이 꽤 달라요. 우선 네이밍 부터가 꽤 달라서 이게 과연 같은 패턴을 사용하는 그것이 맞나 싶을 정도. 아주 조금씩만 써보고 있어서 아직 확실하게 감은 못잡았지만 공부하면서 느끼는 차이는 바로 문서화의 차이에요.

Unity는 마이크로소프트의 P&P에서 나온 만큼 철저하게 MSDN 스타일로 구성되어 있죠. MSDN의 장점은 어떤 라이브러리를 매우 광범위하고 자세하게 기술하고 있다는 점인데요 반면 단점은 Getting started를 봐도 딱 그 정도까지만 쓸 수 있을 뿐 그 이상의 기능을 구현하려면 어떤 클래스를 어떻게 사용할지 찾기가 꽤 어렵다는 점이죠. 그래도 최근엔 HOWTO 토픽도 많이 생기고 해서 이런 점은 많이 해소 되었지만, 그래도 여전히 문서가 너무 딱딱해서 어느정도 기합을 넣고 보지 않으면 잘 읽히지가 않아요.

Ninject는 일단 메인 사이트 부터가 굉장히 팬시해서 둘러보는데 부담이 없어요. 그리고 Ninject는 MSDN처럼 완전히 정리된 문서따위는 없지만 Wiki를 통해 아주 깔끔하게 스텝별로 익힐 수 있도록 되어 있어요. 이게 중요한데요, Ninject는 Ninject를 설명할 때 기능 하나하나에 대한 사용법이나 설명을 독립적으로 하는게 아니라 단계별로 기본적인 사용법과 함께 Ninject가 어떤 부분에 주안점을 가지고 있는지 이해할 수 있도록 구성되어 있죠.

베스트 케이스라면 Getting started를 Ninject스타일로 친절하게 구성하고 세부적인 레퍼런스를 MSDN 스타일로 구성하는 거지만 둘 중 하나를 선택하라면 Ninject 스타일의 손을 들어주고 싶네요. 왜냐면 프레임워크나 라이브러리라는 건 일단 처음 사용하기가 굉장히 괴롭거든요. 일종의 진입 장벽이랄까요? 그런 걸 쉽게 덜어주고 그 라이브러리의 설계 철학이나 흐름을 잡는게 중요하다고 봐요.

왜 이런 소릴 하고 있냐면…

Ninject에서는 모듈(Unity의 컨테이너 개념)에 인터페이스와 클래스의 바인딩을 등록할 때 아주 간단하면서도 유용한 조건을 붙여서 하나의 인터페이스에 여러개의 클래스를 조건에 따라 자동으로 인젝션 되도록 되어 있어요. 예를 들면,

public class Swordsman { 
  [Inject] public IWeapon Weapon { get; set; } 
}

public class Ninja { 
  [Inject] public IWeapon MeleeWeapon { get; set; } 
  [Inject, Range] public IWeapon RangeWeapon { get; set; } 
}

Bind().To(); 
Bind().To().Only(When.Context.Target.HasAttribute());

이건 “IWeapon이라는 인터페이스를 Sword클래스와 Shuriken(수리검^^) 클래스에 바인딩을 하되 Context.Target(인젝션 대상, 여기에서는 Weapon, MeleeWeapon, RangeWeapon과 같은 프로퍼티)이 RangeAttribute라는 어트리뷰트를 가지고 있을 때만 Shuriken을 바인딩 해라”를 의미해요. 정말 기가 막히게 팬시하면서 읽기 쉬운 코드 아닌가요? 거의 영어 문장을 읽는 것과 비슷하게 느껴질 정도로요.

또한 예제 코드 역시 Foo나 Bar 따위가 아니라 검사(Swordman)과 닌자(Ninject를 떠올리는!)가 각각 자신의 역할에 맞게 사용할 무기들을 선택하는 과정이 머리속에 그려지도록 잘 선택되었고요.

다른 방법의 예제를 보면

public class Ninja { 
  [Inject] public IWeapon MeleeWeapon { get; set; } 
  [Inject, Tag("range")] public IWeapon RangeWeapon { get; set; } 
}

Bind().To(); 
Bind().To().Only(When.Context.Tag == "range");

비슷한데 Tag라는 미리 지원되는 어트리뷰트를 가지고 문자열 비교를 할 수 있게 한거에요. 이러면 매번 어트리뷰트를 새로 만들지 않아도 되니 편리하겠죠. 마찬가지로 문장은 정말 쉽게 읽히고요.

거기에 한 단계 더 생각해서, 단지 인젝션 대상의 이름 규칙(convention)만으로도 바인딩 할 수 있게 지원하죠.

public class Service { 
  public Service(ISource remoteSource, ISource localSource) { 
    //... 
  } 
}

Bind().To().Only(When.Context.Target.Name.BeginsWith("remote")); 
Bind().To().Only(When.Context.Target.Name.BeginsWith("local"));

Service라는 클래스는 생성될 때 두 개의 ISource를 받죠. 아래의 바인딩 문은 이 클래스에 인젝션할 때 “타겟이 remote라는 이름으로 시작할 때”, “타겟이 local이라는 이름으로 시작할 때”라는 조건을 붙인 걸 읽을 수 있어요. (진짜 볼 수록 감탄…)

참 쉽죠? (ㅋㅋ)

반면 Unity에서 비슷하게 하나의 컨테이너에 하나의 인터페이스를 여러 개의 클래스와 바인딩하고 싶은데 도무지 어디에서 그런 설정을 해야할지 난감하네요. 아마도 Configuration이라는 클래스를 상속받아 구현하는 것 같은데 실버라이트용에서는 그 Configuration이 빠졌다고 하는군요. 여튼 “동적인 조건부 인젝션”을 어떻게 할지 난감해요.

네, 솔직히 저는 Ninject의 손을 들어주고 있는데요, 다만 Unity for Silverlight는 단일 어셈블리에 용량도 조금이나마 작아서 딱 DI만 쓴다고 봤을 때 좀 더 가벼운데 Ninject는 3개의 어셈블리에 용량도 약간 더 커요. 그래서 Unity쪽도 해보고 둘 중에 하나를 선택하자..는 기분으로 써보고 있는데 이놈의 Unity 문서는 읽다보면 스르르 잠에 빠져서 진도가 안나가네요 ㅠ.ㅜ

그래서 오늘의 결론.

라이브러리 문서화를 잘 합시다. 이왕이면 단계별로 가장 핵심적인 내용을 익힐 수 있도록!
AND
Unity 써보신 분 조건적 인젝션 바인딩을 컨테이너에 어떻게 넣는지 좀 알려줍쇼 ㅠ.ㅜ 굽신굽신 oTL oTL

Posted by gongdo

미국 대통령 취임식위원회(PIC; President Inaugural Committee)는 취임식 이벤트의 온라인 동영상 스트리밍에 Microsoft Silverlight 기술을 선택했다고 해요. 자세한 기사는 16일자 마이크로소프트 프레스에서 볼 수 있고요.

PIC의 이 선택은 실버라이트가 적어도 온라인 미디어 시장 만큼은 확실한 경쟁력과 신뢰를 가지고 있다는 것을 의미한다고 봐요. 물론 마이크로소프트의 사회적인 영향력 탓도 있겠지만요.

특히 버락 오바마 대통령 당선자의 취임식은 미국 역사-아마도 세계 역사의 한 장면을 장식할 것이란 점에서 이 소식이 주는 의미가 남다르게 다가오네요. :D

Posted by gongdo

실버라이트에서 커스텀 컨트롤을 만들 때 각 커스텀 컨트롤들의 기본 스타일은 반드시 /themes/generic.xaml 파일에서  ResourceDictionary로 등록해야 했죠. 때문에 라이브러리가 커지다보면 수많은 기본 스타일들이 하나의 파일에 등록이 되어 있어서 찾아보기가 굉장히 힘들었죠.

과연 마이크로소프트의 개발자들은 이런 문제를 어떻게 해결하고 있는지 궁금했었는데요, Jeff Wilcox의 블로그에서 그에 대한 답을 내놓았네요.

아니나다를까 마이크로소프트의 내부 개발자들은 저런 문제를 쉽게 해결하기 위한 툴들을 만들어쓰고 있네요. 바로 기본 스타일들을 정의하는 Xaml들을 자유롭게 배치하고 대신 해당 xaml의 Build Action을 DefaultStyle이라는 키워드로 선택하기만 하면 빌드시 /themes/generic.xaml에 자동으로 통합을 해주는 MSBuild 툴을 만들어 쓰고 있다고 하는군요.

간단하게 말하자면 MSBuild 프레임워크에 포함된 Task라는 항목이 있는데, 이게 어떤 액션이 일어날 때 처리할 일을 정의하는 넘이죠. 그래서 빌드할 때 프로젝트에 포함된 아이템 중에서 DefaultStyle이라는 키워드의 Build Action이 붙은 모든 아이템들을 가져와서 처리한 후 /themes/generic.xaml에 덮어쓰는거죠. 여기에서 Build Action에 원하는 키워드를 추가하는 방법이 가장 중요한 요소인데요, VSProject를 구성하는 엘리먼트 중에 숨겨진 요소가 있다는군요. 참 Win32부터 그런 것들이 많았었죠 –_-;;

자세한 내용은 Jeff Wilcox의 포스팅에 잘 소개되어 있으니 소스 설명은 하지 않겠고요, 단순히 해당 내용을 구현한 결과물을 소개할께요.

다운 받은 파일의 압축을 풀면 세 개의 파일이 나오는데요, 각각 정해진 위치로 폴더를 만들고 복사하기만 하면 돼요.

  • Engineering.Build.dll
    -> C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Libraries\Extra
  • SilverlightAppWithMultipleResourceDictionary.zip
    -> %내 문서%\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\Silverlight
  • SilverlightDefaultStyleItem.zip
    -> %내 문서%\Visual Studio 2008\Templates\ItemTemplates\Visual C#\Silverlight

zip 파일 들은 내 문서에 있는 Visual Studio 2008 폴더를 찾아서 넣으면 돼요.

자 이제 비주얼 스튜디오를 열고 프로젝트를 하나 만들어 볼까요?

image

Visual C#/Silverlight 타입에 보면 My Templates 그룹 아래에 Silverlight Application with Multiple…이 보이네요. 이 템플릿으로 새 프로젝트를 만들면,

image

보안 경고가 나오는데요, 이건 사용자 정의 템플릿을 사용할 때 항상 뜨는 걸로 아래에 있는 Load project normally를 선택해야 제대로 진행돼요.

image

프로젝트를 만들면 기본 실버라이트 프로젝트와 크게 다르지 않고 다만 /themes/generic.xaml이 기본으로 포함되어 있는 것을 볼 수 있죠.

다음으로 새 DefaultStyle 정의를 위한 xaml 파일을 만들어보죠.

image

프로젝트에 새 아이템을 추가하면…

image

위와 같이 카테고리에서 Visual C#/Silverlight를 선택하면 My Templates 그룹Silverlight Default Style Item이 있죠? 클릭하고 이름을 적당히 넣은 후 추가 버튼을 클릭하면 다음과 같이 보안 경고 메시지가 나와요. 저 믿죠? ㅋㅋ Trust를 클릭하여 커스텀 아이템을 추가하도록 허용합니다.

image

xaml 파일이 프로젝트에 추가되는데 여기에서 Build Action은 수동으로 DefaultStyle을 선택해야 하고 Custom Tool 설정은 삭제하세요.

image

네, 그리고 xaml의 내용에는 기본 스타일을 바로 작성할 수 있도록 ResourceDictionary를 추가해뒀어요. Enjoy it! :)

마지막으로 테스트를 해봐야죠? 새로 생성한 MyControlStyle.xaml에 다음과 같이 간략하게 코드를 작성해보죠.

image

이제 빌드를 하면?

image

/themes/generic.xaml이 외부에서 변경되었다는 메시지가 나오는데요 이건 어쩔 수가 없어요. 아쉽지만 매번 DefaultStyle로 설정한 xaml이 빌드될 때마다 Yes를 눌러줘야죠.

최종 결과물은 위와 같이 별도로 작성한 Style이 generic.xaml에 자동으로 복사된 모습. 자 이제 한 곳에 보기 싫게 몰아넣지 말고 각 커스텀 컨트롤 별로 연관성 있게 파일을 관리할 수 있어요.

단점이라면 하나의 xaml이라도 DefaultStyle을 사용한다면 generic.xaml이 자동으로 생성되기 때문에 모든 DefaultStyle을 별도의 파일로 관리해야 한다는 점이죠.

그렇지만 저는 각 커스텀 컨트롤의 스타일을 별도의 파일로 관리하고 공통으로 사용할 스타일이나 리소스 또한 common.xaml 등으로 따로 관리하는 것이 좋다고 봐요.

여튼 라이브러리나 커스텀 컨트롤을 많이 사용하는 프로젝트에서는 굉장히 유연성있는 스타일 관리를 할 수 있는 좋은 방법이죠. 피드백 바랍니다. :D

Posted by gongdo

개발자들이 블렌드에서 작업을 하기 꺼려하는 이유 중 하나는 바로 XAML을 직접 편집할 때 인텔리센스를 지원하지 않는 다는 점이죠!

잘 알려지진 않았지만 블렌드는 외부 애드인을 사용할 수 있는 준비가 되어 있는데요, 이미 블렌드 2의 CTP 시절에 애드인을 사용하여 인텔리센스를 지원하는 방법이 코드플렉스에 소개되었었죠.

참고 : Intellisense For Expression Blend

당연하겠지만 버전 문제로 위의 링크에 있는 코드는 제대로 동작하지 않아요. 다행히 소스를 제공하기에 새 블렌드 버전에 맞춰서 어셈블리 참조만 바꿔서 동작하도록 수정했고 설치도 한방에 되도록 간단한 커맨드 라인 툴도 만들었어요.

 

파일을 다운 받아 압축을 해제한 후 install.cmd를 반드시! 관리자 권한으로 실행하면 다음과 같이 설치되었다는 메시지가 표시되고 끝. 간단하죠?

그런데 블렌드의 애드인은 설정 등의 값으로 설정하는 것이 아니라 매 실행마다 파라미터를 통해 전달해줘야 해요. 그래서 Blend.exe를 직접 실행해서는 안되고 바로 가기 링크를 바꿀 필요가 있죠. 위의 설치 과정에서 자동으로 바로 가기의 링크도 수정해줘요. 단, 해당 바로 가기는 Windows Vista 이상에서만 동작할거에요. XP에서 사용중이라면 다음과 같은 커맨드라인 파라미터를 사용하여 애드인을 실행해야 해요.

start Blend.exe -addin:Addins\Expression.Blend.IntelliSense.dll

위의 명령어는 블렌드가 설치된 C:\Program Files\Microsoft Expression\Blend 2\Blend.bat 라는 이름으로 자동으로 복사가 되니 참고하세요.

 

여튼 이렇게 프로그램 바로 가기로 실행하면…

XAML 뷰에서 인텔리센스가 지원되는 것을 볼 수 있어요. 물론 비주얼 스튜디오에서 지원하는 것에 비교하면 정말 허접하지만 그나마 없는 것보다는 낫네요. 또 한가지 아쉬운 점은 Split 모드에서는 인텔리센스가 지원되지 않는 다는 점인데 혹시 관심 있는 분이라면 코드플렉스에서 소스를 다운받아 수정해 볼 수도 있겠죠.

Posted by gongdo

SBS드라마 가문의 영광의 홈페이지가 오픈되었는데요, 실버라이트를 적극적으로 활용하고 있어서 소개합니다. ^^

http://tv.sbs.co.kr/gamun/

image

일단 대문부터 실버라이트를 도입했다는 점, 이제 실버라이트를 활용하는 것에 자신이 생겼다고 말 할수도 있겠죠?

image

메인 화면도 큼지막하게 적용했네요. 각 메뉴를 나름 심플하면서도 재미있게 배치했고요 촬영장 스케치나 예고편은 메뉴에서 미리보기를 제공하네요.

우측 상단은 광고…인데 위아래 검은 영역이 없었으면 좋았을 걸 그랬어요. 뭔가 레이아웃이 깨져보이네요. 전체적으로 가장 아쉬웠던 부분.

예고편 미리보기 페이지에도 실버라이트가 적용되어 있네요.

http://tv.sbs.co.kr/gamun/ria/index.jsp?page=preview

image

오른쪽에 메뉴를 눌러보면 세심하게 애니메이션 처리를 한 것을 볼 수 있어요. 근데 저는 애니메이션 속도가 빠른게 좋은데…^^

기본적인 동영상 플레이어가 갖춰야 할 기능들과 구성을 갖추고 있네요. 사용자 입장에서야 귀찮지만 광고모델도 충실하게 구현하고 있고요.

세부적으로는 약간씩 아쉬운 점이 눈에 띄긴 하지만 이제 막 오픈한 따끈따끈한 사이트니까요^^

국내 최초의 실버라이트 서비스인 NView부터 뉴스 뷰어를 거쳐 드라마 홈페이지까지… 국내에서는 SBS가 실버라이트를 가장 적극적으로 도입하고 있는 사이트 중 하나죠. 듣기로는 SBSi의 내부 인력들이 캐고생해가면서 만들었다고 하는데요, 앞으로도 멋진 서비스가 하나하나 만들어지길 바래요.

Posted by gongdo


훈스닷넷 제 2회 실버라이트 컨퍼런스에 참여해주신 여러분께 감사드려요^^

P.S.
이미지 만들어준 294님 감사.
Posted by gongdo
실버라이트 애니메이션 모델에서 가장 아쉬운 점 세 가지를 들어보자면,
1. Path를 따라가는 애니메이션 작성 불가.
2. 임의의 스토리보드를 거꾸로 재생 불가.
3. 스토리보드의 특정 키프레임 혹은 특정 시점에서 발생되는 이벤트 부재.
정도에요.

이 중에서 스토리보드를 거꾸로 재생하는 것은 어느 정도 제한이 있긴 하지만 상당히 간단한 코드로 가능해요.
피터씨가 포스팅한
10. Expression Blend_ListBox 간지나게 보이기
11. Expression Blend_Menu에서 스토리보드를 거꾸로 재생하는 프로토타입 코드가 있었죠.

우선 동작 완구부터...

별거 없어요. 그냥 Forward하면 정방향으로, Reverse하면 역방향으로 애니메이션이 진행되는 거죠.
위 정도의 애니메이션이라면 그냥 노가다로 거꾸로 진행되는 걸 하나 만들어도 그만이죠.
그러나 애니메이션에 들어가는 키프레임이 수백개가 넘는다면? 악몽이죠. 그걸 거꾸로 돌리고 싶다면...

역방향 재생을 어떻게 할 수 있을지 간단하게 정리해 보자면...
1. 실버라이트는 스토리보드를 거꾸로 재생하는 메서드는 제공하지 않지만 AutoReverse 속성을 true로 설정하면 한번은 정방향으로 재생하고 그 직후 다시 역방향으로 재생할 수 있어요.
2. 또한 실버라이트의 스토리보드는 Seek 메서드를 사용하여 특정 시간으로 점프할 수 있어요.
3. 그러므로 역방향으로 재생할 때에는 스토리보드의 AutoReverse 속성을 true로 설정한 후 스토리보드를 처음부터 시작하고 곧바로 Seek메서드를 호출하여 스토리보드의 총 길이 만큼 이동하면 그 직후 AutoReverse에 의해 스토리보드가 거꾸로 가게 되죠.

이 외에도 범용적으로 사용하기 위해 스토리보드의 실제 총 길이를 계산하는 메서드도 필요하고 정방향으로 재생할 때에는 AutoReverse에 의해 역방향으로 재생되지 않도록 적절한 시점에 Pause를 걸어주는 처리도 필요하죠.

이 모든 처리를 하나의 클래스(ReverseStoryboard)에서 사용할 수 있도록 만들어봤어요.
소스 코드를 다운로드해 보시면 쉽게 이해할 수 있을거에요.

사용법은 간단해요.
// 타겟 스토리보드를 가지고 새 인스턴스 생성
private ReverseStoryboard rs = new ReverseStoryboard(TargetStoryboard);
rs.BeginForward();  // 정방향으로 재생
// 또는...
rs.BeginReverse();  // 역방향으로 재생
게다가 빌트인 Storyboard처럼 Completed 이벤트도 지원하고 Completed 이벤트에서 정방향 재생이었는지 역방향 재생이었는지를 알 수 있어요.
참 쉽죠? ;)

Posted by gongdo
2008/11/06 - [프로그래밍/Silverlight] - XAP사이즈, 압축하지 않겠는가?
전에 Rexapper.exe라는 아주 유용한 툴에 대해 소개했는데요, 거기에서는 각 프로젝트의 Build Event의 Post Build에 Rexapper를 실행하도록 하는 방법을 썼죠.

그런데 어떤 경우에는 프로젝트의 설정으로 강제로 넣기보다는 최종 배포 직전에 배포자가 명시적으로 커맨드를 실행하고 싶은 경우도 있고 혹은 Rexapper가 설정되지 않았던 프로젝트의 결과물을 단순히 압축하고 싶을 때가 있을거에요.

이럴 때는 다음의 내용을 메모장에 붙여넣고 rexapper.reg 파일로 저장한 후 관리자 모드로 실행하면 탐색기에서 .xap 파일의 컨텍스트 메뉴를 호출하면(마우스 오른쪽 클릭하면) 제일 위에 ReXap이란 명령어가 보이고 그걸 클릭하면 Rexapper가 해당 xap파일을 압축해요.
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.xap\Shell\ReXap\Command]
@="d:\\rexapper.exe -xap \"%1\""
물론 위에서 rexapper.exe의 경로는 환경에 맞도록 수정해야 겠죠?
ReXap 복용전

ReXap 복용후! 무려 20%나 용량 감소!!

그러니

Posted by gongdo
스캇구 횽님이 실버라이트 3에 대한 떡밥을 살포하셨네요. 이런 건 물어주는게 예의죠^^
그냥 스크린 샷만 봐도 배가 부를 떡밥이에요.
바빠서 세줄 요약.
  • 내년에 실버라이트 3 출시 계획
  • H.264 지원, 하드웨어 가속 지원 등의 미디어/그래픽 처리 향상
  • Visual Studio의 Design 뷰에서 직접 오브젝트 편집 지원
한줄 요약.
블렌드는? 블렌드는? 블렌드는?
Posted by gongdo

실버라이트 애플리케이션을 배포하는 것에서 가장 중요한 원칙은 뭘까요? 밑줄 긋고 외웁시다.

배포 사이즈를 작게 더 작게!

우리는 XAP 파일이 표준 ZIP 압축 알고리즘을 쓰고 있다는 점을 알고 있죠. 그런데 Delay’s Blog의 포스팅에 의하면 실버라이트 2의 XAP 파일은 압축률이 일반적으로 사용되는 것보다 낮다고 해요. 7-Zip의 압축률을 기준으로 1~3 단계 정도면 용량도 더 줄어들면서 압축 시간은 거의 차이가 없는데 여튼 여기에 착안해서 XAP의 압축률을 변경하여 압축하는 것만으로도 약 20~22% 정도의 용량이 줄어드는 마법(!)같은 효과를 얻을 수 있다는 군요.

저도 예전에 실버라이트 1때에는 7-zip command line 툴을 이용해서 비스무레한 일을 했던 적이 있는데 아무래도 2에서는 XAP을 자동으로 만들어줘서 그러려니 했었죠.

여튼 WEB-SNIPPETS 블로그에서는 이 작업을 좀 더 쉽게 해주는 유틸리티를 공개했는데요, 간단하게 옮겨 보죠.

  1. ReXapper를 다운로드
  2. 받은 파일의 압축을 풀어 임의의 장소에 복사(예 : D:\Utils\ReXapper\ReXapper.exe)
  3. 실버라이트 프로젝트의 Properties(속성)을 열어 Build Event탭의 Post-build event command line 박스를 찾아가서
  4. 다음의 코드를 붙여 넣기
    D:\Utils\ReXapper\ReXapper.exe –xap "$(TargetDir)$(TargetName).xap"

일단 저도 하나만 테스트를 해 봤지만, 348,854 bytes가 268,154 bytes로 약 20% 줄어드는 효과를 봤어요!

그러니…

Posted by gongdo


티스토리 툴바