9월 MSDN 세미나에 다녀왔어요. 평일이었는데도 인파가 몰려서 최근 .NET3.0, 특히 실버라이트에 대한 관심이 엄청나다는 걸 실감할 수 있었어요. MS의 세미나 룸은 적은 규모에서 진행하기는 나름 괜찮은 편인데 40명만 넘어도 찜통으로 변하는데에다가 뒤쪽은 상당히 집중하기 힘든 구조라서 다음엔 좀 더 쾌적한 곳에서 진행했으면해요. 하지만 초반에 좋은 자리를 잡은 저는 급하게 배급된 아이스크림 한개로 충분했죠. :)

전 닷넷이고 C#이고 시작한지 얼마 되지도 않았지만 닷넷 3.0이 우리가 생각하는 것보다 훨씬더 깊은 흐름을 가지고 있는 것 같아요. 하지만 강성재과장님이 언급하셨듯이 3.0이 혼자만 너무나도 급하게 나온게 아닌가 하는 생각도 들어요. 3.0이 서둘러 나오는 바람에 제가 무척이나 그 활용성에 대해 기대하고 있는 LINQ라던가 언어적 특성들 -Anonymouse types, var type, Initializer...- 이 3.5라는 다소 애매한 버전으로 출시되고 지금도 그리고 앞으로도 처음 접하는 분들에게 많은 혼란을 줄 것 같아요.

뭐, 비스타에 3.0을 탑재하기 위해서...라는 걸 이해하지 못하는 건 아니지만 과연 이 닷넷 프레임웍 3.5가 자동 업데이트에 포함되는 기본 프레임워크로 채택이 될지 여부도 벌써부터 골치 아파져요. 제가 약간 졸아서 발표를 헛들었을 수도 있지만 조금 더 추가할께요. 사실 닷넷 프레임워크의 코어는 2.0이고 3.0과 3.5는 일종의 피추어셋이라고 할 수 있다고 해요. 그도 그럴 것이 3.0부터 지원되는 WPF, WCF, WF, CartSpace와 관련된 기능을 제외한 코어는 -아마도- 거의 바뀐게 없거든요. 물론 3.0에서 추가된 이 기능들은 하나하나가 눈돌아가는 녀석들이지만요.

또 비스타에는 닷넷 2.0이 통합integrate 되고 3.0은 프리 인스톨pre-install되어서 출시된거라고 들었는데요, 뭐 통합이나 프리 인스톨이나 말장난 같지만 Win2k에 DX9, IE6와 SP5를 통합하기 위해 몇일 밤을 새워보신 분이라면 이 차이는 꽤 크다는 걸 알 수 있을거에요. 사용하는 입장에서야 그거나 그거나이지만요. :(

.NET 3.0~의 새로운 피추어중 WPF는 보여줄게 많은 녀석이라 항상 많은 데모를 끌고 다니고 있죠.^^ 저도 WPF를 매우 좋아하지만 요새 실버라이트에 빠져서 잠시 접었고, 다른 녀석들로 얘기해보죠.

WCF는 다른 무엇보다 프리젠테이션 자료가 예쁘게 잘 만들어졌더군요. WCF를 사용한 데이터의 흐름이 어떻게 이루어지는지 정말 직관적으로 이해할 수 있을 만한 자료였어요. 네트워크라는게 코드 하나하나 설명하는 것보다 전반적인 흐름이 어떻게 되는지 이해하는게 중요하잖아요. 저는 WCF를 처음 접했던 세미나에서는 꾸벅꾸벅 졸았거든요. 계약이 어쩌고 바인딩이 어쩌고.. zzz... WCF는 주로 서버측에 초점이 모일 수밖에 없고 WCF가 많은 종류의 프로토콜을 통합하여 지원하기 때문에 유연하게 연결할 수 있다는 장점을 가지는 것 같아요. 이런 점에서 지금 가장 많이 데모되고 있는 것은 WPF이지만 실제 업무에서 크게 효과를 볼 수 있는 가능성은 WCF가 가장 크다고 생각해요.

WF는 제 머리속에서 명확하게 그려지는 모델이 없어서 막연해요. 작업의 컴포넌트화... 언제나 얘기되었던 것이지만 과연 그것이 쉽게 될지는 확신이 안가요. 제가 이전 회사에서 다뤘던 콜센터 서비스만해도 수 없이 많은 콜센터에서 각기 나름의 업무 규칙으로 돌아가고 이것을 컴포넌트화 한다는게 정말로 어려운 일이었거든요. 발표에서도 사람의 손이 들어가는 일을 플로우에 태운다는건 쉽게 상상이 안가네요. 하지만 WF역시 굉장히 기대하고 있는 기술이고 조만간 좋은 데모가 나오겠죠.

이번 세미나에서 가장 다시 보게 된 기술이 CardSpace에요. 저도 어쩔 수 없는 국내 인터넷환경에 찌든 인간이라 개인의 PC에만 저장되는 인증 카드가 도대체 무슨 소용일까..라고 생각했었어요. 물론 이것은 국내 환경에서는 사실이고, 당장은 큰 메리트가 없겠죠. 하지만 제 아무리 PC방 천지인 우리나라라도 5년, 10년 후의 컴퓨팅 환경을 떠올려보면 개인 인증 수단으로서 CardSpace가 가진 직관성은 매우 유효할 것 같아요. 그 때쯤이면 아마도 휴대용 PC기기의 성능이 지금보다 월등히 높아져 있을테고 개인 정보가 담긴 PC를 항상 휴대한다는게 불가능하지만은 않을거에요. 그리고 보안이나 인증에 대한 인식도 서서히 바뀔테구요. 적어도 지금처럼 부턱대고 주민번호부터 받진 않겠죠.^^ 게다가 지금도 서울지역내에서 서비스되는 와이브로를 보면 유비쿼터스란게 그렇게 멀지는 않아보여요. MS는 바로 이런 5년, 10년 후의 미래를 생각하며 개인 식별/인증 기관으로서의 입지를 강력하게 굳히기 위해 이런 '카드'를 꺼내든거라고 생각해요. 근데 CardSpace가 처음 나왔을 때만 해도 전 MS가 패스포트에 대한 미련을 버리지 못하고 우겨넣은 기술이라고 생각했었거든요. 설마 진짜로 그렇진 않겠죠? MS의 분들 혹시 보고 계시면 비밀댓글로 진실을 살짝 귀뜸해주세요!

아.. 그러고보니 세미나 타이틀은 2008은 까맣게 잊고 있었네요. 뭐 기본적인 베이스는 2005와 별로 다를 게 없어요. 써보신 분들에 따라 다르지만 저는 2005보다 아주 쬐에~끔 가볍다는 느낌이 들고요. 가장 큰 피추어 추가는 데모로 보여주신 성능/행동 분석 툴이 아닐까 싶네요. 이 부분은 과연 비주얼 '스튜디오'라고 불릴 만한 탁월한 통합인 것 같아요. 발표하신 것처럼 이제 개발자들 농땡이 부리면서 시간 축내는건 더 이상 힘들겠죠? ㅠ.ㅜ 개발자 여러분 이 기능은 최대한 조용히 넘어갑시다!!

정리하죠. WPF, WCF, WF, CardSpace를 보면서 느끼는 건 발표 자료에서 얘기되었던 것 처럼 이제는 개발이 성능 이슈나 특수한 기능이 중요한게 아니라 생산성과 유지보수의 측면이 더욱더 강조되고 요구되고 있다는 점이에요. 이 기능들 하나하나가 모두 지금의 기술로도 불가능한 것은 아니겠죠. 이미 구현된 프레임워크들도 있고요. 하지만 지금까지의 기술들이 각자 다른 도구, 다른 언어, 다른 방법, 다른 표현으로 제각각 따로 놀고 있었다면 닷넷 프레임워크 3.0 아래에 모두 같은 도구, -CLR로 통합되는-같은 언어, 같은 방법, 같은 표현으로 통합되고 있다는 것이 닷넷 프레임워크 3.0 기술들과 비주얼 스튜디오가 가진 가장 큰 이점인 것 같아요.

2008년은 -이제 시작했을 뿐이지만-닷넷 개발자로서 엄청나게 기대되는 해에요. 기존의 사이트를 유지보수 하고 계신 여기저기서 '또 바꿔야돼 ㅠ.ㅜ'라는 곡소리가 들리지만(^^) 이제 시작하는 저에게 2008년에 정식 출시되는 Server 2008, IIS7, VS2008, 그리고 .NET Framework 3.5, 마지막으로 실버라이트는 마음을 들뜨게 하고 있어요. 앞으로 개발이란 재밌는 것이다라고 당당하게 얘기할 수 있을지도 몰라요! 뭐가 나오더라도...



쫄지 마세요! 우리는 개발자에요!!

신고
Posted by gongdo
http://hoons.kr/SeminarJoin.aspx

아직 시간이 꽤 남았지만... 훈스닷넷에서 6월 세미나는 패스하고 7월 초에 열립니다.
기본적으로 WPF 컨퍼런스지만 제도 Silverlight 관련 세션을 맡았으니 관심 있는 분들의 많은 참여를 바래요 :)

일시 : 2007년 07월 07일(토) 오전 13시 30분
장소 : 한국 Microsoft 세미나실
세미나 등록비 : 2.000원 (현장 결제)
6회 정기 세미나 신청하기 : http://www.hoons.kr/SeminarJoin.aspx 
 
세미나 Agenda
13:30~14:00
등 록
14:00-14:50
차세대 웹을 위한 RIA 기술의 이해와 그 미래   / 박경훈 (HOONS닷넷 운영자)
14:50-14:20
WPF를 개발하기 위한 툴 소개   / 최우진 (HOONS닷넷 시삽)
14:20~14:40
휴식
14:40~15:10
How to develop XPS with WPF / 김수영 (HOONS닷넷 시삽)
15:10~16:00
실버라이트로 구현하는 포토앨범 / 공도 (유령회사 공도소프트 운영자)
16:00~16:20
휴 식
16:20~16:50
사이드바 가젯 어플리케이션의 구현 / 김창우(HOONS닷넷 시삽)
16:50~17:40
WPF와 WCF로 메신저 만들기 / 서금욱(HOONS닷넷 시삽)
17:40~
경 품 추 첨
발표 내용 소개
차세대 웹을 위한 RIA 기술의 이해와 그 미래 / 박경훈
RIA(Rich Internet Application)라는 기술이 차세대 웹의 트랜드라는 말과 함께 빠르게 전파되어 가고 있습니다. 먼저 이번 시간에는 각 벤더의 RIA 기술들을 살펴 봅니다. 그리고 그 기술을 어떤 시각으로 바라보며 어떻게 적용해야 하는 지에 대한 내용을 전해드립니다. 또한 앞으로의 미래의 RIA 기술을 위해서 어떻게 준비해 나가야 할지에 대한 내용을 다룹니다.
WPF를 개발하기 위한 툴 소개 / 최우진
WPF를 개발을 위해서 상당히 많은 툴들을 제공하고 있습니다. 이번 시간에는 그 툴들을 살펴보고 어떤 개발에 어떤 툴을 도입해야 되는지에 대한 기초적인 내용들을 살펴 보겠습니다.
How to develop XPS with WPF / 김수영
XPS는 MS의 새로운 문서 포맷으로 동적인 무서를 제공할 수 있게 도와 줍니다. 뉴욕타임즈의 XPS 리더기는 이미 많은 사람들로 하여금 그 가치를 느끼게 해 주었고 WPF에서 XPS를 활용할 수 있는 방안도 무궁무진합니다. 이번 시간에는 XPS에 대한 개념과 WPF로 개발하여 활용할 수 있는 방법들을 설명드리겠습니다.
실버라이트로 구현하는 포토앨범 / 공도
Silverlight가 MIX'07을 통해 화려한 데뷔를 했지만 국내에서는 찻잔속의 태풍인 것 같습니다. 아마도 새로운 기술에 대한 접근 장벽도 있지만 단순한 문법 소개나 예제 만으로 어떤 애플리케이션을 어떻게 작성해야 하는지 감이 오지 않아서 더욱 그런 것 같습니다. 이 세션에서는 단순한 문법적인 설명이 아니라 실제 프로덕션 수준의 데모를 통해 Silverlight의 가능성과 활용 방안을 제시하고 특히 사용자 컨트롤 구현에 대한 트릭과 팁을 소개합니다.
사이드바 가젯 어플리케이션의 구현 / 김창우
기존 웹2.0의 참여와 개방에서 웹 3.0이 화두가 되면서 개인화를 위한 요소가 강조되기 시작했습니다. 그 개인화를 위해서 위젯과 MS의 가젯과 같은 개념들이 등장하게 되었다고 할 수 있습니다. 이번 시간에서는 윈도우 비스타의 사이드바 가젯을 만들고 활용하는 방안들을 설명드리독 하겠습니다.
WPF와 WCF로 메신저 만들기 / 서금욱
RIA(Rich Internet Application)라는 기술이 차세대 웹의 트랜드라는 말과 함께 빠르게 전파되어 가고 있습니다. 먼저 이번 시간에는 각 벤더의 RIA 기술들을 살펴 봅니다. 그리고 그 기술을 어떤 시각으로 바라보며 어떻게 적용해야 하는 지에 대한 내용을 전해드립니다. 또한 앞으로의 미래의 RIA 기술을 위해서 어떻게 준비해 나가야 할지에 대한 내용을 다룹니다.
강사 소개
박경훈 

HOONS닷넷 운영자
Microsoft Visual C# MVP
김수영 

(현) 닷넷 프리랜서 개발자
HOONS닷넷 C# 시삽
Microsoft Visual C# MVP
김창우 

(현) 넥슨 개발자
인천정보통신 교육센터 전임강사
RAYPAX - 사내 솔루션/시스템 개발
HOONS닷넷 C# 시삽
공도 

(현) .NET Framework 3.0, 특히 Silverlight에 올인중
수년간 CTI 업계에 종사(VB6 및 VC6)
유령회사 공도소프트 운영자(gongdo.tistory.com)
최우진 

(현) 주식회사 낱말 재직 중
독서 지수(Lectio®) 측정 솔루션 개발
교보 READ 사이트 - 독서력/어휘력 평가 솔루션 개발
CAT 기반 독서력 측정 사이트 작업 중
MCP(켁~), CCNA, 정보처리기사 \BK21
대전.충남지역 대학생 학술 경연 대회 금상 수상 (Virtual My Home 3D)
김창우 

(현) 넥슨SD 개발지원팀/
       월간 마이크로소프트웨어 기사기고
(개발)
- 해석모델 관리시스템(CAES)
- 핼프데스크(Groupware)
- 도면관리 시스템개발
- 시작차 작업 관리 시스템(PWMS) - 아이템25 개발
세미나 장소

[포스코 센터에 오시는길 : 포스코 센터 서관 5층]
경품안내

HOONS 닷넷과 함께하는 .NET Framework 3.0(박경훈,서동진) - 2권


C# 프로그래밍 바이블 with .Net Framework3.0(최재규) - 3권


게시판과 블로그 예제로 배우는 ASP.NET 2.0(김상훈) -2권
세미나 후원
 
신고
Posted by gongdo
어쩌면 데일리절망으로 분류해야 할지도 모르겠네요.

한 3주 전쯤에 WPF 강좌를 한번 해보려고 열심히 후벼팠던 인트로 영상인데요 그간 중국 출장도 있었고 갔다와보니 Silverlight이 의외로 괜찮아서 WPF쪽 관심이 좀 시들해진 것도 있네요.
기껏 만들어 놓은게 아까워서 올려봅니다. -ㅅ-

이 영상은 Expression Blend와 Visual Studio 2005를 사용하여 WPF로 만든걸 캡쳐한거에요.
인트로의 유치함에 대해 미리 변명하자면 전 개발자에요!!

(원본 사이즈는 800x600이에요. 원본 사이즈는 : http://gongdo.tistory.com/attachment/cfile30.uf@240F18425878F5572BD1BE.avi)

요즘 새로운 정보가 그야말로 쏟아져 나오고 있어서 한동안 정신을 못차리고 있었는데 좀 수습을 하고 Silverlight과 시간이 된다면 WPF 정보도 함께 계속 포스팅할 예정이에요. :)

P.S.
캠타시아로 캡쳐했는데 TechSmith의 캠타시아 기본코덱이 제일 깔끔하게 나오는 것 같아요.
윈도 화면 캡쳐용으로 적당한 avi 코덱이 없을까요?
몇 가지 써봤는데 적당한 코덱(또는 옵션)을 못찾겠네요.
신고
Posted by gongdo
이전 포스트에서 Orcas RC1에서는 2005와 달리 Image 엘리먼트의 Source 속성에 siteoforigin PackURI를 사용할 수 없다고 했는데요, Beta1에서는 디자인 타임에서는 여전히 siteoforigin에서 불러온 소스를 표시하지는 않지만 런타임에는 제대로 동작을 합니다.

------------------------------------------------------------------------
요약
Visual Studio Orcas RC1 및 Beta1에서 XAML의 Image 엘리먼트의 Source 속성에 siteoforigin authority를 사용한 PackURI를 사용하면 RC1에서는 디자인타임과 런타임 모두 해당 URI를 인식하지 못하고 Beta1에서는 디자인타임에만 표시되지 않습니다.

재현
1. Visual Studio Orcas RC1 또는 Beta1를 실행합니다.
2. File->New->Project 또는 Ctrl+Shift+N을 눌러 새 프로젝트 다이얼로그를 띄우고 Windows 카테고리에서 WPF Application을 선택하고 [OK]를 누릅니다.
3. 생성된 프로젝트 경로에 아무 이미지(여기서는 a.gif)를 복사해놓고 Window1.xaml을 다음과 같은 코드로 작성합니다.

XAML
<Window x:Class="WPFApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Image Source="pack://siteoforigin:,,,/a.gif" Stretch="None" VerticalAlignment="Top" />
    </Grid>
</Window>
4. Ctrl+Shift+B 또는 F6를 눌러 프로젝트를 빌드하고 이미지를 bin/debug(또는 Release)에 복사한 후 F5을 눌러 실행해 봅니다.
5. Orcas RC1에서는 이미지가 보이지 않고 Beta1에서는 이미지가 보이며 둘 다 디자인 모드에서는 표시되지 않습니다.
------------------------------------------------------------------------

아직 제대로된 해결책은 안나왔어요.

사실 이것 말고도 VS 2005, Orcas에 내장된 XAML 그래픽 편집기(코드명 Cider)는 아직 이런저런 문제를 가지고 있는 것 같아요. 반면 XAML 텍스트 편집기는 매우 훌륭하게 동작되고 인텔리센스도 잘 지원해주고 있지요.

일단 제 경우는 Visual Studio에서 XAML을 건드릴 때는 Cider를 아예 숨겨놓고 쓰고 있어요. XAML을 디자인적으로 수정할 때에는 그냥 Expression Blend를 쓰는게 훨씬 편하니까요. 그런데 Blend와 Visual Studio를 왔다갔다 하다보면 또 XAML코드가 꼬이는 현상도 있어서 그것도 문제지만요. -_-

이런 문제가 나오는 건 아마도 XAML 편집기들이 아직 성숙하지 않아서이기도 하고 개발자 혹은 디자이너도 이런 방식의 편집에 익숙치 않아서일거에요. 늘 그렇듯이 시간이 해결해주겠지요.

그나마 Beta1은 런타임에서는 siteoforigin URI를 인식하고 제대로 동작하니까 특별한 문제는 없을 것 같네요.
저는 이제 2005는 아예 설치도 안하고 Orcas Beta1을 쓰고 있답니다. 해피~ :)
신고
Posted by gongdo

XAML이 컴파일 될 때2

지난 포스팅
에서 WPF 애플리케이션이 컴파일될 때 배경에서 일어나는 일들을 알아봤습니다.
가장 중요한 내용은 XAML이 컴파일 될 때 obj/Release (혹은 Debug)폴더에 name.g.cs라는 이름의 컴파일러가 자동으로 생성하는 코드가 작성된다는 것이었죠. 그리고 미처 소개하지 못한 사실이 하나 더 있다고 예고했는데요, 바로 .baml 파일입니다.

마크업의 약점 
대표적인 마크업인 HTML을 생각해보면 마크업이 가질 수밖에 없는 치명적인 약점을 알 수 있는데요, 바로 마크업이 담고 있는 개체들을 렌더링하는 것보다 마크업 자체를 개체화 하기위해 구문을 파싱하는 노력과 시간이 훨씬 더 많이 들어간다는 점이죠.

일반적으로 자료 구조중에 텍스트가 처리하는 데 프로세서, 메모리를 가장 많이 잡아먹고 그 중에서도 문자열 파싱이 특히 그렇다고 해요.

그렇다면 XAML은?
XAML은 이런 마크업의 약점을 보완하기 위해 프로젝트 컴파일시 XAML 파일 역시 컴파일하여 바이너리 형태로 변환합니다. MSDN의 http://msdn2.microsoft.com/en-us/library/aa970678.aspx#The_Windows_Presentation_Foundation_Build_Pipeline 을 보면 컴파일된 XAML 파일(compiled XAML file)이란 표현을 사용하는데요, 이것은 정확하게는 name.baml 파일을 의미하며 BAML의 B는 Binary를 말합니다.

MSDN에서 설명하는 BAML은 미리 분석되어(pre-tokenized) 런타임에 XAML파일보다 빨리 로딩될 수 있다고 하네요.

새 윈도 애플리케이션 프로젝트를 시작하고 빌드한 후 obj\Release 폴더를 보세요.
아마 Window1.baml 이란 파일이 있을거에요. 앞서 말한 바와 같이 이 파일은 바이너리니까 열어봐도 별 소용은 없을거에요.

어쨌든 XAML은 마크업이 가지는 로딩시 파싱 속도의 문제를 미리 컴파일하여 바이너리화 된 BAML 파일을 생성함으로써 극복하고 있다는 걸 알 수 있습니다.

겨우 이거?
WPF의 초기엔 BAML에다가 CAML이란것도 있었어요. Compiled XAML이란 건데, 이건 WPF의 개발 과정에서 BAML이 충분히 CAML의 속도를 따라 잡았고 CAML은 지역화가 불가능한 문제가 있는 등 여러가지 사정으로 CAML은 사라지고 BAML만 남았지요.

롱혼 SDK에는 XAML을 BAML로 컴파일하는 Xamlc.exe란 애플리케이션도 있었는데 지금은 MSBuild 엔진에 모두 통합되어 더이상 사용되지 않는다고 하네요.

또 과거 WPF를 위한 .NET API에서 LoadBAML이란 메소드가 존재했었는데 이것도 지금은 사라지고 LoadComponent란 메소드로 대체되었고 이 LoadComponent는 XAML이건 BAML이건 가리지 않고 제대로 로드하게 되어있다고 하네요.

지난 포스팅을 쓸 때만 해도 아직 이 사실들을 제대로 모르고 BAML이란 숨겨진 요소(?)에 꽤 흥분했었거든요. 그래서 뭔가 재밌는 비밀이 있을 걸로 기대했는데, 결과적으로 최근엔 개발자를 불편하게 하는 차이점은 죄다 통합되고 사라지게 되었네요. 썰렁;

신고
Posted by gongdo

수학은 절망에서 얘기한 임의의 사각 영역내에서 최대 면적을 가지는 정삼각형을 그리는 예제를 WPF로 만들어봤습니다.

아래 동영상을 보면 뭘 하고 싶었는지 알 수 있을거에요.;;



이를 위해 사용한 알고리즘(...)은 수학은 절망에서 그려놓은 그림과 같구요, 구현한 소스도 첨부했으니 한번 봐주세요.
MaximizedTriangle.zip

최대 면적의 정삼각형 그리기


그래픽 프로그래밍을 해보지 않은 초보가 단지 두어시간 투자해서 이정도 결과물을 얻기란 쉽지가 않다고 생각하는데, MSDN도 정말 정리 잘되어있고 forum.microsoft.com에서도 쓸만한 정보를 얻을 수 있었어요. WPF가 정말 강력하고 편리하다는건 인정 안할수가 없을거에요.

XAML에 대해 상세히 알아보는 글을 올려야 하는데 예제 만든답시고 딴데로 빠졌네요.-_-
게다가 다음주는 내내 중국에 갈 예정.
XAML에 관해서는 다다음주에나 올릴 수 있을 것 같습니다.

...뭐, 관심있는 분도 기다리는 분도 없는 것 같긴 하지만요. -ㅅ-
신고
Posted by gongdo
오 Main이여 어디있는가?

애플리케이션이 실행 될 때
많은 프레임웍이 그렇듯 WPF도 애플리케이션을 대표하고 애플리케이션 전역에서 공통적으로 필요한 많은 기능들을 제공하는 클래스를 제공합니다. VC프로그래머라면 MFC에서 애플리케이션이 CWinApp를 상속받은 클래스를 theApp라는 전역 개체로 인스턴스화 하여 사용했던 기억이 날 거에요.

WPF는 그러한 애플리케이션의 대표자로써 Application 클래스를 제공합니다. Application 클래스는 .NET Framework 3.0에서 System.Windows 네임스페이스에 정의되어 있고 마크업(XAML)에서는 Application 엘리먼트로 표현되지요.
예를 들어 XAML에서 Window1.xaml을 시작 윈도로 정의하는 애플리케이션을 다음과 같이 정의할 수 있습니다. (XAML에 대한 내용은 이 글의 주제에서 벗어나니 다른 문서를 참고해주세요.) 어떻게 만드냐구요? 간단해요 이 포스팅을 확인하시고 먼저 .NET Framework 3.0 개발환경을 갖추신 후 VS2005에서 새 프로젝트->C#->.NET Framework 3.0->Windows Application(WPF)를 선택하세요.

APP.XAML

<Application

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    x:Class="App" StartupUri="Window1.xaml" />

WINDOW1.XAML

<Window

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    x:Class="Window1" Width="300" Height="300" />


컴파일하고 빌드해보면 오 놀라워라 애플리케이션이 실행되네요!

윈도우즈 개발자라면 여기서 놀라야 해요. 왜냐? 무슨 언어를 사용하든 윈도우즈 환경에서 윈도우즈 애플리케이션(여기에서는 exe형태로 실행가능한 독립 실행형 애플리케이션)은 Entry point라고 불리는 메인 프로시저(WinMain)를 OS가 호출함으로써 시작되기 때문이죠. C, C++은 말할것도 없고 C#, 심지어 그 오래된 VB조차도 엔트리 포인트를 제공하는데, XAML은 생긴건 HTML이랑 별 차이도 없어보이는게 브라우저 상에서 표시되는 것도 아니고 곧바로 윈도를 띄울 수 있는 exe파일을 만들었잖아요?

놀란 가슴을 진정시키고 생각해보죠. WPF가 제 아무리 편리하고 화려한 기술로 무장하고 있어봤자 윈도우즈 애플리케이션입니다. 지가 무슨 재주로 엔트리 포인트도 없이 애플리케이션으로 실행될 수 있겠어요?
분명 자기가 여러분보다 스마트한줄 알고 있는 VS2005(혹은 Orcas)가 컴파일 과정에서 코드를 자동으로 생성 했음을 짐작할 수 있을거에요. 그런데 사실 VS2005가 저보다는 훨씬 스마트한 것 같아요. VS가 발전하다보면 제가 필요 없어질 날이 올지도 몰라요ㅠ.ㅜ

애플리케이션의 Main 찾기
그러면, 자동으로 생성된 파일은 어딨을까요?
여기에서 이 포스팅의 주제가 나옵니다. 오 Main이여 어디있는가?

아주 조금 눈치 빠른 개발자라면 회심의 미소를 지으며, 어쩌면 머리를 쓸어 넘기며 이렇게 얘기할 것입니다.
"훗, 범인은 솔루션 익스플로러 안에 있어. 범인은 app.xaml 바로 아래에 들어가 있는 app.xaml.cs 너다! 게다가 app.xaml.cs를 보면 Application을 상속하는 App 클래스가 정의되어 있어. 움직일 수 없는 증거지. 음하하핫!"

안됐지만 땡입니다.
app.xaml.cs도 물론 새 WPF 프로젝트를 만들때 위저드가 자동으로 만들어준 파일이긴 한데, app.xaml.cs를 삭제해도 아무런 이상 없이 잘 돌아갑니다. 정말이에요. 한번 새 WPF Windows 애플리케이션을 하나 만들고 app.xaml.cs 파일을 지워보세요. Window1.xaml.cs도 마찬가지구요.

그렇다면 진짜 범인은,
범인은.
범인은!
범인은?

해당 프로젝트 폴더아래의 obj\Debug (또는 Release)안에 App.g.cs안에 있어요. 끝.

썰렁.
옙. 썰렁하지만 그냥 이게 다에요.
...라고 하면 돌이 날라올지도 모르니 좀 더 자세히 얘기해볼께요.

XAML이 컴파일 될 때
서두에 WPF 애플리케이션은 Application 클래스로 대표되고 Application은 XAML에서 Application 엘리먼트로 표현된다고 했습니다. 이 말은 반대로 생각하면 XAML에서 Application 엘리먼트는 내부적으로 Application 클래스를 생성한다고도 할 수 있어요. 따라서 XAML에서 사용된 Application 엘리먼트는 우리의 스마트한 VS2005의 컴파일러가 내부적으로 Application 클래스를 정의하는 C#(또는 VB.NET) 코드를 생성할 거에요.

이렇게 컴파일러에 의해 자동으로 생성된 코드가 바로 "파일명.g.cs"(VB일 경우 .vb)인데요 추가 확장자 .g는 바로 자동으로 생성되었다는 Generated를 의미합니다.

기술적으로 좀더 정확하게 표현하자면 Application 엘리먼트는 반드시 x:Class 어트리뷰트를 통해 생성할 클래스의 이름을 정의해야하고 이렇게 정의된 클래스가 Application 클래스를 상속하여 코드로 구현되는거지요. 위의 예제에서라면 x:Class="App"라고 선언했으니 내부적으로 Class App : Application (VB.NET이라면 Class App Inherits Application)이란 코드를 생성해야겠죠. 이런 특성은 디자인타임에 프로젝트에 포함되어 컴파일되는 모든 XAML에 동일하게 적용되구요, 예를 들어 Window1.xaml이 어떤 클래스 정의을 가지고 있다면 Window1.g.cs란 파일이 생성되겠지요.

친절한 컴파일러씨
정말로 컴파일러가 필요한 클래스들을 정의했는지 app.g.cs를 열어볼까요? 눈치채셨겠지만 이미 다 확인하고 얘기하는거에요. 믿으세요. :-)

app.g.cs

//------------------------------------------------------------------------------

// <auto-generated>

//    This code was generated by a tool.

//    Runtime Version:2.0.50727.1302

//

//    Changes to this file may cause incorrect behavior and will be lost if

//    the code is regenerated.

// </auto-generated>

//------------------------------------------------------------------------------


using System;

// 나머지 using 선언은 생략...


namespace WindowsApplication2 {



    /// <summary>

    /// App

    /// </summary>

    public partial class App : System.Windows.Application {


        /// <summary>

        /// InitializeComponent

        /// </summary>

        [System.Diagnostics.DebuggerNonUserCodeAttribute()]

        public void InitializeComponent() {


            #line 4 "..\..\App.xaml"

            this.StartupUri = new System.Uri("Window1.xaml", System.UriKind.Relative);


            #line default

            #line hidden

        }


        /// <summary>

        /// Application Entry Point.

        /// </summary>

        [System.STAThreadAttribute()]

        [System.Diagnostics.DebuggerNonUserCodeAttribute()]

