생애 첫 번째 윈도우 폰7 애플리케이션 만들기 마지막회
최종회로 지난번에 만든 게임을 업데이트해서 다음 번에 게임을 실행했을 때 게임을 다시 시작할 수 있도록 게임의 상태를 저장할 수 있도록 만든다. 이를 위해 격리된 저장소를 사용한다.
격리된 저장소는 관리된 애플리케이션이 지역 저장소에 정보를 저장하고 가져올 수 있게 해준다. 이 아키텍처는 실버라이트 4에서 사용한 것과 아주 비슷하다. 모든 I/O 작업은 격리된 저장소의 범위로 제한되고 이들은 내부 운영체제 파일 시스템에 직접 접근하지 못한다.
격리된 저장소의 사용에서 생기는 몇 가지 이점은 다른 애플리케이션으로부터 데이터를 보호하고 할당량을 관리하며 애플리케이션이 자체의 고유 데이터만을 다루도록 보장하는 블라인드가 된다.
[어셈블리 참조와 다른 자원 추가하기]
격리된 저장소를 이용하려면 프로젝트에 헬퍼파일과 어셈블리 참조를 제공해야 한다.
1. [생애 첫 번째 윈도우 폰 7 애플리케이션 만들기 6회]의 각 단계를 완료했다면, 그 때의 솔루션을 계속 사용할 것이므로 해당 솔루션을 비주얼 스튜디오에서 연다.
2. System.Servicemodel.Web 어셈블리에 대한 참조를 추가한다. [솔루션 탐색기]에서 [References]를 오른 클릭하고 [Add Reference]를 선택한다. [.NET] 탭을 선택하고 컴포넌트 목록에서 [System.Servicemodel.Web] 어셈블리를 선택한 뒤 [확인]을 클릭한다.
[그림 17] 프로젝트에 어셈블리 참조 추가하기
3. 이제 분리된 저장소를 다루기 위해 헬퍼 클래스를 추가한다. [Solution Explorer]에서 프로젝트를 오른 클릭하고 [Add]를 가리킨 후 [Existing Item]을 선택한다. 이 랩의 소스 폴더(C:\WP7TrainingKitOffline\Labs\YourFirstWP7Application\Source\Assets)를 찾아서 "IsolatedStorageHelper.cs"를 선택하고 추가한다.
4. 참조와 헬퍼 클래스 파일을 추가한 후, 솔루션 탐색기는 다음처럼 보인다.
[그림 18] 새로 추가된 컴포넌트를 보여주는 솔루션 탐색기
[퍼즐 UI 업데이트]
이번 작업에서는 게임 페이지를 업데이트해서 사용자가 게임의 상태를 로드하고 저장, 삭제할 수 있는 세 가지 버튼을 제공한다. 이러한 목적을 위해 사용자 인터페이스의 XAML 마크업을 수정해 필요한 요소를 정의한 후 각 버튼에 대한 이벤트 핸들러를 생성한다. 마지막으로 "IsolatedStorageHelper" 클래스에서 필요로하는 메서드를 구현해 격리된 저장소에서 직렬화된 객체를 불러오고 저장, 삭제한다.
1. [솔루션 탐색기]에서 "PuzzlePage.xaml"을 더블클릭해 해당 게임의 UI를 담고 있는 페이지를 연다.
2. XAML 뷰로 전환한다. 해당 뷰를 변경하고 뷰 영역을 최대화 하기 위해, 디자이너 창의 오른편 끝에서 [XAML] 탭을 더블 클릭한다.
3. 다음의 강조된 XAML 마크업을 TapToContinueTextBlock 라는 마지막 "TextBlock" 요소 이후에 바로 삽입한다.
<Grid x:Name="LayoutRoot" Background="Transparent">
<StackPanel Orientation="Vertical" VerticalAlignment="Top" Grid.Row="1">
<Border x:Name="CongratsBorder"...>
...
</Border>
<Border x:Name="border"...>
...
</Border>
<TextBlock x:Name="TapToContinueTextBlock" HorizontalAlignment="Center" Text="Tap the picture to start the puzzle" TextWrapping="Wrap" Foreground="#FFD0D0D0" FontSize="17.333"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" >
<Button x:Name="LoadButton" Content="Load" Margin="10" />
<Button x:Name="SaveButton" Content="Save" Margin="10" />
<Button x:Name="ClearStorageButton" Content="Clear" Margin="10" />
</StackPanel>
</StackPanel>
</Grid>
...
4. [디자인] 뷰로 전환하면 새로운 버튼이 추가된 사용자 인터페이스의 변경사항을 볼 수 있다. 이를 위해서는 디자이너 창의 오른편 끝에서 [디자인] 탭을 더블 클릭한다.
[그림 19] 갱신된 사용자 인터페이스를 보여주는 디자인 뷰
5. 각 버튼에 대한 이벤트 핸들러를 정의한다. 디자이너 화면에서 "Load"라는 버튼을 클릭해 선택하고 [F4]를 눌러 [속성] 창을 연다.
6. [속성] 창에서 [Events] 탭을 클릭해 사용할 수 있는 이벤트 목록을 가진 창을 표시한다. 이 목록에서 [Click] 이벤트를 찾아서 이벤트 옆에 있는 텍스트 상자에 "LoadButton_Click"이라고 입력한다. [엔터]를 누르면 이 이름의 이벤트 핸들러를 생성하고 코드 숨김 파일을 열어 비주얼 스튜디오가 생성한 메서드 스텁을 표시한다.
7. "LoadButton_Click" 이벤트 핸들러의 본문에 다음의 강조된 코드를 삽입한다.
{
var gameState = IsolatedStorageHelper.GetObject<PuzzleState>("PuzzleState");
if (gameState == null)
{
MessageBox.Show("Sorry, no game state found.", "Oops!", MessageBoxButton.OK);
}
else
{
// set game state
this.game.SetState(gameState);
}
}
8. 코드 편집기 창을 오른 클릭하고 [View Designer]를 선택해 다시 디자인 뷰로 전환한다. 디자이너화면에서 "Save"라는 버튼을 클릭하고 [F4]를 눌러 해당 속성 창을 연다.
9. [속성] 창에서 [Events] 탭을 클릭해 사용할 수 있는 이벤트 목록을 가진 창을 표시한다. 이 목록에서 [Click] 이벤트를 찾아서 이벤트 옆에 있는 텍스트 상자에 "SaveButton_Click"이라고 입력한다. [엔터]를 누르면 이 이름의 이벤트 핸들러를 생성하고 코드 숨김 파일을 열어 비주얼 스튜디오가 생성한 메서드 스텁을 표시한다.
10. "SaveButton_Click" 이벤트 핸들러의 본문에 다음의 강조된 코드를 삽입한다.
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
// save game state
PuzzleState gameState = this.game.GetState();
IsolatedStorageHelper.SaveObject("PuzzleState", gameState);
}
11. 다시 한번 코드 편집기 창을 오른 클릭하고 [View Designer]를 선택해 다시 디자인 뷰로 전환한다. 디자이너화면에서 "Clear"라는 버튼을 클릭하고 [F4]를 눌러 해당 속성 창을 연다.
12. [속성] 창에서 [Events] 탭을 클릭해 사용할 수 있는 이벤트 목록을 가진 창을 표시한다. 이 목록에서 [Click] 이벤트를 찾아서 이벤트 옆에 있는 텍스트 상자에 "ClearStorageButton_Click"이라고 입력한다. [엔터]를 누르면 이 이름의 이벤트 핸들러를 생성하고 코드 숨김 파일을 열어 비주얼 스튜디오가 생성한 메서드 스텁을 표시한다.
13. "ClearStorageButton_Click" 이벤트 핸들러의 본문에 다음의 강조된 코드를 삽입한다.
{
// remove state and image
IsolatedStorageHelper.DeleteObject("PuzzleState");
}
14. [솔루션 탐색기]에서 "IsolatedStorageHelper.cs" 파일을 더블 클릭해서 연다.
15. [GetObject<T>] 메서드를 찾아서 다음의 강조된 코드로 바디를 대체한다.
public static T GetObject<T>(string key)
{
if (IsolatedStorageSettings.ApplicationSettings.Contains(key))
{
string serializedObject = IsolatedStorageSettings.ApplicationSettings[key].ToString();
return Deserialize<T>(serializedObject);
}
return default(T);
}
GetObject<T> 메서드는 주어진 개체 키로 격리된 저장소에서 객체를 가져온다. 격리된 저장소에서 키-값 쌍의 딕셔너리를 저장하는 IsolatedStorageSettings을 이용한다. 객체들은 System.ServiceModel.Web
어셈블리에서 제공된 DataContractJsonSerializer을 사용해 직렬화된 형식으로 저장되는데, 이 메서드는 객체를 JSON으로 직렬화하고 JSON 데이터를 객체로 역직렬화한다.
16. 다음의 강조된 코드를 SaveObject<T> 메서드의 바디에 넣는다.
{
string serializedObject = Serialize(objectToSave);
IsolatedStorageSettings.ApplicationSettings[key] = serializedObject;
}
SaveObject<T> 메서드는 주어진 개체 키로 격리된 저장소에 개체를 저장한다.
17. 마지막으로 다음의 강조된 코드를 사용해 [DeleteObject<T>] 메서드를 구현한다.
{
IsolatedStorageSettings.ApplicationSettings.Remove(key);
}
DeleteObject 메서드는 격리된 저장소에서 주어진 개체의 키로 개체를 제거한다
18. 최종 변경을 테스트 하기 위해 [F5]를 눌러 애플리케이션을 빌드하고 에뮬레이터로 다시 한번 배포한다. 잠깐 기다리면 시작 화면이 나타나고 그 때 [START] 버튼을 클릭해 새로운 게임을 시작한다.
19. 게임 페이지에서 [Load]를 클릭해 저장된 상태의 복구를 시도한다. 아마 저장된 상태가 없다는 오류 메시지를 뿌릴 것이다. [OK]를 클릭해 오류 메시지를 해제한다.
[그림 20] 이전 상태가 없을 때 load 작업이 실패한다.
20. 보드에서 퍼즐의 조각 일부를 드래그 한다. 하나 이상의 조각을 배치하는 경우 쉽게 기억할 수 있는 패턴으로 하는것이 상책이다. 나중에 게임 상태를 다시 로드한 후 동일한 패턴인지를 알아보기가 훨씬 쉽다.
21. [Save] 를 클릭해 게임의 현재 상태를 저장한다.
22. 이제 다른 배열로 조각을 계속 이동한 다음 이번에는 게임의 상태를 저장하지는 않는다.
23. 다시 한번 [Load]를 클릭해 이전의 저장된 상태를 복원한다. 이번에는 해당 조각의 위치가 원래 저장했던 게임 상태와 동일한 패턴으로 복구된다.
24. 에뮬레이터 윈도우에서 뒤로 가기 버튼(<-)을 눌러 시작화면을 담은 홈 페이지로 간다. 그 다음 뒤로 가기 버튼을 다시 클릭해 애플리케이션을 나가면 [Quick Launch] 메뉴를 표시한다.
25. 이제, [Quick Launch] 메뉴에서 해당 애플리케이션을 재 시작하기 위해 다시 한번 [WindowsPhonePuzzle]을 클릭한다.
[그림 21] Quick Launch 메뉴에서 애플리케이션을 시작한다.
26. [START]를 클릭해 새로운 게임을 시작하고 게임 페이지에서 [Load]를 클릭해 이전 상태를 로드 한다. 보드 패턴이 이전에 애플리케이션에서 저장했던 상태인지 확인한다.
27. [Clear]를 클릭해 분리된 저장소에서 저장된 상태를 지운다.
28. 이제 다시 한번 [Load]를 클릭하면 앞서 저장된 상태가 없을 때 보았던 동일한 오류 메시지를 받게 된다.