티스토리 툴바



 쇼티입니다. 간단하게 버전체크하는 방법을 알려드리려고 해요.

 Silverlight.js라는 파일이 기본 샘플프로젝트에 포함이 되어있죠. 이 js 파일이 사실 굉장히 유용한 함수들이 많지요.
 (http://msdn.microsoft.com/ko-kr/library/cc838126(VS.95).aspx)

 물론 버전관리하는 함수도 있습니다. 바로 isInstalled 라는 함수입니다.

 
 (bool) = Silverlight.isInstalled(버전번호)


 이렇게 들어가구요. 이 버전 번호는 각각, 

 버전 MIME TYPE  버전 번호  설치관리자 URL 
 Silverlight 1.0 application/x-silverlight  1.0  
 Silverlight 2.0 application/x-silverlight-2, 2.0.31005  
 Silverlight 3.0 application/x-silverlight-2, 3.0.40620   
  (설치관리자 URL : http://go.microsoft.com/fwlink/?LinkID=149156 공용 실버라이트 3 플러그인 링크)

 이렇습니다. 그래서, isInstalled('2.0'), isInstalled('3.0') 이라고 넣어주는것이 아니라,
 isInstalled('2.0.31005'), isInstalled('3.0.40620') 으로 확실히 넣어주어여 bool 값이 제대로 찍힙니다.

 그리고 버전에 관계없이 실버라이트 설치 여부는 isInstalled(null) 로 식별이 가능하니 이것도 참고해주시구요.

 그리고 MIME TYPE은 <object> 태그에서의 마임입니다. 절대 iis의 마임타입이 아닙니다. 
 iis의 마임타입은 'application/x-silverlight-app' 입니다. 

 혹시 실버라이트 배포하실때 2104 에러가 났다면 십중팔구 마임타입 설정 안되있는 것입니다. 해당 xap가 들어있는 디렉토리
 에 MIME 타입을 걸어주세요.
 
 참고되시길 바랍니다. ^^
 

이번에 Silverlight 3에서 Out Of Browser라는 것이 생겼다.
브라우저 밖에서. 즉, 실버라이트 어플리케이션이 웹을 거치지 않는 환경에서 실행이 된다는 내용인데.

오늘 실버라이트 코리아 포럼에 답글을 달다가 문득 느꼈다.
'그러면 Out of Browser는 웹이 안들어오는 환경에서 실행이 가능한가?'

... 곰곰히 생각해봤는데. 이게 안되는거다.
흔히 Out Of Browser를 소개할 때, '웹이 안되는 환경에서도 실버라이트 어플리케이션을 구동할 수 있습니다' 라고들 한다.

이 말이 틀린 말은 아니다. 일단 실버라이트를 인스톨을 하고 나면 웹이 안들어와도 실행이 되게끔 로직을 짜면 되니까.
그런데. 아이러니하게도 '최초 한번은 실버라이트 웹페이지에 랜선을 연결하고 접속을 해야된다'는 이야기가 되지 않는가?

물론 아는 분들은 '그걸 염두에 두고' 프로세스를 진행하니까 별 문제 없지만,
고객에게 자사의 웹페이지를 Out Of Browser 기능을 사용하여 꾸미고, '웹이 들어오지 않는 환경에서도 접속이 가능합니다'
라고 물으면.. 분명히 나중에 클레임이 들어온다는 이야기다. '장난하는거냐'고.

필자 입장에서도 기가 막힐꺼다. 아니 '웹이 들어오지 않는 환경'이라고 해놓고, 일단 '접속해서 다운을 받아야 한다'니.
머라 할 수도 없다. 그 문장 해석의 차이가 되버리니까.

그러면, 한번만 랜선을 연결해서 다운받고 그뒤부터는 랜선을 뽑고 작업을 해야된다는건가?

좀 더 생각을 해봐야한다. 또, 인터넷이 불가능한 지역에서 뭘 얼마나 더 할 수 있을까도 생각해봐야 한다.

Out Of Browser라는 것은 편의성 측면에서는 분명 좋은 기능임에는 틀림이 없지만.
요즘 회사에 랜선이 안들어오는 회사가 그렇게 많이 존재할까? 그렇게 Out Of Browser가 대단한 기능인지는
의문이 앞선다.

생각을 해보자. 고객 입장에서 '인스톨을 해놓고 그 실버라이트 어플리케이션을 바탕화면 바로가기' 해두는 것과
'실버라이트 웹페이지 즐겨찾기'를 해두는 것. 과연 차이가 크게 날까.

그리고, 고객에게 '랜선이 들어오지 않는 환경'으로 실버라이트 작업을 한다 하면, 과연 활용범위가 넓을까?
개인이 그냥 '우와 이쁘다~' 하고 그 실버라이트 어플리케이션을 계속 실행시킬까.

모르긴 몰라도 회사 그룹웨어나, 기타 기업웹페이지같이, DB를 중점적으로 사용하는 업체에서는
도대체 랜선이 들어오지 않는 실버라이트 어플리케이션으로. 무엇을 할 수 있을까.
9월 16일 세미나 준비를 하는 중에, Local Connection이.. 좋긴 한데 확장성이 그닥 좋지 않은것을 알았습니다.


몇가지 실험 결과를 알려드립니다.

(1) 대소문자 구별.

 - 즉, Sender가 "ShortyLuv"였을 때, Receiver가 "shortyLuv"라면 전달루틴이 발생하지 않습니다.
   이건 예상 가능했기 때문에 그렇다 치죠.


(2) 2개 이상 등록 불가능

 - 가장 황당했던 에러입니다.


  그러니까, Sender가 1개, Receiver가 2개라고 했을 경우에 이 Receiver 2개는 각각 다른 식별키를 사용해야 한다는
  이야기죠. 즉 Sender에서도 LocalMessageSender를 2개를 등록해야 한다는 이야기가 나옵니다.

  ... 이럼... 이게 몹니까.. 그냥 Silverlight -> JavaScript -> Silverlight 와 다른 것이 없어보입니다.
  그것보다 1:1만 지원이 된다는 것이 좀 아쉽네요.
 

(3) HtmlPage 클래스의 함수 사용에 제약이 있는 듯 보입니다.

 

  글쎄요.. 혹시 LocalConnection 자체가 HtmlDom을 읽어들이지 못하는건가 생각해봤는데. 생성자에 Alert를 붙여보니
  그건 또 잘 동작하더군요. HtmlDom을 사용하는 루틴은 사용하지 못하는 듯 보입니다.

  다른 변수로 바꿔서 실행해봐도 그렇고, 따로 함수로 빼서 실행해봐도 결과는 같습니다.

  ... 왜이럴까요..


결론은 LocalConnection은 'XAP이 2개일때만 쓰자' 라는 것이 되겠네요.. 구글링을 해봐도
아직까지 Silverlight 3에 대한 이슈는 활성화되어있지 않은 모양입니다..

mms. 말 그대로 Windows Media Server를 이용하여 스트리밍으로 뿌려주는 프로토콜이지요.

이 mms를 실버라이트에서 쓰려고 할때, 잘 안되는 경우가 많습니다.


분명 웹페이지에 그냥 mms://어쩌고.. 쓰면 잘 나오던 것이 왜 실버라이트로 가면 안되는가..

구글링 끝에 해결이 되었습니다. (제 경우라 다양한 돌발상황에 대해서는 잘 모릅니다;;)


일단 보통 서버에는 IIS가 올라가있을 것입니다. 이게 포트 80번을 씁니다.

그런데 미디어서버를 설치하면. 이거 역시 포트 80번을 씁니다. (WMS HTTP Protocol)

실버라이트에서는 포트번호를 명시하지 않고 바로 MediaElement의 Source에 mms://어쩌고.. 를 바로 써주면

이 포트 80번의 충돌때문에 미디어가 동작하지 않는 것이지요..

즉.


이 WMS HTTP 서버 제어 프로토콜이 기본적으로 포트 80을 사용한다는 데에 문제가 있었습니다.

(그리고 아마 '사용 안함'으로 되어있을 것입니다. 그래서 포트를 설정해주고 '사용' 해주어야 합니다)


이 아이템을 클릭하면 바로 속성창이 나옵니다.


그래서 다른 포트 사용을 하셔서.. 사용하지 않는 포트 숫자를 적어줍니다. 서비스를 다시 실행시키고 나서

기존의 소스를 적용해봅니다.

     this.me_played.Source = new Uri("mms://xxxxxx:7870/xxxxxx.wmv", UriKind.Absolute);
     this.me_played.Play();        


그럼 적용이 잘 될 것입니다.

주의할 점은, 반드시 포트번호를 명시해주어야 한다는점. 그리고 이 경우의 7870이 방화벽에 막혀있음 안될 가능성도 있으니

방화벽 포트 예외 처리를 해주어야 한다는 점 등이 있습니다.


감사합니다.
  

쇼티예요. 이번 릴리즈 된 실버라이트 3에는 Blend에 Sketch Flow라는 기능이 추가가 되었습니다.

이게 무엇이냐 하면, 기업에서 쓰는 워크플로우를 실버라이트 버전으로 구성한 것이라고 할 수 있겠습니다.
(자세히는 모르겠는데, 일반적으로 이걸로 서비스를 할 수는 없어보이는군요)

일단 무엇인지를 알아보기 위해서 튜토리얼을 간단하게 해보겠습니다.
(출처 : 튜토리얼 참고는 이곳에서 했습니다)

일단 blend 3에서 프로젝트를 새로 만드시려 하시면 다음과 같은 메뉴를 볼 수 있습니다.


WPF로도 SketchFlow 버전이 있습니다만. 실버라이트 3를 선택해보도록 하겠습니다.
만드시면.. 일단 프로젝트 내용을 보면.


Fonts는, 스케치플로우에 필요한 폰트들이 들어가있는 곳입니다. 이건, 스케치플로우 전용 컨트롤들이 여기에 걸려있는듯
보입니다. 새로 추가할 수 있는지는 모르겠습니다.
Screen 1은, 가장 맨 처음에 띄울 장면, Sketch.Flow는 이 스크린들이 어떤 루트로 가는지를 설정해둔 파일이구요.
SketchStyles.xaml은, 이 스케치플로우를 돌릴때 필요한 스타일들을 정의해둔 곳입니다.


그니까 이런 식으로 사용이 가능합니다. 기획자가 어떤 프로젝트에 대한 기획을 한다고 해봅시다.
그리고, 맨 처음에 PPT로 제안서를 제출하죠. 이때 이 제안서를 만드는 과정이라 보시면 됩니다.

홈페이지를 예로 들면, '홈', '회사 소개', '제품 소개', '컨택트 어스' 이런 식으로 메뉴가 쭉 있습니다.
그리고, '회사소개'에는 '연혁', 'CEO 인사말', '조직구성' 등등이 들어가겠지요. 이렇게 구성을 하는것입니다.

위 표를 보시면, 맨 처음에 Start입니다. 여기 다음은 WelCome이고, 그다음에 그 부속으로 Customers, Products, Orders가
있고, 이 다음단계는 공통으로 CheckCustomerReport가 있고, 그 다음으로 CheckOut이 있습니다.

그래서 흔히 기업에서 이야기하는 '워크플로우'를 구성하는 것이 목적입니다.

워크플로우를 구성하면서 가장 중요한것은 뭐가 있을까요. 'FeedBack'이 제일 중요할 것입니다.
이 제안서를 나만 볼려고 만드는게 아니라, 여러사람에게 보여주고 '뭐가 잘못됐다' '이건 이렇게 했으면 좋겠다' 라고,
자문을 구해서, 그걸 수정해서 최종적으로 OK 사인을 받고 일정을 잡고... 이런 식이죠.


일단 밑에 Screen 1이라는 것이 보일 것입니다. 이것의 이름을 Start로 바꾸고, 여기에 마우스를 대면 밑에 메뉴가 뜨는데,
여기서 create a connected screen을 누릅니다.

한번 해보시면 대충 방식을 아실 듯 보입니다. 이렇게 해서, 이 전의 그림처럼 플로우를 구성해줍니다.
(보시면, 이 스크린 하나 만들때마다 xaml은 계속 생기는 것을 볼 수 있지요)

여기까지 누르시고 f5로 빌드 + 실행을 해보시면 이와 같이 나옵니다.


기본 스케치플로우의 아웃풋입니다. 좌측의 Navigate는, 이 상태에서 현재 갈 수 있는 곳을 쭉 표시해줍니다.
지금 현재는 Start의 화면이기때문에, 나오는 것이 Welcome밖에 없겠지만 아마 Welcome을 선택한 후에는
예의 3가지가 뜨겠지요.

그리고 프로그레시브 바로 이 배율을 지정해줄 수 있고.. 밑에는 피드백을 위한 도구들이 있습니다.

이정도입니다. 이제부터는 여러분들이 화면을 구성하시고, 디자인하셔서 이것을 서버에 올려놓고 피드백을 다른 분들
에게 받으시면 됩니다.

중간에 한가지 팁이 나오는데요. 만약 한 화면에 중복된 컴포넌트를 쓰고 싶다라고 하시면, 이걸 따로 빼는 방법이 있어요.


이렇게, 자주 쓰이겠다 싶은 구성물들을 그룹으로 묶어주고 오른쪽 버튼으로 Make into Component Screen을 누르면,
플로우 맵에 다음과 같이 나타나네요.


그래서, 다음과 같이 이 컴포넌트가 필요한 스크린에 전부 걸 수 있습니다.


그다음에 스테이트를 줄 수 있고, 네비게이트를 줄 수 있습니다.

그리고, 이제 이것을 서버에 올려놓으면 피드백을 받을 수 있는데요. 피드백 주는 방법은 간단합니다.
그냥 그리고 저장해두면 됩니다.


필요한 부분을 표시를 하고, 왼쪽 아래와 같이 브리핑을 할 수도 있습니다.

그래서 브리핑부분 위에 '폴더' 모양 비스무리하게 되어있는 곳을 클릭하면 'Export'를 선택할 수 있는데,
여기서 이름과, 파일로 저장이 가능합니다.

이렇게 되면, 블랜드로 열었을 때, 피드백 탭을 선택하여 상대가 피드백을 지정한 부분이 보이게 되는거죠.


이것이 대강의 스케치플로우의 흐름입니다만. 글쎄요.
스타일러스(전자펜 같은거)가 지원이 되는지는 잘 모르겠습니다만, 그닥 고객들이 선뜻 쓰려 하지 않을꺼같다는 느낌이
드네요. 왠지 '기획'이라는걸 하면서도 프로그래밍을 해야된다는 느낌이랄까요.

이 기능은 아마도 개발자보다는 PM이나, 그쪽에서 많이 쓸 듯 싶은데, 좀 많이 복잡하네요.
블렌드에서 스케치플로우를 쓰는건 무리수가 아니었나 하는 생각이 듭니다.

그리고 원터치로 서버 변수를 지정해주면 자동으로 올릴 수 있는 기능이.. 혹시 있는지는 모르겠습니다만.
이거 바꾸고 계속 서버(IIS 등)로 옮겨줘야 한다는 것이 또한 애로사항일 지도 모르겠습니다.

취지는 좋았지만, 좀 더 '보여주기' 식이 아닌, '주 고객층이 누구인지를 생각하는' 모델이 되었으면 하는
아쉬움이 있습니다.

그냥 'PPT'로 그리고 말죠..


쇼티예요.

http://silverlight.net 에 7월 7일부로 업데이트 가능하게 되었더군요. 예의 Get Started 메뉴에서



최소한으로는 저 빨갛게 네모를 친 것만 다운로드 하셔서 설치하시면 됩니다.

또, Web Platform 이라고 해서, 이런 류의 프로그램을 개발하는데 필요한 플랫폼을 다운로드 받을 수 있습니다.
(정확히 말하면 이 안에 Silverlight 3 Tools가 포함되어있습니다)
별건 없기때문에, 그냥 실버라이트만 개발하려고 이 페이지에 들어오신 분들에게는 필요 없습니다.

아래의 DeepZoom과 Silverlight Toolkit은 취향에 따라 받으시면 됩니다.

그리고 .NET RIA Service도 June 버전으로 업데이트되었습니다.
자세한건 한번 해보고 나서 알려드려야겠습니다.. (아직, 기존 강좌도 다 못써가지고;;)

http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=76bb3a07-3846-4564-b0c3-27972bcaabce
이건 닷넷 리아서비스 June버전 링크입니다.



이제 메타데이터에 대한 이야기를 해보려 합니다. 3장에서 잠시 메타데이터에 대해서 알아보았었는데,
이번 8장과 9장은 이 메타데이터를 다루는 방법에 대해서 자세히 알아보겠습니다.

Entity가 Partial Class라는 점을 이용합니다. 분리형 클래스로 하나는 엔티티, 하나는 메타데이터.. 이렇게 서로 합쳐서
속성을 가미한 클래스가 탄생(?)을 하는거죠.

메타데이터는 왜 사용을 할까요. 이 컬럼의 속성을 따로 관리하는 클래스를 서버단에서 만드는데 그 이유가 있지요.
Generated Code에서 알맞은 속성을 부여하다가, 리뉴얼되서 그 속성이 망가지는 경우도 있을 수 있겠고..
따로 속성을 관리하는 클래스를 만든다는 측면에서. 유지보수도 쉽겠죠.

이 장에서는 어떻게 만드는지. 만 간략하게 보고, 다음장에서 이것을 이용하여 실제로 유효성 체크하는 법을 알아보도록
하겠습니다.


[ 준비 사항 ]

1. 일단 ADO.NET Entity Framework 같은, 엔티티가 하나 있어야죠 (대상이 있어야 그 대상의 속성도 만들테니까..)
2. 메타데이터로까지 확장시켜야 할 엔티티클래스와 메타데이터 클래스는, Partial class여야 합니다.
   (엔티티와 메타데이터를 합쳐서 Generated Code를 만든다는거죠)

두가지가 있습니다. 하나는 자동으로 생성되는거, 하나는 일일이 다 치는거(-_-).
이와 비슷한걸 3장에서 해봤기때문에, 그냥 눈으로 보셔도 됩니다.

[ 방법 / 자동 ]

1. 웹 프로젝트에 Domain Service Class를 추가합니다.


2. 밑에 Generate associated classes for metadata 를 체크합니다.
   (이때 미리 생성해둔 Entity가 있어야합니다)



다 됐습니다. (...) 하나는 DomainService Class이고, 하나는 Metadata Class입니다.

[ 방법 / 수동 ]

이번엔 수동으로 하는 방법을 알아보겠습니다. 가령 엔티티가 이렇다고 해보죠.
(이 엔티티는 물론 미리 생성되어있어야 합니다)


1. 일단 "ProductMetadata.cs"라는 파일를 만듭니다.

2. 이 파일 안에 ProductMetadata라는 클래스를 만듭니다.

3. 그다음에 Product "Partial" class를 만듭니다. 엔티티와 같은 이름의 부분클래스를 만드는 것이지요. 
   엔티티 클래스와 비교해보시면서 만드세요. 네임스페이스, 엔티티 클래스 네임. 모든것이 같아야 합니다.

4. 이 Product partial class 속성을 MetadataType이라고 하세요. 그리고 2번에 만들어두었던 클래스를 typeof로
   넘기세요.

   [MetadataType(typeof(ProductMetadata))]

   이런 느낌으로 하시면 됩니다. 즉, 메타데이터 타입을 설정하고, 메타데이타로 사용할 클래스를 넘긴다는 뜻입니다.

5. 이제 이 ProductMetadata 클래스 안에 여러분들이 필요한 컬럼의 유효성이나, 속성을 설정하시면 되겠습니다. 이
   Product 엔티티에 보면, ProductName이라는 속성이 있는데, 여기에 속성을 줘보도록 하죠.
   (스샷에서는 Name에 줬습니다만.. 뭐 상관없습니다.)


이런 느낌으로 하시면 됩니다.

namespace NorthwindExample

{

    using System;

    using System.ComponentModel.DataAnnotations;

    using System.Data;

 

    // The MetadataTypeAttribute allows us to associated the

    // ProductMetadata type with our Product entity type.

    [MetadataType(typeof(ProductMetadata))]

    public partial class Product { }

 

    // This class is used to provide additional metadata.

    public partial class ProductMetadata

    {

        // Adding additional validation metadata to the

        // Product.ProductName property by placing it here.

        [Required]

        [RegularExpression("[A-Z][A-Za-z0-9]*")]

        [StringLength(32)]

        public string ProductName;

    }

} 

둘다 해보시면 아시겠지만, 자동으로 하면 현재 이 Product의 필드가 전부 기록이 됩니다. 그래서 필요한것들을 골라서
속성을 지정하고 나머지는 지우고.. 그러셔도 되죠.

사실 익숙해지시면 그냥 새로 하나 만드는 방법이 나을꺼같아보여요. 생각보다 코딩해야될 내용이 별로 없습니다 ^^


지금까지, 간단하게 엔티티 속성에 메타데이터를 삽입하는 방법에 대해 알아보았습니다.
다음 포스트에서는 이 메타데이터로 실제로 어떻게 유효성을 체크하는지를, 예제와 함께 알아보겠습니다.

감사합니다. ^^


쇼티예요.

바로 전 포스트에서는 리스트뷰에 아이템을 삽입하는 법을 알아보았었습니다. 그럼 이번엔
삭제하는 방법을 알아보도록 하겠습니다.

거두절미하고 일단 예제~!

 <slt:TreeView.ItemTemplate>
    <slt:HierarchicalDataTemplate ItemsSource="{Binding SubTopics}">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
            <Button ToolTipService.ToolTip="Delete Topic" Margin="2,0,0,0"
                    input:CommandService.Command="DeleteTopic"
                    input:CommandService.CommandParameter="{Binding}">
                <Image Source="/EditableTreeView;component/Resources/delete.png" Stretch="None" />
            </Button>
        </StackPanel>
    </slt:HierarchicalDataTemplate>
</slt:TreeView.ItemTemplate>

1장이었죠? 거기서 썼던 예제를 이렇게 변형해보았습니다.
보시면 일반 트리뷰에 아이템 템플릿, 즉 이 '리스트뷰'에 나타나는 객체는 TextBlock과 Button입니다.
쉽게 말해서, 이렇게 만들겠다는 이야기예요.


저 - 표시가 있는 버튼을 눌렀을 경우, DeleteTopic이라는 커맨드가 실행이 되게끔 하는 것입니다.
그리고, 비하인드 코딩은 이렇게.. 여전히 PageViewModel.cs에서 코딩을 해주시면 됩니다.

 private void OnDeleteTopicExecuted(object sender, ExecutedEventArgs e)
{
    HelpTopic topic = e.Parameter as HelpTopic;

    bool isRemoved = topic.RemoveFromStructure(HelpTopics, item => { return item.SubTopics; });
    if (!isRemoved)
    {
        throw new InvalidOperationException(string.Format("Topic '{0}' couldn't be removed.", topic.Name));
    }
}

2장에서 하신대로 똑같이, 이벤트 핸들러 함수를 잡아주시고, 이렇게 코딩하시면 됩니다.
e.Parameter에서, 부모값(그 단락의 최상위 제목)을 참조하여 제거하는 것이지요.
(소스에 보시면 LinqExtensions.cs라는 파일에 RemoveFromStructure라는 함수가 정의되어있습니다. 이 분이 편의를 위해서
 만든 것으로 보이네요.. ^^)

이제 제거가 완료되었습니다. 그런데, 살짝.. 이게 하나하나 다 - 표시가 보이는건 좀 지저분해 보이죠.
이것을 딱 '선택한' 부분에만 나타날 수 있도록 하려면 어떻게 해야 할까요..

전체 소스에서는 App.xaml에서 이것들을 정의하고 있습니다. (근데 스타일 양이 적진 않네요.. 밑의 소스를 참고해주세요)
여기서는 간단히 그 과정만 한번 보도록 하겠습니다.

<StackPanel Orientation="Horizontal">
    <ContentPresenter x:Name="content" Cursor="{TemplateBinding Cursor}" Content="{TemplateBinding Content}"
     
  ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Left"
        Margin="{TemplateBinding Padding}" />
    <Button x:Name="commands" ToolTipService.ToolTip="Delete Topic" Margin="4,0,0,0" Opacity="0"
        input:CommandService.Command="DeleteTopic"
        input:CommandService.CommandParameter="{Binding}">
        <Image Source="/EditableTreeView;component/Resources/delete.png" Stretch="None" />
    </Button>
</StackPanel>

지금 이것은 그 App.xaml에 들어가있는 이 리스트뷰 각각의 아이템을 스타일로 다시 구성해둔 것입니다.
살짝, '어디서나 쓸수 있는' 스타일은 아니게 되는것이 조금 문제같긴 하지만.. 아무튼 이렇게 해서 일단
버튼의 Opacity를 0으로 맞춰준 후에

 <DoubleAnimation Storyboard.TargetName="commands" Storyboard.TargetProperty="Opacity" Duration="0" To="1" />

마우스가 이 해당 아이템에 올라갔을 때, 버튼을 보이게 하는 구조로 나가면 되는 것이지요.
(MouseOver 같은 State를 감지하면 될 듯 싶습니다)

소스에서는 이 스타일을 TreeVireItemContainerStyle이라는 이름으로 정의했습니다.

<slt:TreeView VerticalAlignment="Stretch" Grid.Row="1" ItemsSource="{Binding HelpTopics}"
    ItemContainerStyle="{StaticResource TreeVireItemContainerStyle}">
    <slt:TreeView.ItemTemplate>
        <slt:HierarchicalDataTemplate ItemsSource="{Binding SubTopics}">
            <TextBlock Text="{Binding Name}" VerticalAlignment="Center" />
        </slt:HierarchicalDataTemplate>
    </slt:TreeView.ItemTemplate>
</slt:TreeView>

몇가지 주의할 점은,

이 TextBlock의 중복은 일종의 트릭입니다. 보시면 기본 ItemTemplate에도 TextBlock이 있고, 스타일에도 Button과 함께
TextBlock이 있습니다. 이는 '마우스 오버' 시에 Rectangle을 하이라이트 시키기 위해서입니다. 스타일에 보시면 이 TextBlock 을 포함하는 content라고 이름지어진 ContentPresenter가 있는데, 이 것과 겹치는 효과를 내서 테두리 및 둘러싼
사각형에 하이라이트 효과를 주는 것이지요.

그리고, 아마.. 이 원 소스의 Toolkit은 구버전이라 아마 네임스페이스 에러가 날 것입니다. (XAML에서)
이 현상은 첫번째 포스트를 참고하셔서 바꾸시면 되겠습니다. ^^ 레퍼런스를 새로 추가하셔서
(System.Windows.Controls.Toolkit), slt의 네임스페이스를 바꿔주시고, HierarchicalData가 이 툴킷의
(System.Windows)에 있습니다.

그러면 이렇게 됩니다.


깔끔해졌죠.. ^^ 여기까지입니다. 다음은 수정하는 방법에 대해서 해보겠습니다.

감사합니다.


(소스는 이곳에서 다운받으실 수 있습니다.)

쇼티예요.

이번엔 저번에 이어서 Command를 내리는 방법을 살펴볼 예정입니다. MVVM 패턴에서는
'액션이 가능한 엘리먼트의 액션과 연결하여 어떤 일련의 행동들을 정의하는' 방법이 있습니다.

.... 뭔 말인지 모르겠습니다. 쉽게 풀어보자면.
'액션이 가능한 엘리먼트(Actionable Element)'라는 것은 '유저가 일으킬 수 있는 이벤트가 있고, 그 이벤트가 발생하면 일련의 동작을 취할 수 있는 엘리먼트'를 말합니다.

버튼의 경우를 보죠. 사용자가 이 버튼을 클릭합니다. 그러면 발생되는 이벤트는? 그렇져. Click 이벤트지요.
우리가 이 이벤트가 일어났을 때, 콜백 함수를 만들 수가 있었죠? 그래서.
클릭되는 모든 것들은 '액션이 가능한 엘리먼트' 라고 할 수가 있겠네요.

그럼 텍스트박스는 어떨까요. 이거 액션이 가능한가요? 이것도 맞지요.
여러가지 이벤트를 발생시킬 수 있지요. 엔터가 눌렸을 때라던지..

리스트박스는 어떨까요. SelectionChanged 이벤트. 그렇죠?

그럼 그 '액션'이라는걸 다시 볼께요. 아주 간단한 이벤트로. 또, 가장 만만한(-_-)걸로. 버튼을 예로 들어보겠습니다.
우리가 일반적으로 Button을 XAML에 정의합니다.

 <Button Click="Button_Click" />

그리고 비하인드 코드에 다음과 같이 클릭이벤트에 대한 핸들러 함수를 쓰겠죠?

 private void Button_Click(object sender, RoutedEventArgs e)
{
    // 어쩌고 저쩌고
}

이걸 다음과 같이 바꿉니다.

 <Button Command="커맨드명" />

그리고 이벤트 핸들러 함수가 xaml.cs가 아닌, ViewModel로 지정되어있는 cs에 쓰게 된다면, 똑같은 역할을
하는거겠죠.

실제로, 후에 공용소스를 보시면 아시겠지만, 이 버튼에 커맨드를 달아놓고, 이 버튼 자체에 Command를 주면, 자동적으로
이 Command가 '언제' 발생될지에 대해서 정의하는 부분이 있습니다. 그래서, 이경우에 Click을 했을 때,
커맨드 명에 따라 주어진 행동을 하게 되는 것이지요.

이렇게 되면, 핸들러 함수때문에, 어쩔 수 없이 버튼 달아놓고 클릭이벤트를 xaml.cs에 쓰는 경우를 막을 수 있겠죠.
애초에 목표가 xaml.cs를 썰렁하게 하자. 라고 하면, 꽤 좋은 성과가 있는 것입니다.

이 Commanding이 WPF와 Silverlight가 다른 점 중에 하나인데요. 사실 WPF에는 이런 Commanding 기술이 많습니다
(라고 합니다..;; 해보지 않아 잘은 모르겠네요). 그러나 실버라이트는 거의 이런 기능이 없죠. ICommand라는 인터페이스
가 있긴 한데, 너무나 부실합니다.

그러다가 최근의 실버라이트3에서는 이 기능이 결국 정식으로 서비스가 된다 하죠. Attached 'Behavior'라는 기능으로..

그럼. 어떻게 하는가. 예제를 통해 알아보도록 하겠습니다. 전 포스트에서 썼던 프로젝트를 그대로 가져와볼께요.

이 기능을 하려면, 네. 누가 만든 코드를 가져와야되죠..; 실버라이트에서 정식으로 제공하는 기능은 아니기때문에.
CodeGuru에서 SLExtention이라는 아주 멋진 프로젝트가 있습니다. 굉장히 잘 알려진 예제 소스입니다.
이 압축을 푸시고 나면, SLExtentions라는 디렉토리 안에 Input이라는 디렉토리가 있습니다. 여기 cs파일이 7,8개정도
있는데, 이것을 다 우리의 프로젝트로 옮겨주고, 안에 있는 네임스페이스를 프로젝트명과 맞게 변경을 합니다.

새 아이템을 추가하셔서 Command.cs라는 파일을 만들고, 이렇게 코딩을 합니다.

 public static class Commands
{
    static Commands()
    {
        AddTopic = new Command("AddTopic");
        DeleteTopic = new Command("DeleteTopic");
    }

    public static Command AddTopic { get; private set; }
    public static Command DeleteTopic { get; private set; }
}

이 Command는, 추가한 SLExtentions 중에 Command.cs를 쓰는 것이기때문에, 색이 안변한다. 싶으시면
네임스페이스를 한번 확인해주세요.

대충 보면 컬럼을 새로 만들고, 지우고.. 하는 두개의 커맨드같애보여요.

그리고, xaml.cs로 버튼을 두개 추가합니다. 적당한 곳에 추가를 해주세요.

<StackPanel Orientation="Horizontal">
    <Button Content="Add Topic" input:CommandService.Command="AddTopic" />
    <Button Content="Delete Topic" input:CommandService.Command="DeleteTopic" />
</StackPanel>

보시면, CommandPattern을 보시면서 많이 보던 용법이 나오죠. input:CommandService...
이게 여기서 나오는것입니다. 당연히 XAML 내의 input으로, 아까 그 SLExtentions를 끌어왔던 디렉토리를 잡아주셔야하구요.
우리가 앞에서 AddTopic이라는 명령을 추가했기 때문에, 첫째버튼이 동작을 하고,
DeleteTopic이라는 명령을 추가했기 때문에, 두번째 버튼이 동작을 하는 것이죠.

그리고, ViewModel에 이벤트 핸들러 처리를 해줍니다.

 (PageViewModel.cs 의 생성자 부분이나.. 원하는 곳에)

 Commands.AddTopic.Executed += new EventHandler<ShortyCPattern.Input.ExecutedEventArgs>  
                                                   (AddTopic_Executed);

 Commands.DeleteTopic.Executed += new EventHandler<ShortyCPattern.Input.ExecutedEventArgs>
                                                   (DeleteTopic_Executed);

(생성된 이벤트 핸들러 함수에)

 private void OnAddTopicExecuted(object sender, ExecutedEventArgs e)
 {
    HelpTopics[0].SubTopics.Add(new HelpTopic() { Name = "Custom Topic" });
 }

 private void OnDeleteTopicExecuted(object sender, ExecutedEventArgs e)
 {
    if (HelpTopics[0].SubTopics.Count == 0)
    {
        return;
    }
    HelpTopics[0].SubTopics.RemoveAt(HelpTopics[0].SubTopics.Count - 1);
 }

실행시켜서 버튼을 눌러보면, 잘 되는 것을 알 수 있습니다. HelpTopics[0]에 추가했기 때문에, 첫번째 아티클의 하부
HelpTopics들에게만 더하고 빼기가 적용이 되겠군요. 여기까지, Page.xaml.cs에는 InitializeComponent() 말고는
없죠.. ^^

델같은 경우, 저렇게 Count == 0인지 체크해서 return 을 호출하기때문에 스택 언더플로우는 일어나지 않겠습니다.
비슷하게, CanExecuted 이벤트도 저렇게 이벤트 핸들러로 잡아주고, 이 값을 쓰는 방법도 있습니다.

 private void DeleteTopic_CanExecute(object sender, CanExecuteEventArgs e)
 {
    e.CanExecute = HelpTopics[0].SubTopics.Count > 0;
 }

이건 필요할 때 쓰실 수 있겠죠. 소스에서는

 Commands.DeleteTopic.RaiseCanExecute(null);

조금, 복잡하긴 하지만, xaml.cs에 아무것도 쓰지 않는다는 것은 곳, 이 모듈을 다른쪽에 옮기기에도 용이하다는 이야기니까,
유용하게 쓰실 수 있을 것입니다.

다음 장에서는, Delete 기능을 좀더 꾸며보도록 하겠습니다.

감사합니다. 소스는 이곳에서 직접 받으실 수 있습니다.

Domain Service Class에서 각 Operation들을 사용할 때의 규칙을 정리해 보겠습니다.

[ Insert ]

 - void 형이어야 할 것. 첫번째 파라미터로 Entity에 해당하는 객체가 올 것
 - 접두어가 Insert / Add / Create 중 하나일 것

public void InsertEmployee(Employee newEmployee) {...}


 - 단, 앞에 [Insert] 속성을 붙이면 함수 이름은 관계없음

[Insert]

public void YourFavoriteMethodName(Employee newEmployee) {...}



 [ Update ]

 - void 형이어야 할 것. 첫번째 파라미터, 혹은 첫번째와 두번째 파라미터로 Entity에 해당하는 객체가 올 것
 - 접두어가 Update / Change / Modify 중 하나일 것

public void UpdateEmployee(Employee changedEmployee, Employee

              originalEmployee)


 - 단, 앞에 [Update] 속성을 붙이면 함수 이름은 관계없음

[Update]

public void YourFavoriteMethodName(Employee changedEmployee, Employee

                                    originalEmployee)



 [ Delete ]

 - void 형이어야 할 것. 첫번째 파라미터, 혹은 첫번째와 두번째 파라미터로 Entity에 해당하는 객체가 올 것
 - 접두어가 Delete / Remove 중 하나일 것

public void DeleteEmployee(Employee currentEmployee, Employee

                     originalEmployee)


 - 단, 앞에 [Delete] 속성을 붙이면 함수 이름은 관계없음

[Delete]

public void YourFavoriteMethodName(Employee currentEmployee, Employee

                                           originalEmployee)



 [ Query ]

 - 리턴형은 IEnumerable<T>나 IQueryable<T>. 이때 T는 엔티티여야 함
 - 접두어가 Get / Fetch / Find / Query / Retrieve / Select 중에 하나일 것

public IQueryable<Employee> GetEmployee() {...}


 - Generated Code 단으로 가면 이름이 바뀔 가능성이 있음. 즉 GetEmployee()라고 Domain Service Class에서 썼다면, 
   이는 Employee 엔티티를 '얻는' 개념. Generated로 가는 클라이언트에선 Employee를 '읽는' 개념. 이렇게 바뀌긴 하지만
   그냥, 바뀐 이름으로 호출하면 끝나는 문제

 - [Query] 속성을 붙여서 명확히 표시해줄 수 있음 (꼭 필요하진 않아요)

[Query]

public IQueryable<Employee> GetEmployee() {...}


 - 쿼리를 멀티로 짤 수도 있지만, 조심해야합니다. 예를 들어 GetEmployee와 FetchEmployee로 두 오퍼레이션을 짰다면,
   클라이언트단에선 전부 LoadEmployee()로 변할 수 있고, 이 과정에서 에러가 날 수가 있습니다.

 - 속성에 PreserveName = true로 하면, 클라이언트단으로 갈 때 메서드 이름이 바뀌지 않음

[Query(PreserveName=true)]

public IQueryable<Employee> GetEmployee() {...}



 [ Custom ]

 - void형이어야 할 것. 첫번째 인자로 Entity형이 와야하고, Insert, Update, Delete 형태의 로직이 아니어야 할 것

 public void ApproveEmployee(Employee changedEmployee, ...)


 - [Custom] 속성을 붙여서 명확히 표시해줄 수 있음 (꼭 필요하진 않아요)

[Custom]

public void ApproveEmployee(Employee changedEmployee, ...)



 [ Resolve ]

 - bool형이어야 할 것. 인자는 세개의 Entity 필요.
 - 접두어는 무조껀 Resolve여야 할 것

public bool ResolveEmployee(PurchaseOrder currentEmployee, PurchaseOrder originalEmployee, PurchaseOrder storeEmployee, bool deleteOperation)


 - Update 의 로직구현이 필요합니다. 별도의 속성은 없습니다.


 [ Service Operation ]

 - [ServiceOperation] 속성을 반드시 붙여야 함. 그 이외에는 제약사항 없음.

[ServiceOperation]

public byte[] GetProductImage(){...}



 [ Opt-Out ]

 - Omit~♡ 

[IgnoreOperation]

public void InsertEmployee(Employee newEmployee) {...}


 - 클라이언트 코드로 전달되지 않는다.

----------------------------------------------------------------------------------------------------------------

지금까지 일반적인 오퍼레이션의 사용법에 대해서 알아보았습니다. 그닥 특별한 내용이 없네요.

다음 포스팅은 '메타데이터를 사용하는 방법' 에 대해서.입니다.


감사합니다.