        public static void Main() {

            WindowsApplication2.App app = new WindowsApplication2.App();

            app.InitializeComponent();

            app.Run();

        }

    }


첫머리에 <auto-generated>라고 박혀있고 고쳐봤자 헛수고이니 삽질하지 말라고 친절하게 경고충고해주고 있네요.

다음으로 애플리케이션에서 사용하게될 어셈블리들을 using 문으로 선언해줬구요.
그 아래에 드디어 얘기 드린 Application을 상속하는 App 클래스의 정의가 나옵니다.
멤버 메소드로 InitializeComponent가 만들어져 있는데 자세한건 좀 이따가 보고 그 아래에 더 중요한 Main() 프로시저를 먼저 살펴볼께요.

네, 이 포스팅의 주제인 Main의 행방을 드디어 찾았네요. 이 시점에 바로 무릎을 치거나 이마를 치거나 손가락으로 딱 소리를 내면서 '그럼 그렇지 Main이 어디 가겠어!'라고 해야 합니다. 전 그랬어요. ㄱ-

Main의 코드 구성에 대해 설명드리는건 아마도 C#(또는 VB)를 해보신 분께는 무의미하겠구요, 그렇지 않은 분께는 죄송하게도 저 역시 .NET 언어를 제대로 공부하지 않은 상태라서 무리일 것 같습니다. 단순히 통빱으로 얘기드릴테니 헛소리한다 싶으면 가차없이 지적해주세요.

다시 반복하자면 Main() 프로시저는 곧 이 애플리케이션을 실행할 때 OS에 의해 호출되는 첫번째 프로시저이자 애플리케이션의 시작 지점이 됩니다. 흔히 엔트리 포인트라고 부르지요.
당연히 이 Main() 프로시저는 애플리케이션 전역에서 유일해야 하므로 Static 으로 선언되었죠.

바로 이 유일한 Main() 프로시저 내에서 우리가 작성할 애플리케이션의 새 인스턴스를 생성하고 뭐하는 놈인지 InitializeComponent 메소드를 호출한 후 너무나도 명백하게 애플리케이션을 시작하라는 Run 메소드를 호출하는게 전부입니다.

여기서 통밥이 있는 개발자라면 '아 Run은 애플리케이션이 어떻게든 종료되기 전까지는 반환되지 않겠군'이라고 감이 오실겁니다. 이에 대한 자세한 내용은 다음 기회로 미루고요. 먼저 밀려있는 InitializeComponent를 살펴보죠.

뭐 의미상으로는 명백합니다. 바로 해당 XAML에서 사용될 컴포넌트, 즉 마크업에서 정의된 엘리먼트나 엘리먼트의 어트리뷰트를 코드-비하인드의 클래스 및 속성으로 치환해주는 역할이죠.

곧바로 보기도 애매한 #line 지시자가 나오는데요, 보면 #line 4, #line default, #line hidden이 있네요. #line 4는 해당 라인의 해당 파일에 있는 코드라는 것을 의미하고, #line default/hidden은 디버깅시 브레이크 포인트를 보일지 숨길지 여부인데 어차피 이 코드들은 컴파일러가 자동으로 작성하니까 크게 중요하진 않아요.

중요한건, 아래의 코드죠.
this.StartupUri = new System.Uri("Window1.xaml", System.UriKind.Relative);
이건 좀 의미심장하죠? 바로 위의 #line 지시자가 가리키는 App.xaml의 4번째 라인을 볼까요?
StartupUri="Window1.xaml"
네, 바로 XAML에서 정의한 StartupUri가 어떻게 코드로 변환되었는지를 보여줍니다. XAML이 얼마나 직관적으로 프로그래밍을 할 수 있는지를 잘 보여줍니다.

나는 내 코드를 통제하고 싶다.
오 과연 친절한데다가 똑똑하기까지한 VS. 그간의 골치아픈 코드와의 전쟁은 가고 바야흐로 인간 중심적인 프로그래밍의 신천지가 열리는 것인가!

다시 한번 안됐지만 그럴리가 있겠습니까. 공짜 점심도 한두번이죠.
위의 예제야 단순한 코드라서 XAML의 장점이 눈에 띄지만 XAML이 만능은 아니에요.
분명히 XAML은 어떤 개체를 선언하고 속성을 설정하는데에는 C#, VB코드에 비해 보다 직관적이고 간단하지만 어떤 고도의 연산 작업이나 논리적인 상태를 관리하는데에는 절대적으로 코드가 우수합니다.

필연적으로 XAML의 선언만으로 코드를 자동 생성해서 쓰는게 아니라 내가 직접 작성한 코드를 끼워넣고 나아가 코드를 통해 XAML로 선언한 개체들을 제어할 필요가 있을 거에요. 개발자라면 내가 작성하는 애플리케이션의 코드는 내가 원하는대로 통제하고 싶어하는 게 당연하잖아요?

그런데 예제의 경우만 봐도 XAML에서 정의한 App 클래스가 멋대로 자동 생성 코드에서 정의 되어버렸죠. 그럼 자동으로 생성된 App 클래스를 수정해봤자 다시 컴파일하면 도루묵이 된단 얘기잖아요?!

상황이 역전되었지만 그럴리가 있겠습니까.
XAML에서 생성한 클래스는 얼마든지 개발자가 직접 작성한 코드에서 다시 정의할 수 있습니다. 바로 partial 키워드 덕분이죠.

쪼개고 쪼개고 또 쪼개고
객체지향의 개념이 등장한 이래로 코드 블럭은 점점 더 작아지고 갈라지고 전문화 되는게 일반적인 스타일이 되었죠. partial 키워드는 여기에 한술 더 떠서 동일한 클래스, 상속받거나 포함하는게 아닌 정확히 같은 클래스에 대한 정의를 코드의 다른 장소에서 할 수 있게 해줍니다.

※ 전통적인 C/C++에서 어떤 클래스를 컴파일러에게 알려주는 것을 선언(declaration)이라고 하고 선언된 클래스의 코드를 작성하여 구현한 것을 정의(definition)이라고 명백하게 구분하고 있지만 C#이나 VB처럼 선언과 동시에 정의하는 언어에서는 이 둘의 구분은 별 의미가 없을 것 같습니다. 본문에서 선언과 정의를 혼용하는 경우가 있으니 널리 양해 바랍니다.
다시 한번 졸린 눈을 비비고 App 클래스의 정의를 살펴볼까요?
public partial class App : System.Windows.Application
이 코드를 보고 아무 느낌이 없으셨다면 이미 이 글을 보실 필요가 없는 레벨의 분이거나 진지하게 자아비판을 좀 하셔야 할 분이겠습니다. 이 코드를 보고 뭔가 이상한 냄새를 맡았거나 partial이 뭐야? 하신 분은 축하합니다. 안드로메다 깐따삐야 별의 이름을 걸고 단언컨대 개발자로 대성하실거에요!

앞서 말한 것처럼 partial 키워드는 클래스의 정의를 다른 곳에서도 할 수 있게 하므로 App 클래스는 우리가 원하는 위치에 제어하고 싶은 코드를 자유롭게 배치할 수 있다는 거죠.

시작하면서 지워버렸던 App.xaml.cs 기억나시나요? 바로 여기에 위의 코드와 마찬가지로 Application을 상속하는 App 클래스가 정의되어 있었죠. 벌써 지워버린걸 어떻게 확인하겠습니까. 귀찮더라도 프로젝트를 하나 더 만들어서 확인해보죠.

App.xaml.cs

using System;

// 나머지 using 선언은 또 생략...

namespace WindowsApplication2

{

    /// <summary>

    /// Interaction logic for App.xaml

    /// </summary>


    public partial class App : System.Windows.Application

    {


    }

}


비록 내부에 아무런 코드도 없지만 컴파일러가 자동으로 생성한 App 클래스의 정의와 정확히 일치하는걸 확인할 수 있네요. partial 키워드가 서로 다른 위치에서 클래스의 정의를 할 수 있다고 해도 당연히 같은 클래스는 같은 형(type)을 가져야 합니다.

제가 얘기한게 참말인지 거짓말인지 확인하려면 간단히 partial 키워드를 지워보거나 public을 private으로 바꿔보시면 빌드에 실패하는 것을 확인할 수 있을거에요.

물론 C#이나 VB.NET을 제대로 공부하셨다면 partial 쯤이야 대수롭지 않게 여기시겠지만 저처럼 곁눈질로 공부하신 분이라면 MSDN을 참고하세요. 다행히 한글로도 있네요. :)


WPF는 화장빨?
WPF라고 해도 Foundation, 기초화장에 지나지 않아요. 화장이란건 때론 변장에 가까울 정도로 진할때도 있잖아요? WPF는 특히나 더 예쁘고 진한 화장을 위한 기초라고 생각해요. 그 뒤에는 아직 WIN32 API가 자리잡고 있죠. 뭔가 속은 듯한 기분도 들고 그렇죠?

하지만 Foundation이 '기초'화장이란것에 주목하세요. 분명 그 위에 개발자와 디자이너가 꿈꾸는 화려하고 멋진 인터페이스를 올려나갈 수 있다는게 더 중요하죠.

어쩌면 WPF가 화장빨인지 모르겠지만, 사실 사용자들에겐 밤새워 최적화한 눈물어린 코드 보다는 적당히 화사한 화장빨이 훨씬 더 잘 먹히잖아요? 늘 하는 얘기지만 좋은게 좋은거죠. :)


다음회 예고!
아직 XAML이 컴파일될 때 일어나는 비밀스런 일 중에 아직 소개하지 못한 것이 있었으니... 기대하시라 개봉박두?
신고
Posted by gongdo
지난 포스팅(http://gongdo.tistory.com/69)에서 VS2005의 XAML 디자인뷰상의 버그에 대해 얘기했는데요, 이 버그를 피해가는 간단한 방법입니다.

우선 문제는 XAML 코드에서 Window 엘리먼트의 AllowsTransparency 어트리뷰트를 인식하지 못한다는 점이었습니다.
아주 간단하게 AllowsTransparency 어트리뷰트를 XAML에서 제거하고 코드-비하인드에서 해당 속성을 설정해주면 되는군요.

해당 XAML과 C#코드는 다음과 같습니다.

XAML
<Window x:Class="NonRectangularWindow.Window1"

xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="NonRectangularWindow" SizeToContent="WidthAndHeight"

    WindowStyle="None"  Background="Transparent"

    MouseLeftButtonDown="Window1_MouseLeftButtonDown"

    MouseDoubleClick="Window1_MouseDoubleClick"

    >

    <Image Source="pack://siteoforigin:,,,/res/만년삼.gif" Stretch="None" />

</Window>

(AllowsTransparency 어트리뷰트를 제거했습니다.)

C#

using System;

using System.Collections.Generic;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;


namespace NonRectangularWindow

{

    /// <summary>

    /// Interaction logic for Window1.xaml

    /// </summary>


    public partial class Window1 : System.Windows.Window

    {


        public Window1()

        {

            InitializeComponent();

            this.AllowsTransparency = true;

        }


        void Window1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            this.DragMove();    // 드래그 처리

        }

        void Window1_MouseDoubleClick(object sender, MouseButtonEventArgs e)

        {

            this.Close();       // 윈도 닫기

        }

    }

}


달라진거라곤 Window 생성자 -여기에서는 Window1()- 에서 코드로 직접 AllowsTransparency를 true로 설정해줬다는 점 뿐입니다.

한가지 주의할 점은 AllowsTransparency와 같은 윈도 형태(appearance)와 관련된 일부 속성은 런타임 도중에 윈도가 이미 보여지고 있을 경우 변경이 불가능하다는 것입니다.

이 외에 다른 XAML 디자인 뷰와 관련된 문제도 코드-비하인드를 적절하게 섞어서 사용하여 회피할 수 있을 것입니다.
신고
Posted by gongdo
MSDN : http://msdn2.microsoft.com/en-us/library/ms748873.aspx

Imaging Overview

이 토픽은 Microsoft WPF 이미징 컴포넌트의 소개를 제공합니다. WPF 이미징은 개발자가 이미지를 화면에 표시하고 변형하고 포맷을 지정할 수 있게 합니다.

이 토픽은 다음 섹션을 담습니다.
WPF 이미징 컴포넌트

WPF 이미징은 Microsoft Windows내에서의 이미징 능력에 상당한 향상을 제공합니다. 비트맵을 보여주거나 커먼 컨트롤에 이미지를 사용하는 것 같은 이미징 능력은 이전엔 Microsoft Windows Graphics Device Interface(GDI)나 Microsoft Windows GDI+ 라이브러리에 의존했습니다. 이 API는 이미징 기능의 기반을 제공했지만 코덱 확장성이나 고선명 이미지 지원과 같은 기능이 부족했습니다. WPF 이미징은 GDI와 GDI+의 단점을 극복하기 위해 디자인 되었고, 애플리케이션에 이미지를 표시하고 사용하기 위한 새로운 API 세트를 제공합니다.

WPF 이미징 API에 접근 방법은 매니지드(managed) 컴포넌트와 언매니지드(unmanaged) 컴포넌트의 두 가지 방법이 있습니다. 언매니지드 컴포넌트는 다음 기능을 제공합니다.
  • 새롭거나 소유권 있는 이미지 포맷을 위한 확장성있는 모델
  • 비트맵(BMP), Joint Photographics Experts Group(JPEG), Portable Network Graphics(PNG), Tagged Image File Format(TIFF), Microsoft Windows Media Photo, Graphics Interchange Format(GIF) 및 icon(.ico)를 포함하는 네이티브 이미지 상에서의 향상된 성능과 보안
  • 채널당 32비트까지의 고 비트 이미지 데이터 보존
  • 손실 없는 이미지 배율 조절(scaling), 자르기(cropping) 및 회전
  • 단순화된 색상 관리
  • 파일 내에 있는 소유권 메타데이터 지원
매니지드 컴포넌트는 사용자 인터페이스(UI), 애니메이션 및 그래픽 같은 다른 WPF 기능과 이미지의 매끄러운(seamless) 통합을 제공하기 위해 언매니지드 인프라스트럭쳐를 이용합니다. 매니지드 컴포넌트는 또한 WPF 애플리케이션에서 자동화된 새 이미지 포맷의 인식을 가능케 하는 WPF 이미징 코덱 확장 모델의 혜택을 받습니다.

매니지드 WPF 이미징 API의 대부분은 System.Windows.Media.Imaging 네임스페이스에 존재하지만 ImageBrushImageDrawing과 같은 몇 가지 중요한 타입은 System.Windows.Media 네임스페이스에 존재하며 ImageSystem.Windows.Controls 네임스페이스에 존재합니다.

WPF 이미징 포맷

코덱은 특정한 미디어 포맷을 디코드하거나 인코드합니다. WPF 이미징은 BMP, JPEG, PNG, TIFF, Windows Media Photo, GIF 및 ICON 이미지 포맷을 위한 코덱을 포함합니다. 각 코덱은 애플리케이션이 디코드하고, ICON을 제외한 각각의 이미지 포맷을 인코드할 수 있게 합니다.

BitmapSource는 이미지의 디코딩과 인코딩에 사용되는 중요한 클래스입니다. 이것은 WPF 이미징 파이프라인의 기본 빌딩 블럭이며 어떤 크기와 해상도에서 픽셀의 단일한 불변의 세트를 나타냅니다. BitmapSource는 다중 프레임 이미지의 개별적인 프레임이 될 수 있고 또는 BitmapSource상의 변형 동작의 결과가 될 수도 있습니다. BitmapSourceBitmapFrame과 같은 WPF 이미징에서 사용되는 많은 주요 클래스의 부모가 됩니다.

BitmapFrame은 이미지 포맷의 실제 비트맵 데이터를 저장합니다. 많은 이미지 포맷이 단지 하나의 BitmapFrame만을 지원하지만 GIF나 TIFF와 같은 포맷은 한 이미지에서 다중 프레임을 지원합니다. 프레임은 디코더에게 입력 데이터로 사용되고 인코더에게 이미지 파일을 생성하도록 전달됩니다.

다음 예제는 BitmapSource로부터 BitmapFrame을 생성하고 TIFF 이미지에 추가하는 방법을 보여줍니다.

C#
BitmapSource image5 = BitmapSource.Create(

    width,

    height,

    96,

    96,

    PixelFormats.Indexed1,

    BitmapPalettes.WebPalette,

    pixels,

    stride);


FileStream stream5 = new FileStream("palette.tif", FileMode.Create);

TiffBitmapEncoder encoder5 = new TiffBitmapEncoder();

encoder5.Frames.Add(BitmapFrame.Create(image5));

encoder5.Save(stream5);


이미지 포맷 디코딩
이미지 디코딩은 시스템이 사용할 수 있도는 이미지 데이터로 이미지 포맷을 번역하는 것입니다. 번역된 이미지 데이터는 화면 표시, 처리 또는 다른 포맷으로 인코드될 수 있습니다. 디코더 섹션은 이미지 포맷에 기반합니다. 코덱 섹션은 디코더가 지정되지 않으면 자동으로 지정됩니다. Displaying Images in WPF의 예제 섹션은 자동 디코딩을 시연합니다. 사용자 정의 포맷 디코더는 언매니지드 WPF 이미징 인터페이스를 사용하여 개발되고 자동으로 디코더 섹션에 포함되어 시스템에 등록됩니다. 이것은 사용자 정의 포맷이 WPF 애플리케이션에서 자동으로 표시될 수 있도록 합니다.

다음 예제는 BMP 포맷 이미지를 디코드하기 위한 비트맵 디코더의 사용을 보여줍니다.(※역주: C++코드는 생략합니다.)

C#
// Open a Uri and decode a BMP image

Uri myUri = new Uri("tulipfarm.bmp", UriKind.RelativeOrAbsolute);

