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

 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 타입을 걸어주세요.
 
 참고되시길 바랍니다. ^^
 

쇼티예요. 이번 릴리즈 된 실버라이트 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'로 그리고 말죠..


이제 메타데이터에 대한 이야기를 해보려 합니다. 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 기능을 좀더 꾸며보도록 하겠습니다.

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