BmpBitmapDecoder decoder2 = new BmpBitmapDecoder(myUri, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

BitmapSource bitmapSource2 = decoder2.Frames[0];


// Draw the Image

Image myImage2 = new Image();

myImage2.Source = bitmapSource2;

myImage2.Stretch = Stretch.None;

myImage2.Margin = new Thickness(20);


이미지 포맷 인코딩
이미지 인코딩은 이미지 데이터를 특정 이미지 포맷으로 번역하는 것입니다. 인코드된 이미지 데이터로는 새 이미지 파일을 생성할 수 있습니다. WPF 이미징은 위에서 기술한 이미지 포맷 각각을 위한 인코더를 제공합니다.

다음 예제는 새로 생성한 비트맵 이미지를 저장하는 인코더의 사용을 보여줍니다.

C#

FileStream stream = new FileStream("new.bmp", FileMode.Create);

BmpBitmapEncoder encoder = new BmpBitmapEncoder();

TextBlock myTextBlock = new TextBlock();

myTextBlock.Text = "Codec Author is: " + encoder.CodecInfo.Author.ToString();

encoder.Frames.Add(BitmapFrame.Create(image));

encoder.Save(stream);


WPF에서 이미지 표시하기

BitmapImage는 XAML 로딩을 위해 최적화된 특수한 BitmapSource이고 Image 컨트롤의 Source로 이미지를 표시하는 쉬운 방법입니다.

이미지 컨트롤 사용하기
Image는 프레임워크 엘리먼트이고 애플리케이션에서 이미지를 표시하는 주된 수단입니다. XAML에서 Image는 어트리뷰트 문법 또는 프로퍼티 문법의 두 가지 방법으로 사용될 수 있습니다. 다음 예제는 어트리뷰트 문법과 프로퍼티 태그 문법을 모두 사용하여 이미지를 200 픽셀 너비로 그리는 방법을 보여줍니다. 어트리뷰트 문법과 프로퍼티 문법의 더 자세한 정보는 Dependency Properties Overview를 참고하십시오.

XAML

<!-- 간단한 이미지 렌더링. 그러나 이 방법으로 렌더링하는 것은 애플리케이션 메모리의 사용량에는 좋지 않습니다. 같은 결과를 더 적은 메모리로 생성하는 아래쪽 마크업을 참고하십시오. -->

<Image Width="200"

Source="C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water Lilies.jpg"/>


<Image Width="200">

  <Image.Source>

    <!-- 상당한 애플리케이션 메모리를 아끼기 위해 이미지 소스의 BitmapImage의 DecodePixelWidth나 DecodePixelHeight값을 원하는 높이와 너비로 설정하십시오. 그렇게 하지 않으면 애플리케이션은 이미지를 화면에 표시될 크기가 아닌 그것의 보통 크기로 렌더링 될 것이라고 생각하고 캐슁할 것입니다. -->

    <!-- Note: 원래 비율을 보호하기 위해 DecodePixelHeight이나 DecodePixelWidth중 하나만을 설정하십시오. -->

    <BitmapImage DecodePixelWidth="200" 

    UriSource="C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water Lilies.jpg" />

  </Image.Source>

</Image>


다음 예제는 코드를 사용하여 이미지를 200픽셀 너비로 그리는 방법을 보여줍니다.

노트:
BitmapImage는 다중 속성에서 초기화 최적화를 위해 ISupportInitilize 인터페이스를 구현합니다. 속성 변경은 오직 객체 초기화 중에만 발생할 수 있습니다. 초기화가 시작되었음을 알리는 신호로 BeginInit을 호출하고 초기화가 완료되었음을 알리는 신호로 EndInit을 호출합니다. 일단 초기화되면 속성 변경은 무시됩니다.


C#
// 이미지 엘리먼트 생성

Image myImage = new Image();

myImage.Width = 200;


// 소스 생성

BitmapImage myBitmapImage = new BitmapImage();


// BitmapImage.UriSource는 반드시 BeginInit/EndInit 블럭 내에 있어야 합니다.

myBitmapImage.BeginInit();

myBitmapImage.UriSource = new Uri(@"C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water Lilies.jpg");


myBitmapImage.DecodePixelWidth = 200;

myBitmapImage.EndInit();

// 이미지 소스 설정

myImage.Source = myBitmapImage;


이미지 회전, 변환(converting) 및 자르기(cropping)
WPF는 BitmapImage의 속성을 사용하거나 CroppedBitmap이나 FormatConvertedBitmap과 같은 추가적인 BitmapSource 객체를 사용하여 이미지를 변형 할 수 있습니다. 이 이미지 변형은 이미지를 배율 조절하거나 회전할 수 있고 이미지의 픽셀 포맷을 바꾸거나 이미지를 자를 수 있습니다.

이미지 회전은 BitmapImageRotation 속성을 사용하여 작동됩니다. 회전은 90도씩만 증가될 수 있습니다. 다음 예제에서 이미지는 90도 회전됩니다.

XAML
<Image Width="150" Margin="5" Grid.Column="0" Grid.Row="1">
  <Image.Source>
    <TransformedBitmap Source="/sampleImages/watermelon.jpg" >
      <TransformedBitmap.Transform>
        <RotateTransform Angle="90"/>
      </TransformedBitmap.Transform>
    </TransformedBitmap>
  </Image.Source>
</Image>


C#
// 이미지 엘리먼트 생성
Image rotated90 = new Image();
rotated90.Width = 150;

// 이미지 소스로써 사용할 TransformedBitmap 생성
TransformedBitmap tb = new TransformedBitmap();

// 소스로써 사용할 (비트맵이미지)소스 생성
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(@"sampleImages/watermelon.jpg", UriKind.RelativeOrAbsolute);
bi.EndInit();

// 속성은 반드시 BeginInit과 EndInit 호출 사이에서 설정해야 합니다.
tb.BeginInit();
tb.Source = bi;
// 이미지 회전을 설정
RotateTransform transform = new RotateTransform(90);
tb.Transform = transform;
tb.EndInit();
// 이미지 소스를 설정
rotated90.Source = tb;

이미지를 그레이스케일과 같은 다른 픽셀 포맷으로 변환하는 것은 FormatConvertedBitmap을 사용합니다. 다음 예제에서 이미지는 Gray4로 변환됩니다.

XAML
<!-- Grayscale XAML Image -->
<Image Width="200" Grid.Column="0" Grid.Row="1">
   <Image.Source>
      <FormatConvertedBitmap Source="/sampleImages/rocks.jpg"  DestinationFormat="Gray4" />
   </Image.Source>
</Image>


C#
// 이미지 엘리먼트 생성
Image grayImage = new Image();
grayImage.Width = 200;
grayImage.Margin = new Thickness(5);

// XAML에서 정의된 리소스를 사용하여 소스 생성
FormatConvertedBitmap fcb = new FormatConvertedBitmap(
   (BitmapImage)this.Resources["masterImage"],PixelFormats.Gray4,null,0);
// 이미지 소스 설정
grayImage.Source = fcb;

이미지를 자르기 위해서 ImageClip 속성이나 CroppedBitmap을 사용할 수 있습니다. 일반적으로 단지 이미지의 일부분만 보여주고 싶다면 Clip이 사용됩니다. 만약 잘린 이미지를 인코드하고 저장할 필요가 있다면 CroppedBitmap이 사용됩니다. 다음 예제에서 이미지는 EllipseGeometry를 사용한 Clip 속성을 사용하여 잘립니다.

XAML
<!-- Cropping an Image using Clip -->
<Image Width="200" Grid.Column="0" Grid.Row="5" Margin="5"
   Source="/sampleImages/gecko.jpg">
  <Image.Clip>
    <EllipseGeometry Center="75,50" RadiusX="50" RadiusY="25" />
  </Image.Clip>
</Image>


C#
// 클리핑을 위한 이미지 생성
Image clipImage = new Image();
clipImage.Width = 200;
clipImage.Margin = new Thickness(5);
 
// 소스 생성 및 설정
BitmapImage bi = new BitmapImage();
// BitmapImage.UriSource는 반드시 BeginInit/EndInit 블럭 내에 있어야 합니다.
bi.BeginInit();
bi.UriSource = new Uri("pack://application:,,/sampleImages/gecko.jpg");
bi.EndInit();
clipImage.Source = bi;

// EllipseGeometry를 사용하여 클립
EllipseGeometry clipGeometry = new EllipseGeometry(new Point(75, 50), 50, 25);
clipImage.Clip = clipGeometry;

이미지 늘리기
Stretch 속성은 이미지가 컨테이너에 어떻게 늘려져서 채워질지를 제어합니다. Stretch 속성은 Stretch 열거값에서 정의된 다음 값을 받아들입니다.
  • None: 이미지는 출력 영역을 늘려져서 채우지 않습니다. 이미지가 출력 영역보다 클 경우 이미지는 출력 영역에 그려지고 맞지 않는 부분은 잘립니다.
  • Fill: 이미지는 출력 영역에 맞춰 배율이 조절됩니다. 이미지의 높이와 너비가 독립적으로 조절되기 때문에 이미지의 원래 비율은 보호되지 않을 것입니다. 이미지는 출력 컨테이너를 완전히 채우도록 정렬되어 비뚤어질 것입니다.
  • Uniform: 이미지는 출력 영역 내에 완전하게 맞춰서 배율 조절 됩니다. 이미지의 비율은 보호됩니다.
  • UnformFill: 이미지는 출력 영역을 이미지의 원래 배율을 보존하면서 완전하게 채워 배율 조절 됩니다.
※역주: 원문에는 여기에 각각의 Stretch를 다르게 설정한 예제 코드만 나와있습니다. 여기에서는 이 코드를 거의 그대로 사용한 수정된 코드 및 결과물을 대신 올립니다.

XAML
<Window x:Class="WindowsApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="WindowsApplication1" Height="500" Width="400">

  <DockPanel>


    <Border DockPanel.Dock="Top" Background="Black">

      <TextBlock Foreground="White" HorizontalAlignment="Stretch" FontSize="20">

        Stretching an Image

      </TextBlock>

    </Border>


    <Grid Name="simpleGrid"

      Margin="10"

      ShowGridLines="True"

      VerticalAlignment="Center"

      HorizontalAlignment="Center">

      <Grid.ColumnDefinitions>

        <ColumnDefinition Width="175" />

        <ColumnDefinition Width="175" />

      </Grid.ColumnDefinitions>

      <Grid.RowDefinitions>

        <RowDefinition Height="200"/>

        <RowDefinition Height="200"/>

      </Grid.RowDefinitions>

      <!-- Labels -->

      <TextBlock

        Grid.Column="0" Grid.Row="0">None</TextBlock>

      <TextBlock

        Grid.Column="1" Grid.Row="0">Uniform</TextBlock>

      <TextBlock

        Grid.Column="0" Grid.Row="1">UniformToFill</TextBlock>

      <TextBlock

        Grid.Column="1" Grid.Row="1">Fill</TextBlock>

      <Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="Black">

        <!-- None: 이미지는 배율 조절되지 않습니다. 이미지가 출력 영역보다 크다면 이미지는 출력 영역의 크기로 잘릴 것입니다.-->

        <Image

          Source="sampleImages/만년삼.gif"

          Stretch="None" />

      </Border>

      <Border Grid.Column="1" Grid.Row="0" BorderThickness="1" BorderBrush="Black">

        <!-- Uniform: 출력 영역에 맞춰 배율 조절합니다. 비율은 보호됩니다.-->

        <Image

          Source="sampleImages/만년삼.gif"

          Stretch="Uniform" />

      </Border>

      <Border Grid.Column="0" Grid.Row="1" BorderThickness="1" BorderBrush="Black">

        <!-- UniformToFill: 출력 영역을 완전히 채우도록 배율 조절됩니다. 비율은 보호됩니다. 크기에 따라 잘릴 수 있습니다.-->

        <Image 

          Source="sampleImages/만년삼.gif"

        Stretch="UniformToFill" />

      </Border>

      <Border Grid.Column="1" Grid.Row="1" BorderThickness="1" BorderBrush="Black">

        <!-- Fill: 출력 영역을 완전히 채우도록 배율 조절됩니다. 비율은 보호되지 않을 것입니다.-->

        <Image

          Source="sampleImages/만년삼.gif"

          Stretch="Fill" />

      </Border>

    </Grid>

  </DockPanel>


</Window>




이미지로 페인팅하기
이미지는 또한 Brush로 페인팅하여 애플리케이션에서 표시될 수 있습니다. 브러쉬는 단순한 단일 컬러에서 복잡한 패턴의 세트와 이미지까지 어떤 것으로도 UI 객체를 칠할 수 있게 합니다. 이미지를 칠하려면 ImageBrush를 사용합니다. ImageBrush는 자신의 내용을 비트맵 이미지로 정의하는 TileBrush의 타입입니다. ImageBrushImageSource 속성에서 지정된 단일 이미지를 표시합니다. 이미지가 늘려지고 정렬되고 배열되는 방법을 제어할 수 있고 왜곡을 막고 패턴과 다른 효과를 제작하는 것을 가능케 합니다. 다음 그림은 ImageBrush로 얻을 수 있는 몇 가지 효과를 보여줍니다.

도형, 컨트롤, 텍스트 및 기타를 채울 수 있는 이미지 브러쉬


다음 예제는 버튼의 배경을 ImageBrush를 사용하여 이미지로 칠할 수 있는 방법을 보여줍니다.

XAML
<!-- 버튼의 배경 속성을 이미지 브러쉬로 설정합니다. 결과적으로 버튼은 배경에 이미지를 가지게 됩니다. -->

<Button Grid.Row="3" Grid.Column="2"

 Height="75" Width="100" Foreground="White" FontWeight="Bold"

 HorizontalAlignment="Left">

  A Button

  <Button.Background>

    <ImageBrush ImageSource="sampleImages\blueberries.jpg" />

  </Button.Background>

</Button>


ImageBrush와 칠하기에 대한 추가적인 정보는 Painting with Images, Drawings, and Visuals를 참고하십시오.

이미지 메타데이터

어떤 이미지 파일은 컨텐트나 파일의 특성을 기술하는 메타데이터를 포함합니다. 예를 들어 대부분의 디지털 카메라는 이미지 캡쳐에 사용된 카메라의 메이커와 모델에 관한 메타데이터를 포함하는 이미지를 생성합니다. 각 이미지 포맷은 메타데이터를 다르게 처리하지만 WPF 이미징은 지원되는 각 이미지 포맷을 위한 통합된 메타데이터 저장과 얻는 수단을 제공합니다.

메타데이터 접근은 BitmapSource 객체의 Metadata 속성을 통해 제공됩니다. Metadata는 이미지가 담고 있는 모든 메타데이터를 포함하는 BitmapMetadata 객체를 반환합니다. 이 데이터는 하나의 메타데이터 스키마 또는 다른 스키마의 조합일 수 있습니다. WPF 이미징은 다음의 메타데이터 스키마를 지원합니다. Exchangeable image file(Exif), tExt(PNG Textual Data), Image File Directory(IFD), International Press Telecommunications Council(IPTC) 및 Extensible Metadata Platform(XMP)

메타데이터 읽기 프로세스를 단순화하기 위해 BitmapMetadataAuthor, TitleCameraModel과 같이 쉽게 접근할 수 있는 몇 가지 명명된 속성을 제공합니다. 이들 명명된 속성의 대부분은 또한 메타데이터 기록에도 사용될 수 있습니다. 메타데이터 읽기를 위한 추가적인 지원은 메타데이터 쿼리 리더가 제공합니다. GetQuery 메소드는 "/app1/exif/"와 같은 문자열 쿼리를 제공함으로써 메타데이터 쿼리 리더를 받습니다. 다음 예제에서 GetQuery는 "/Text/Description" 위치에 저장된 텍스트를 얻기 위해 사용됩니다.

C#
// 텍스트 블럭에 비트맵 이미지의 메타데이터를 추가
TextBlock myTextBlock = new TextBlock();
myTextBlock.Text = "The Description metadata of this image is: " + pngInplace.GetQuery("/Text/Description").ToString();

메타데이터를 쓰기 위해 메타데이터 쿼리 라이터(writer)가 사용됩니다. SetQuery는 쿼리 라이터를 얻고 원하는 값을 설정합니다. 다음 예제에서 SetQuery는 "/Text/Description" 위치에 저장된 텍스트를 쓰기 위해 사용됩니다.

C#
Stream pngStream = new System.IO.FileStream("smiley.png", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);

PngBitmapDecoder pngDecoder = new PngBitmapDecoder(pngStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);

BitmapFrame pngFrame = pngDecoder.Frames[0];

InPlaceBitmapMetadataWriter pngInplace = pngFrame.CreateInPlaceBitmapMetadataWriter();

if (pngInplace.TrySave() == true)

{ pngInplace.SetQuery("/Text/Description", "Have a nice day."); }

pngStream.Close();



코덱 확장성

WPF 이미징의 핵심 기능은 새로운 이미지 코덱을 위한 확장성 있는 모델입니다. 이 언매니지드 인터페이스는 코덱 개발자가 WPF와 코덱을 통합할 수 있게 하므로 새로운 이미지 포맷이 WPF 애플리케이션에서 자동으로 사용될 수 있습니다.

확장 API의 샘플은 Win32 Sample Codec을 참고하십시오. 이 샘플은 사용자 정의 이미지 포맷을 위한 디코더와 인코더를 만드는 방법을 제시합니다.

노트:
코덱은 그것을 인식하기 위해 반드시 시스템을 위한 디지털 사인이 되어 있어야 합니다.

See also

Reference
BitmapSource
BitmapImage
Image
BitmapMetadata

Other Resources
Win32 Sample Codec
Photo Store Demo
신고
Posted by gongdo

몇 개 안되는 MSDN 토픽 번역하느라 한달이 흘러갔네요.
이제 좀 더 쓸모있는 WPF 애플리케이션 작성에 대한 글을 써볼까! 하고 모처럼의 휴일에도 일찍 일어나서 PC앞에 앉았는데... VS2005+WPF익스텐션의 버그가 절 반갑게 맞이하네요.ㄱ-

우선 작성하려던 프로그램은 이렇습니다.
1. 비-시각형(Non-rectangular) 윈도 만들기 : http://gongdo.tistory.com/58#윈도 형태에서 비시각형 윈도 스타일 참고.
2. Path가 아닌 Image를 사용한 비-사각형 윈도 만들기
3. 이미지의 불투명한 부분만 윈도의 영역으로 취급되도록(클릭되도록) 처리
4. 이미지를 왼쪽 마우스 버튼으로 드래그 하여 윈도 옮기기
5. 이미지를 왼쪽 더블클릭하면 애플리케이션 종료.

있는 그림가지고 쓰면 재미가 없잖아요?
간만에 예술혼을 불태워 그림까지 직접 그렸죠.
그 그림은...

more..

중요한건 이게 아니고;
WPF에서는 위와 같은 요구 사항의 프로그램을 매~우 쉽게 작성할 수 있습니다.
원래는 이 주제로 포스팅을 했어야 하는데 쩜쩜쩜.

자세한 코드 설명은 생략하고 문제의 화면은 다음과 같습니다.


VS2005의 WPF 익스텐션에서 특정 XAML 키워드를 IDE의 XAML 디자인 뷰(코드명 Cider였죠?)에 표시할 수 없는 문제가 있습니다.

위의 상황에서는 Windows 엘리먼트의 AllowsTransparency 어트리뷰트를 사용하면 XAML 디자인 뷰에서 Whoops! 하고 울부짖으면서 표시를 못하고 에러를 뿜습니다.

하지만 이 상태에서도 XAML 문법이 틀린 것은 아니고 따라서 빌드 및 실행은 정상적으로 되는데요, 기껏 디자인 뷰가 있는데 볼 수 없다면 안타깝잖아요?
한번 문제의 AllowsTransparency 어트리뷰트를 지워봤습니다.


네, 예상대로 디자인 뷰에 정상적으로 표시되는군요.

한번 더 꼼수를 부려 이상태에서 다시 XAML 코드 뷰에 AllowsTransparency를 넣으면...


오우! 디자인뷰가 살아있네요?!
실행을 한번 해볼까요?

의도대로 불투명한 영역(그림이 있는 부분)을 마우스로 드래그 하여 옮길 수 있고 더블클릭하면 종료됩니다.

하.지.만. VS2005의 WPF 익스텐션에서 디자인 뷰는 디자인 뷰가 활성화 될 때 다시 렌더링 되는 것 같습니다. 그래서 디자인 뷰를 선택하면 도로 Whoops!를 보게되지요.
결국 버그는 버그. 방법이 없습니다.

전 이럴 때 버그를 해결하기 위해 구글 신에게 간절히 기도하곤 합니다.
문제의 에러 메시지로 기도했더니 아니나 다를까 이 문제로 고민하고 있는 사람들이 있었습니다.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1371261&SiteID=1

답변은 조금 실망스럽게도, 2005 IDE에서는 아직 WPF를 개발하기엔 좋지 않으니까 Orcas를 써볼래? 정도네요.

뭐 이때까지만 해도 Orcas가 새로 나온 익스텐션인줄 알고 올타꾸나하곤 링크를 타고 들어갔는데요.
            ┘└
            ┐┌

┓━



차기 버전의 Visual Studio의 베타판을 말하는거였군요.
용량은 무려 4G를 훌쩍 넘기고... 후우...

선택의 여지가 없네요. 일단 설치. 설치에 관한건 http://gongdo.tistory.com/68 에서 간단히 리뷰했습니다.

아직 Orcas라고 해도 2005와 크게 달라진 부분은 없었고 2005에서 작성했던 프로젝트를 훌륭하게 컨버전할 수 있었습니다.
기대를 걸고 프로젝트를 열어봤더니...


Whoops!

...후우...MS 우리 이제 편하게 좀 살자 응?

이번엔 다른 어트리뷰트에서 인식 문제가 발생하네요.
물론 이 문제도 빌드가 잘 됩니다.
이전과는 좀 다른게 위에서 문제되는 어트리뷰트는 이미지의 소스 경로를 지정하는 부분인데요,
"pack://siteoforigin:,,,/res/만년삼.gif"가 바로 불러올 이미지의 경로를 말하고
약간은 생소하지만 pack://siteoforigin,,,은 간단하게 말해서 대상 리소스가 디자인 타임에 결정될 수 없고 런타임에 해당 경로에서 검색된다는 것을 의미하는데요.
예를 들어 애플리케이션에서라면 프로그램의 실행경로 혹은 절대경로가 될 수 있겠고 웹 애플리케이션이라면 URL주소가 될 수 있겠죠. 자세한건 http://gongdo.tistory.com/57를 참고.

어쨌든, 이 문제는 VS2005 IDE에서보다 더 심각합니다.
왜냐면 디자인뷰의 Whoops!를 무시하고 빌드한 후 실행해보면 진짜로 해당 리소스의 경로를 인식하지 못하여 화면에 표시해줄 수 없기 때문입니다.
아마도 Orcas에서는 pack://siteoforgin,,,이라는 키워드를 인식하지 못하거나 처리에 버그가 있거나 혹은 사용 규칙이 바뀌었을 수 있을 것 같습니다.

Orcas와 WPF 그리고 siteoforgin에 대해 구글 신에게 간절히 기도했지만, 아직은 별다른 정보가 없네요. 혹은 제가 못찼았거나.

결국은 Orcas에서의 외부 리소스 사용은 단순히 프로젝트에 해당 리소스를 추가시켜서 쓸 수밖에 없었습니다.


오른쪽 프로젝트 뷰에서 추가된 이미지 리소스를 확인하실 수 있을거에요.

아마도 대부분의 샘플 프로젝트나 개념 설명 등에서 간단하게 하기 위해 이미지 같은 리소스를 프로젝트에 포함시키고 Source="이미지.bmp" 이런식으로만 사용했을 것 같습니다.
저라도 단순함 샘플 프로그램은 저렇게 했을거에요.


정리.
1. VS2005 + WPF 익스텐션에서는 XAML 디자인뷰 상의 버그로 AllowsTransparency 어트리뷰트가 포함된 Window 엘리먼트롤 표시할 수 없다. 단, 빌드는 정상적으로 수행되며 실행도 정상적으로 수행된다.
2. Orcas 2007 3월 CTP에서는 AllowsTransparency 어트리뷰트가 정상적으로 렌더링된다. 하지만 pack://siteoforigin,,, 형식의 Pack URI를 제대로 인식하지 못하는 문제가 있고 이것은 빌드를 정상적으로 해도 런타임에 해당 URI를 로딩하지 못한다.
3. 2의 문제는 버그가 아닌 Pack URI 스펙의 변경일 가능성도 있지만 아직 자세한 정보를 찾지 못했다.

끄~읕.

P.S.
혹시라도 이 글을 보시는 WPF 고수님, 부디 이 문제를 해결할 수 있는 한줄의 가르침을...

신고
Posted by gongdo
MSDN : http://msdn2.microsoft.com/en-us/library/ms748373.aspx

WPF Graphics Rendering Overview

이 토픽은 WPF 비주얼 레이어의 개요를 제공하며 WPF 모델에서 렌더링 지원을 위한 Visual 클래스의 역할에 초점을 맞추고 있습니다.

이 토픽은 다음 섹션을 담고 있습니다.
Visual 객체의 역할

Visual 클래스는 모든 FrameworkElement 객체가 파생(derive)하는 기본 추상 클래스입니다. 또한 WPF에서 새 컨트롤을 작성하는 엔트리 포인트와 Win32 애플리케이션 모델에서의 윈도 핸들(HWND)처럼 생각할 수 있는 많은 방법을 제공합니다.

Visual 객체는 핵심 WPF 객체로써 주 역할은 렌더링 지원을 제공하는 것입니다. Button이나 TextBox와 같은 사용자 인터페이스 컨트롤은 Visual 클래스에서 파생되며, 렌더링 데이터를 지속하기 위해 사용합니다. Visual 객체는 다음 지원을 제공합니다.
  • 화면 출력 : 비주얼의 그릴 내용을 지속적으로, 직렬화하여 렌더링.
  • 변형(Transformations) : 비주얼 상의 변형을 실행.
  • 클리핑(Clipping) : 비주얼을 위한 클리핑 영역(region) 지원을 제공.
  • 충돌 검사(Hit testing) : 좌표나 지오메트리가 비주얼의 영역 내에 포함되는지를 결정.
  • 경계 박스(Bounding box) 계산 : 비주얼의 경계 사각형(Bounding rectangle)을 결정.
※역주: Bounding rectangle은 어떤 비정형의 비주얼 객체를 완전히 포함하는 최소의 크기를 가진 사각형을 말합니다. 다음 그림을 참고하십시오.


그러나 Visual 객체는 다음과 같은 비렌더링 기능을 위한 지원은 포함하지 않습니다.
  • 이벤트 핸들링
  • 레이아웃
  • 스타일
  • 데이터 바인딩
  • 세계화(Globalization)
Visual은 파생되어야 하는 자식 클래스로부터 public abstract 클래스로써 노출됩니다. 다음 그림은 WPF에서 노출되는 비주얼 객체의 계층 구조를 보여줍니다.

Visual 클래스 계층 구조
사용자 삽입 이미지

DrawingVisual 클래스
DrawingVisual은 도형, 이미지나 텍스트를 그리는 가벼운 드로잉 클래스입니다. 이 클래스는 레이아웃이나 이벤트 핸들링을 제공하지 않기 때문에 그것의 런타임 성능을 향상시키는 가벼움이 고려되었습니다. 이런 이유로 드로잉은 배경과 클립아트에 이상적입니다. DrawingVisual은 사용자 정의 비주얼 객체를 생성하는데 사용될 수도 있습니다. 더 자세한 정보는 Using DrawingVisual Objects를 참고하십시오.

Viewport3DVisual 클래스
Viewport3DVisual2D VisualVisual3D 객체간의 다리를 제공합니다. Visual3D 클래스는 모든 3D 비주얼 엘리먼트를 위한 기반 클래스입니다. Viewport3DVisualCamera 값과 Viewport 값의 정의를 요구합니다. 카메라(camera)는 씬(scene)을 볼(view) 수 있게 합니다. 뷰포트(viewport)는 2D 표면상에 프로젝션 맵을 생성합니다. WPF에서의 3D에 관한 더 자세한 정보는 3-D Graphics Overview를 참고하십시오.

ContainerVisual 클래스
ContainerVisual 클래스는 Visual 객체의 컬렉션을 위한 컨테이너로써 사용됩니다. DrawingVisual 클래스는 비주얼 객체의 컬렉션을 담을 수 있게 하는 ContainerVisual 클래스에서 파생됩니다.

Visual 객체에서 드로잉 컨텐트
Visual
객체는 자신의 렌더 데이터를 벡터 그래픽 인스트럭션 목록(vector graphics instruction list)으로 저장합니다. 인스트럭션(※역주:단위 명령어) 목록의 각 아이템은 그래픽 데이터와 연관된 리소스의 로우레벨 세트를 직렬화된 포맷으로 나타냅니다. 드로잉 컨텐트를 담을 수 있는 렌더 데이터는 네 가지 종류가 있습니다.

드로잉 컨텐트 종류
설명
벡터 그래픽
벡터 그래픽 데이터 및 모든 연관된 BrushPen 정보를 나타냅니다.
이미지
Rect에 의해 정의된 영역(region) 내의 이미지를 나타냅니다.
도안(Glyph)
특정 폰트 리소스의 도안 순서인 GlyphRun을 그리는 드로잉을 나타냅니다. 텍스트는 이렇게 해서 표시됩니다.
비디오
비디오를 그리는 드로잉을 나타냅니다.


DrawingContextVisual이 비주얼 컨텐트와 함께 위치할 수 있도록 합니다. DrawingContext 객체의 draw 커맨드를 사용하면 실제로는 그래픽 시스템에 의해 나중에 사용될 렌더 데이터의 세트를 저장하게되며 실시간으로 스크린에 드로잉하는 것이 아닙니다.

Button과 같은 WPF 컨트롤을 만든다면 컨트롤은 명시적으로 스스로를 드로잉할 렌더 데이터를 생성합니다. 예를 들어, ButtonContent 속성을 설정함으로써 컨트롤이 도안의 렌더링 표현을 저장하게 합니다.

VisualDrawingGroup내에 담긴 하나 이상의 Drawing 객체로 그것의 컨텐트를 설명합니다. DrawingGroup은 또한 불투명도 마스크, 변형, 비트맵 효과 및 컨텐트에 적용될 다른 오퍼레이션을 설명합니다. DrawingGroup 명령은 컨텐트가 그려질 때 다음 순서로 적용됩니다. OpacityMask, Opacity, BitmapEffect, ClipGeometry, GuidelineSet 그리고 Transform.

다음 그림은 렌더링 시퀀스동안 적용되는 DrawingGroup 오퍼레이션의 순서를 보여줍니다.

DrawingGroup 오퍼레이션의 순서
사용자 삽입 이미지
더 자세한 정보는 Drawing Objects Overview를 참고하십시오.

Visual 레이어에서의 드로잉 컨텐트
DrawingContext
를 직접 인스턴스화 할 수 없으며, System.Windows.Media.DrawingGroup.Open이나 System.Windows.Media.DrawingVisual.RenderOpen과 같은 메소드를 통해 드로잉 컨텍스트를 획득할 수 있습니다. 다음 예제는 DrawingVisual에서 DrawingContext를 얻고 사각형을 그리는데 사용합니다.

C#
// 사각형을 포함한 DrawingVisual을 생성.
private DrawingVisual CreateDrawingVisualRectangle()
{
    DrawingVisual drawingVisual = new DrawingVisual();

    // 새 드로잉을 생성하기 위해 DrawingContext얻음.
    DrawingContext drawingContext = drawingVisual.RenderOpen();

    // 사각형을 생성하고 DrawingContext로 그것을 칠함.
    Rect rect = new Rect(new Point(160, 100), new Size(320, 80));
    drawingContext.DrawRectangle(Brushes.LightBlue, (Pen)null, rect);

    // 드로잉 컨텐트를 지속.
    drawingContext.Close();

    return drawingVisual;
}

비주얼 레이어에 있는 드로잉 컨텐트 열거
추가적인 다른 사용법으로, Drawing 객체는 또한 Visual의 켄텐츠를 열거하기 위한 객체 모델을 제공합니다.

노트:
비주얼의 컨텐츠를 열거하면 Drawing 객체를 얻을 수 있지만 근본적으로 벡터 그래픽 인스트럭션 리스트로써 렌더 데이터의 표현을 얻을 수 없습니다.

다음 예제는 GetDrawing 메소드를 사용하여 VisualDrawingGroup 값을 얻고 열거합니다.

C#
public void RetrieveDrawing(Visual v)
{
    DrawingGroup dGroup = VisualTreeHelper.GetDrawing(v);
    EnumDrawingGroup(dGroup);

}

// DrawingGroup내의 드로잉을 열거.
public void EnumDrawingGroup(DrawingGroup drawingGroup)
{
     DrawingCollection dc = drawingGroup.Children;

     // 드로잉 컬렉션에 있는 드로잉을 열거.
     foreach (Drawing drawing in dc)
     {
         // 드로잉이 DrawingGroup이면, 재귀 호출.
         if (drawing.GetType() == typeof(DrawingGroup))
         {
             EnumDrawingGroup((DrawingGroup)drawing);
         }
         else if (drawing.GetType() == typeof(GeometryDrawing))
         {
             // 드로잉 종류에 따른 동작 실행.
         }
         else if (drawing.GetType() == typeof(ImageDrawing))
         {
             // 드로잉 종류에 따른 동작 실행.
         }
         else if (drawing.GetType() == typeof(GlyphRunDrawing))
         {
             // 드로잉 종류에 따른 동작 실행.
         }
         else if (drawing.GetType() == typeof(VideoDrawing))
         {
             // 드로잉 종류에 따른 동작 실행.
         }
     }
}

비주얼 객체가 컨트롤을 빌드하는 방법

많은 WPF의 객체는 다른 비주얼 객체들로 구성되어 있으며 변화하는 자손 객체의 계층구조를 담을 수 있다는 걸 의미합니다. WPF의 컨트롤과 같은 많은 사용자 인터페이스 엘리먼트는 렌더링 엘리먼트의 다른 종류를 나타내는 다중 비주얼 객체로 구성되어 있습니다. 예를 들어 Button 컨트롤은 ClassicBorderDecorator, ContentPresenterTextBlock을 포함한 여러개의 다른 객체를 담을 수 있습니다.

다음 코드는 마크업에서 정의된 Button 컨트롤을 보여줍니다.

XAML
<Button Click="OnClick">OK</Button>


기본적인 Button 컨트롤을 구성하는 비주얼 객체를 열거한다면 아래와 같은 비주얼 객체의 계층 구조를 확인할 수 있을 것입니다.

비주얼 트리 계층 구조의 다이어그램

Button 컨트롤은 ContentPresenter 엘리먼트를 포함하는 ClassicBorderDecorator 엘리먼트를 포함합니다. ClassicBorderDecorator 엘리먼트는 Button을 위한 테두리와 배경을 그릴 책임이 있습니다. ContentPresenter 엘리먼트는 Button의 내용을 표시할 책임이 있습니다. 이 경우에서 텍스트를 표시하기 때문에 ContentPresenter 엘리먼트는 TextBlock 엘리먼트를 포함합니다. Button 컨트롤이 ContentPresenter를 사용한다는 사실은 컨텐트가 ImageEllipseGeometry와 같은 다른 엘리먼트로 대체될 수 있다는 것을 의미합니다.

컨트롤 템플릿
컨트롤의 계층으로 컨트롤을 확장하는 핵심은 ControlTemplate입니다. 컨트롤 템플릿은 컨트롤의 기본 비주얼 계층을 말합니다. 명시적으로 컨트롤을 참조하면 암시적으로 그것의 비주얼 계층을 참조합니다. 컨트롤을 사용자 정의된 비주얼 형태로 만들기 위해 컨트롤 템플릿의 기본 값을 오버라이드 할 수 있습니다. 예를 들어 Button 컨트롤의 배경 색상 값을 단일 색상 값 대신 선형 그레이디언트 색상 값으로 변경할 수 있습니다. 더 자세한 정보는 Button ControlTemplate Example을 참고하십시오.

Button 컨트롤과 같은 사용자 인터페이스 엘리먼트는 컨트롤의 전체 렌더링 정의를 설명하는 몇 개의 벡터 그래픽 인스트럭션 리스트를 포함합니다. 다음 코드는 마크업에서 정의된 Button 컨트롤을 보여줍니다.

XAML
<Button Click="OnClick">
  <Image Source="images\greenlight.jpg"></Image>
</Button>

Button 컨트롤을 구성하는 비주얼 객체와 벡터 그래픽 인스트럭션 리스트를 열거한다면 아래 그림과 같은 객체의 계층 구조를 확인할 수 있을 것입니다.

비주얼 트리와 렌더링 데이터의 다이어그램

Button 컨트롤은 ContentPresenter 엘리먼트를 포함하는 ClassicBorderDecorator 엘리먼트를 포함합니다. ClassicBorderDecorator 엘리먼트는 버튼의 경계나 배경을 만드는 모든 구분된 그래픽 엘리먼트를 그릴 책임을 가지고 있습니다. ContentPresenter 엘리먼트는 Button의 내용을 표시할 책임이 있습니다. 이 경우에서 이미지를 표시하기 때문에 ContentPresenter 엘리먼트는 Image 엘리먼트를 포함하게 됩니다.

비주얼 객체와 벡터 그래픽 인스트럭션 목록에 관하여 주의해야 할 몇 가지 사항이 있습니다.
  • 계층 구조에서 순서는 드로잉 정보의 렌더링 순서를 말합니다. 루트 비주얼 엘리먼트에서 자식 엘리먼트까지 왼쪽에서 오른쪽, 위에서 아래로 순회(traversed)됩니다. 엘리먼트가 비주얼 차일드 엘리먼트를 가진다면 엘리먼트의 형제보다 먼저 순회됩니다.
  • 계층 구조에서 ContentPresenter와 같은 리프가 아닌 노드 엘리먼트는 자식 엘리먼트를 담게 되며 인스트럭션 목록을 포함하지 않습니다.
  • 비주얼 엘리먼트가 벡터 그래픽 인스트럭션 리스트와 비주얼 자식 모두를 포함한다면 부모 비주얼 엘리먼트에 있는 인스트럭션 목록은 어떤 자식 비주얼 객체에 있는 드로잉보다 먼저 그려집니다.
  • 벡터 그래픽 인스트럭션 목록에 있는 아이템은 왼쪽에서 오른쪽으로 그려집니다.
비주얼 트리

비주얼 트리는 애플리케이션의 사용자 인터페이스에 사용되는 모든 비주얼 엘리먼트를 포함합니다. 비주얼 엘리먼트가 지속되는 드로잉 정보를 담기 때문에 비주얼 트리를 디스플레이 장치에 출력물을 구성하기 위해 필요한 모든 렌더링 정보를 담는 씬 그래프로 생각할 수 있습니다. 이 트리는 코드든 마크업이든 애플리케이션에 의해 직접적으로 생성된 모든 비주얼 엘리먼트의 집합입니다. 비주얼 트리는 또한 컨트롤과 데이터 객체와 같은 엘리먼트의 템플릿 확장에 의해 생성된 모든 비주얼 엘리먼트도 포함합니다.

다음 코드는 마크업에서 정의된 StackPanel 엘리먼트를 보여줍니다.

XAML
<StackPanel>
  <Label>User name:</Label>
  <TextBox />
  <Button Click="OnClick">OK</Button>
</StackPanel>

마크업 예제의 StackPanel 엘리먼트를 구성하는 비주얼 객체를 열거한다면 아래 그림과 같은 비주얼 객체의 계층 구조를 확인할 수 있을 것입니다.

비주얼 트리 계층 구조의 다이어그램


렌더링 순서
비주얼 트리는 WPF 비주얼과 드로잉 객체의 렌더링 순서를 결정합니다. 순회의 순서는 비주얼 트리의 최상위 노드인 루트 비주얼에서 시작됩니다. 루트 비주얼의 자식은 왼쪽에서 오른쪽으로 순회됩니다. 비주얼이 자식을 가진다면 그 자식은 비주얼의 형제보다 먼저 순회됩니다. 이것은 자식 비주얼의 컨텐트는 비주얼 자신의 컨텐트에 앞서 그려진다는 것을 의미합니다.

비주얼 트리 렌더링 순서의 다이어그램


루트 비주얼
root visual
은 비주얼 트리 계층 구조에서 최상위 엘리먼트입니다. 대부분의 애플리케이션에서 루트 비주얼의 기반 클래스는 Window 또는 NavigateWindow입니다. 그러나 Win32 애플리케이션에서 비주얼 객체를 호스팅한다면 루트 비주얼은 Win32 윈도에서 호스팅한 최상위 비주얼이 될 것입니다. 더 자세한 정보는 Tutorial: Hosting Visual Objects in a Win32 Application을 참고하십시오.

로지컬 트리와의 관계
WPF에서 로지컬 트리는 런타임시 애플리케이션의 엘리먼트를 말합니다. 비록 이 트리를 직접 다루지 않더라도 이 애플리케이션의 뷰는 속성 상속과 이벤트 라우팅을 이해하는데 유용합니다. 비주얼 트리와는 다르게 로지컬 트리는 ListItem과 같은 비-비주얼 데이터 객체를 나타낼 수 있습니다. 많은 경우에서 로지컬 트리는 애플리케이션의 마크업 정의와 매우 밀접하게 연결됩니다. 다음 코드는 마크업에서 정의된 DockPanel 엘리먼트를 보여줍니다.

XAML
<DockPanel>
  <ListBox>
    <ListBoxItem>Dog</ListBoxItem>
    <ListBoxItem>Cat</ListBoxItem>
    <ListBoxItem>Fish</ListBoxItem>
  </ListBox>
  <Button Click="OnClick">OK</Button>
</DockPanel>


마크업 예제의 DockPanel 엘리먼트를 구성하는 로지컬 객체를 열거한다면 아래 그림과 같은 로지컬 객체의 계층 구조를 확인할 수 있을 것입니다.

로지컬 트리의 다이어그램

비주얼 트리와 로지컬 트리 모두 애플리케이션의 현재 세트, 모든 엘리먼트의 추가, 삭제 또는 변경의 반영에 동기화됩니다. 그러나 이 트리들은 애플리케이션의 뷰를 다르게 나타냅니다. 비주얼 트리와 달리 로지컬 트리는 컨트롤의 ContentPresenter 엘리먼트를 확장하지 않습니다. 이것은 같은 객체의 세트를 위한 로지컬 트리와 비주얼 트리간의 일대일 일치성이 없다는 것을 의미합니다. 사실 LogicalTreeHelper 객체의 GetChildren 메소드와 VisualTreeHelper 객체의 GetChild 메소드를 파라미터로 같은 엘리먼트를 사용하여 호출하는 것은 다른 결과를 산출합니다.

로지컬 트리에 관한 더 자세한 정보는 Element Tree를 참고하십시오.

XamlPad로 비주얼 트리를 보기
WPF 툴인 XamlPad는 현재 정의된 XAML 컨텐트와 일치하는 비주얼 트리를 보여주고 탐색하기 위한 옵션을 제공합니다. 비주얼 트리를 표시하기 위해 메뉴 바에 있는 Show Visual Tree 버튼을 클릭하십시오. 다음 그림은 XamlPad의 Visual Tree Explorer패널에서 비주얼 트리 노드에 있는 XAML 컨텐트를 확장한 것입니다.

XamlPad에서 Visual Tree Explorer

Label, TextBoxButton 컨트롤이 XamlPad의 Visual Tree Explorer에서 각각 분리된 비주얼 객체 계층 구조를 표시하는 것에 주목하십시오. 이것은 WPF 컨트롤이 그 컨트롤의 비주얼 트리를 담는 ControlTemplate를 가지고 있기 때문입니다. 명시적으로 컨트롤을 참조하면 그것의 비주얼 계층 구조를 암시적으로 참조하게 됩니다.

비주얼 성능 분석
WPF는 애플리케이션의 런타임 동작 분석과 적용 가능한 성능 최적화의 종류의 결정을 가능케 하는 성능 분석 툴 스위트(suite)를 제공합니다. Visual Profiler 툴은 애플리케이션의 비주얼 트리에 직접 매핑된 성능 데이터의 풍부한 시각적인 뷰를 제공합니다. 이 스크린 샷에서 Visual Profiler의 CPU Usage 섹션은 렌더링이나 레이아웃 같은 오브젝트의 WPF 서비스 사용에 대한 정확한 구분을 제공합니다.

Visual Profiler 화면 출력
사용자 삽입 이미지
WPF 성능 툴에 관한 더 자세한 정보는 Performance Profiling Tools for WPF를 참고하십시오.

비주얼 렌더링 동작

WPF는 비주얼 객체- 보유 모드 그래픽, 벡터 그래픽 및 장치 독립적인 그래픽 - 의 렌더링 동작에 영향을 주는 몇 가지 기능을 소개합니다.

보유 모드 그래픽(Retained Mode Graphics)
비주얼 객체의 역할을 이해하는 핵심 중 하나는 즉시 모드(immediate mode)보유 모드(retained mode) 그래픽 시스템간의 차이를 이해하는 것입니다. 표준 Win32 애플리케이션은 GDI나 GDI+을 사용한 즉시 모드 그래픽 시스템에 기반합니다. 이것은 애플리케이션이 윈도의 크기가 변경되거나 객체가 자신의 시각적인 형태를 변경하는 것 같은 액션이 있을 때 무효화된 클라이언트 영역의 부분을 다시 칠할(repainting) 책임이 있다는 것을 의미합니다.

Win32 렌더링 시퀀스의 다이어그램


이에 비해 WPF는 보유 모드 시스템을 사용합니다. 이것은 시각적인 형태를 가지는 애플리케이션 객체가 직렬화된 드로잉 데이터의 세트를 정의한다는 것을 의미합니다. 일단 드로잉 데이터가 정의되면 시스템은 그 이후의 애플리케이션 객체를 그리기 위한 모든 다시 칠하기 요청에 대해 응답할 책임을 가집니다. 보유 모드 그래픽 시스템의 장점은 그릴 정보가 항상 애플리케이션에 의해 직렬화된 상태를 유지하지만 렌더링할 책임은 시스템에게 넘어간다는 점입니다.

WPF 렌더링 시퀀스의 다이어그램


지능적인 리드로잉
보유 모드 그래픽을 사용하여 얻을 수 있는 가장 큰 이득 중 하나는 WPF가 애플리케이션에서 리드로잉될 필요가 있을 때 효과적으로 최적화 할 수 있다는 것입니다. 심지어 다양한 불투명도의 레벨을 가진 복잡한 신에서도 일반적으로 최적화된 리드로잉를 위한 특별한 동작을 작성할 필요가 없습니다. 이것을 Win32 프로그래밍에서 업데이트 영역 내의 다시 그릴 양을 최소화하여 애플리케이션을 최적화하기 위해 들일 수 있는 엄청난 노력의 낭비와 비교해 보십시오. Win32 애플리케이션에서 다시 그리기 최적화와 관계된 복잡한 유형의 예를 Redrawing in the Update Region에서 참고하십시오.

벡터 그래픽
WPF는 렌더링 데이터 포맷으로써 벡터 그래픽을 사용합니다. 벡터 그래픽스 - Scalable Vector Graphics(SVG), Windows metafiles(.wmf) 및 트루타입 폰트를 포함하는 - 는 렌더링 데이터를 저장하고 원본 그래픽을 사용하여 이미지를 재생성하는 방법을 기술하는 인스트럭션의 목록으로 그것을 전달합니다. 예를 들어 트루타입 폰트는 픽셀을 배열이 아닌 직선, 곡선 및 커맨드의 세트를 기술하는 아웃라인 폰트입니다. 벡터 그래픽의 주요한 장점 중 하나는 크기와 해상도에 관계 없이 스케일(배율 조절) 할 수 있는 능력입니다.

벡터 그래픽과 달리 비트맵 그래픽은 이미지를 나타내는 데 픽셀 단위로 렌더링 데이터를 저장하고, 특정 해상도에 맞춰 미리 렌더링됩니다. 비트맵과 벡터 그래픽 포맷간의 주요한 차이점 중 하나는 원본 이미지에 대한 충실도입니다. 예를 들어 원본 이미지의 크기가 변경되면 비트맵 그래픽 시스템은 이미지를 잡아 늘이는(stretch) 반면 벡터 그래픽 시스템은 이미지의 충실도를 보존하면서 스케일링 합니다.

다음 그림은 300% 리사이즈된 원본 이미지를 보여줍니다. 벡터 그래픽 이미지와는 달리 비트맵 그래픽 이미지의 잡아 늘여져서 왜곡된 모습을 주목하십시오.

래스터와 벡터 그래픽간의 차이점

다음 마크업은 두 Path 엘리먼트의 정의를 보여줍니다. 두번째 엘리먼트는 첫번째 엘리먼트의 드로잉 인스트럭션을 300% 확대하는 데 ScaleTransform을 사용합니다. Path 엘리먼트의 드로잉 인스트럭션이 변형되지 않고 남아있는 것에 주목하십시오.

XAML
<Path
  Data="M10,100 C 60,0 100,200 150,100 z"
  Fill="{StaticResource linearGradientBackground}"
  Stroke="Black"
  StrokeThickness="2" />

<Path
  Data="M10,100 C 60,0 100,200 150,100 z"
  Fill="{StaticResource linearGradientBackground}"
  Stroke="Black"
  StrokeThickness="2" >
  <Path.RenderTransform>
    <ScaleTransform ScaleX="3.0" ScaleY="3.0" />
  </Path.RenderTransform>
</Path>

해상도와 장치 독립적 그래픽에 대하여
스크린상에서 텍스트와 그래픽의 크기를 결정하는 두가지 시스템 요인이 있습니다. 바로 해상도(resolution)와 DPI입니다. 해상도는 스크린상에 나타날 픽셀의 수를 기술합니다. 해상도가 높아질 수록 픽셀은 작아지기 때문에 그래픽과 텍스트는 작게 나타납니다. 그래픽은 모니터를 1024 x 768로 설정할 때보다 1600 x 1200으로 설정할 때 더 작게 보일 것입니다.

다른 시스템 설정인 DPI는 스크린 인치(inch)당 픽셀의 크기를 기술합니다. 대부분의 윈도 시스템은 96 DPI를 가지고 있고 스크린 인치는 96 픽셀이라는 것을 의미합니다. DPI 설정을 증가시키는 것은 스크린 인치를 크게 하고 DPI를 감소시키는 것은 스크린 인치를 작게 합니다. 이것은 스크린 인치는 실제의 인치와 같은 크기가 아니라는 것을 의미합니다. 대부분의 시스템에서 대개 그렇습니다. DPI를 증가시키면, 스크린 인치의 크기가 증가되기 때문에 DPI맞춤(DPI-aware) 그래픽과 텍스트는 커집니다. DPI를 증가시키는 것은 특히 높은 해상도에서 텍스트 가독성을 높혀줍니다.

모든 애플리케이션이 DPI맞춤은 아닙니다. 어떤 것은 측정의 주단위로 하드웨어 픽셀을 사용합니다. 이런 애플리케이션에서 시스템 DPI를 변경하는 것은 아무런 효과도 없습니다. 많은 다른 애플리케이션은 폰트 크기를 기술하기 위해 DPI맞춤 단위를 사용하지만 다른 모든 것을 기술하는데에 픽셀을 사용합니다. DPI를 너무 작거나 너무 크게 하는 것은 애플리케이션의 레이아웃 문제를 일으키는 원인이 되기 때문에 애플리케이션의 텍스트는 시스템의 DPI 세팅에 따라 조절되지만 애플리케이션의 UI는 그렇지 않습니다.

이 문제는 WPF를 사용하여 개발된 애플리케이션에서 제거되었습니다. WPF는 하드웨어 픽셀 대신 측정의 주단위로써 장치 독립적인 픽셀을 사용하여 자동 스케일링을 지원합니다. 그래픽과 텍스트는 애플리케이션 개발자의 어떤 추가 작업 없이도 제대로 조절됩니다. 다음 그림은 WPF가 텍스트와 그래픽을 다른 DPI 설정에서 표시하는 방법을 보여줍니다.

다른 DPI 설정에서의 그래픽과 텍스트


VisualTreeHelper 클래스

VisualTreeHelper 클래스는 비주얼 객체 레벨에서 프로그래밍하기 위한 저수준(low-level) 기능을 제공하는 static 헬퍼 클래스이며, 고성능 사용자 정의 컨트롤 개발과 같은 매우 특이한 시나리오에서 유용합니다. 대부분의 경우에서 CanvasTextBlock과 같은 고수준(higer-level) WPF 프레임웍 객체는 매우 융통성있고 사용하기 쉽습니다.

충돌 테스팅
VisualTreeHelper 클래스는 기본 히트 테스트가 필요에 맞지 않을 경우 비주얼 객체상의 충돌 테스팅을 위한 메소드를 제공합니다. 컨트롤이나 그래픽 엘리먼트와 같은 객체의 영역 내에 지오메트리나 포인트 좌표값이 있는지 여부를 결정하기 위하여 VisualTreeHelper에서 HitTest 메소드를 사용할 수 있습니다. 예를 들어 원형의 지오메트리를 둘러싸는 사각형(bounding rectangle) 영역내에 마우스 클릭이 있었는지 여부를 결정하기 위해 히트 테스팅을 사용할 수 있습니다. 또한 직접 작성한 히트 테스트 계산 동작을 위해 기본 히트 테스팅 구현을 오버라이드하는 방법을 택할 수도 있습니다.

히트 테스팅에 관한 더 자세한 정보는 Hit Testing in the Visual Layer를 참고하십시오.

비주얼 트리 열거하기
VisualTreeHelper
클래스는 비주얼 트리의 멤버를 열거하는 기능을 제공합니다. (트리의) 부모를 얻기 위해 GetParent를 호출합니다. 자식을 얻거나 비주얼 객체의 자손을 가리키기 위해 GetChild 메소드를 호출합니다. 이 메소드는 지정된 인덱스에 있는 부모의 자식 Visual을 반환합니다.

다음 예제는 비주얼 객체의 모든 자손을 열거하는 방법을 보여줍니다. 이것은 비주얼 객체 계층 구조의 모든 렌더링 정보를 직렬화하고 싶다면 꼭 사용해야 할 기술입니다.

C#
// 비주얼 객체의 모든 자손을 열거.
static public void EnumVisual(Visual myVisual)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
    {
        // 지정된 인덱스 값의 자식 비주얼을 얻음.
        Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);

        // 자식 비주얼 객체로 처리 수행.

        // 자식 비주얼 객체의 자식을 열거(재귀 호출).
        EnumVisual(childVisual);
    }
}

대부분의 경우에서 로지컬 트리는 WPF 애플리케이션의 엘리먼트를 나타내는데 더 유용합니다. 비록 로지컬 트리를 직접적으로 수정하지 않을 지라도 이 애플리케이션의 뷰는 상속과 이벤트 라우팅을 이해하는데 유용합니다. 비주얼 트리와는 달리 로지컬 트리는 ListItem과 같은 비-비주얼 데이터 객체를 나타낼 수 있습니다. 로지컬 트리에 대한 더 자세한 정보는 Element Tree를 참고하십시오.

VisualTreeHelper 클래스는 비주얼 객체를 둘러싸는 사각형을 반환하는 메소드를 제공합니다. GetContentBounds를 호출하여 비주얼 객체를 둘러싸는 사각형을 반환할 수 있습니다. 또한 GetDescendantBound를 호출하여 비주얼 객체 자신을 포함한 모든 비주얼 객체를 둘러싸는 사각형을 반환할 수 있습니다. 다음 코드는 비주얼 객체와 모든 자신의 자손을 둘러싸는 사각형을 계산하는 방법을 보여줍니다.

C#
// 부모 비주얼 객체와 모든 자손을 둘러싸는 사각형을 반환.
Rect rectBounds = VisualTreeHelper.GetDescendantBounds(parentVisual);

See Also

Reference
Visual
VisualTreeHelper
DrawingVisual

Concepts
Hit Testing in the Visual Layer
Using DrawingVisual Objects
Tutorial: Hosting Visual Objects in a Win32 Application
Optimizing WPF Application Performance
신고
Posted by gongdo
MSDN : http://msdn2.microsoft.com/en-us/library/ms742562.aspx

WPF Graphics, Animation, and Media Overview


이 토픽은 애플리케이션에 그래픽, 변형 효과, 사운드 및 비디오를 추가할 수 있게 하는 WPF의 그래픽, 애니메이션 및 미디어 기능을 소개합니다.

WPF는 이 전에는 전문적인 라이브러리를 통해서만 가능했던 - 특히, Microsoft Windows Graphics Device Interface(GDI) 와 Microsoft Windows GDI+ - 고급 드로잉과 애니메이션 기능을 제공합니다. WPF는 이제 멀티미디어, 벡터 그래픽, 애니메이션 및 컨텐트 작성을 위한 통합된 지원을 제공하며 개발자가 쉽게 재밌는 사용자 인터페이스와 컨텐트를 만들수 있도록 합니다. Microsoft Visual Studio .NET이나 심지어 노트패드와 같은 텍스트 에디터를 사용할 수도 있습니다. 벡터 그래픽이나 복잡한 애니메이션을 만들 수 있고 애플리케이션에 미디어를 통합할 수 있습니다.

이 토픽은 다음 섹션을 담고 있습니다.
WPF에서의 그래픽와 멀티미디어의 새로운 점

WPF는 윈도 개발자에게 다음과 같은 이점을 갖는 새 그래픽 기능을 소개합니다.
  • 해상도(resolution)와 장치에 독립적인 그래픽.
    WPF 그래픽 시스템은 해상도와 장치에 종속적이지 않은 장치 독립적인 유닛을 사용합니다. 각 장치 독립적 픽셀은 시스템의 DPI(dots-per-inch) 세팅에 맞춰 자동적으로 조절(scale)합니다.
  • 향상된 정밀도.
    WPF 좌표계는 floats 대신 doubles를 사용합니다. 변형과 불투명도 값 또한 doubles를 사용하여 표현됩니다. WPF는 또한 더 넓은 색상 영역(scRGB)을 지원하고 서로 다른 색공간에서의 입력을 관리하는 통합된 지원을 제공합니다.
  • 고급 그래픽과 애니메이션 지원.
    WPF는 신(scene) 그래프를 관리함으로써 그래픽 프로그래밍을 단순화합니다. 더 이상 신 처리, 렌더링 루프 및 이중 선형 보간법(bilinear interpolation)에 대해 걱정할 필요가 없습니다. WPF는 애니메이션 시스템에 통합된 충돌 테스팅을 제공하고 완전한 알파 합성을 지원합니다.
  • 하드웨어 가속.
    WPF 그래픽 시스템은 CPU 사용량을 최소화 하기 위해 그래픽 하드웨어의 유리함을 취하도록 디자인되었습니다.
2-D 도형(Shapes)
WPF는 다음 그림과 같이 직사각형과 타원과 같은 일반적으로 사용되는 벡터 방식의 2-D 도형의 라이브러리를 제공합니다.
사용자 삽입 이미지
이 내부적인 WPF 도형은 단순한 도형이 아니라 대부분의 일반적인 컨트롤에서 기대하는 키보드 및 마우스 입력을 포함한 많은 기능을 구현하는 프로그래밍 가능한 요소입니다.

XAML

<Window

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  x:Class="Window1" >

  <Ellipse Fill="LightBlue" MouseUp="ellipseButton_MouseUp" />

</Window>


C#

public partial class Window1 : Window

{

    void ellipseButton_MouseUp(object sender, MouseButtonEventArgs e)

    {

        MessageBox.Show("Me, a simple ellipse, was mouse clicked!");

    }

}


다음 그림은 전술한 XAML 마크업과 코드-비하인드의 출력을 보여줍니다.
사용자 삽입 이미지
더 자세한 정보는 Shapes and Basic Drawing in WPF Overview를 참고하십시오. 예제 샘플은 Shape Elements Samples를 참고하십시오.

2-D 기하(Geometries, 이하 지오메트리)
WPF가 제공하는 2-D 도형이 충분하지 않다면 지오메트리와 패스(path)를 위한 WPF 지원을 사용하여 직접 만들 수 있습니다. 다음 그림은 드로잉 브러쉬로 도형을 만들고 다른 WPF 엘리먼트로 클립하기 위해 지오메트리를 사용하는 방법을 보여줍니다.
사용자 삽입 이미지
더 자세한 정보는 Geometry Overview를 참고하고, 소개 샘플은 Geometries Sample을 참고하십시오.

2-D 효과
WPF는 다양한 효과를 생성하기 위해 사용할 수 있는 2-D 클래스 라이브러리를 제공합니다. WPF의 2-D 렌더링 능력은 UI 엘리먼트를 그레이디언트, 비트맵, 드로잉 및 비디오로 칠할(paint) 수 있는 능력을 제공하고 그것을 회전, 스케일링 및 비틀기를 사용하여 조작할 수 있습니다. 다음 그림은 WPF 브러쉬를 사용하여 얻을 수 있는 많은 효과의 예를 보여줍니다.
사용자 삽입 이미지
더 자세한 정보는 WPF Brushes Overview를 참고하고, 소개 샘플은 Brushes Sample을 참고하십시오.

3-D 렌더링
WPF는 더욱 확장된 레이아웃, UI와 데이터 시각화를 만들기 위해 2-D 그래픽 지원과 함께 통합되는 3-D 렌더링 기능의 세트를 제공합니다. 스펙트럼의 끝에서 WPF는 다음 그림에서 보여주는 것 처럼 3-D 도형의 표면상에 2-D 이미지를 그릴 수 있게 합니다.
사용자 삽입 이미지
더 자세한 정보는 3-D Graphics Overview를 참고하고 소개 샘플은 3-D Solids Sample을 참고하십시오.

애니메이션

애니메이션을 사용하여 컨트롤과 엘리먼트를 확대하고 흔들고 회전하고 페이드할 수 있으며, 재밌는 페이지 변형 등을 만들 수 있습니다. WPF가 대부분의 속성을 움직일(animate)수 있게 하기 때문에 대부분의 WPF 객체를 움직일 수 있을 뿐만 아니라 생성한 사용자 정의 객체도 WPF를 사용하여 움직일 수 있습니다.
사용자 삽입 이미지

더 자세한 정보는 Animation Overview를 참고하고, 소개 샘플은 Animation Example Gallery를 참고하십시오.

미디어

이미지, 비디오 및 오디오는 정보와 사용자 경험을 전달하는 최선의 방법입니다.

이미지
아이콘, 배경뿐만 아니라 애니메이션의 부분까지도 포함하는 이미지는 많은 애플리케이션의 핵심 부분입니다. 이미지를 사용할 필요가 자주있기 때문에 WPF는 다양한 방법으로 이미지를 포함한 작업을 할 수 있는 기능을 노출합니다. 다음 그림은 그 방법 중 하나를 보여줍니다.
사용자 삽입 이미지
더 자세한 정보는 Imaging Overview를 참고하고, 소개 샘플은 WPF Photo Viewer Demo를 참고하십시오.

비디오와 오디오
WPF 그래픽 기능의 핵심은 비디오와 오디오를 포함한 멀티미디어 작업을 위한 네이티브 지원을 제공하는 것입니다. 다음은 애플리케이션에 어떻게 미디어 플레이어를 삽입하는지 보여줍니다.

XAML

<MediaElement Source="media\numbers.wmv" Width="450" Height="250" />


MediaElement
는 비디오와 오디오 모두를 재생할 수 있고 사용자 정의 UI의 작성을 쉽게할 수 있는 충분한 확장성이 있습니다.

더 자세한 정보는 Multimedia Overview를 참고하고 소개 샘플은 Media Gallery를 참고하십시오.

See Also

Concepts
Shapes and Basic Drawing in WPF Overview
Painting with Solid Colors and Gradients Overview
Painting with Images, Drawings, and Visuals
Animation Overview
신고
Posted by gongdo
MSDN : http://msdn2.microsoft.com/en-us/library/ms742196.aspx

Graphics Rendering Tiers


렌더링 단계(Rendering tier)는 WPF 애플리케이션을 실행하는 장치를 위한 그래픽 하드웨어 능력과 성능의 레벨을 정의합니다.

이 토픽은 다음 섹션을 담고 있습니다.
그래픽 하드웨어

렌더링 단계 레벨에 가장 큰 영향을 주는 그래픽 하드웨어의 기능(feature)은 다음과 같습니다.
  • 비디오 램 ; 그래픽 하드웨어에 장착된 비디오 메모리의 합계로써 복합적인 그래픽에 사용될 수 있는 버퍼의 크기와 수를 결정합니다.
  • 픽셀 셰이더 ; 픽셀 셰이더는 픽셀 기반의 효과를 계산하는 그래픽 처리 기능입니다. 표시되는 그래픽의 해상도에 따라서 각 디스플레이 프레임을 위해 처리될 필요가 있는 수백만 픽셀이 있을 수 있습니다.
  • 버텍스(vertex) 셰이더 ; 객체의 버텍스 데이터상에 있는 수학적인 연산 동작을 수행하는 그래픽 처리 기능입니다.
  • 멀티텍스쳐 지원 ; 멀티텍스쳐 지원은 3D 그래픽 객체상에서 블렌딩 연산 도중 두개 혹은 그 이상으로 구분되는 텍스쳐를 적용하는 능력을 말합니다. 멀티텍스쳐 지원의 정도는 그래픽 하드웨어상의 멀티텍스쳐 유닛의 수에 따라 결정됩니다.
픽셀 셰이더, 버텍스 셰이더 및 멀티텍스쳐 기능은 특정 DirectX 버전 레벨을 정의하는데 사용되고 WPF에서 렌더링 단계의 차이를 정의합니다.

렌더링 단계 정의

그래픽 하드웨어에서 제공되는 기능은 WPF 애플리케이션의 렌더링 능력을 결정합니다. WPF 시스템은 다음의 세 렌더링 단계를 정의합니다.

  • 렌더링 단계 0 ; 그래픽 하드웨어 가속 없음. DirectX 버전 레벨은 버전 7.0보다 낮습니다.
  • 렌더링 단계 1 ; 부분적인 그래픽 하드웨어 가속. DirectX 버전 레벨은 버전 7.0과 같거나 더 높고 버전 9.0보다 낮습니다.
  • 렌더링 단계 2 ; 대부분의 그래픽 기능을 사용하는 그래픽 하드웨어 가속. DirectX 버전 레벨은 버전 9.0과 같거나 더 높습니다.
Tier 속성은 애플리케이션 런타임에 렌더링 단계를 얻을 수 있게 하며, 개발자가 장치에 특정 하드웨어 가속된 그래픽 기능을 지원하는지를 결정할 수 있게 합니다. 그러므로 애플리케이션은 장치에 의해 지원되는 렌더링 단계에 따라서 런타임에 다른 코드 경로를 취할 수 있습니다.

렌더링 단계 0
렌더링 단계 0은 장치상에 애플리케이션을 위한 그래픽 하드웨어 가속 지원이 없다는 것을 의미합니다. 이 단계 레벨에서 개발자는 모든 그래픽이 하드웨어 가속 없이 소프트웨어에 의해 그려진다고 가정해야 합니다. 이 단계의 기능성은 DirectX 버전 7.0 미만과 일치합니다.

렌더링 단계 1
렌더링 단계 1은 비디오 카드에서 일부 그래픽 하드웨어 가속이 가능하다는 것을 의미합니다. 이것은 DirectX 버전 7.0 이상과 9.0 미만과 일치합니다.

다음 기능과 능력은 렌더링 단계 1에서 하드웨어 가속됩니다.

2D 렌더링 ; 대부분의 2D 그리기가 지원됩니다.
3D 래스터화 ; 대부분의 3D 래스터화가 지원됩니다. 그러나 WPF는 버텍스 컬러로 하드웨어에 전달된 버텍스 조도를 계산하는데 소프트웨어를 사용할 것입니다.
3D 비등방성 필터링(anisotrophic) ; 렌더링 단계가 1이상이면 WPF는 3D 컨텐트를 그릴 때 비등방성 필터링 사용을 시도합니다. 비등방성 필터링은 카메라의 주의에서 멀리 있고 급한 각도를 이루는 표면상의 이미지 품질을 높이는 것을 말합니다.
3D MIP 매핑 ; 렌더링 단계가 1이상이면 WPF는 3D 컨텐트를 그릴 때 MIP 매핑 사용을 시도합니다. MIP 매핑은 텍스쳐가 Viewport3D에 있는 뷰보다 작은 필드를 차지할 경우 텍스쳐의 품질을 향상시킵니다. (※역주 : 비등방성 필터링이나 MIP매핑 등의 정의는 3D 그래픽 영역에 속하며 위키 사전등을 참조하도록 합니다. 비등방성 필터링(테크노아), MIP Map(위키)  )

다음 기능과 능력은 렌더링 단계 1에서는 하드웨어 가속되지 않습니다.
비트맵 효과 ; 시각적 표현의 비트맵 효과는 하드웨어 가속 없이 그려지도록 강제됩니다.
출력된 컨텐트 ; 모든 출력된 컨텐트는 WPF 소프트웨어 파이프라인을 사용하여 그려집니다.
RenderTargetBitmap 객체를 사용하여 래스터화된 컨텐트 ; 모든 컨텐트는 RenderTargetBitmapRender 메소드를 사용하여 그려집니다.
TileBrush를 사용한 격자 컨텐트 ; TileBrushTileModeTile로 설정된 모든 격자 컨텐트.
그래픽 하드웨어의 최대 텍스쳐 크기를 초과한 표면 ; 대부분의 그래픽 카드는 2048 x 2048 또는 4096 x 4096 픽셀보다 큰 크기의 표면을 지원하지 않습니다.
그래픽 하드웨어의 메모리를 초과하는 비디오 램을 요청하는 모든 명령 ; Windows SDK에 포함된 Preforator 툴을 사용하여 애플리케이션의 비디오 램 사용량을 모니터할 수 있습니다.
레이어드(layered) 윈도 ; 레이어드 윈도는 WPF 애플리케이션이 비사각형 윈도의 화면에 컨텐트를 그릴 수 있게 합니다. 윈도 Vista에서는 레이어드 윈도가 하드웨어 가속됩니다. 윈도 XP와 같은 다른 시스템에서는 레이어드 윈도는 하드웨어 가속 없이 소프트웨어에 의해 그려집니다. 다음의 Window 속성을 세팅함으로써 WPF에서 레이어드 윈도를 사용할 수 있습니다.
원형 그레디언트 ; 모든 RadialGradientBrush의 사용.
3D 조명 계산 ; WPF는 버텍스당 조명으로 작동하며 이것은 조도가 메쉬에 적용되는 각 마테리얼을 위해 버텍스마다 계산되어야 한다는 것을 의미합니다. 단계 1에서 이 계산은 소프트웨어에 의해 작동됩니다. 단계 2에서는 이 계산이 하드웨어에 의해 작동됩니다.
텍스트 렌더링 ; 서브-픽셀 폰트 렌더링은 그래픽 하드웨어상의 픽셀 셰이더를 사용할 수 있습니다.
3D 안티-앨리에싱 ; 모든 3D 안티-앨리어싱의 사용.

다음 그래픽 하드웨어 기능은 렌더링 단계 1을 정의합니다.
DirectX 버전 ; 7.0 이상이고 9.0 미만이어야 합니다.
비디오 램 ; 30MB 이상이어야 합니다.
멀티텍스쳐 유닛 ; 2이상의 유닛이 있어야 합니다.

다음 표는 렌더링 단계1을 지원하는 일반적인 그래픽 카드의 목록입니다.

ATI ; Radeon 모델 : 256, 7000, 7500, 8500, 9000, 9100, 9200 및 9250
Intel ; 인텔 Extreme Graphics 모델 : 845G, 845GE, 845GL 및 845GV
        인텔 Extreme Graphics II 모델 : 852GME, 855GM, 855GME, 865G 및 865GV
NVidia ; GeForce 256
            GeForce2 모델 : GTS, MX, MX100, MX200, MX400, Pro, Ti 및 Ultra
            GeForce3 모델 : Ti200 및 Ti500
            GeForce4 모델 : MX420, MX440, MX460, MX4000, Ti4200, Ti4400, Ti4600 및 Ti4800
 
렌더링 단계2
렌더링 단계 2는 WPF의 그래픽 피추어 대부분이 가용 시스템 리소스가 고갈되지 않도록 하드웨어 가속을 사용할 필요가 있습니다. 이것은 9.0 이상의 DirectX 버전과 일치합니다.

다음 기능 및 기능은 렌터링 단계2를위해 하드웨어 가속됩니다.
단계 1 기능 ; 모든 단계 1의 기능을 포함합니다.
원형 그레이디언트 ; 지원될지라도 큰 객체상에서의 RadialGradientBrush의 사용은 피하십시오.
3D 조명 계산 ; WPF는 버텍스당 조명으로 동작하며, 이것은 조도가 메쉬에 적용될 각 마테리얼을 위한 각각의 버텍스에서 계산되어야 한다는 것을 의미합니다. 계층 1에서 이 계산은 소프트웨어에 의해 동작합니다. 계층 2에서 이 계산은 하드웨어에서 동작합니다.
텍스트 렌더링 ; 서브-픽셀 폰트 렌더링은 그래픽 하드웨어 상의 픽셀 셰이더를 사용할 수 있습니다.
3D 안티-앨리어싱 ; 3D 안티-앨리어싱은 윈도 Vista에서만 지원됩니다.

다음 기능과 능력은 렌터링 단계 2에서 하드웨어 가속되지 않습니다.
비트맵 효과 ; 시각적 표현의 비트맵 효과는 하드웨어 가속 없이 그려지도록 강제됩니다.
출력된 컨텐트 ; 모든 출력된 컨텐트는 WPF 소프트웨어 파이프라인을 사용하여 그려집니다.
RenderTargetBitmap 객체를 사용하여 래스터화된 컨텐트 ; 모든 컨텐트는 RenderTargetBitmap의 Render 메소드를 사용하여 그려집니다.
TileBrush를 사용한 격자 컨텐트 ; TileBrush의 TileMode가 Tile로 설정된 모든 격자 컨텐트.
그래픽 하드웨어의 최대 텍스쳐 크기를 초과한 표면 ; 대부분의 그래픽 카드는 2048 x 2048 또는 4096 x 4096 픽셀보다 큰 크기의 표면을 지원하지 않습니다.
그래픽 하드웨어의 메모리를 초과하는 비디오 램을 요청하는 모든 명령 ; Windows SDK에 포함된 Preforator 툴을 사용하여 애플리케이션의 비디오 램 사용량을 모니터할 수 있습니다.
레이어드(layered) 윈도 ; 레이어드 윈도는 WPF 애플리케이션이 비사각형 윈도의 화면에 컨텐트를 그릴 수 있게 합니다. 윈도 Vista에서는 레이어드 윈도가 하드웨어 가속됩니다. 윈도 XP와 같은 다른 시스템에서는 레이어드 윈도는 하드웨어 가속 없이 소프트웨어에 의해 그려집니다. 다음의 Window 속성을 세팅함으로써 WPF에서 레이어드 윈도를 사용할 수 있습니다.
  • WindowStyle = None
  • AllowsTransparency = true
  • Transparent
다음 그래픽 하드웨어 기능은 렌더링 단계 2를 정의합니다.

DirectX 버전 ; 9.0 이상이어야 합니다.
비디오 램 ; 120MB 이상이어야 합니다.
픽셀 셰이더 ; 버전 2.0 이상이어야 합니다.
버텍스 셰이더 ; 버전 2.0 이상이어야 합니다.
멀티텍스쳐 유닛 ; 유닛의 수가 4 이상이어야 합니다.

다음 표는 렌더링 단계 2를 지원하는 일반적인 그래픽 카드의 목록입니다.
ATI ; Radeon 모델 : 9550, 9600, 9800 및 X-시리즈
Intel ; 인텔 GMA900 모델 : 915G
         인텔 GMA950 모델 : 945G
NVidia ; Geforce FX-시리즈, 6xxx-시리즈 및 7xxx-시리즈

다른 리소스

다음 리소스는 WPF 애플리케이션의 성능 특성을 분석하는데 도움을 줄 수 있습니다.

그래픽 렌더링 레지스트리 설정
WPF는 WPF 렌더링을 제어하기 위해 네가지 레지스트리 설정을 제공합니다.

하드웨어 가속 옵션 비활성화 ; 하드웨어 가속을 활성화할지 지정합니다.
최대 멀티샘플 값 ; 3D 컨텐트 안티-앨리어싱을 위한 멀티샘플링의 정도를 지정합니다.
비디오 드라이버 날짜 요구사항 설정 ; 2004년 11월 이전에 공개된 드라이버에서는 시스템이 하드웨어 가속을 비활성화할지 지정합니다.
래스터라이저 참조 사용 옵션 ; WPF가 래스터라이저 참조를 사용해야 하는지 지정합니다.

이 세팅은 WPF 레지스트리 설정을 어떻게 참조하는지 아는 모든 외부 설정 유틸리티에 의해 접근될 수 있습니다. 이 설정은 또한 윈도 레지스트리 편집기를 사용함으로써 값을 직접 접근하여 만들거나 변경할 수 있습니다. 더 자세한 정보는 Graphics Rendering Registry Settings를 참고하십시오.

WPF 성능 분석 툴
WPF는 애플리케이션의 런타임 동작 분석과 적용 가능한 성능 최적화의 종류를 결정하는 것을 가능케 하는 성능 분석 툴 스위트(suite)를 제공합니다. 다음 표는 Windows SDK 툴인 WPFPerf에 포함된 다섯개의 성능 분석 툴을 열거합니다.

EventTrace ; 이벤트를 분석하고 이벤트 로그 파일을 생성하는데 사용합니다.
Perforator ; 렌더링 동작을 분석하는데 사용합니다.
Trace Viewer ; WPF 사용자 인터페이스 포맷의 Event Tracing for Windows(ETW) 로그 파일을 기록하고, 표시하고 보여줍니다.
Visual Profiler ; 시각적인 트리에서 엘리먼트 별로 레이아웃과 이벤트 핸들링과 같은 WPF 서비스의 사용을 분석하는데 사용합니다.
Working Set Viewer ; 애플리케이션의 working set 특성을 분석하는데 사용합니다.

Visual Profiler 툴 스위트는 성능 데이터의 풍부한 그래피컬 뷰를 제공합니다. 아래의 스크린샷은 Visual Profiler의 CPU Usage 섹션이 객체의 렌더링이나 레이아웃 같은 WPF 서비스의 사용에 대한 정확한 분석을 제공합니다.

Visual Profiler 화면 표시
사용자 삽입 이미지

WPF 성능 툴에 대한 더 자세한 정보는 Performance Profiling Tools for WPF를 참고하십시오.

DirectX 진단 툴
DirectX 진단 툴인 Dxdiag.exe는 DiretX와 관련된 주제의 문제 해결을 돕도록 디자인되었습니다. DiretX 진단 툴의 기본 설치 경로는 \Windows\System32 입니다.

DirectX 진단 툴을 실행하면 메인 윈도는 DirectX와 관련된 정보를 표시하고 진단하도록 하는 탭의 세트를 적재합니다. 예를 들어 System 탭은 컴퓨터에 관한 시스템 정보와 컴퓨터에 설치되어 있는 DirectX의 버전을 제공합니다.

DirectX 진단 툴 메인 윈도
사용자 삽입 이미지

See Also

Reference
RenderCapability
RenderOptions

Concepts
Optimizing WPF Application Performance
Graphics Rendering Registry Settings

Other Resources
Performance Profiling Tools for WPF
신고
Posted by gongdo
MSDN : http://msdn2.microsoft.com/en-us/library/aa969773.aspx

Dialog Boxes Overview

일반적으로 독립 애플리케이션은 시각적인 데이터 및 데이터와 상호작용하기 위한 기능을 노출하는 메인 윈도를 가집니다. 예를 들어 워드 프로세서 애플리케이션은 문서를 시각화하고, 파일 관리 및 문서와 상호작용하기 위한 편집 기능을 노출합니다.

단순하지 않은 애플리케이션의 일반적인 라이프 사이클의 한 부분으로 메인 윈도는 사용자에게서 정보를 수집하거나 표시하기 위해 하나 이상의 이차적인 윈도를 사용자에게 보여줄 것입니다. 이차적인 윈도는 다이얼로그 박스(dialog boxes, ※역주 : 일반적으로 대화 상자라고 하지만 다이얼로그 박스로 그대로 사용하였습니다.)라고 알려져 있고, 모덜(modal)과 모덜리스(modeless)의 두가지 종류가 있습니다.

모덜 다이얼로그 박스는 사용자에게 작업을 완료하기 위해 필요한 추가적인 정보를 보여주거나 보통 그것이 열려 있는 동안 다른 윈도의 활성화를 막기 위해 사용됩니다. 예를 들어 사용자가 워드 프로세서에서 문서를 열고 싶다면, 애플리케이션은 사용자가 열기를 원하는 파일의 이름을 물을 것입니다. 사용자가 이름을 제공해주기 전까지는 문서를 열 수 없기 때문에 다이얼로그 박스는 사용자가 이름을 입력하여 다이얼로그 박스를 확인(accept)하거나 보통 Cancel 버튼을 눌러 다이얼로그 박스를 취소하기를 기다려야 합니다.

한편, 모덜리스 다이얼로그 박스는 그것이 열려있는 동안 다른 윈도의 활성화를 막지 않습니다. 예를 들어 사용자가 문서내에서 부분적인 단어의 존재를 찾고 싶을 때 메인 윈도는 사용자가 찾고 있는 단어를 묻는 다이얼로그 박스를 열 것입니다. 그러나, 단어를 찾는 것은 문서 편집을 막지 않으므로 모덜이 되어야 할 필요가 없습니다. 모덜리스 다이얼로그 박스는 보통 Close 버튼을 클릭하여 닫습니다.

WPF는 메시지 박스, 커먼(common) 다이얼로그 박스 및 사용자 정의 다이얼로그 박스와 같은 몇 가지 다이얼로그 박스의 종류를 만들 수 있게 해줍니다. 이 토픽은 각각에 대해 다루며, Dialog Box Sample은 해당 예제를 제공합니다.

이 토픽은 다음과 같은 섹션을 담고 있습니다.

메시지 박스

메시지 박스는 정보를 보여주거나 사용자가 결정을 할 수 있도록 하기 위해 사용되는 간단한 다이얼로그 박스입니다. 다음 그림은 정보를 표시하고 사용자로부터 응답을 유도하는 메시지 박스를 나타냅니다.
사용자 삽입 이미지

메시지 박스를 만들기 위해,
MessageBox 클래스를 사용합니다. MessageBox는 다음과 같은 코드를 사용하여 메시지 박스 내용, 제목, 아이콘 및 버튼들을 설정할 수 있게합니다.

C#
// Configure the message box
string messageBoxText = "This document needs to be saved ... .";
string caption = "Word Processor";
MessageBoxButton button = MessageBoxButton.YesNoCancel;
MessageBoxImage icon = MessageBoxImage.Warning;

메시지 박스를 보여주기 위해, 다음과 같이 static Show 메소드를 호출해야 합니다.

C#
// Display message box
MessageBox.Show(messageBoxText, caption, button, icon);

사용자가 클릭한 버튼을 검출하고 그에 맞는 응답을 하기 위해, 다음과 같이 메시지 박스의 결과를 받고 처리할 수 있습니다.

C#
// Display message box
MessageBoxResult messageBoxResult = MessageBox.Show(messageBoxText, caption, button, icon);
// Process message box results
switch (messageBoxResult)
{
    case MessageBoxResult.Yes: // Save document and exit
        SaveDocument();
        break;
    case MessageBoxResult.No: // Exit without saving
        break;
    case MessageBoxResult.Cancel: // Don't exit
        e.Cancel = true;
        break;
}

한 가지 중요한 MessageBox의 기능은 부분적으로 신뢰(partial trust)된 상태에서 동작하는 XAML 브라우저 애플리케이션(XBAPs)에서도 보여줄 수 있다는 것입니다(WPF Security를 참고).

메시지 박스 사용에 관한 더 자세한 정보는 MessageBox, MessageBox Sample 또는 Dialog Box Sample에서 찾을 수 있습니다.

커먼 다이얼로그 박스

메시지 박스가 유용한 정보를 보여주고 사용자에게 선택을 제공하는 동안, 대부분의 다이얼로그 박스는 일반적으로 어떤 버튼이 눌렸는가 보다는 더 자세한 정보를 사용자로부터 수집할 필요가 있습니다.

윈도는 파일 열기, 파일 저장하기 및 인쇄하기와 같이 모든 애플리케이션에서 공통적인 다양한 다이얼로그 박스를 구현합니다. 이것은 모든 애플리케이션이 사용하므로 커먼 다이얼로그 박스로 알려져 있습니다. 따라서, 이것은 애플리케이션 간의 일관성 있는 사용자 경험을 제공하는데 도움이 됩니다.

WPF는 독립 애플리케이션에서 사용할 수 있도록 관리된 클래스로 공통 파일 열기, 파일 저장 및 인쇄 다이얼로그 박스를 캡슐화합니다. 이 토픽은 각각의 개관을 제공합니다.

파일 열기 다이얼로그
다음 그림에서 보여주는 것과 같은 파일 열기 다이얼로그 박스는 파일 열기 기능에서 사용자가 열기를 원하는 파일명을 얻기 위해 사용됩니다.
사용자 삽입 이미지

공통 파일 열기 다이얼로그 박스는 OpenFileDialog 클래스로 구현되고, 다음 코드는 어떻게 생성, 설정 및 보여주고, 어떻게 결과를 처리하는지를 보여줍니다.

C#
void OpenDocument()
{
    // 파일 열기 다이얼로그 박스를 설정
    OpenFileDialog dlg = new OpenFileDialog();
    dlg.FileName = "Document"; // 기본 파일명
    dlg.DefaultExt = ".wpf"; // 기본 확장자
    dlg.Filter = "Word Processor Files (.wpf)|*.wpf"; // 확장 파일 필터

    // Show open file dialog box
    Nullable<bool> result = dlg.ShowDialog();

    // Process open file dialog box results
    if (result == true)
    {
        // Open document
        string filename = dlg.FileName;
    }
}

파일 열기 다이얼로그 박스에 대한 더 자세한 정보는 Microsoft.Win32.OpenFileDialog를 참고합니다.

OpenFileDialog는 부분적으로 신뢰되어 동작중인 애플리케이션에서 파일명을 안전하게 얻는데 사용할 수 있습니다(WPF Security를 참고). 예제는 Safe File Upload from an XBAP Sample을 참고합니다.

파일 저장 다이얼로그 박스
다음 그림에서 보여주는 것과 같은 파일 저장 다이얼로그 박스는 파일 저장 기능에서 사용자가 저장을 원하는 파일명을 얻기 위해 사용됩니다.
사용자 삽입 이미지

공통 파일 저장 다이얼로그 박스는 SaveFileDialog 클래스로 구현되고, 다음 코드는 어떻게 생성, 설정 및 보여주고, 어떻게 결과를 처리하는지를 보여줍니다.

C#
void SaveDocument()
{
    // 파일 저장 다이얼로그 박스 설정
    SaveFileDialog dlg = new SaveFileDialog();
    dlg.FileName = "Document"; // 기본 파일명
    dlg.DefaultExt = ".wpf"; // 기본 파일 확장자
    dlg.Filter = "Word Processor Files (.wpf)|*.wpf"; // 확장 파일 필터

    // Show save file dialog box
    Nullable<bool> result = dlg.ShowDialog();

    // Process save file dialog box results
    if (result == true)
    {
        // Save document
        string filename = dlg.FileName;
    }
}

파일 저장 다이얼로그 박스에 대한 더 자세한 정보는 Microsoft.Win32.SaveFileDialog를 참고합니다.

인쇄 다이얼로그 박스
다음 그림에서 보여주는 것과 같은 인쇄 다이얼로그 박스는 인쇄 기능에서 사용자가 출력을 원하는 파일에 대한 프린터 선택 및 설정을 위해 사용됩니다.
사용자 삽입 이미지

공통 인쇄 다이얼로그 박스는 PrintDialog 클래스로 구현되고, 다음 코드는 어떻게 생성, 설정 및 보여주는지를 보여줍니다.

C#
void PrintDocument()
{
    // Configure printer dialog box
    PrintDialog dlg = new PrintDialog();
    dlg.PageRangeSelection = PageRangeSelection.AllPages;
    dlg.UserPageRangeEnabled = true;

    // Show save file dialog box
    Nullable<bool> result = dlg.ShowDialog();

    // Process save file dialog box results
    if (result == true)
    {
        // Print document
    }
}

인쇄 다이얼로그 박스에 대한 더 자세한 정보는 System.Windows.Controls.PrintDialog를 참고합니다. WPF에서 인쇄하기에 관한 자세한 논의는 Printing Overview를 참고합니다.

사용자 정의 다이얼로그 박스

메시지 박스보다 복잡하고, 커먼 다이얼로그 박스에 의해 지원되지 않는 다이얼로그 박스가 필요할 때, 직접 다이얼로그 박스를 만들 필요가 있습니다. WPF는 모덜과 모덜리스 다이얼로그 박스 모두 Window를 사용하여 만들 수 있게 합니다.

모덜 사용자 정의 다이얼로그 박스 만들기
MarginsDialogBox와 같은 모덜 다이얼로그 박스는 시각적인 요구사항과 동작의 세트를 가진 다음 그림에서 보여주고 있습니다.
사용자 삽입 이미지

이 토픽은 이를 설명하고 다이얼로그 박스 구현의 기본 구성요소를 제시합니다.

모덜 다이얼로그 박스 설정하기
전형적인 다이얼로그 박스를 위한 사용자 인터페이스는 다음과 같은 몇 가지 주요 동작을 가집니다.
  • 사용자가 다이얼로그 박스를 닫고 처리를 계속하기 위해(OK) 클릭할 Button. 이 버튼은 또한 자신의 IsDefault 속성을 true로 설정하기 위해 필요합니다. 이것은 사용자가 ENTER 키를 눌러 다이얼로그 박스를 확인(accept)할 수 있게 합니다.
  • 사용자가 다이얼로그 박스를 닫고 취소하는 기능을 위해(Cancel) 클릭할 Button. 이 버튼은 자신의 IsCancel 속성을 true로 설정하기 위해 필요합니다. 이것은 사용자가 ESC 키를 눌러 다이얼로그 박스를 취소할 수 있게 합니다.
  • 타이틀바에 Close 버튼 보여주기.
  • Icon 보여주기.
  • 최소화, 최대화 및 이전 크기로 버튼 보여주기.
  • 다이얼로그 박스의 시스템 메뉴에 최소화, 최대화, 이전 크기로 및 닫기 버튼 보여주기.
  • 다이얼로그 박스를 연 윈도의 한가운데에 열기.
  • 다이얼로그 박스는 너무 작게 되는 것을 막기 위해 가능하다면 크기 조절이 가능해야하며, 사용자에게 유용한 기본 크기를 제공하기 위해 기본적인 크기와 최소 크기를 각각 설정할 필요가 있습니다.
Dialog Box Sample에서와 같이 Margin 다이얼로그 박스 같은 Window를 사용한 설정을 가진 다이얼로그 박스를 만들 수 있습니다.

XAML
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="MarginsDialogBox"
    xmlns:local="clr-namespace:DialogBoxSample"
    Title="Margins"
    Height="190" Width="300"
    MinHeight="190" MinWidth="300"
    ResizeMode="CanResizeWithGrip"
    ShowInTaskbar="False"
    WindowStartupLocation="CenterOwner"
    FocusManager.FocusedElement="{Binding ElementName=leftMarginTextBox}">

  <Grid>

    <!-- Grid column and row definitions -->

    <Grid.Resources>
      <!-- Grid column and row definitions -->
      ...
    </Grid.Resources>

    <!-- Grid column and row definitions -->
    <Grid.ColumnDefinitions>
      <!-- Column definitions -->
      ...
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
      <!-- Row definitions -->
      ...
    </Grid.RowDefinitions>

    <!-- Left Margin -->
    <Label Grid.Column="0" Grid.Row="0">Left Margin:</Label>
    <TextBox Name="leftMarginTextBox" Grid.Column="1" Grid.Row="0">
    </TextBox>

    <!-- Top Margin -->
    <Label Grid.Column="0" Grid.Row="1">Top Margin:</Label>
    <TextBox Name="topMarginTextBox" Grid.Column="1" Grid.Row="1">
    </TextBox>

    <!-- Right Margin -->
    <Label Grid.Column="0" Grid.Row="2">Right Margin:</Label>
    <TextBox Name="rightMarginTextBox" Grid.Column="1" Grid.Row="2">
    </TextBox>

    <!-- Bottom Margin -->
    <Label Grid.Column="0" Grid.Row="3">Bottom Margin:</Label>
    <TextBox Name="bottomMarginTextBox" Grid.Column="1" Grid.Row="3">
    </TextBox>

    <!-- Accept or Cancel -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4">
      <Button Name="okButton" IsDefault="True">OK</Button>
      <Button Name="cancelButton" IsCancel="True">Cancel</Button>
    </StackPanel>

  </Grid >
</Window>

다이얼로그 박스를 위한 사용자 경험은 또한 메뉴바로 확장됩니다. (※역주 : 이 문장은 의미를 잘 모르겠습니다. When a function that shows a dialog box to collect user information, the menu item that exposes that function will have an ellipses in its header, as shown here: )
(※역주 : 원문에는 C#이라고 나와있지만 XAML 코드입니다.)

XAML
<Window ... >
  ...
    <MenuItem 
      Name="formatMarginsMenuItem"
      Header="_Margins..."
      Click="formatMarginsMenuItem_Click" />
  ...
</Window >

About 다이얼로그 박스처럼, 오직 정보만을 표시하는 다이얼로그 박스가 열렸을 때, 이에 대응하는 메뉴 아이템은 타원을 필요로하지 않습니다.

모덜 다이얼로그 박스 열기
사용자가 다이얼로그 박스를 보여주기 위해 메뉴 아이템을 선택하였을 때, 다이얼로그 박스는 다른 메시지 박스와 커먼 다이얼로그 박스처럼 인스턴스화 되고, 설정되고, 열려야 합니다.

다음 코드는 어떻게 다이얼로그 박스를 인스턴스화 하는지 보여줍니다.

C#
public partial class MainWindow : Window
{
    ...
    void formatMarginsMenuItem_Click(object sender, RoutedEventArgs e)
    {
        // Instantiate the dialog box
        MarginsDialogBox dlg = new MarginsDialogBox();
    }
    ...
}

다음으로, 다이얼로그 박스는 사용되기 전에 설정될 필요가 있습니다.

C#
public partial class MainWindow : Window
{
    ...
    void formatMarginsMenuItem_Click(object sender, RoutedEventArgs e)
    {
        // Instantiate the dialog box
        MarginsDialogBox dlg = new MarginsDialogBox();

        // Configure the dialog box
        dlg.Owner = this;
        dlg.DocumentMargin = this.documentTextBox.Margin;
    }
    ...
}

여기에서, 코드는 기본 정보를 다이얼로그 박스로 전달합니다. 또한 System.Windows.Window.Owner 속성을 다이얼로그 박스를 보여주는 윈도의 레퍼런스로 설정합니다. 일반적으로, 모든 다이얼로그 박스를 위한 공통적인 윈도 상태-관련(state-related) 동작(behavior)를 제공하기 위해 항상 다이얼로그 박스의 소유자를 설정해야 합니다(WPF Windows Overview 참고).

노트:
반드시 다이얼로그 박스를 위한 유저 인터페이스(UI) 자동화를 지원하는 소유자를 제공해야 합니다(UI Automation Overview 참고).

일단 다이얼로그 박스가 설정되면, 그것은 보여줄 준비가 된 것입니다. 모덜 다이얼로그 박스는 다음과 같이 ShowDialog 메소드를 호출하여 보여지게 됩니다.

C#
public partial class MainWindow : Window
{
    ...
    void formatMarginsMenuItem_Click(object sender, RoutedEventArgs e)
    {
        // Instantiate the dialog box
        MarginsDialogBox dlg = new MarginsDialogBox();

        // Configure the dialog box
        dlg.Owner = this;
        dlg.DocumentMargin = this.documentTextBox.Margin;

        // Open the dialog box modally
        dlg.ShowDialog();

    }
    ...
}

ShowDialog는 다이얼로그 박스를 모덜로 열고, 그 후 사용자는 다이얼로그 박스를 확인(accept)하거나 취소(cancel)하기 전까지 데이터를 입력합니다.

사용자 제공 데이터 유효성 검사
일단 다이얼로그 박스가 열리고 사용자가 데이터를 입력하면, 다이얼로그 박스는 다음과 같은 세가지 이유로 사용자의 입력이 유효한지 여부를 확인할 책임이 있습니다.
  • 보안상의 관점에서, 모든 입력은 유효해야 합니다.
  • 애플리케이션의 관점에서, 데이터 유효성 검사는 잠재적으로 예외를 던질 수 있는 코드에 의한 처리로부터 잘못된 자료를 방지합니다.
  • 사용자 경험의 관점에서, 다이얼로그 박스는 어떤 데이터가 잘못 입력되었는지를 보여줌으로서 사용자를 도울 수 있습니다.
WPF에서 바운드된 컨트롤을 유효화하기 위해, 유효성 규칙(validation rule)을 만들고 그것을 바인딩과 연결시킬 필요가 있습니다.

유효성 규칙은 ValidationRule을 상속하는 사용자 정의 클래스입니다. 다음 예제는 바운드 값이 double이고 지정된 범위내에 있는지를 검사하는 MarginValidationRule을 보여줍니다.

C#
using System.Windows.Controls;
public class MarginValidationRule : ValidationRule
{
    double minMargin;
    double maxMargin;

    public double MinMargin
    {
        get { return this.minMargin; }
        set { this.minMargin = value; }
    }

    public double MaxMargin
    {
        get { return this.maxMargin; }
        set { this.maxMargin = value; }
    }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        double margin;

        // Is a number?
        if (!double.TryParse((string)value, out margin))
        {
            return new ValidationResult(false, "Not a number.");
        }

        // Is in range?
        if ((margin < this.minMargin) || (margin > this.maxMargin ))
        {
            string msg = string.Format("Margin must be between {0} and {1}.", this.minMargin, this.maxMargin);
            return new ValidationResult(false, msg);
        }

        // Number is valid
        return new ValidationResult(true, null);
    }
}

바운드 값을 유효화하기 위해, 위의 코드에서 보여주는 것 처럼 Validate를 오버라이드하고 적절한 ValidationResult를 반환해야 합니다.

유효성 규칙을 바운드 컨트롤과 연결하기 위해, 다음과 같은 마크업을 사용합니다.

XAML
<Window ... >
    ...
    <!-- Left Margin -->
    <Label Grid.Column="0" Grid.Row="0">Left Margin:</Label>
    <TextBox Name="leftMarginTextBox" Grid.Column="1" Grid.Row="0">
      <TextBox.Text>
        <Binding Path="Left" UpdateSourceTrigger="PropertyChanged">
          <Binding.ValidationRules>
            <local:MarginValidationRule MinMargin="0" MaxMargin="10" />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    ...
</Window>

WPF는 데이터가 바운드 컨트롤로 입력되었을 때 자동으로 이 바인딩을 승인하고, 기본적으로 유효하지 않은 컨트롤 주위에 붉은 테두리를 보여줍니다. 이것은 다음 그림과 같습니다.
사용자 삽입 이미지

WPF는 사용자가 유효한 데이터를 입력할 때까지 유효하지 않은 컨트롤을 제한하지 않습니다. 이것은 사용자가 다이얼로그 박스에서 데이터가 유효하든 그렇지 않든 간에 자유롭게 컨트롤들을 이동할 수 있으므로 다이얼로그 박스를 위해 좋은 동작입니다. 그러나, 이것은 사용자가 유효하지 않은 데이터를 입력하고 OK 버튼을 누를 수 있다는 것을 의미합니다. 이런 이유로, 코드는 또한 유효하지 않은 데이터에 대한 보호를 위하여, 다이얼로그 박스가 승인되기 전에 모든 컨트롤을 유효화할 필요가 있습니다. 이것은 OK 버튼의 Click 이벤트 핸들러에서 할 수 있습니다.

C#
public partial class MarginsDialogBox : Window
{
    ...
    void okButton_Click(object sender, RoutedEventArgs e)
    {
        // 유효하지 않은 데이터가 있을 경우 다이얼로그 박스를 승인하지 말 것.
        if (!IsValid(this)) return;
        ...
    }
    ...
    // 윈도에 있는 모든 의존성있는 객체의 유효성 확인.
    bool IsValid(DependencyObject node)
    {
        // 객체가 있는지 확인.
        if (node != null)
        {
            // 객체가 유효한지 확인.
            // NOTE: Validation.GetHasError는 유효성 규칙이 붙은 컨트롤을 위해 동작합니다.
            bool isValid = !Validation.GetHasError(node);
            if (!isValid)
            {
                // 객체가 유효하지 않고, 포커스를 받을 수 있다면,
                // 포커스를 맞춤.
                if (node is IInputElement) Keyboard.Focus((IInputElement)node);
                return false;
            }
        }

        // 객체가 유효하다면, 모든 자식 객체를 확인.
        foreach (object subnode in LogicalTreeHelper.GetChildren(node))
        {
            if (subnode is DependencyObject)
            {  
                // 자식 객체가 유효하지 않다면 즉시 false를 반환하고,
                // 그렇지 않으면 검사를 계속함.
                if (IsValid((DependencyObject)subnode) == false) return false;
            }
        }

        // 모든 객체가 유효함.
        return true;
    }
}

이 코드는 윈도 상에서 의존성 있는 모든 객체들을 열거하며, GetHasError의 반환값이 하나라도 유효하지 않다면, 유효하지 않은 컨트롤에 포커스가 맞춰지고, IsValidfalse를 반환하며, 윈도는 유효하지 않다고 판단합니다.

일단 다이얼로그 박스가 유효화되면, 안전하게 닫고 반환할 수 있습니다. 이것은 다이얼로그 결과가 설정되었다는 것을 의미합니다.

모덜 다이얼로그 결과 설정하기
ShowDialog를 사용하여 다이얼로그 박스를 여는 것은 호출하는 코드에 있는 코드는 ShowDialog가 반환될 때까지 기다린다는 점에서 기본적으로 메소드 호출과 비슷합니다. ShowDialog가 반환되었을 때, 호출하는 코드는 사용자가 다이얼로그 박스에서 적용(apply)하여 수집한 데이터인지 아닌지 즉, 확인했는지 취소했는지를 결정할 필요가 있습니다. 사용자가 다이얼로그 박스를 확인했는지 취소했는지 보고하기 위해, 다이얼로그 박스는 반드시 다이얼로그 박스 결과값을 반환해야 합니다.

다이얼로그 박스가 확인되면, OK 버튼을 눌렀을 때 DialogResult 속성을 설정함으로써 획득되는 true의 다이얼로그 박스 결과를 반환해야 합니다.

XAML
<Window ... >
    ...
    <!-- Accept or Cancel -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4">
      <Button Name="okButton" Click="okButton_Click" IsDefault="True">OK</Button>
      ...
    </StackPanel>
  </Grid >
</Window>

C#
public partial class MarginsDialogBox : Window
{
    ...
    void okButton_Click(object sender, RoutedEventArgs e)
    {
        // Dialog box accepted
        this.DialogResult = true;
    }
    ...
}

DialogResult 속성을 설정하는 것은 또한 명시적으로 Close를 호출하지 않아도 윈도가 자동으로 닫히는 원인이 된다는 것을 유념하십시오.

취소된 다이얼로그 박스는 false의 다이얼로그 박스 결과를 가져야 합니다.

XAML
<Window ... >
    ...
    <!-- Accept or Cancel -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4">
      ...
      <Button Name="cancelButton" Click="cancelButton_Click" IsCancel="True">Cancel</Button>
    </StackPanel>
  </Grid >
</Window>

C#
public partial class MarginsDialogBox : Window
{
    ...
    void cancelButton_Click(object sender, RoutedEventArgs e)
    {
        // Dialog box canceled
        this.DialogResult = false;
    }
    ...
}

그러나 IsCancel 속성이 true로 설정되어 있는 버튼이 있고 사용자가 그 버튼이나 ESC키를 눌렀을 때는 DialogResult는 자동으로 false로 설정됩니다. 다음의 코드는 Click 이벤트 핸들러가 없이 전술한 코드와 같은 효과를 얻을 수 있습니다.

XAML
<Window ... >
    ...
    <!-- Accept or Cancel -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4">
      ...
      <Button Name="cancelButton" IsCancel="True">Cancel</Button>
    </StackPanel>
  </Grid >
</Window>

다이얼로그 박스는 사용자가 타이틀 바에 있는 Close 버튼을 누르거나 시스템 메뉴에서 Close 메뉴 아이템을 선택할 때 자동으로 true를 반환할 것입니다.

모덜 다이얼로그 박스에서 반환된 데이터 처리하기
DialogResult가 다이얼로그 박스에 의해 설정되면 호출하는 코드는 ShowDialog 메소드가 반환되었을 때 DialogResult 속성을 검사하여 다이얼로그 박스 결과를 얻을 수 있습니다.

C#
public partial class MainWindow : Window
{
    ...
    void formatMarginsMenuItem_Click(object sender, RoutedEventArgs e)
    {
        // Instantiate the dialog box
        MarginsDialogBox dlg = new MarginsDialogBox();

        // Configure the dialog box
        dlg.Owner = this;
        dlg.DocumentMargin = this.documentTextBox.Margin;

        // Open the dialog box modally
        dlg.ShowDialog();

        // Process data entered by user if dialog box is accepted
        if (dlg.DialogResult == true)
        {
            // Update fonts
            this.documentTextBox.Margin = dlg.DocumentMargin;
        }
    }
    ...
}

사용자가 다이얼로그 박스를 확인하면 호출하는 코드는 그것을 사용자에 의해 다이얼로그 박스로 입력된 값을 받고 처리하기 위한 큐로 사용합니다.

노트:
일단 ShowDialog가 반환되면 다이얼로그 박스는 다시 열 수 없고, 새 인스턴스를 대신 생성해야 합니다.

사용자가 다이얼로그 박스를 취소하면 호출하는 코드는 사용자가 제공한 데이터를 처리해선 안됩니다.

모덜리스 사용자 정의 다이얼로그 박스 생성하기
다음 그림의 FindDialogBox와 같은 모덜리스 다이얼로그 박스는 모덜 다이얼로그 박스와 같은 기본 형태를 가집니다.
사용자 삽입 이미지

그러나 동작은 다음 토픽에서 설명되는 것처럼 약간 다릅니다.

모덜리스 다이얼로그 박스 열기
모덜리스 다이얼로그 박스는 Show 메소드를 호출함으로써 열립니다.

C#
void editFindMenuItem_Click(object sender, RoutedEventArgs e)
{
    // Instantiate the dialog box
    FindDialogBox dlg = new FindDialogBox(this.documentTextBox.Text);

    // Open the dialog box modally
    dlg.Show();
}

다이얼로그 박스를 열기위해 ShowDialog를 사용하는 것과는 달리 Show는 즉시 반환됩니다. 따라서 호출한 윈도는 모덜리스 다이얼로그 박스가 닫혔을 때 전달할 수 없습니다. 그러므로 더 자세한 처리를 위해 다이얼로그 박스로부터 다이얼로그 박스 결과를 검사하거나 데이터를 얻을 수 있는 시점을 알 수 없습니다. 다이얼로그 박스는 처리를 위해 호출한 윈도에게 데이터를 반환할 대안을 만들 필요가 있습니다.

모덜리스 다이얼로그 박스에서 반환된 데이터 처리하기
이 예제에서 FindDialogBox는 어떤 정해진 빈도 없이 검색된 텍스트에 따라 하나 또는 그 이상의 검색 결과를 메인 윈도에 반환할 것입니다. 모덜 다이얼로그 박스의 경우처럼 모덜리스 다이얼로그 박스는 속성을 사용하여 결과를 반환할 수 있습니다. 그러나 다이얼로그 박스를 소유한 윈도는 언제 그 속성을 검사해야 하는지 알 필요가 있습니다. 다이얼로그 박스가 이것을 가능하게 하는 한가지 방법은 언제든 텍스트가 발견되었을 때 발생하는 이벤트를 구현하는 것입니다. FindDialogBox는 이런 목적으로 딜리게이트를 최우선으로 요구하는 TextFoundEvent를 구현합니다.

C#
public delegate void TextFoundEventHandler(object sender, EventArgs e);

TextFoundEventHandler 딜리게이트를 이용하여 FindDialogBox는 다음과 같이 TextFoundEvent를 구현합니다.

C#
public partial class FindDialogBox : Window
{
    ...
    public event TextFoundEventHandler TextFound;
    ...
    protected virtual void OnTextFound()
    {
        TextFoundEventHandler textFound = this.TextFound;
        if (textFound != null) textFound(this, EventArgs.Empty);
    }
}

따라서 찾기는 검색 결과가 나오면 이벤트를 띄웁니다.

C#
public partial class FindDialogBox : Window
{
    ...
    void findButton_Click(object sender, RoutedEventArgs e) {
        ...
        // Text found
        this.index = match.Index;
        this.length = match.Length;
        OnTextFound();
        ...
    }
    ...
}

소유 윈도는 이 이벤트를 등록하고 처리할 필요가 있습니다.

C#
public partial class MainWindow : Window
{
    ...
    void dlg_TextFound(object sender, EventArgs e)
    {
        // Get the find dialog box that raised the event
        FindDialogBox dlg = (FindDialogBox)sender;

        // Get find results and select found text
        this.documentTextBox.Select(dlg.Index, dlg.Length);
        this.documentTextBox.Focus();
    }
}

모덜리스 다이얼로그 박스 닫기
DialogResult가 설정될 필요가 없기 때문에 다음을 포함하는 시스템 제공 메커니즘을 사용하여 모덜리스 다이얼로그를 닫을 수 있습니다.
  • 타이틀 바의 Close 버튼을 클릭.
  • ALT+F4 누름.
  • System Menu | Close를 선택
또는 취소 버튼을 클릭하면 코드에서 Close를 호출할 수 있습니다.

C#
public partial class FindDialogBox : Window
{
    ...
    void cancelButton_Click(object sender, RoutedEventArgs e)
    {
        // Close dialog box
        this.Close();
    }
}


See Also

Concepts
Popup Overview

Other Resources
Dialog Box Sample
Wizard Sample
ColorPicker Custom Control Sample
Font Dialog Box Demo
신고
Posted by gongdo

MSDN : http://msdn2.microsoft.com/en-us/library/ms748948.aspx

WPF Windows Overview

사용자는 WPF 독립 애플리케이션과 주로 시각화한 데이터 컨텐트를 보여주고 사용자를 데이터와 상호작용 할 수 있도록 하는 윈도를 통하여 상호작용 합니다.

이 토픽은 다음 섹션을 담고 있습니다.

Window 클래스

다음 그림은 일반적인 윈도의 요소를 나타냅니다.

사용자 삽입 이미지

윈도는 비클라이언트 영역과 클라이언트 영역의 두 영영으로 나뉩니다.

윈도의 비클라이언트 영역은 WPF에 의해 구현되고, 대부분의 윈도에게 공통적인 다음과 같은 요소를 포함합니다.

  • 테두리(border)
  • 타이틀 바
  • 아이콘
  • 최소화, 최대화 및 원래 크기로 버튼
  • 닫기 버튼
  • 최소화, 최대화, 원래 크기로, 이동, 크기 조정 및 닫기를 위한 메뉴 아이템을 제공하는 시스템 메뉴

윈도의 클라이언트 영역은 윈도의 비클라이언트 영역 내의 영역이고, 윈도-정의(window-specific)내용을 표현하기 위해 개발자가 사용합니다.

WPF에서, 윈도는 다음과 같은 것을 가능케하는 Window 클래스에 의해 캡슐화됩니다.

  • 윈도의 크기, 위치 및 형태를 설정.
  • 윈도 내에 내용을 얹음.
  • 윈도를 보여줌.
  • 윈도의 라이프타임을 관리

메시지 박스와 다이얼로그 박스는 윈도의 다른 형식이며 Dialog Boxex Overview에서 다룹니다. 또한, NavigationWindowWindow에서 상속된 윈도의 특별한 형식이며 컨텐트를 표시하고 운항(네비게이션)하기 위한 지원을 확장합니다(Navigation Overview를 참고합니다).

윈도 구현

윈도의 구현은 형태(appearance)와 동작(behavior)을 포괄합니다. 형태는 윈도를 보여주는 방법이며, 동작은 윈도가 작용하는 방법입니다. WPF에서, 코드나 마크업 혹은 둘 다를 사용하여 윈도의 형태와 동작을 구현할 수 있습니다. 그러나, 마크업과 코드-비하인드가 -둘을 모두 사용하는 것이- 가장 일반적입니다.

마크업과 코드-비하인드에서 윈도를 정의하기
마크업과 코드-비하인드를 모두 사용하여 윈도를 구현하는 것은, 형태를 정의하기 위해 XAML의 풍부한 표현을 활용할 수 있고, 동작을 구현하기 위해 코드를 사용하는 것과 같이 둘 모두를 위해 가장 좋습니다.

다음 예제는 마크업과 코드를 모두 사용하여 구현된 윈도를 보여줍니다.
(※역주 : 원문에는 모두 XAML 코드로 나와있지만 내용상 XAML과 C#으로 구분하였습니다.)

XAML
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="MarkupAndCodeBehindWindow">
  <!-- Client Area (for content) -->
</Window>
C#

using System.Windows;

public partial class MarkupAndCodeBehindWindow : Window

{

    public MarkupAndCodeBehindWindow()

    {

        InitializeComponent();

    }

}

마크업 파일과 코드-비하인드 파일이 함께 동작하게 하기 위해서 다음과 같은 사항이 요구됩니다.

  • 마크업에서, Window 엘리먼트는 x:Class 어트리뷰트에 의해 지정된 이름을 가진 프로젝트가 빌드될 때 마크업 파일을 위한 partial class를 생성하도록 MSBuild에게 지시하는 x:Class 어트리뷰트를 포함해야 합니다. 이것은 XAML 스키마(xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml")를 위한 추가적인 XML 네임스페이스 선언을 필요로 합니다. 부분적으로 생성된 partial class는 이벤트를 등록하고 마크업에 의해 구현된 프로퍼티를 설정하기 위해 호출되는 InitializeComponent를 구현합니다.
  • 코드-비하인드에서, 클래스는 반드시 마크업의 x:Class 어트리뷰트에 의해 지정된 것과 같은 이름을 가진 partial class이어야 하고, Window를 상속할 필요가 있습니다. 이것은 코드-비하인드 파일이 빌드될 때 마크업 파일을 위해 생성된 partial class와 연결되도록 합니다(Building a WPF Application을 참고).
  • 코드-비하인드에서, 클래스는 반드시 InitializeComponent 메소드를 호출하는 생성자를 구현해야 하며 그렇지 않을 경우, 마크업은 적용되지 않을 것입니다.
노트:
Microsoft Visual Studio를 사용하여 프로젝트에 새 윈도를 추가할 때, 윈도는 마크업과 코드-비하인드를 모두 사용하여 구현되고, 마크업과 코드-비하인드 파일 사이의 연결을 생성하는데 필요한 설정을 포함합니다.

다음 예제는 어떻게 윈도 타이틀 설정, XAML을 사용한 버튼 선언 및 코드-비하인드에서 Click 이벤트를 처리하기 위해 마크업과 코드-비하인드를 사용하는지를 보여줍니다.
(※역주 : 역시 XAML과 C#으로 나눕니다.)

XAML
<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="MarkupAndCodeBehindWindow"
  Title="WindowsOverviewSnippetsCSharp"
  Height="800"
  Width="600">
    <!-- Client Area (for content) -->
    <Button Click="button_Click">Window Content</Button>
</Window>
C#

using System.Windows;

public partial class MarkupAndCodeBehindWindow : Window

{

    public MarkupAndCodeBehindWindow()

    {

        InitializeComponent();

    }


    void button_Click(object sender, RoutedEventArgs e)

    {

        MessageBox.Show("Button was clicked.");

    }

}

MSBuild를 위한 윈도 정의 설정하기

윈도를 어떻게 정의하느냐에 따라 Microsoft build engine(MSBuild)를 위해 어떻게 설정되었는지를 결정합니다. 마크업과 코드-비하인드 모두를 사용하여 정의된 윈도의 경우,

  • 마크업 파일은 MSBuild Page 아이템으로 설정되어야 합니다.
  • 코드-비하인드 파일은 MSBuild Compie 아이템으로 설정해야 합니다.

이것은 다음 예제와 같이 표시할 수 있습니다.
(※역주 : 다음 코드는 Visual Studio의 프로젝트 파일(.csproj 또는 .vbproj)를 텍스트 편집기로 열여보면 확인할 수 있고, 물론 IDE를 사용하는 경우 이 코드를 직접 수정할 필요가 없습니다. 또한 원문에서의 Include 속성에 들어간 공백은 오타로 생각되어 제거 하였습니다.)

<Project
    DefaultTargets="Build"
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    ...
    <Page Include="MarkupAndCodeBehindWindow.xaml" />
    <Compile Include="MarkupAndCodeBehindWindow.xaml.cs" />
    ...
</Project>

여기에서, 코드-비하인드 파일은 MSBuild Compile 아이템으로 설정되었습니다. 또한, 코드-비하인드 파일은 마크업 파일의 이름에 추가적인 언어 구분 접미어(.cs 또는 .vb)가 붙은 것과 같은 이름을 갖습니다. 이 접미어는 마이크로소프트 비주얼 스튜디오가 기본적으로 이런 규칙을 사용하긴 하지만 반드시 필요하진 않습니다.

노트:
WPF 애플리케이션 빌드하기의 더 자세한 내용은 Building a WPF Application을 참고합니다.

윈도 라이프타임

한 번 윈도가 MSBuild를 위해 정의되고 설정되면, 애플리케이션에서 그것을 보여줄 수 있습니다. 어떤 클래스에서도, 윈도는 최초로 인스턴스화 되었을 때 시작되어, 그것이 보이게 된 후 활성화되며 최종적으로 닫히기 전에 비활성화되는 라이프타임을 가집니다.

윈도 열기
윈도를 열기 위해, 다음 예제와 같이 먼저 그것의 인스턴스를 생성해야 합니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    Startup="app_Startup">
</Application>
C#

using System.Windows;

public partial class App : Application

{

    void app_Startup(object sender, StartupEventArgs e)

    {

        // Create a window

        MarkupAndCodeBehindWindow window = new MarkupAndCodeBehindWindow();


        // Open a window

        window.Show();

    }

}

이 예제에서, MarkupAndCodeBehindWindow는 애플리케이션이 시작될 때 인스턴스화 됩니다(Application Management Overview를 참고).

윈도가 인스턴스화 될 때, 그것의 참조가 Application 객체에 의해 관리되는 윈도의 목록(System.Windows.Application.Windows 참고)에 자동적으로 추가됩니다. 또한, 첫 윈도가 인스턴스화 될 때, Application에 의해 기본값으로 메인 애플리케이션 윈도로 설정됩니다(System.Windows.Application 참고).

윈도는 Show 메소드를 호출함으로서 다음 그림이 보여주는 것 처럼 마침내 열립니다.

사용자 삽입 이미지

Show 호출에 의해 열린 윈도는 모덜리스 윈도이며, 이것은 애플리케이션이 사용자가 같은 애플리케이션 내의 다른 윈도를 활성화 할 수 있도록 허용하는 모드로 동작한다는 것을 의미합니다. Window는 또한 모덜 윈도로 열기위해 사용되는 ShowDialog 메소드를 구현합니다. 대부분의 모덜 윈도의 일반적 형식은 Dialog Boxes Overview에서 자세히 다루는 다이얼로그 박스입니다.

더 간단하게, 윈도 구현이 마크업을 포함하는 한, 마크업의 StartupUri 속성을 설정하여 메인 윈도를 자동적으로 열 수 있도록 애플리케이션 정의를 선언적으로 설정할 수 있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MarkupAndCodeBehindWindow.xaml">
</Application>

애플리케이션이 시작할 때, 이 마크업은 MarkupAndCodeBehindWindow 윈도가 Show 메소드 호출에 의해 모덜리스로 열리도록 합니다.

Show가 호출되었을 때, 윈도는 그것이 실제로 보이기 전에 처리할 어떤 초기화 작업이 필요합니다. 초기화의 초점은 사용자 입력을 받도록 할 기반의 구성에 맞춰져 있습니다. 초기화가 완료되었을 때, SourceInitialized 이벤트가 발생되고 윈도가 보이게 됩니다.

윈도 소유권
윈도가 Show 호출로 열렸을 때, 그것과 관계가 있는, 또는 그것을 생성한 윈도에 대한 어떠한 정보도 가지고 있지 않습니다. 사용자는 다른 것들과 독립적으로 각 윈도와 상호작용 할 수 있을 것입니다. 이것은 윈도가 다음과 같이 할 수 있다는 것을 의미합니다.

  • 다른 윈도를 가리기(true로 설정된 Topmost 속성을 가진 윈도가 없다면).
  • 다른 윈도에 영향을 주지 않고 최소화, 최대화 및 원래 크기로 돌아가기.

어떤 애플리케이션을 위해, 열려있는 윈도는 그것을 열었던 윈도와 밀접한 관계를 가질 필요가 있습니다. 예를 들어, 통합 개발 환경(IDE) 애플리케이션은 항상 다른 윈도를 생성하거나, 닫거나, 최소화 하거나, 최대화 하거나, 복구하기 위해서 그들의 위에 있어야 할 속성이나 툴 윈도와 같은 윈도를 열 것입니다.

Owner 속성을 설정하여 준비되는 한 윈도가 다른 윈도를 소유(own)하는 관계를 만들어 이러한 동작을 구현할 수 있습니다.

C#
using System.Windows;
public partial class OwnerWindow : System.Windows.Window
{
    public OwnerWindow()
    {
        InitializeComponent();
    }

    void OpenOwnedWindow()
    {
        // NOTE: 소유자는 다른 윈도를 소유하기 전에 반드시 보여야 합니다.
        // Create new owned window and show it
        OwnedWindow ownedWindow = new OwnedWindow();
        ownedWindow.Owner = this;
        ownedWindow.Show();
    }
}

한번 소유권이 만들어지면, 소유된 윈도는 Owner 속성을 통해 자신을 소유한 윈도를 참조할 수 있습니다. 소유 윈도는 OwnedWindows 속성을 나열하여 그것이 소유하고 있는 모든 윈도를 알 수 있습니다.

윈도 활성화
윈도가 처음으로 열렸을 때, 키 눌림과 마우스 클릭과 같은 현재 사용자의 입력을 포착하는 것을 의미하는 포그라운드 윈도(foreground window)가 되며, 이 윈도는 활성 윈도(active window)라고도 합니다. 윈도는 처음으로 열렸거나, 사용자가 선택하였을 때 활성 윈도가 됩니다. 이 모든 경우, 윈도는 Activated 이벤트를 띄웁니다.

노트:
윈도가 처음으로 열렸을 때, Activated 이벤트가 발생된 후 LoadedContentRendered 이벤트가 발생합니다. 따라서, 윈도는 ContentRendered가 발생하였을 때만 열려야 할지를 고려해야 합니다.

윈도가 활성화 된 후, 사용자는 같은 애플리케이션 내의 또 다른 윈도를 활성화 하거나 다른 애플리케이션을 활성화 할 수 있습니다. 그랬을 때, 현재 활성 윈도는 비활성화 되고 Deactivated 이벤트를 띄웁니다.

ActivatedDeactivated를 처리하는 한 가지 이유는 예를 들어, 게임이나 비디오 플레이어와 같이 끊임 없는 사용자의 입력이나 집중이 필요한 윈도가 활성화 되었을 때에만 동작할 수 있는 기능을 사용 가능 또는 사용 불가능하게 하기 위해서 입니다.

다음 예제는 이 동작을 구현하기 위해 어떻게 ActivatedDeactivated를 처리하는지 보여줍니다.

XAML
<Window
    x:Class="CustomMediaPlayerWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Custom Media Player"
    Activated="window_Activated"
    Deactivated="window_Deactivated">
  ...
</Window>
C#

using System.Windows;

public partial class CustomMediaPlayerWindow : Window

{

    ...

    bool isMediaElementPlaying;

    void window_Activated(object sender, EventArgs e)

    {

        // 윈도가 활성화 되면 미디어 재생을 재개

        If (this.isMediaElementPlaying) this.mediaElement.Play();

    }

    void window_Deactivated(object sender, EventArgs e)

    {

        // 미디어가 재생 중이고 윈도가 비활성화 되면 재생을 일시 중지

        if (this.isMediaElementPlaying) this.mediaElement.Pause();

    }

}

노트:
완전한 샘플은 Window Activation and Deactivation Sample을 참고합니다.

때로, 윈도는 활성 윈도가 아닐지라도 코드를 계속 실행해야 할지도 모릅니다. 예를 들어, 메일 클라이언트는 사용자가 다른 애플리케이션을 사용하는 동안 메일 서버를 지속적으로 폴링할 것입니다. 이와 같은 애플리케이션은 종종 메인 윈도가 비활성화 되었을 때 다른, 혹은 추가적인 동작을 제공합니다. 메일 프로그램의 경우를 고려해봤을 때, 이것은 새 메일을 받은 편지함에 추가하는 것 뿐만 아니라, 사용자에게 통지하기 위한 시스템 트레이에 아이콘을 추가하는 것도 의미합니다. 애플리케이션이 이런 동작을 수행할 필요가 있는지를 결정하기 위해, IsActive 속성을 검사하여 윈도가 활성화 되었는지 아닌지를 검출할 수 있습니다.

다른 경우, 윈도는 특별히 더 긴급하게 사용자에게 통지를 할 필요가 있다면, 활성 윈도가 되길 원할 것입니다. 이것은 Activate 메소드를 호출하여 가능합니다.

노트 :
System.Windows.Application.ActivatedSystem.Windows.Application.Deactivated 이벤트를 사용하여 애플리케이션-스코프 활성화를 처리할 수 있습니다.

윈도 닫기
사용자가 윈도 사용을 마쳤을 때, 그것을 닫길 바랄 것입니다. 윈도는 다음과 같은 비클라이언트 영역의 요소를 사용하여 닫을 수 있습니다.

  • System 메뉴의 Close 아이템.
  • ALT+F4를 누름.
  • Close 버튼을 누름.

개발자는 윈도를 닫기 위해 다음과 같이 클라이언트 영역에 커스텀 메커니즘을 추가할 수도 있습니다.

  • 일반적으로 메인 애플리케이션을 위한 File 메뉴에 있는 Exit 아이템.
  • 일반적으로 보조 애플리케이션 윈도 상의 File 메뉴에 있는 Close 아이템.
  • 일반적으로 모덜 다이얼로그 박스에 있는 Cancel 버튼.
  • 일반적으로 모덜리스 다이얼로그 박스에 있는 Close 버튼.

위의 개발자 제공의 메커니즘 중 하나에 응답하여 윈도를 닫기 위해, Close 메소드를 호출할 필요가 있습니다.

다음 예제는 File | Exit 메뉴가 클릭되었을 때 어떻게 윈도를 닫는지를 보여줍니다.

XAML
<Window
    x:Class="CustomMediaPlayerWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Custom Media Player"
    Activated="window_Activated"
    Deactivated="window_Deactivated">
  ...
  <MenuItem Header="_File">
      <MenuItem Header="_Exit" Click="exitMenu_Click" />
  </MenuItem>
  ...
</Window>
C#

using System.Windows;

public partial class CustomMediaPlayerWindow : Window

{

    ...

    void exitMenu_Click(object sender, EventArgs e)

    {

        // Close the window

        this.Close();

    }

}

윈도가 닫힐 때, 윈도는 ClosingClosed 이벤트를 띄우게 됩니다.

Closing은 윈도가 닫히기 전에 발생하고, 윈도가 실제로 닫히는 것을 막을 수 있게 합니다. 이렇게 하는 일반적인 이유로는 윈도가 저장되어야 할 데이터를 포함하고 있거나 어떤 활동이 일어나는 경우가 있습니다. 이 상황에서, 윈도는 Closing 이벤트를 사용하여 사용자에게 통지하고 계속해서 윈도를 닫을지 말지 물을 수 있습니다. 본질적으로, 이것은 저장되지 않은 데이터를 포함한 윈도를 닫도록 시도할지 모르는 사용자를 위한 백업 테크닉입니다.

다음과 같이 Closing 처리를 할 수 있습니다.

XAML
<Window
    x:Class="CustomMediaPlayerWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Custom Media Player"
    Activated="window_Activated"
    Deactivated="window_Deactivated"
    Closing="window_Closing">
  ...
</Window>
C#

using System.Windows;

public partial class CustomMediaPlayerWindow : Window

{

    ...

    void window_Closing(object sender, CancelEventArgs e)

    {

        // Ask user if they want to close the window

        if (this.isMediaElementPlaying)

        {

            string msg = "Media is playing. Really close?";

            string title = "Custom Media Player?";

            MessageBoxButton buttons = MessageBoxButton.YesNo;

            MessageBoxImage icon = MessageBoxImage.Warning;


            // Show message box and get user's answer

            MessageBoxResult result =

                MessageBox.Show(msg, title, buttons, icon);


            // Don't close window if user clicked No

            e.Cancel = (result == MessageBoxResult.No);

        }

    }

}

Closing 이벤트 핸들러는 true로 설정하여 윈도 닫기를 막기 위한 Boolean Cancel 속성을 노출하는 CancelEventArgs에 의해 전달됩니다.

Closing이 처리되지 않거나 처리되었지만 취소되지 않았다면, 윈도는 닫힙니다. 윈도를 닫기 직전에, 그리고 Closing이 처리된 후, Closed가 발생됩니다.

노트:
애플리케이션은 메인 윈도가 닫히거나(MainWindow 참고) 마지막 윈도가 닫힐 때 자동적으로 종료될 수 있도록 설정할 수 있습니다. ShutdownMode를 참고합니다.

윈도는 비클라이언트 영역과 클라이언트 영영이 제공하는 메커니즘을 통해 명시적으로 종료될 수 있으며, 또한 다음과 같이 애플리케이션이나 윈도의 다른 부분에 의한 동작의 결과에 의해 묵시적으로 닫힐 수도 있습니다.

  • 사용자가 로그 오프하거나 윈도를 종료하는 경우.
  • 윈도의 소유자가 닫히는 경우(Owner 참고).
  • 메인 애플리케이션 윈도가 닫히고 ShutdownMode가 OnMainWindowClose인 경우.
  • Shutdown이 호출된 경우.
노트:
윈도는 닫힌 이후에 다시 열 수 없습니다.

윈도 라이프타임 이벤트
다음 그림은 윈도의 라이프타임내의 핵심 이벤트와 그것이 발생하는 순서를 나타냅니다.

사용자 삽입 이미지

윈도 위치

윈도가 열리면, LeftTop 속성으로부터 확정될 수 있는 데스크탑(바탕 화면)에 대한 xy 경로를 가집니다. 윈도의 현재 위치를 변경하고 싶다면 이 속성을 설정할 수 있습니다. Window는 또한 처음으로 열려 나타날 때의 위치를 설정할 수 있습니다.

시작 위치
윈도가 열릴 때 윈도가 나타날 위치는 WindowStartupLocation 속성에 의해 결정되며, 다음의 WindowStartupLocation 열거 값중 하나로 설정할 수 있습니다.

시작 위치가 Manual로 지정되면, 윈도는 Windows(운영체제)에 나타날 위치를 물을 것입니다. LeftTop 속성을 사용하여 위치를 지정하는 것으로 이것을 오버라이드 할 수 있습니다.

최상위 윈도와 Z-Order
윈도는 각각 LeftTop 속성으로 지정되는 xy위치를 가지고 있습니다. 추가적으로, 윈도는 다른 윈도에 대한 수직 위치를 결정하는 z 축에 대한 위치를 가집니다. 이것은 z-order로 알려져 있고, 일반(normal) z-order최상위(topmost) z-order의 두 형식을 가집니다. 일반 z-order에 있는 윈도는 언제나 최상위 z-order에 있는 윈도보다 아래에 위치합니다. 최상위 z-order에 위치하기 위해서, 윈도는 반드시 Topmost 속성을 true로 설정해야 합니다.

XAML
<Window ... Topmost="True">
  ...
</Window>

각 z-order에서, 현재 활성 윈도가 같은 z-order에 있는 다른 모든 윈도보다 위에 나타납니다.

다음 그림은 일반과 최상위 z-order에 있는 윈도상에서 Topmost 속성의 효과를 나타냅니다.

사용자 삽입 이미지

윈도 크기

데스크탑에서의 위치를 가지는 것 외에, 윈도는 그것이 보이는 크기를 가집니다. 윈도 크기는 각종 너비와 높이 속성 및 SizeToContent와 같은 몇몇 Window 속성에 의해 결정됩니다.

MinWidth, WidthMaxWidth는 윈도가 그 라이프타임동안 가질 수 있는 너비의 범위를 관리하고, 다음과 같이 설정할 수 있습니다.

XAML
<Window ... MinWidth="300" Width="400" MaxWidth="500">
  ...
</Window>

다음 그림은 어떻게 이 속성들이 윈도의 너비 범위에 적용되는지를 나타냅니다.

사용자 삽입 이미지

윈도 높이는 MinHeight, HeightMaxHeight에 의해 관리되고, 다음과 같이 설정할 수 있습니다.

XAML
<Window ... MinHeight="300" Height="400" MaxHeight="500">
  ...
</Window>

다음 그림은 어떻게 이 속성들이 윈도의 높이 범위에 적용되는지를 나타냅니다.

사용자 삽입 이미지

이러한 너비값과 높이값은 각각 범위를 지정하기 때문에, 크기 변경 가능한 윈도의 너비와 높이는 각 축을 위한 지정된 범위내에서만 지정하는 것이 가능합니다. 현재 너비와 높이는 각각 ActualWidthActualHeight를 조사하여 알 수 있습니다.

윈도의 너비와 높이가 윈도 내용의 크기에 맞출 필요가 있다면, SizeToContent 속성을 사용하여 다음과 같은 값으로 설정할 수 있습니다.

  • Manual. 효과 없음(기본값).
  • Width. 내용의 너비에 맞춤 : MinWidthMaxWidth를 내용의 너비로 설정하는 것과 같은 효과.
  • Height. 내용의 높이에 맞춤 : MinHeightMaxHeight을 내용의 높이로 설정하는 것과 같은 효과.
  • WidthAndHeight. 내용의 너비와 높이에 맞춤 : MinWidth, MaxWidthMinHeight, MaxHeight를 각각 내용의 너비 및 높이로 설정하는 것과 같은 효과.

다음 코드는 윈도가 처음 나타날 때 자동으로 내용의 수직과 수평에 맞춘 크기가 되는 윈도를 보여줍니다.

XAML
<Window ... SizeToContent="WidthAndHeight">
  ...
</Window>

크기 조절 속성의 우선 순위

본래, 윈도의 각 사이즈 속성은 크기 조절 가능한 윈도를 위한 너비와 높이의 범위를 정의하여 조합됩니다. 올바른 범위가 유지될 수 있도록, 윈도는 다음 우선 순위를 사용하여 크기 속성들의 값을 평가합니다.

  • 높이 속성들:
    System.Windows.FrameworkElement.MinHeight >
    System.Windows.FrameworkElement.MaxHeight >
    System.Windows.SizeToContent.Height / System.Windows.SizeToContent.WidthAndHeight >
    System.Windows.FrameworkElement.MaxHeight
  • 너비 속성들:
    System.Windows.FrameworkElement.MinWidth >
    System.Windows.FrameworkElement.MaxWidth >
    System.Windows.SizeToContent.Width / System.Windows.SizeToContent.WidthAndHeight >
    System.Windows.FrameworkElement.MaxWidth

Window Sizing Order of Precedence Sample을 사용하여 우선 순위를 실험해 볼 수 있습니다.

우선 순위는 또한 WindowState 속성으로 관리되는 윈도의 크기가 최대화 되었을 때의 크기도 결정합니다.

윈도 상태

크기 조절 가능한 윈도의 라이프타임동안, 윈도는 보통(normal), 최소화(minimized) 및 최대화(maximized) 상태를 가질 수 있습니다. 보통(normal) 상태의 윈도는 윈도의 기본 상태입니다. 이 상태를 가진 윈도는 현재 보이거나 테두리가 있고 크기 조절이 가능하다면, 사용자가 크기 조절 그립을 사용하여 이동하고 크기를 조절할 수 있게 합니다.

최소화(minimized) 상태의 윈도는 ShowInTaskbartrue로 설정이 되어 있을 경우, 태스크바의 버튼으로 축소합니다. 그렇지 않을 경우, 윈도는 가능한 가장 작은 크기로 축소하고, 자신의 위치를 데스크탑의 좌측 하단 구석으로 변경합니다. 어떤 최소화된 윈도의 종류도 테두리나 크기 조절 그립을 사용하여 크기를 조절하거나, 태스크 바에 있는 최소화된 윈도를 드래그하여 데스크탑으로 돌린다거나 할 수 없습니다. 다음 그림은 최소화된 윈도의 두 종류를 보여줍니다. (※역주 : MSDN! 그림을 빼먹다니! 이 부분은 다들 무슨 얘긴지 아시리라 믿습니다.)

최대화(maximized) 상태의 윈도는 MaxWidth, MaxHeightSizeToContent 속성에서 가능한 최대의 크기로 확장합니다. 최소화된 윈도처럼, 최대화된 윈도도 크기를 변경하거나 드래그할 수 없습니다.

윈도의 상태는 WindowState 속성을 세팅하여 설정할 수 있고, 다음 WindowState 열거 값중 하나의 값을 가집니다.

다음 예제는 어떻게 윈도가 열렸을 때 최대화되어 보이는 윈도를 생성하는지 보여줍니다.

XAML
<Window ... WindowState="Maximized">
  ...
</Window>

일반적으로, 윈도의 초기 상태를 설정하기 위해 WindowState를 설정할 것입니다. 일단 크기 변경 가능한 윈도가 보이면, 사용자는 윈도 상태를 변경하기 위하여 윈도의 타이틀 바에 있는 최소화, 최대화 및 이전 크기로 버튼을 사용할 수 있습니다.

윈도 형태

윈도-지정 내용에 버튼, 라벨 및 텍스트 박스 같은 것을 추가하여 윈도의 클라이언트 영역의 형태를 변경할 수 있습니다. 비클라이언트 영역을 설정하기 위해, Window는 윈도의 아이콘을 설정하기 위한 Icon과 제목을 설정하기 위한 Title과 같은 몇 가지 속성을 제공합니다.

또한 윈도의 resize mode, window style 및 데스크탑의 태스크바에 버튼을 보여줄지 여부를 설정하여 비클라이언트 영역의 형태나 동작을 변경할 수 있습니다.

Resize Mode
WindowStyle에 따라서, 사용자가 윈도의 크기를 변경하거나 다른 모든 것들을 할 수 있는지를 변경할 수 있습니다. 사이즈 조절과 관련하여, 윈도 스타일의 선택은 사용자가 윈도의 테두리를 마우스로 드래그하여 크기를 변경할 수 있을지, Minimize, MaximizeResize 버튼이 비클라이언트 영역에 나타날지, 사용가능한지에 영향을 미칩니다.

ResizeMode 속성을 설정하여 윈도 크기 조절이 어떻게 될지를 설정할 수 있고, 다음 ResizeMode 열거 값중 하나를 가집니다.

다음 그림은 각각의 효과를 나타냅니다.

사용자 삽입 이미지

WindowStyle과 같이, 윈도의 resize mode는 그 라이프타임 동안 변경되지는 않을 것이고, 대부분 마크업에서 그것을 설정할 것이라는 것을 의미합니다.

XAML
<Window ... ResizeMode="CanResizeWithGrip">
    ...
</Window>

윈도가 최대화, 최소화 또는 이전 크기로 되었는지는 WindowState 속성을 조사하여 알 수 있습니다.

Window Style
윈도의 비클라이언트 영역에서 노출되는 테두리는 대부분의 애플리케이션에 적당합니다. 그러나, 윈의 종류에 따라서는, 다른 형태의 테두리가 필요하거나, 전혀 필요하지 않은 상황이 있습니다.

윈도가 가질 테두리의 종류를 컨트롤하기 위해, WindowStyle 속성을 다음과 같은 WindowStyle 열거 값중 하나로 설정할 수 있습니다.

이 윈도 스타일의 효과는 다음 그림에 나타나 있습니다.

사용자 삽입 이미지

WindowStyle은 마크업이나 코드를 사용하여 설정할 수 있지만, 윈도의 라이프타임 동안 그것을 변경할 것 같지는 않으므로 대부분 마크업을 사용하여 설정할 것입니다.

XAML
<Window ... WindowStyle="ToolWindow">
    ...
</Window>

비사각형 윈도 스타일
또한 WindowStyle이 허용하는 테두리 스타일이 충분하지 않은 상황이 있습니다. 예를 들어, Microsoft Windows Media Player가 사용하는 것 처럼, 비사각형 테두리를 가진 애플리케이션을 만들고 싶은 경우가 있습니다.

예를 들어, 다음 그림과 같은 풍선 윈도를 생각해볼 수 있습니다.

사용자 삽입 이미지

이런 종류의 윈도는 WindowStyle 어트리뷰트를 None으로 설정하고, Window가 투명함을 가질 수 있는 특별한 지원을 사용하여 만들 수 있습니다.

XAML
<Window ...
  WindowStyle="None"
  AllowsTransparency="True"
  Background="Transparent">
    ...
</Window>

이것은 WindowStyle, AllowsTransparencyBackground 어트리뷰트의 조합이고 이 값들은 윈도를 완전히 투명하게 그리도록 합니다. 이것은 Path를 사용하여 원하는 테두리를 제공하여 가능합니다. 예를 들면,

XAML
<Window ...
  WindowStyle="None"
  AllowsTransparency="True"
  Background="Transparent">
    ...
    <!-- Path and fill for speech bubble UI -->
    <Path Stroke="DarkGray" StrokeThickness="2">
        ...
    </Path>
    ...
</Window>

윈도를 비사각형 테두리로 생성하였다면, WPF가 제공하는 비클라이언트 영역 요소를 사용할 수 없다는 것을 의미하며, 따라서, 반드시 스스로 구현해야 합니다.

Non-Rectangular Windows Sample을 참고합니다.

태스크바 표시
윈도의 기본 형태는 다음 그림과 같이 태스크바 버튼을 포함합니다.

사용자 삽입 이미지

어떤 윈도는 메시지 박스와 다이얼로그 박스(Dialog Boxes Overview를 참고)와 같이 태스크바 버튼을 가지지 않습니다. 윈도를 위한 태스크바 버튼이 보일지 말지를 ShowInTaskbar 속성(기본값으로 true)을 설정하여 변경할 수 있습니다.

XAML
<Window ... ShowInTaskbar="False">
    ...
</Window>


보안 고려 사항

Window는 인스턴트화 되기 위해 UnmanagedCode 보안 허가를 요구합니다. 로컬 머신에 설치되고 실행되는 애플리케이션을 위해, 이것은 애플리케이션에 부여되는 허가의 세트에 포함됩니다.

그러나, ClickOnce를 사용하여 Internet이나 LocalIntranet에서 시작된 애플리케이션에 부여된 허가의 세트에는 포함되지 않습니다. 따라서, 사용자는 ClickOnce 보안 경고를 받을 것이며, 애플리케이션이 완전히 신뢰되기 위한 허가 세트를 높힐 필요가 있을 것입니다.

추가적으로, XBAPs는 기본적으로 윈도나 다이얼로그 박스를 보일 수 없습니다. 독립 애플리케이션 보안에서의 고려사항을 다루는 WPF Security Strategy - Platform Security를 참고합니다.

See Also

Reference
Window
MeesageBox
NavigationWindow
Application
Concepts
Dialog Boxes Overview
Building a WPF Application

신고
Posted by gongdo

MSDN : http://msdn2.microsoft.com/en-us/library/aa970069.aspx

Pack URIs in WPF

애플리케이션 데이터 파일(WPF Application Data Files 참고)을 식별하기 위해, WPF는 잘 알려지고 확장성있는 uniform resource identifier(URI) 기반의 Pack URI Scheme 메커니즘을 제공합니다. Pack URI Scheme의 확장성은 WPF가 다양한 장소에 존재할 수 있는 몇 가지 애플리케이션 데이터 파일의 종류를 식별하기 위한 일관성있는 수단을 지원할 수 있게 합니다.

이 토픽은 다음과 같은 섹션을 담고 있습니다.

Pack URI 스키마

Open Packaging Conventions(OPC) 명세는 컨텐트를 조직하고 식별하기 위한 모델을 설명합니다. 이 모델의 핵심은 두 논리적인 패키지와 파츠(parts) 요소를 중심으로 합니다. pacakge는 다음 그림에서 표현된 하나 혹은 그 이상의 컨텐트 parts를 위한 컨테이너입니다.

사용자 삽입 이미지

이 파츠를 식별하기 위해, OPC는 RFC 2396의 확장성을 이용하여 Pack URI 스키마를 정의합니다. URI에 의해 지정되는 스키마는 http, ftpfile과 같이 잘 알려진 접두어에 의해 정의됩니다. Pack URI 스키마는 pack을 스키마로 사용하고 권한(autority)과 경로(path) 두 컴포넌트를 포함합니다. autority는 파트가 포함된 패키지의 종류를 지정하고, path는 패키지내의 파트의 위치를 지정합니다. 이 컨셉은 다음 그림으로 표현됩니다.

사용자 삽입 이미지

Pack URI 스키마에 따르는 URI는 "pack URI"이라고 하며, 다음과 같은 포맷을 가집니다.

pack://<authority><path>

패키지와 파츠의 컨셉은 애플리케이션(package)이 하나 또는 그 이상의 다음과 같은 애플리케이션 데이터 파일(parts)을 포함할 수 있다는 점에서 애플리케이션과 애플리케이션 데이터 파일과 비슷합니다.

  • 로컬 어셈블리에 컴파일된 리소스 파일
  • 참조된 어셈블리에 컴파일된 리소스 파일
  • 참조한 어셈블리에 컴파일된 리소스 파일
  • 컨텐트 파일
  • Site of origin 파일

이 애플리케이션의 네가지 종류에 접근하기 위해서, WPF는 application:/// 과 siteoforigin:///의 두 권한을 지원합니다. application:///권한은 컴파일 시점에 알려진 리소스 파일과 컨텐트 파일을 포함하는 애플리케이션 데이터 파일 식별에 사용됩니다. siteoforigin:/// 권한은 site of origin 파일의 식별에 사용됩니다.

노트:
Pack URI의 권한 컴포넌트는 패키지를 가르키는 임베디드 URI이고 반드시 RFC 2396을 준수해야 합니다. 추가적으로, "/" 문자는 반드시 ","로 대치되어야 하고, "%"와 "?" 같은 예약된 문자는 이스케이프될 필요가 있습니다. 상세한 것은 OPC를 참고합니다.

각 권한의 스코프는 다음 그림에 나타나 있습니다.

사용자 삽입 이미지

다음 토픽들은 리소스 파일, 컨텐트 파일 및 site of origin 파일을 구분하기 위한 경로와 함께, 이 두 권한을 사용한 Pack URI를 어떻게 생성하는지 설명합니다.

리소스 파일 Pack URI - 로컬 어셈블리

로컬 어셈블리에 컴파일된 리소스 파일을 위한 Pack URI는 다음의 권한과 경로를 사용합니다.

  • 권한(Authority): application:///
  • 경로(Path): 로컬 어셈블리 프로젝트 폴더의 루트에 대한 경로를 포함한 리소스 파일의 이름.

다음 예제는 로컬 어셈블리 프로젝트 폴더의 루트에 위치한 XAML 리소스 파일의 pack URI를 보여줍니다.

pack://application:,,,/ResourceFile.xaml

다음 예제는 로컬 어셈블리 프로젝트 폴더의 서브 폴더에 위치한 XAML 리소스 파일의 pack URI를 보여줍니다.

pack://application:,,,/Subfolder/ResourceFile.xaml

리소스 파일 Pack URI - 참조된 어셈블리

참조된 어셈블리에 컴파일된 리소스 파일을 위한 Pack URI는 다음의 권한과 경로를 사용합니다.

  • 권한(Authority): application:///
  • 경로(Path): 참조된 어셈블리에 컴파일된 리소스파일의 경로는 다음과 같은 포맷을 따릅니다.
    AssemblyShortName[;Version][;PublicKey];component/Path
    - AssemblyShortName은 참조된 어셈블리의 짧은 이름입니다.
    - ;Version [옵셔널]은 리소스파일을 담고 있는 참조된 어셈블리의 버전을 가리킵니다. 이것은 두개 혹은 그 이상의 참조된 어셈블리가 같은 짧은 이름을 가지고 로드되었을 때 사용됩니다.
    - ;PublicKey [옵셔널]은 참조된 어셈블리의 사인으로 사용된 공용 키를 가리킵니다. 이것은 두개 혹은 그 이상의 참조된 어셈블리가 같은 짧은 이름을 가지고 로드되었을 때 사용됩니다.
    - ;component는 어셈블리가 로컬 어셈블리로부터 참조되어 있음을 지정합니다.
    - /Path는 참조된 어셈블리 프로젝트 폴더의 루트에 대한 경로를 포함한 리소스 파일의 이름입니다.

다음 예제는 참조된 어셈블리 프로젝트 폴더의 루트에 위치한 XAML 리소스 파일의 pack URI를 보여줍니다.

pack://application:,,,/ReferencedAssembly;component/ResourceFile.xaml

다음 예제는 참조된 어셈블리 프로젝트 폴더의 서브 폴더에 위치한 XAML 리소스 파일의 pack URI를 보여줍니다.

pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml

다음 예제는 참조된 버전을 지정하는 어셈블리 프로젝트 폴더의 루트에 위치한 XAML 리소스 파일의 pack URI를 보여줍니다.

pack://application:,,,/ReferencedAssembly;v1.0.0.1;component/ResourceFile.xaml

컨텐트 파일 Pack URI

컨텐트 파일의 Pack URI는 다음과 같은 권한과 경로를 사용합니다.

  • 권한(Authority): application:///
  • 경로(Path): 애플리케이션의 메인 실행 어셈블리의 파일 시스템 위치에 대한 경로를 포함하는 컨텐트 파일의 이름입니다.

다음 예제는 실행 어셈블리와 같은 폴더에 위치한 XAML 컨텐트 파일의 Pack URI를 보여줍니다.

pack://application:,,,/ContentFile.xaml

다음 예제는 애플리케이션의 메인 실행 어셈블리의 파일 시스템 위치의 서브 폴더에 위치한 XAML 컨텐트 파일의 Pack URI를 보여줍니다.

pack://application:,,,/Subfolder/ContentFile.xaml

Site of Origin 파일 Pack URI

site of origin 파일의 Pack URI는 다음과 같은 권한과 경로를 사용합니다.

  • 권한(Authority): siteoforigin:///
  • 경로(Path): 실행 어셈블리가 실행된 위치에 대한 경로를 포함한 site of origin 파일의 이름입니다.

다음 예제는 실행 어셈블리가 실행된 위치에 저장된 XAML site of origin 파일의 Pack URI를 보여줍니다.

pack://siteoforigin:,,,/SiteOfOriginFile.xaml

다음 예제는 실행 어셈블리가 실행된 위치의 서브 폴더에 저장된 XAML site of origin 파일의 Pack URI를 보여줍니다.

pack://siteoforigin:,,,/Subfolder/SiteOfOriginFile.xaml

절대 vs 상대 Pack URI

전체 경로(fully-qualified) Pack URI는 스키마, 권한 및 경로를 포함하고 절대 Pack URI라고 생각할 수 있습니다. 개발자의 편의를 위해, XAML 엘리먼트는 일반적으로 경로만을 포함하는 상대 Pack URI를 가진 어트리뷰트를 적절하게 설정할 수 있게 합니다.

예를 들어, 다음과 같이 로컬 어셈블리에 있는 리소스 파일의 절대 Pack URI를 생각할 수 있습니다.

pack://application,,,/ResourceFile.xaml

이 리소스 파일을 가리키는 상대 Pack URI는 다음과 같습니다.

/ResourceFile.xaml

노트:
site of origin 파일은 어셈블리와 연관성이 없기 때문에, (절대) Pack URI만을 사용하여 가리킬 수 있습니다.

기본적으로, 상대 Pack URI 참조는 참조를 포함하는 마크업 파일이나 코드 파일에 대한 상대 경로로 생각할 수 있습니다. 그러나 제일 앞에 백슬래쉬가 사용된 경우, 상대 Pack URI 참조는 애플리케이션의 루트에 대한 상대 경로로 생각됩니다. 예를 들어, 다음 프로젝트 구조를 생각할 수 있습니다.

[Root]
App.xaml
Page2.xaml
    \[SubPages]
       Page1.xaml
       Page2.xaml

(SubPages에 있는)Page1.xaml이 SubPages에 있는 Page2.xaml 파일의 상대 Pack URI를 만들고 싶다면 다음과 같이 사용할 수 있습니다.

Page2.xaml

그러나, (SubPages에 있는)Page1.xaml이 루트 폴더에 있는 Page2.xaml 파일의 상대 Pack URI를 만들고 싶다면 다음과 같이 사용할 수 있습니다.

Page1.xaml (※역주 : 이 부분은 Page2.xaml이 되어야 맞다고 생각하는데... 좀 당황스럽고 자신이 없네요;)

Pack URI 분석

Pack URI 형식은 Pack URI가 로컬 리소스 파일 및 컨텐트 파일과 동일하게 보이도록 합니다.

pack://application:,,,/ResourceOrContentFile.xaml

이는 다음 상대 URI와 동일합니다.

/ResourceOrContentFile.xaml

이런 유사성 때문에, WPF는 Pack URI가 리소스 파일을 가리키는지 컨텐트 파일을 가리키는지를 결정하기 위한 방법을 필요로 합니다. Pack URI가 정보의 이 유형을 포함하지 않기 때문에, WPF는 다음과 같은 발견적(heuristic)인 방법을 사용하여 URI를 결정합니다.

  1. Pack URI와 일치하는 AssemblyAssociatedContentFileAttribute의 어셈블리 메타 데이터를 조사합니다.
  2. AssemblyAssociatedContentFileAttribute 어트리뷰트가 발견되면, Pack URI는 컨텐트 파일의 경로를 가리킵니다.
  3. AssemblyAssociatedContentFileAttribute 어트리뷰트가 발견되지 않으면, 열거된 로컬 어셈블리에 컴파일된 리소스 파일의 세트를 조사합니다.
  4. Pack URI의 경로와 일치하는 리소스 파일이 발견되면, Pack URI의 경로는 리소스 파일을 가리킵니다.
  5. 리소스가 발견되지 않으면, 내부적으로 무효화된(invalid) Uri를 생성합니다.
노트:
절대와 상대 Pack URI만이 리소스 분석에 포함됩니다. 참조된 어셈블리의 컨텐트 파일은 WPF에 의해 지원되지 않기 때문에 포함되지 않습니다. 참조된 어셈블리에 있는 임베디드 파일의 Pack URI는 참조된 어셈블리와 ;component 접미사에 의해 포함되기 때문에 고유합니다. site of origin 파일의 Pack URI는 siteoforigin:/// 권한만을 사용하기 때문에 고유합니다.

WPF 리소스 분석은 애플리케이션 데이터 파일의 위치에 독립적인 Pack URI를 생성할 수 있게 합니다. 따라서, Resource에서 Content로 비실행 데이터 파일의 빌드 형식을 바꿔도, Pack URI를 변경하지 않아도 됩니다.

Pack URI로 프로그램하기

다음과 같이 많은 WPF 클래스가 Pack URI를 요구하는 프로퍼티를 구현합니다. System.Windows.Application.StartupUri, System.Windows.Controls.Frame.Source, System.Windows.Navigation.NavigationWindow.Source, System.Windows.Documents.Hyperlink.NavigateUri, System.Windows.Window.Icon, 및 System.Windows.Controls.Image.Source.

이 프로퍼티는 마크업과 코드 모두를 사용하여 설정할 수 있습니다.

마크업에서 Pack URI 사용하기

마크업에서, Pack URI의 문자열 값을 가진 어트리뷰트의 엘리먼트를 설정하여 Pack URI를 지정합니다. 예를 들면,

<element attribute="pack://application:,,,/File.xaml" />

다음 표는 마크업에서 문자열 값을 사용하여 지정할 수 있는 각종 절대/상대 Pack URI를 나타냅니다.

표 1: 마크업에서의 절대/상대 Pack URI
(※역주 : 원문에는 표 형태로 되어 있으나 편집의 문제로 항목으로 옮깁니다. 각 항목은 ":"을 중심으로 두 가지 요소로 구분되며 전자는 데이터 파일의 종류를, 후자는 Pack URI 표현을 말합니다.)
리소스 파일 - 로컬 어셈블리 [절대] : "pack://application:,,,/File.xaml"
서브 폴더에 있는 리소스 파일 - 로컬 어셈블리 [절대] : "pack://application:,,,/Subfolder/File.xaml"
리소스 파일 - 참조된 어셈블리 [절대] : "pack://application:,,,/ReferencedAssembly;component/File.xaml"
서브 폴더에 있는 리소스 파일 - 참조된 어셈블리 [절대] : "pack://application:,,,/ReferencedAssembly;component/Subfolder/File.xaml"
리소스 파일 - 버전이 붙은 참조된 어셈블리 [절대] : "pack://application:,,,/ReferencedAssembly;v1.0.0.0;component/File.xaml"
컨텐트 파일 [절대] : "pack://application:,,,/File.xaml"
서브 폴더에 있는 컨텐트 파일 [절대] : "pack://application:,,,/Subfolder/File.xaml"
Site of origin 파일 [절대] : "pack://siteoforigin:,,,/File.xaml"
서브 폴더에 있는 Site of origin 파일 [절대] : "pack://siteoforigin:,,,/Subfolder/File.xaml"
리소스 파일 - 로컬 어셈블리 [상대] : "/File.xaml"
서브 폴더에 있는 리소스 파일 - 로컬 어셈블리 [상대] : "/Subfolder/File.xaml"
리소스 파일 - 참조된 어셈블리 [상대] : "/ReferencedAssembly;component/File.xaml"
서브 폴더에 있는 리소스 파일 - 참조된 어셈블리 [상대] : "/ReferencedAssembly;component/Subfolder/File.xaml"
컨텐트 파일 [상대] : "/File.xaml"
서브 폴더에 있는 컨텐트 파일 [상대] : "/Subfolder/File.xaml"

노트:
Site of origin 파일은 오직 절대 Pack URI로만 참조가 가능합니다.

코드에서 Pack URI 사용하기

코드에서, Uri 클래스를 인스턴스화 하고 Pack URI를 생성자의 파라미터로 전달하여 Pack URI를 지정합니다. 이것은 다음의 예제에서 시연됩니다.

C#
Uri uri = new Uri("pack://application:,,,/File.xaml");

기본적으로, Uri 클래스는 절대 Uri로 참조되었다고 생각합니다. 따라서, 다음과 같이 상대 Pack URI를 참조하는 Uri 클래스의 인스턴스를 사용하려고 시도할 때 예외가 발생합니다.

C#
Uri uri = new Uri("/File.xaml");

운 좋게도, Uri 클래스 생성자 중 하나는 Pack URI가 절대인지 상대인지를 지정할 수 있는 UriKind{http://msdn2.microsoft.com/en-us/library/system.urikind.aspx} 형의 파라미터를 받아들입니다.

C#
// 절대 URI(기본)
Uri absoluteUri = new Uri("pack://application:,,,/File.xaml", UriKind.Absolute);
// 상대 URI
Uri relativeUri = new Uri("/File.xaml", UriKind.Relative);

제공할 Pack URI는 AbsoluteRelative중 하나로 확정하여 지정해야 합니다. 그러나, 어떤 상황에서는 Pack URI가 절대인지 상대인지 컴파일 시점에서는 알 수 없는 경우가 있을 것입니다. 예를 들어 애플리케이션이 런타임에 Pack URI가 절대나 상대가 될 수 있는 사용자 제공의 Pack URI를 수용할 수도 있습니다. 이 경우, RelavieOrAbsolute를 대신 사용할 수 있습니다.

C#
// 상대 또는 절대 URI
TextBox userProvidedTextBox = new TextBox();
Uri uri = new Uri(userProvidedUriTextBox.Text, UriKind.RelativeOrAbsolute);

다음 표는 코드에서 System.Uri를 사용하여 지정할 수 있는 각종 절대/상대 Pack Uri를 나타냅니다.

표2: 코드에서의 상대/절대 Pack URI
(※역주 : 표1과 같은 방법으로 표시합니다.)

리소스 파일 - 로컬 어셈블리 [절대] : Uri uri = new Uri("pack://application:,,,/File.xaml", UriKind.Absolute");
서브 폴더에 있는 리소스 파일 - 로컬 어셈블리 [절대] : Uri uri = new Uri("pack://application:,,,/Subfolder/File.xaml", UriKind.Absolute");
리소스 파일 - 참조된 어셈블리 [절대] : Uri uri = new Uri("pack://application:,,,/ReferencedAssembly;component/File.xaml", UriKind.Absolute");
서브 폴더에 있는 리소스 파일 - 참조된 어셈블리 [절대] : Uri uri = new Uri("pack://application:,,,/ReferencedAssembly;component/Subfolder/File.xaml", UriKind.Absolute");
리소스 파일 - 버전이 붙은 참조된 어셈블리 [절대] : Uri uri = new Uri("pack://application:,,,/ReferencedAssembly;v1.0.0.0;component/File.xaml", UriKind.Absolute");
컨텐트 파일 [절대] : Uri uri = new Uri("pack://application:,,,/File.xaml", UriKind.Absolute");
서브 폴더에 있는 컨텐트 파일 [절대] : Uri uri = new Uri("pack://application:,,,/Subfolder/File.xaml", UriKind.Absolute");
Site of origin 파일 [절대] : Uri uri = new Uri("pack://siteoforigin:,,,/File.xaml", UriKind.Absolute");
서브 폴더에 있는 Site of origin 파일 [절대] : Uri uri = new Uri("pack://siteoforigin:,,,/Subfolder/File.xaml", UriKind.Absolute");
리소스 파일 - 로컬 어셈블리 [상대] : Uri uri = new Uri("/File.xaml", UriKind.Relative");
서브 폴더에 있는 리소스 파일 - 로컬 어셈블리 [상대] : Uri uri = new Uri("/Subfolder/File.xaml", UriKind.Relative");
리소스 파일 - 참조된 어셈블리 [상대] : Uri uri = new Uri("/ReferencedAssembly;component/File.xaml", UriKind.Relative");
서브 폴더에 있는 리소스 파일 - 참조된 어셈블리 [상대] : Uri uri = new Uri("/ReferencedAssembly;component/Subfolder/File.xaml", UriKind.Relative");
컨텐트 파일 [상대] : Uri uri = new Uri("/File.xaml", UriKind.Relative");
서브 폴더에 있는 컨텐트 파일 [상대] : Uri uri = new Uri("/Subfolder/File.xaml", UriKind.Relative");

노트:
Site of origin 파일은 오직 절대 Pack URI로만 참조가 가능합니다.

See Also

Concepts
WPF Application Data Files
Other Resources
Pack URI Sample

신고
Posted by gongdo

MSDN : http://msdn2.microsoft.com/en-us/library/aa970494.aspx

WPF Application Data Files

마이크로소프트 윈도 애플리케이션은 빈번히 XAML, 이미지, 비디오 및 오디오와 같은 비실행 데이터를 포함하는 파일들에 의존합니다. WPF는 설정, 식별 및 애플리케이션 데이터 파일이라고 불리는 이런 종류의 데이터 파일들을 사용하기 위한 특별한 지원을 제공합니다. 이 지원은 다음과 같은 애플리케이션 데이터 파일 종류의 특정한 세트를 주로 다룹니다.

  • Resource 파일 : 실행가능한 또는 라이브러리 WPF 어셈블리에 포함되어 컴파일되는 데이터 파일
  • Content 파일 : 실행가능한 XAML 어셈블리와 명시적인 연결을 갖는 독립적인 데이터 파일
  • Site of Origin 파일(※적당한 번역이 생각나지 않아 site of origin으로 표기합니다.) : 실행가능한 XAML 어셈블리와 관계 없는 독립적인 데이터 파일

이 세가지 종류를 만들기 위한 한가지 중요한 구분은 리소스 파일과 컨텐트 파일은 빌드 시점에 알려지고 어셈블리는 명시적으로 그들을 파악한다는 것입니다. 그러나, site of origin 파일의 경우 어셈블리는 그 파일에 대해 아무런 정보도 가지고 있지 않거나 Pack URI(Uniform Resource Identifier)를 통해 암시적인 정보를 가질 것이고, 후자의 경우는 참조된 site of origin 파일의 존재에 대한 보증이 없습니다.

애플리케이션의 데이터 파일을 참조하기 위해, WPF는 WPF에서의 Pack URI에 상세히 설명된 Pack URI 스키마를 사용합니다.

이 토픽은 애플리케이션 데이터 파일을 어떻게 설정하고 사용하는지에 대해 설명합니다.

이 토픽은 다음 섹션을 담고 있습니다.

리소스 파일

만약 애플리케이션 데이터 파일이 애플리케이션에게 항상 사용 가능해야 한다면, 사용 가능함을 보증할 수 있는 유일한 방법은 애플리케이션의 메인 실행 어셈블리 또는 그것의 참조된 어셈블리 중 하나에 넣어 컴파일하는 것입니다. 이런 애플리케이션 데이터 파일의 종류는 리소스 파일이라고 알려져 있습니다.

다음의 경우에 리소스 파일을 사용해야 합니다.
어셈블리에 넣어 컴파일된 후 리소스 파일의 내용을 업데이트할 필요가 없을 때.
파일 의존성을 줄여 애플리케이션 배포의 복잡함을 간소화 하고 싶을 때.
애플리케이션 데이터 파일이 지역화될 필요가 있을 때(WPF Globalization and Localization Overview를 참고).

노트:
리소스 사전(탑레벨 엘리먼트처럼 ResourceDictionary를 가진 XAML 파일)은 WPF 리소스 파일이 아닙니다. WPF 리소스 파일이 리소스 사전이 될지라도, 리소스 사전은 리소스 파일이 될 수 없습니다(ResourceDictionary를 참고).
-...도저히 번역 불가... .NET 프레임웍 3.0에서 지원하는 임베디드 리소스를 쓸 수 있다는 얘긴지 없단 얘긴지 모르겠고 당장 중요한 내용은 아니라고 생각됩니다.-

리소스 파일 설정
WPF에서, 리소스 파일은 다음과 같은 Resource 아이템처럼 MSBuild(Microsoft build engine) 프로젝트에 포함됩니다. (※역주 : 다음 코드는 VS의 프로젝트파일(.csproj 또는 .vbproj)를 텍스트 에디터로 열어보면 확인할 수 있습니다. 물론 VS IDE상에서 수정이 가능하므로 직접 수정하는 경우는 거의 없습니다.)

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
... >
...
<ItemGroup>
<Resource Include="ResourceFile.xaml" />
</ItemGroup>
...
</Project>

노트:
마이크로소프트 비주얼 스튜디오 2005에서, 파일을 프로젝트에 추가하고 Resource에 그것의 Build Action을 설정하여 리소스 파일을 추가할 수 있습니다.

프로젝트가 빌드되었을 때, MSBuild는 리소스를 어셈블리로 컴파일합니다.

리소스 파일 사용
리소스 파일을 로드하기 위해, 원하는 리소스 파일을 식별하는 Pack URI를 전달하는 Application 클래스의 GetResourceStream을 호출할 수 있습니다. GetResourceStream은 리소스 파일을 Stream으로 노출하고 그것의 컨텐트 종류를 설명하는 StreamResourceInfo 객체를 반환합니다.

예를 들어, 다음 코드는 어떻게 GetResourceStream을 사용하여 Page 리소스 파일과 Frame의 컨텐츠로 그것을 설정하는지를 보여줍니다.

C#
// Navigate to xaml page
Uri uri = new Uri("/PageResourceFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetResourceStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);
this.pageFrame.Content = page;

GetResourceStream호출이 Stream으로의 접근을 제공할 때, 그것과 함께 세팅할 프로퍼티의 종류로 그것을 변환하는 추가적인 작업이 필요합니다. 아니면, 코드를 사용하여 프로퍼티에 직접 리소스 파일을 로딩하여 WPF가 Stream 열기와 변환을 관리하도록 할 수 있습니다.

다음 예제는 코드를 사용하여 FramePage를 직접 로딩하는 방법을 보여줍니다.

C#
Uri pageUri = new Uri("/PageResourceFile.xaml", UriKind.Relative);
this.pageFrame.Source = pageUri;

다음 예제는 전의 예제와 동일한 마크업입니다.

XAML
<Frame Name="pageFrame" Source="PageResourceFile.xaml" />

리소스 파일로서의 애플리케이션 코드 파일
WPF 애플리케이션 코드 파일의 특별한 세트는 윈도, 페이지, 플로우 문서 및 리소스 사전을 포함하는 Pack URI를 사용하여 참조될 수 있습니다. 예를 들어, System.Windows.Application.StartupUri 속성을 애플리케이션이 시작할 때 로드할 윈도나 페이지를 참조하는 Pack URI로 설정할 수 있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    StartupUri="MainWindow.xaml">
</Application>

이는 XAML 파일이 MSBuild 프로젝트에 Page 아이템으로 포함되었을 때 가능합니다.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
... >
...
<ItemGroup>
<Page Include="MainWindow.xaml" />
</ItemGroup>
...
</Project>
노트:
마이크로소프트 비주얼 스튜디오 2005에서, 새 Window, NavigationWindow, Page, FlowDocument 혹은 ResourceDictionary를 프로젝트에 추가하면, 마크업 파일을 위한 Build Action은 기본적으로 Page가 될 것입니다.

Page 아이템이 있는 프로젝트가 컴파일되었을 때, XAML 아이템은 바이너리 포맷으로 변환되고 연관된 어셈블리에 포함되어 컴파일됩니다. 따라서, 이 파일은 일반적인 리소스 파일과 같은 방법으로 사용할 수 있습니다.

노트:
XAML 파일이 Resource 아이템으로 설정되었고, 코드-비하인드 파일을 가지지 않는다면, XAML의 바이너리 버전이 아닌 생(raw) XAML이 어셈블리에 포함되어 컴파일됩니다.

컨텐트 파일

content file은 실행 어셈블리와 나란히 느슨한 파일(loose file)로 배포됩니다. 파일이 어셈블리에 포함되어 컴파일되지 않더라도, 어셈블리는 각 컨텐트 파일에 대한 관계를 맺는 메타데이터로 컴파일됩니다.

애플리케이션이 그들을 소비하는 어셈블리를 재컴파일하지 않고도 업데이트할 수 있는 애플리케이션 데이터 파일의 특정한 세트가 필요할 경우 반드시 컨텐트 파일을 사용해야 합니다.

컨텐트 파일 설정
프로젝트에 컨텐트 파일을 추가하기 위해, 애플리케이션 데이터 파일은 반드시 Content 아이템으로 포함되어야 합니다. 게다가, 컨텐트 파일은 어셈블리로 직접 컴파일되지 않으므로, MSBuild CopyToOutputDirectory 메타데이터 엘리먼트를 컨텐트 파일이 빌드된 어셈블리와 관계되어 복사될 위치로 정의하도록 설정할 필요가 있습니다. 프로젝트가 빌드될 때마다 리소스가 빌드 출력 폴더에 복사되기를 원한다면, CopyToOutputDirectory 메타데이터 엘리먼트를 Always 값으로 설정합니다. 그외의 경우, PreserveNewest 값을 사용하여 리소스의 새 버전이 있을 때만 빌드 출력 폴더에 복사할 수 있습니다.

다음은 컨텐트 파일을 프로젝트에 리소스의 새 버전이 추가되었을 때 빌드 출력 폴더에 복사하는 설정의 파일을 보여줍니다.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
... >
...
<ItemGroup>
<Content Include="ContentFile.xaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
...
</Project>
노트:
마이크로소프트 비주얼 스튜디오 2005에서, 컨텐트 파일을 프로젝트에 파일을 추가하고 그것의 Build ActionContent로 설정하고, 그것의 Copy to Output DirectoryCopy Always(=Always)와 Copy if newer(=PreserveNewest)로 설정하여 생성합니다.

프로젝트가 빌드되었을 때, 다음과 같이 AssemblyAssociatedContentFileAttribute 어트리뷰트가 각 컨텐트 파일을 위해 어셈블리의 메타데이터로 컴파일됩니다.

[assembly: AssemblyAssociatedContentFile("ContentFile.xaml")]

AssemblyAssociatedContentFileAttribute의 값은 프로젝트에서 그것의 위치에 관련된 컨텐트 파일의 경로를 암시합니다. 예를 들어, 컨텐트 파일이 프로젝트의 서브폴더에 위치하였다면, 추가 경로 정보는 다음과 같이 AssemblyAssociatedContentFileAttribute 값에 통합될 것입니다.

[assembly: AssemblyAssociatedContentFile("Resources/ContentFile.xaml")]

AssemblyAssociatedContentFileAttribute 값은 빌드 출력 폴더에서 컨텐트 파일의 경로의 값이기도 합니다.

컨텐트 파일의 사용
컨텐트 파일을 로드하기 위해, Application 클래스의 GetContentStream 메소드를 호출 할 수 있습니다. GetContentStream은 컨텐트 파일을 Stream으로 노출하고 그것의 컨텐트 타입을 설명하는 StreamResourceInfo 객체를 반환합니다.

예를 들어, 다음 코드는 GetContentStream을 사용하여 어떻게 Page 컨텐트 파일을 로드하고 Frame의 컨텐트로 설정하는지 보여줍니다.

C#
// Navigate to xaml page
Uri uri = new Uri("/PageContentFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetContentStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);
this.pageFrame.Content = page;

GetContentStream호출이 Stream으로의 접근을 제공할 때, 그것과 함께 세팅할 프로퍼티의 종류로 그것을 변환하는 추가적인 작업이 필요합니다. 아니면, 코드를 사용하여 프로퍼티에 직접 리소스 파일을 로딩하여 WPF가 Stream 열기와 변환을 관리하도록 할 수 있습니다.

다음 예제는 코드를 사용하여 FramePage를 직접 로딩하는 방법을 보여줍니다.

C#
Uri pageUri = new Uri("/PageContentFile.xaml", UriKind.Relative);
this.pageFrame.Source = pageUri;

다음 예제는 전의 예제와 동일한 마크업입니다.

XAML
<Frame Name="pageFrame" Source="PageContentFile.xaml" />

Site of Origin 파일

리소스 파일은 AssemblyAssociatedContentFileAttribute에 의해 정의되어 함께 배포되는 어셈블리와 명시적인 관계를 가집니다. 하지만, 다음과 같은 경우를 포함하는 암묵적이거나 어셈블리와 애플리케이션 데이터 파일간의 관계가 없는 초기화를 원하는 경우도 있습니다.

  • 파일이 컴파일 시점에 존재하지 않을 때.
  • 어셈블리가 런타임이 될때까지 파일을 필요로 하지 않을 때.
  • 연관된 어셈블리를 재컴파일 하지 않고 파일을 업데이트하고 싶을 때.
  • 애플리케이션이 오디오나 비디오 처럼 큰 데이터 파일을 사용하고 사용자의 선택에 따라 그것을 다운로드하길 원할 때.

이는 file:///http:// 스키마와 같은 전통적인 URI 스키마를 통해 이런 파일의 타입을 로드할 수 있습니다.

XAML
<Image Source="file:///C:/DataFile.bmp"></Image>
<Image Source="http://www.datafilewebsite.com/DataFile.bmp"></Image>

그러나, file:///http:// 스키마는 애플리케이션에게 완전한 신뢰(full trust)를 요구합니다. 만약 애플리케이션이 인터넷이나 인트라넷에서 시작된 XAML 브라우저 애플리케이션(XBAPs)라면, 그것은 그 위치에서 시작된 애플리케이션을 위해 허용된 허가(permissions)의 세트에 대해서만 요청하고, 느슨한 파일은 애플리케이션의 시작점(site of origin, 시작된 위치)에서만 로드할 수 있습니다. 이런 파일들은 site of origin파일이라고 합니다.

Site of origin 파일은 제한이 없는 부분적으로 신뢰된 애플리케이션이더라도 부분적으로 신뢰된 애플리케이션이 선택할 수 있는 유일한 옵션입니다. 완전히 신뢰된 애플리케이션은 여전히 빌드 시점에 확인하지 못한 애플리케이션 데이터 파일 로드를 필요로 할지도 모릅니다. 완전히 신뢰된 애플리케이션은 file:///을 사용할 수 있고, 아마도 애플리케이션 데이터 파일은 같은 폴더나 애플리케이션 어셈블리의 서브 폴더에 설치될 것입니다.
이 경우 site of origin 참조를 사용하는 것은 file:///을 사용하는 것보다 쉽습니다. 왜냐하면, file:///은 파일을 full 경로로 표기할 것을 요구하기 때문입니다.

노트:
Site of origin 파일은 컨텐트 파일이 클라이언트 머신의 XAML 브라우저 애플리케이션(XBAP)에 있을 때는 캐쉬되지 않습니다. 따라서, 그들은 특수한 요청이 있을 때만 다운로드 됩니다. 만약 XAML 브라우저 애플리케이션(XBAP) 애플리케이션이 커다란 미디어 파일을 가진다면, 그것들을 site of origin 파일로 설정한다는 것은 애플리케이션 초기화가 더 빨라진다는 것을 의미하고, 꼭 필요할 때에만 다운로드된다는 것을 말합니다.

Site of Origin 파일 설정
site of origin 파일이 존재하지 않거나 컴파일 시점에 알 수 없다면, XCopy 커맨드 라인 프로그램이나 Microsoft Windows Installer를 포함한 런타임 중 요청된 파일이 사용 가능함을 확실히 하기 위한 전통적인 개발 매커니즘을 사용할 필요가 있습니다.

컴파일 시점에 site of origin에 위치하길 원하는 파일을 알고 있지만, 여전히 명시적인 의존성을 피하고 싶다면, 이 파일들을 None 아이템으로 MSBuild 프로젝트에 추가할 수 있습니다. 컨텐트 파일과 같이 MSBuild CopyToOutputDirectory 어트리뷰트를 AlwaysPreserveNewest로 지정하여 빌드된 어셈블리와 관련하여 site of origin파일이 복사될 위치로 지정하는 설정이 필요합니다.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
... >
...
<None Include="PageSiteOfOriginFile.xaml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
...
</Project>
Note:
마이크로소프트 비주얼 스튜디오 2005에서는, 파일을 프로젝트에 추가하고 그것의 Build ActionNone으로 설정하여 site of origin 파일을 추가합니다.

Site of Origin 파일 사용
site of origin 파일을 로드하기 위해, 원하는 site of origin 파일을 식별하는 Pack URI를 전달하는 Application 클래스의 GetRemoteStream 메소드를 호출할 수 있습니다.
GetRemoteStream은 site of origin 파일을 Stream으로 노출하고 그것의 컨텐트 타입을 설명하는 StreamResourceInfo 객체를 반환합니다.

예를 들어, 다음 코드는 GetRemoteStream을 사용하여 어떻게 Page 컨텐트 파일을 로드하고 Frame의 컨텐트로 그것을 설정하는지 보여줍니다.

C#
// Navigate to xaml page
Uri uri = new Uri("/SiteOfOriginFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetRemoteStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);
this.pageFrame.Content = page;

GetRemoteStream호출이 Stream으로의 접근을 제공할 때, 그것과 함께 세팅할 프로퍼티의 종류로 그것을 변환하는 추가적인 작업이 필요합니다. 아니면, 코드를 사용하여 프로퍼티에 직접 리소스 파일을 로딩하여 WPF가 Stream 열기와 변환을 관리하도록 할 수 있습니다.

다음 예제는 코드를 사용하여 Frame에 Page를 직접 로딩하는 방법을 보여줍니다.

C#
Uri pageUri = new Uri("pack://siteoforigin:,,,/Subfolder/SiteOfOriginFile.xaml", UriKind.Absolute);
this.pageFrame.Source = pageUri;

다음 예제는 전의 예제와 동일한 마크업입니다.

XAML
<Frame Name="pageFrameSOO" Source="pack://siteoforigin:,,,/PageContentFile.xaml" />
XAML
<Frame Name="pageFrame" Source="pack://siteoforigin:,,,/SiteOfOriginFile.xaml" />

빌드 타입이 변경된 후 다시 빌드하기

애플리케이션 데이터 파일의 빌드 타입을 변경한 후에는, 그 변경을 올바로 적용하기 위해 해당 애플리케이션을 다시 빌드할 필요가 있습니다. 단지 애플리케이션만 빌드하는 경우라면, 변경 사항은 적용되지 않습니다.

See Also

Cencepts
Pack URIs in Windows Presentation Foundation

신고
Posted by gongdo
MSDN : http://msdn2.microsoft.com/en-us/library/ms743714.aspx

Application Management Overview

이 토픽은 Application 클래스를 소개하는 것으로 시작해 Application에 의해 제공되는 애플리케이션, 애플리케이션의 라이프타임 및 부가 서비스를 어떻게 정의하는가와 같은 서비스의 개요를 제공합니다.

이 토픽은 다음 섹션과 같은 내용을 담고 있습니다. 애플리케이션 클래스 The Application Class

WPF에서, 애플리케이션은 Application 클래스로 캡슐화됩니다. Application은 다음과 같은 기능의 서비스를 제공합니다.
  • 공통 애플리케이션 기반(infrastructure)을 생성하고 관리
  • 애플리케이션 라이프타임에 상호 작용하고 추적
  • 커맨드 라인 파라미터를 받고 처리
  • 애플리케이션 스코프 속성과 리소스를 공유
  • 핸들되지 않은 예외를 검출하고 반응
  • 종료 코드를 반환
  • 독립 애플리케이션들의 윈도들을 관리(MSDN : WPF Windows Overview 참고)
  • XAML 브라우저 애플리케이션(XBAPs)에서의 내비게이션 및 내비게이션 윈도와 프레임을 가진 독립 애플리케이션의 추적(MSDN : Navigation Overview 참고)

애플리케이션 구현

일반적인 WPF 애플리케이션은 마크업과 코드-비하인드 모두를 사용하여 정의됩니다. 이것은 마크업을 사용하여 선언적으로 애플리케이션 속성, 리소스 및 이벤트 등록을 설정할 수 있게 하고, 이벤트를 처리할 때 코드-비하인드에서 특정한 동작을 구현합니다. 다음 예제는 어떻게 마크업과 코드-비하인드를 모두 사용하여 애플리케이션을 정의하는지를 보여줍니다.

XAML

<Application

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    x:Class="App" />

C#
using System.Windows;
public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }
}

마크업 파일과 코드-비하인드 파일을 함께 사용하기 위해서 다음의 사항이 필요합니다.

  • 마크업에서, Application 엘리먼트는 반드시 x:Class 어트리뷰트에 의해 정의되는 이름으로 프로젝트가 빌드될 때 마크업 파일을 위해 partial class를 만들기 위한 MSBuild를 구성하는 x:Class 어트리뷰트를 포함해야 합니다. 이것은 XAML 스키마(xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml")를 위한 XML 네임스페이스 선언의 추가를 요구합니다. 부분적으로 생성된 partial class는 이벤트 등록과 마크업에서 구현된 프로퍼티의 설정을 위해 호출되는 InitializeComponent를 구현합니다.
  • 코드-비하인드에서, 클래스는 반드시 마크업에서 x:Class 어트리뷰트로 지정된 partial class와 같은 이름이어야 하고, Application을 상속할 필요가 있습니다. 이는 코드-비하인드 파일이 빌드될 때 마크업 파일을 위해 생성된 partial class와 연결되는 것을 가능케합니다(WPF 애플리케이션 빌드하기 참고).
  • 코드-비하인드에서, 클래스는 반드시 마크업에서 x:Class로 설정된 같은 이름의 partial class로 정의되어야 합니다. 이것은 코드-비하인드 파일이 프로젝트가 빌드될 때 마크업 파일을 위해 생성된 partial class와 연결되는 것을 가능케 합니다(WPF 애플리케이션 빌드하기 참고).
노트:
Microsoft Visual Studio를 사용하여 새 Windows Application(WPF)나 XAML 브라우저 애플리케이션(WPF)프로젝트를 생성할 때, 애플리케이션 정의는 기본적으로 마크업과 코드-비하인드를 모두 포함합니다.

이 최소한의 애플리케이션 정의는 WPF 애플리케이션 작업으로 생성하기 위해 작성할 필요가 있는 코드의 전부입니다. 그러나, 애플리케이션 정의는 특정한 방법의 MSBuild를 위해 설정될 필요가 있습니다.

MSBuild를 위한 애플리케이션 정의 설정

모든 WPF 실행 애플리케이션(독립적인 애플리케이션과 XBAP)은 실행하기 위한 기반(infrastructure)의 어떤 레벨의 구현이 필요합니다. 먼저, 이것은 애플리케이션을 시작시키기 위해 OS가 호출하는 함수로 유명한 엔트리 포인트 함수를 포함합니다. 전통적으로 개발자는 전부는 아니더라도 그것을 위한 코드의 어느 정도를 작성해야 했습니다. 그러나, WPF는 MSBuild ApplicationDefinition 아이템과 같은 애플리케이션 정의 마크업 파일을 설정하는 것에 의해 이 코드를 생성할 것입니다. 필요한 설정은 다음 예제에서 보여줍니다. (※역주 : 다음의 코드는 VS2005의 프로젝트 파일(.csproj또는 .vbproj)를 텍스트 편집기에서 열어 확인할 수 있습니다. 물론 일반적인 경우 이 코드를 직접 수정하는 경우는 없습니다.)

<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<ApplicationDefinition Include="App.xaml" />
<Compile Include="App.xaml.cs" />
...
</Project>

여기에서, 코드-비하인드 파일은 MSBuild의 Compile 아이템에 의해 설정됩니다. 또한, 코드-비하인드 파일은 추가적인 언어 정의 접미어(.cs.vb)가 붙은 마크업 파일과 같은 이름을 가지고 있습니다. 이 접미어는 Microsoft Visual Studio가 기본적으로 이 규칙을 사용하긴 하지만 반드시 필요한 것은 아닙니다.

노트:
WPF 애플리케이션 작성의 자세한 사항은, WPF 애플리케이션 만들기를 참조합니다.

애플리케이션 정의와 MSBuild 설정과의 조합의 효과는 MSBuild가 다음과 같은 코드를 생성하게 합니다.

C#
public class App : Application
{
    public App() { }
    [STAThread]
    public static void Main()
    {
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }

    public void InitializeComponent()
    {
        // Register XAML-declared events
        ...
        // Set XAML-declared properties
        ...
    }
}

이 애플리케이션이 시작되었을 때, 윈도는 엔트리 포인트 함수(C#과 Visual Basic 모두 Main)를 호출할 것입니다. 이후, 엔트리 포인트 함수는 이벤트 등록과 마크업에서 구현된 속성 설정을 위한 InitializeComponent를 호출하기 전에 사용자 애플리케이션 클래스의 새 인스턴스를 생성합니다.

거의 대부분, AppDomain별로 단 하나의 Application 클래스나 서브클래스의 인스턴스만이 존재합니다. 이 때문에, 애플리케이션-스코프 속성과 리소스(나중에 다룹니다)의 단일 셋으로의 공유 접근을 지원하기 위해, Application은 싱글톤 패턴을 사용하여 구현됩니다. 싱글톤 패턴은 싱글톤(C#에서 싱글톤 구현하기 참고)이라고 알려진 단 하나의 인스턴스만을 가지는 클래스 생성을 위한 모델입니다. 싱글톤 클래스는 static 속성으로 공유 접근을 제공하는 자신의 인스턴스를 만듭니다. Application 클래스를 위한 이 속성은 다음과 같은 Current입니다.

C#
// Get reference to application
App currentApp = (App)Application.Current;

생성된 코드에서, App가 인스턴스화된 이후 호출되는 Run 메소드를 알 수 있을 것입니다. 이것은 WPF 애플리케이션의 생명이 시작된다는 것을 말합니다.

노트:
실행 애플리케이션의 단 하나의 인스턴스가 실행될 것을 검출할 필요가 있을 때를 포함하여, 이 코드를 직접 작성할 필요가 있는 상황이 있습니다. 더 자세한 정보는 단일 인스턴스 검출 샘플을 참고합니다.

애플리케이션 라이프타임

System.Windows.Application.Run이 호출된 후, 애플리케이션이 시작되고, 라이프타임 동안 사용자나 프로그램 조건에 응답하여 최종적으로 실행을 중지하기 전까지 여러번 비활성화와 활성화할 것입니다.

애플리케이션 시작하기
System.Windows.Application.Run
이 호출된 후, Application은 모든 애플리케이션에서 공통적으로 필요한 기반의 셋을 초기화합니다. 기반이 구축될 때, Application은 WPF 애플리케이션이 시작되는 순간을 의미하는 Startup 이벤트를 발생시킵니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    Startup="App_Startup" />
C#
public partial class App : Application
{
   
void App_Startup(object sender, StartupEventArgs e)
    {
       
// Application is running
    }
}

MSBuild ApplicationDefinition 아이템과 같은 애플리케이션 정의를 구현할 때 시작(launching), 실행(running) 및 애플리케이션 기반의 초기화(establishing) 작업이 WPF에 의해 작동됩니다. 이것은 대부분 애플리케이션의 시작을 위해 항상 필요한 UI 보여주기와 커맨드라인 매개 변수 처리를 포함하는 기능 구축에 집중할 수 있게 합니다.

유저 인터페이스 보여주기
대부분의 윈도 애플리케이션은 유저 인터페이스를 열기 때문에 실행을 시작할 때 Startup 이벤트 핸들러는 그것을 수행하기에 적합한 위치입니다. 독립적인 애플리케이션을 위해, 이것은 다음과 같이 윈도를 보여주는것에 관계됩니다.

C#
public partial class App : Application
{
    void App_Startup(object sender, StartupEventArgs e)
    {
        // Application is running

        // Show the main window
        MainWindow mainWindow = new MainWindow();
        mainWindow.Show();
    }
}

단지 Startup을 처리하는 것이 윈도를 보여주기 위한 것이라면, 대신에 마크업에서 StartupUri를 설정할 수 있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml" />

독립 애플리케이션의 UI가 윈도 대신에 페이지(Page)의 조합일 경우에도, StartupUri를 시작 페이지로 설정하는데 사용할 수 있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="HomePage.xaml" />

XBAP 애플리케이션에서도, StartupUri을 사용하여 윈도 Internet Explorer에서 보여줄 첫번째 페이지를 지정할 수 있습니다.

UI를 보여주는 것에 관하여 StartupUri를 세팅하는 대신 Startup처리가 필요가 있는 주 원인은 기본 생성자를 사용하지 않거나 UI를 보여주기 전에 속성을 설정하는 것을 구현하는 클래스를 인스턴스화하기 위해서 입니다.

애플리케이션에서 커맨드라인 매개 변수를 받아서 처리하기 위해서도 Startup을 처리할 필요가 있을 것입니다.

커맨드라인 매개 변수 처리하기
윈도에서, 독립 애플리케이션은 커맨드 프롬프트나 데스크탑에서 시작될 수 있습니다. 두 경우 모두, 커맨드라인 매개 변수를 다음과 같은 문법을 사용하여 애플리케이션에 전달할 수 있습니다.

wpfapplication.exe /winstate:maximized

애플리케이션이 초기화하는 동안, WPF는 OS로부터 커맨드라인 매개 변수를 받고 그것을 StartupEventArgs 파라미터의 Args 프로퍼티를 통해 Startup 이벤트 핸들러에 전달합니다. 다음과 같은 코드를 사용하여 커맨드라인 매개 변수를 받아 저장할 수 있습니다. (※역주 : 다음 코드는 원문에서 XAML과 C#으로 나뉘어 있지 않고 XAML이라고만 표시되어 있는데 내용상 C#으로 분리해야 맞다고 생각됩니다. 또한 이후에도 두세번 이런 코드가 있는데 모두 XAML과 C#으로 분리하였습니다.)

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml"
    Startup="App_Startup" />
C#

using System;
using System.Collections;
using System.Text.RegularExpressions;
using System.Windows;

public partial class App : Application

{

    // Indexed command line args using hash table

    public static Hashtable CommandLineArgs = new Hashtable();


    void App_Startup(object sender, StartupEventArgs e)

    {

        // Don't bother if no command line args were passed

        // NOTE: e.Args is never null - if no command line args were passed,

        //       the length of e.Args is 0.

        if (e.Args.Length == 0) return;


        // Parse command line args for args in the following format:

        //   /argname:argvalue /argname:argvalue /argname:argvalue ...

        //

        // Note: This sample uses regular expressions to parse the command line arguments.

        // For regular expressions, see:

        // http://msdn.microsoft.com/library/en-us/cpgenref/html/cpconRegularExpressionsLanguageElements.asp

        string pattern = @"(?<argname>/\w+):(?<argvalue>\w+)";

        foreach (string arg in e.Args)

        {

        Match match = Regex.Match(arg, pattern);


        // If match not found, command line args are improperly formed.

        if (!match.Success) throw new ArgumentException("The command line arguments are improperly formed. Use /argname:argvalue.");


        // Store command line arg and value

        CommandLineArgs[match.Groups["argname"].Value] = match.Groups["argvalue"].Value;

    }

}

 

노트:
자세한 정보는 커맨드라인 매개 변수 처리하기 샘플을 참고합니다.

애플리케이션 활성화와 비활성화
애플리케이션의 라이프타임 동안, 사용자는 그것과 현재 실행중인 다른 애플리케이션 사이의 전환을 할 것입니다. 사용자가 다른 애플리케이션의 윈도 중 하나를 활성화하여 전환하는 시점에 현재 애플리케이션은 비활성화됩니다. 이 상황은 Deactivated를 처리하여 검출할 수 있습니다. 반대로, 사용자가 다시 애플리케이션의 윈도 중 하나를 선택하면, 애플리케이션은 활성화되고 따라서, Activated가 발생됩니다. ActivatedDeactivated 모두 다음과 같이 처리될 수 있습니다.

XAML
<Application
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="App"
  StartupUri="MainWindow.xaml"
  Activated="App_Activated"
  Deactivated="App_Deactivated" />
C#
using System;

using System.Windows;

public partial class App : Application

{

    bool isApplicationActive;


    void App_Activated(object sender, EventArgs e)

    {

        // Activated

        this.isApplicationActive = true;

    }


    void App_Deactivated(object sender, EventArgs e)

    {

        // Deactivated

        this.isApplicationActive = false;

    }

}

각 윈도 별로 활성화와 비활성화를 처리할 필요가 있다면, 전체 애플리케이션 대신, System.Windows.Window.ActivatedSystem.Windows.Window.Deactivated를 처리할 수 있습니다.

처리되지 않은 예외
애플리케이션이 실행되고 있을 때, 애플리케이션 코드는 예상치 못한 잠재적으로 처리되지 않은 예외를 던질 수 있습니다. 기본적으로 .NET Framework 3.0은 애플리케이션이 다운되기 전에 다음 그림과 같은 알림을 사용자에게 보여주는 것으로 처리되지 않은 예외에 응답합니다.

사용자 삽입 이미지

유저 경험의 관점에서, 애플리케이션이 다음 항목의 일부 혹은 전부를 수행하여 기본 동작을 피하는 것이 더 낫습니다.

  • 처리되지 않은 예외를 검출하기 위한 공통적인 메카니즘을 제공.
  • 사용자에게 더욱 설명적인 정보를 제공.
  • 애플리케이션이 계속 실행되도록 시도.
  • 일반적으로 윈도 이벤트 로그에 설명적인 정보를 로깅.

이 지원을 구현하는 것은 다음과 같이 DispatcherUnhandledException이 발생하여 처리되지 않은 예외를 검출할 수 있게 되는 것에 달려있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml"
    DispatcherUnhandledException="App_DispatcherUnhandledException" />
C#
public partial class App : Application

{

    void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)

    {

        // Process unhandled exception

        ...

        // Prevent default unhandled exception processing

        e.Handled = true;

    }

}

DispatcherUnhandledException 이벤트 핸들러는 예외 자신(System.Windows.Threading.DispatcherUnhandledExceptionEventArgs.Exception)을 포함하여 처리되지 않은 예외에 관한 컨텍스트 정보를 담고있는 DispatcherUnhandledExceptionEventArgs 파라미터로 전달됩니다. 이 정보를 사용하여 예외가 복구가능한지 아닌지를 결정할 수 있습니다. 복구 가능한 예외의 예를 들자면 FileNotFoundException이 있고, 복구 불가능한 예외의 예는 StackOverflowException가 있습니다.

언제든 DispatcherUnhandledExecption을 처리할 때는, 반드시 System.Windows.Threading.DispatcherUnhandledExceptionEventArgs.Handled 프로퍼티를 true(※역주 : MSDN에는 false로 나와있는데 문맥상 true가 맞다고 생각됩니다.)로 설정해야 합니다. 그렇지 않으면, WPF는 여전히 예외가 처리되지 않았다고 생각하여 기본 동작으로 애플리케이션을 종료합니다.

노트:
처리되지 않은 예외가 발생하였고 DispatcherUnhandledException 이벤트가 처리되지 않거나 이벤트가 Handledfalse로 설정하여 처리되었다면, 애플리케이션은 곧바로 종료될 것입니다. 게다가, Application에 의해 정의된 어떤 이벤트도 발생하지 않습니다. 따라서, 애플리케이션이 애플리케이션의 종료 시점에 실행할 필요가 있는 코드를 가졌다면 DispatcherUnhandledException을 처리할 필요가 있을 것입니다.

상세한 내용은 DispatcherUnhandledExecption 처리하는 것을 보여주는 Unhandled Application Exceptions Sample을 참고합니다.

애플리케이션 종료(Shutdown)
처리되지 않은 예외가 일어났을 때 예외가 여전히 처리되지 않았다면 애플리케이션이 종료될 것입니다. 애플리케이션은 아마도 다음과 같은 경우로 종료되는 경우가 더 많을 것입니다.

  • 사용자에 의해 모든 윈도가 닫혔을 때.
  • 사용자에 의해 메인 윈도가 닫혔을 때.
  • 사용자가 로그 오프나 윈도 종료로 윈도 세션을 끝낼 때.
  • 종료를 의미하는 조건을 만났을 때.

이 모든 경우에, Shutdown메소드가 호출됩니다. Shutdown이 코드에서 호출되는지 WPF에서 호출되는지는 Application 설정을 어떻게 하느냐에 달려있습니다.

종료(Shutdown) 모드
일반적으로, 독립 애플리케이션의 라이프타임은 그것이 보여주는 윈도의 라이프타임을 캡슐화 합니다. 애플리케이션의 종류에따라, 애플리케이션은 모든 윈도가 닫히거나 메인 윈도가 닫혔을 때 종료될 것입니다. 이 두가지 시나리오는 가장 일반적이기 때문에, ShutdownMode를 다음의 ShutdownMode 열거값 중 하나로 설정할 때 Application을 자동으로 종료하도록 설정할 수 있습니다.

예를 들어, 애플리케이션이 메인 윈도가 닫혔을 때 종료되도록 설정하고 싶다면 다음과 같이 하면 됩니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    ShutdownMode="OnMainWindowClose" />
노트:
메인 윈도는 System.Windows.Application.MainWindow 프로퍼티에 의해 참조되는 윈도입니다. 기본적으로 이 프로퍼티의 값은 인스턴스화 된 애플리케이션의 첫번째 윈도를 참조하지만 이 값은 나중에 프로그램에서 변경할 수 있습니다.

ShutdownMode를 설정하는 것은 언제, 누구에 의해 Shutdown이 호출되는지를 명시합니다. ShutdownMode가 OnLastWindowClose나 OnMainWindowClose로 설정한 경우, WPF는 이 열거값 설정에의해 지정된 조건이 특정되었을 때 자동으로 Shutdown 메소드를 호출합니다.

ShutdownModeOnExplicitShutdown으로 설정되었다면, Shutdown을 호출하는 것은 개발자의 책임이고, 그렇지 않을 경우 애플리케이션은 모든 윈도가 닫힌다고 해도 계속 실행될 것입니다.

세션 종료(Ending)
ShutdownMode를 세팅하는 것은 애플리케이션 종료(shutting down)을 위한 내부 메커니즘이며, 애플리케이션은 또한 다음과 같은 외부 조건에 의해 종료될 수 있습니다.
  • 사용자가 윈도를 로그 오프하여 세션이 종료될 때.
  • 사용자가 윈도를 종료하거나 재시작하거나 하이버네이션하여 세션이 종료될 때.

사용자가 세션을 종료할 때, 윈도는 현재 동작중인 각 애플리케이션을 위해 언제 그랬는지 검출하고, 필요하다면 세션이 종료되는 것을 막도록 기회를 줍니다. 사용자가 데이터를 편집할 수 있는 (워드 프로세서나 스프레드 시트와 같은)애플리케이션은 이 기능을 사용하기에 가장 적합합니다. 사용자는 실수로 저장하지 않은 문서를 닫도록 시도할 수도 있고, 예기치 못하게 애플리케이션에 저장하지 않은 문서를 둔 채로 윈도를 종료할 수도 있습니다. 따라서, 애플리케이션은 애플리케이션 데이터가 저장되었는지를 확인할 기회를 사용자에게 줄 수 있고, 아니면 사용자에게 윈도의 종료를 막을 기회를 줄 수 있습니다.

Application은 언제 윈도가 세션 종료 알림을 발생시키는지, SessionEnding 이벤트가 언제 일어났는지를 검출합니다. SessionEnding을 처리하여 다음과 같이 검출하고, 응답하고, 세션 종료를 취소할 수 있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    SessionEnding="App_SessionEnding" />
C#

public partial class App : Application

{

    void App_SessionEnding(object sender, SessionEndingCancelEventArgs e)

    {

        using (StreamWriter writer = File.AppendText("output.txt"))

        //using (FileStream stream = File.AppendText("output.txt"))

        //using (StreamWriter writer = new StreamWriter(stream))

        {

            writer.WriteLine("OnSessionEnding");

        }


        // Ask the user if they want the session to end

        string msg = "The application is shutting down for the following reason: " + e.ReasonSessionEnding + "\n\nShutdown?";

        string title = "An Application";

        MessageBoxButton buttons = MessageBoxButton.YesNo;

        MessageBoxImage icon = MessageBoxImage.Stop;

        MessageBoxResult result = MessageBox.Show(msg, title, buttons, icon);


        // If they don't, prevent both the session from ending and the

        // application from shutting down

        e.Cancel = (result == MessageBoxResult.No);

    }

}



SessionEnding 이벤트 핸들러에게 전달된 SessionEndingCancelEventArgs 파라미터는 ReasonSessionEnding 속성을 통해 세션 종료의 원인을 노출하고, 세션 종료를 취소할 수 있는 Cancel 속성을 구현합니다.

종료(Exit)
애플리케이션에 의해 - 처리되지 않은 예외나 사용자 세션 종료에 의한 종료가 아닌 - 종료(shutdown)가 시작되었다면 애플리케이션의 라이프타임은 끝이 날 것입니다. 종료하기 전, 애플리케이션은 애플리케이션의 상태를 보관하는 것과 같은 최종 처리 동작을 필요로 할 것입니다. 이 경우, Exit 이벤트를 처리할 수 있습니다.

XAML
<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="App"
    StartupUri="MainWindow.xaml"
    Startup="App_Startup"
    Exit="App_Exit" />


C#

using System;

using System.Windows;

using System.IO;

using System.IO.IsolatedStorage;

public partial class App : Application

{

    string filename = "App.txt";

    void App_Exit(object sender, ExitEventArgs e)

    {

        // Persist application-scope property to isolated storage

        IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForDomain();

        using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filename, FileMode.Create, storage))

        using (StreamWriter writer = new StreamWriter(stream))

        {

            // Persist each application-scope property individually

            foreach (string key in this.Properties.Keys)

            {

                writer.WriteLine("{0},{1}", key, this.Properties[key]);

            }

        }

    }

}


종료 코드(Exit Code)
애플리케이션은 사용자 요청에 응답한 OS에 의해 가장 많이 시작됩니다. 그러나, 애플리케이션은 어떤 특정한 작업을 수행하기 위하여 다른 애플리케이션으로부터 시작될 수 있습니다. 이 경우, 호출하는 쪽과 호출받은 애플리케이션은 분리된 프로세스에서 동작합니다. 이런 상황에서 호출한 애플리케이션의 실행은 호출받은 애플리케이션이 어떻게 종료되었냐에 달려있습니다. 이 상황에서, 호출받은 애플리케이션은 exit code로 알려진 특수한 정수 코드를 사용함으로서 어떻게 종료되었는지를 알리는 값을 반환할 수 있습니다. 기본적으로, Application0의 값을 반환할 것입니다. 이 값을 변경하기 위해, exit code가 될 정수 매개 변수를 받아들이도록 오버로드된 Shutdown을 호출할 수 있습니다.

C#
// Shutdown and return a custom exit code
Application.Current.Shutdown(-1);


Exit 이벤트를 처리하여 exit code의 값을 검출하고 변경할 수 있습니다. Exit 이벤트 핸들러는 ApplicationExitCode 속성으로 exit code에의 접근을 제공하는 ExitEventArgs를 전달합니다. 자세한 정보는 Exit를 참고합니다.

애플리케이션 라이프타임 이벤트
다음 그림은 애플리케이션의 라이프타임에서 중요한 이벤트와 그것이 발생하는 순서를 표현합니다.
사용자 삽입 이미지

다른 애플리케이션 서비스

이 토픽은 Application에 의해 나타나는 애플리케이션의 핵심 라이프타임을 다룹니다. 그러나, 애플리케이션은 다음을 포함하는 더욱 자세하게 논의될 또 다른 서비스를 제공합니다.
  • 애플리케이션과 윈도 관리.
  • 애플리케이션-스코프 속성.
  • 애플리케이션-스코프 리소스.
  • 애플리케이션과 내비게이션 관리.

애플리케이션과 윈도
ApplicationWindow는 가까운 관계에 있습니다. 앞서 살펴본 것처럼, 애플리케이션의 라이프타임은 ShutdownMode 속성으로 지정되는 윈도의 라이프타임에 달려있다고 할 수 있습니다. 애플리케이션은 어떤 윈도가 메인 애플리케이션 윈도(System.Windows.Application.MainWindow)로 지정되었는지 기록하고, 현재 인스턴스화 된 윈도(System.Windows.Application.Windows)의 목록을 관리합니다.

더욱 자세한 정보는 WPF Windows Overview를 참고합니다.

애플리케이션 스코프 속성
애플리케이션은 애플리케이션 영역을 넘어 공유할 수 있는 상태를 노출하기 위한 Properties 속성을 구현합니다. 다음은 Properties를 사용하는 예제를 제공합니다.

C#
  // Set an application-scope property with a custom type
  CustomType customType = new CustomType();
  Application.Current.Properties["CustomType"] = customType;
...
  // Get an application-scope property
  // NOTE: Need to convert since Application.Properties is a dictionary of System.Object
  CustomType customType = (CustomType)Application.Current.Properties["CustomType"];

자세한 정보는 다음을 참고합니다.

애플리케이션-스코프 리소스
애플리케이션은 개발자가 애플리케이션간 유저 인터페이스(UI) 리소스를 공유할 수 있게 하는 Resources 속성을 구현합니다. 다음은 Resources(※역주 : 원문에는 Properties라고 되어 있지만, 오류라고 생각됩니다.)를 사용하는 예제를 제공합니다.

C#
  // Set an application-scope resource
  Application.Current.Resources["ApplicationScopeResource"] = Brushes.White;
...
  // Get an application-scope resource Brush whiteBrush = (Brush)Application.Current.Resources["ApplicationScopeResource"];

자세한 정보는 다음을 참고합니다.

애플리케이션과 내비게이션
내비게이션을 가진 독립적인 애플리케이션을 위해, NavigationWindow과 Frame이나 Application이 검출하는 애플리케이션내의 내비게이션과 다음과 같은 적절한 이벤트를 사용합니다.

자세한 정보는 Navigation Overview를 참고합니다.

애플리케이션과 애플리케이션 데이터 파일
WPF 애플리케이션은 리소스 데이터 파일, 컨텐트 데이터 파일 및 원격 데이터 파일을 포함한 몇 가지 데이터 파일의 종류를 관리할 수 있습니다. 다음 헬퍼 메소드는 이 데이터 파일의 종류를 로드하는데 사용할 수 있습니다.

참고

레퍼런스
Application
컨셉
WPF Windows Overview
Navigation Overview
WPF Data Files

신고
Posted by gongdo
몇 달 전에 개짓을 가지고 놀면서 .NET Framework 3.0과 함께 소개된 WPF. 지금까지 소개되었던 MS의 어떤 기술보다도 직접적인 매력을 보여줬습니다.
바로 공부를 시작하고 싶었는데 근 4개월간 프로젝트에 치이는 바람에 손도 못대고 있다가 최근 약간의 짬이 나고 있어 마음을 다잡고 시작해보려고 합니다.

제일 먼저 시작하기 위한 준비가 있어야 겠죠?
WPF를 제대로 공부하기 위한 요소들은 다음과 같습니다.
  • OS : Windows XP SP2 이상
  • .NET Framework 3.0
  • Microsoft Visual Studio 2005
  • Visual Studio Extensions for WCF, WPF
  • Microsoft Windows SDK

먼저 OS에 .NET Framework 3.0을 설치해야 합니다. 안타깝지만 OS 지원은 XP SP2 이상만을 지원합니다. 즉, XP SP2, 2003 Server, VISTA를 지원하며 윈도 2000은 제외되었는데요, 시스템/서버 분야에서 Win2K의 유용성을 생각해보면 참 아쉬운 부분입니다.

유명한 '윈도의 최대의 적은 이전 버전의 윈도'란 얘기가 단지 농담으로 들리지 않는 것이 바로 이런 점 때문인 것 같네요.

사실 MS의 윈도 2000 배제는 진작부터 눈에 띄었습니다. 아시다시피 WIn2k와 XP SP1까지는 거의 차이가 없다고 해도 무방했는데요, Win32 Shell에서의 풍선툴팁이라던가 하는 자잘한 요소 부터 차별화를 주기 시작했고 정식 SP5도 거의 다 만들어놓고는 Service Rollup이라는 애매한 업데이트로 대체해버리더니 급기야 DirectX의 최신 버전도 2000을 더 이상 지원하지 않기로 했습니다.

이전 버전 윈도 사용자로서는 이런 점에 불만이 없을 수가 없는데요, 반대로 개발자의 입장으로 생각해보면 그간 이전 버전의 윈도를 지원하기 위한 소모적 작업에 치를 떨어왔던지라 이런 움직임을 비난할수만은 없습니다. 좋은게 좋은거죠-_-

얘기가 잠깐 샜는데요, 어쨌든 프레임웍이 설치되었으면 개발 툴을 준비해야겠죠. 기존에 Visual Studio 2005를 쓰고 계셨더라도 새 프레임웍 설치에 별 다른 문제 없이 이용이 가능하구요, Visual Studio가 없으신 분들도 Express 버전이나 평가 버전 등을 사용하실 수 있지만 역시나 불편해서 도저히 못해먹겠더군요. 어떻게든 설치하시는게 정신 건강상 좋은 것 같습니다.

VS 2005를 설치했으면 VS를 위한 WCF, WPF 확장 지원을 설치하셔야 합니다. 베타 시절에는 버전별로 머리 아팠는데 지금은 릴리즈된 버전을 사용하시면 별 문제 없이 설치할 수 있게 되었습니다.

WPF만을 위해서라면 별 관계 없긴 하지만 Windows SDK는 반드시 설치하시길 강권합니다!
SDK에는 XP, 2003, VISTA 각 OS에 해당하는 업데이트된 헤더와 API들이 포함되어 있거니와 SDK에 포함된 문서는 개발에 많은 도움이 되니까요.

위의 요소들은 모두 다음 링크에서 다운 받을 수 있습니다. http://msdn2.microsoft.com/en-us/windowsvista/aa904955.aspx
하지만 링크가 언제 바뀔지는 모르니까 찾는 방법 정도는 익혀두시는게 좋겠죠.

이제 환경은 갖췄고, 시작을 해야겠는데... 도무지 어디서부터 시작해야 할지 감이 안오는겁니다.
제 경우는 이럴 때 국내의 커뮤니티를 찾아가 봅니다. 국내에도 .NET과 관련된 커뮤니티는 상당히 많으며 그 중 가볼 만한 곳을 꼽아보자면...

  • http://hoonsbara.com ; 훈스닷넷으로 알려진 C# 위주의 .NET 커뮤니티로 최근 WPF와 WCF에 관한 강좌도 활발하게 올라오고 있습니다.
  • http://vismuri.com/Blog.aspx ; 본격적으로 VISTA를 준비하는 블로그로 역시 .NET Framework 3.0에 관한 아티클과 동영상(!)이 포스팅되고 있습니다.
  • http://www.mhvb.net/ ; 비베따위를! 이라는 자조적인 타이틀을 달고 있는 VB.NET 커뮤니티입니다. 저도 VB6를 주로 사용해왔고 나름 VB에 애정(?)을 담고 있는데 타이틀처럼 '비베따위!'란 얘길 많이 들어와서 안습이죠. 어쩌면 .NET에서 가장 큰 혜택을 입은게 Visual Basic 이란 언어인데, VB6에 비해 상당히 많은 변화가 있어서 VB6 유저들에게 외면을 받게된 슬픈 사연이...

이 외에도 많은 커뮤니티 들이 있고 Devpia나 gosu.net 등에서는 방대한 Q&A 및 아티클을 얻을 수 있습니다.

커뮤니티들을 돌아보면서 어느 정도 감을 잡았지만 사실 초창기의 커뮤니티 강좌나 Q&A만으로는 상세한 내용을 접하기가 어려운 면이 있습니다.

기술은 외국에서 왔고 결국은 외국 사이트를 검색, 또 검색해야 좀 더 폭넓은 정보를 얻을 수 있는데요, 그 중에서도 가장 먼저 갈 곳은 역시 MSDN입니다. MSDN에는 Getting start, Overview와 같은 아티클에서 전반적인 개념을 확실히 잡을 수 있게 하고 또한 관련된 사이트로 연결되는 통로가 되기도 하지요.

그 외에 .NET과 관련된 사이트로는

이 외에도 수많은 개인 블로그와 커뮤니티가 존재하구요, 필요할 정보는 먼저 구글링 하시는 센스~가 필요하죠 :)


저는 영어를 지질이도 못하지만, 관심있는 MSDN 토픽을 번역해가면서 공부해나갈 예정이고 그 결과물을 꾸준히 포스팅할 계획입니다.
미숙한 실력으로 번역을 해놔서 문장이 상당히 어색하네요. 혹시 전혀 다른 의미로 번역되거나 일반적으로 통용되는 용어의 어색한 번역을 지적해주시면 앞으로의 진행에 정말 큰 도움이 될 것 같습니다.

마지막으로, 우리나라는 유독 개발자들의 블로그 활성화가 안되는 것 같습니다. .NET이나 WPF를 구글링해보면 수많은 해외 블로그가 잡히고 상당히 활발한 논의가 이루어지는데 국내에는 몇몇 커뮤니티를 제외하면 소개에 그치는 경우가 많은 것 같네요. 저 부터가 생업을 핑계로 칙칙하게 살고 있지만 .NET에 관해 많은 교류가 생기길 기대해봅니다. :)

신고
Posted by gongdo

대세는 블로그!
MS의 Windows SDK 툴 및 개발 환경에 관한 블로그에서 필요한 다운로드를 쉽게 찾을 수 있습니다.
여기가 메인 블로그 : Tom Archer's Blog
운영체제별, 릴리즈 버전별 다운로드 위치 매트릭스 : http://blogs.msdn.com/tomarcher/archive/2006/07/17/668572.aspx#VistaSeptCtp

운영체제별, 버전별로 어떤걸 함께 받아야 하는지 잘 나와있어서 좋습니다. ^^

신고
Posted by gongdo
MSDN .Net Framework 3.0 세미나에 다녀왔습니다.
전반기 DevDays에 참석하지 못하기도 했고 .Net과 .Net 2.0에 실망하고 관심이 없었는데 갔다오길 정말 잘했다는 생각이 들었습니다.

열심히 준비해주신 세션1 WCF의 HOONS님께는 참 죄송한 말이지만 세미나를 한다면 좀 더 안정되고 전문적인 진행을 해주셔야 할 것 같습니다.
일반 애플리케이션 개발자로서 WCF도 상당히 관심이 가는 부분이었는데 뭐랄까 굳이 발표자의 설명이 없어도 프리젠테이션과 데모 코드만으로 이해가 갈만한 내용이었습니다.
그러니까 프리젠테이션을 단지 똑같이 말하는 건 별 의미가 없었다는 것이지요.

WCF도 기술적인 편의성이 돋보이고 참으로 MS스러운 네이밍 센스와 통합은 괜찮아 보였습니다. 하지만 매우 짧은 트랜잭션을 요구하는 통신일 경우에 과연 이렇게 복잡한 과정을 거치는 코드가 충분한 성능을 발휘할지... 확신이 안서더군요.
C#의 컴파일 능력은 제가 직접 써보지 않아서 뭐라 말할 수 없으니 그냥 그런가보다 정도로 넘어갔습니다.

사실 세션1 끝나고 나갈려고 했는데 눈앞에 어른거리는 경품을 차마 뿌리치지 못하고 세션2 WPF를 맞았습니다. 와! 정말 포스가 느껴지는 발표!! 내공이란 이런걸 말하는 구나... 싶을 정도로 감탄스러웠습니다.
김영욱님의 발표도 멋졌지만 WPF가 지향하는 방향도 상당히 신선했습니다.


개짓을 가지고 놀아보니 웹 브라우저의 iframe 태그 내에서 로딩되는 페이지는 구동 효율이 훨씬 더 떨어진다는 걸 알게 되었습니다. Gamelib라는 괴물같은 JavaScript 라이브러리가 있는데요, 이걸로 만든 스프라이트 기반의 간단한 게임을 브라우저에 올려보면 그럭저럭 잘 구동됩니다. 하지만 개짓을 위하여 iframe에 올려보면 부하량이 대폭 증가하여 뚝뚝 끊기게 되더군요.

결국, 개짓에서의 화려한 인터랙티브 UI의 구현은 무리라고 생각됩니다.
아마 이런데에서 부딪친 웹 개발자라면 플래쉬에서 답을 찾았을거라고 봅니다.
개짓 갤러리에 가보면 화려한 사용자 반응을 보여주는 대부분의 개짓이 과연 플래쉬를 올려놓은 것 뿐이더군요.

하지만, 늘상 부족한 시간에 쫒기는 개발자가 플래쉬의 액션 스크립트까지 배워야 할까요.

그에 대한 해법을 WPF가 제시하고 있었습니다.
가장 마음에 들었던 부분은 바로 디자인 영역과 코드 영역의 명백한 분리였습니다.
디자이너는 디자이너의 의도가 충분히 반영된 UI를 마치 플래쉬 무비를 만들 듯이 만들고 이 데이터를 XAML로 저장하여 프로그래머에게 넘기면 프로그래머는 디자이너의 의도를 전혀 해치지 않고 객체화된 디자인 요소를 이용할 수 있는 체계이죠.

다음으로는 UI의 벡터화.
기본적인 디자인이 벡터에 기반하므로 UI의 확대/축소와 레이아웃을 자연스럽게 구현할 수 있습니다.
특히 데모에서 보여준 스플리터 컨트롤을 보면서 그간 .Net도 아니고 VC++6과 VB6에서 스플리터 구현하기 위해 지새웠던 날들에 눈물이 날 지경이었습니다.

잊어버리기 전에 한번 테스트라도 해보자! 라는 마음으로 열심히 다운받고 툴들을 돌려봤습니다. 아직 CTP 버전이라고 해도 이정도로 완성도 있는 프로그램을 그냥 받을 수 있다니 참 세상 좋아졌죠;;

MS Expression Graphic Designer는 포토샵과 페인터를 사용하여 그림을 그리는 지인에게 테스트를 부탁해봤습니다.
생각보다 완성도가 상당히 높았습니다! 무엇보다 브러쉬의 드로잉 감이 상당히 부드럽더군요.(타블렛을 사용하고 있습니다.)
아직 초기 버전인데 이 정도의 품질이라면 앞으로 발매될 서드파티의 툴이나 애드온들도 기대됩니다.

다음으로 MS Expression Interactive Designer는 꽤나 재밌는 아이디어 들이 곳곳에 눈에 보여서 좋았습니다. 게다가 이 툴만으로도 애플리케이션의 UI를 작성하여 구동시켜 볼 수 있다는 건 엄청난 감동이었습니다.

내친 김에 테스트도 할 겸 블로그 레이아웃을 UI로 만들어 봤습니다.

여기서 사이즈 조절을 해보면 레이아웃이 깨지지 않고 그대로 따라갑니다.

아... 감동의 도가니탕이네요. T^T
이젠 진정으로 레이아웃이 살아있는 UI를 부담 없이 개발 할 수 있겠습니다.


어쨌든, 이 WPF는 적어도 윈도 플랫폼 상의 UI 혁명을 가져다 줄 것은 분명해 보입니다.
아쉬운 점은 이 모든 것은 .Net Framework 3.0 런타임에서 구동되므로 대다수의 유저가 Vista를 쓰게되는 시점까진 제대로된 배포를 하기가 매우 곤란하다는 점입니다.
이 문제는 XP SP2가 나올 때도 .Net 프레임웍이 기본으로 설치되지 않아 결국 .Net으로의 강제 이전을 포기했던 기억을 되살려 주네요.
그나마 Vista에는 기본 장착되니 다행이라고 해야 할까요...

또 개짓과의 연계가 아직 이루어지지 않고 있는 것 같습니다.
개짓 갤러리에 가보면 WPF를 활용한 데모도 올라오고 있지만 아직 SDK 차원에서의 통합은 멀어 보입니다. 개짓의 효용성에 기대를 걸고있는 제겐 좀 많이 아쉬운 부분이네요.

마지막으로 걸리는 부분은 Microsoft는 Vista를 계기로 또다시 자신의 '제국'을 쌓으려는 것 처럼 보입니다.
세미나에서 소개된 기술과 툴들은 Adobe와의 첨예한 대립을 피할 수 없는 것들이고 모든 플랫폼에서 짝짝꿍을 맞추고 있는 Adobe의 다른 툴들과는 달리 MS는 또다시 .Net이라는 고립된 플랫폼에서 외롭고 소모적인 전투를 벌일 것이 훤히 보입니다.

왜 그 MS 제국과 자유 연합과의 전쟁을 그린 시리즈 있잖습니까?
중앙에 빌 형님의 MS Empire가 있고 주변에 Linux며 Google이며 자유 재단이며 이런게 포진해서 전투를 벌리는 그림...
여기에 Adobe와의 전투도 추가되겠지요.

저는 .Net이 지금의 Java 플랫폼과 같은 위치를 차지하기를 바랬는데, 뭐 미천한 제가 생각하는게 무슨 의미가 있겠습니까. MS에서도 아득한 차원의 심사숙고 끝에 나온 결론이겠지요.

그래도 결국 윈도와 MS의 개발툴을 버릴 수 없었던 저는 .Net Framework가 그간의 미진함을 떨치고 하다 못해 윈도라는 틀 안에서라도 표준 플랫폼의 면모를 갖췄으면 합니다.

빌 형님, 우리 이제 좀 편하게 살자구요 네?
신고
Posted by gongdo


티스토리 툴바