티스토리 뷰
Grid App analyze Part 2 - Windows 8 Release Preview
kaki104 2012. 6. 9. 12:17지난 포스트에 이어서 LayoutAwarePage.cs에 대해서 살펴 보도록 하겠다.
Grid App analyze Part 1 - Windows 8 Release Preview
Grid App analyze Part 2 - Windows 8 Release Preview - 현재
1. 정리
LayoutAwarePage.cs는 몇개의 구분을 가지는데, 기본부분, Navigation support, Visual state switching, Process lifetime management 정도가 되겠다.
기본부분 :
DefaultViewModel을 만드는 부분과, 기본 생성자가 포함된다.
Navigation support :
GoHome, GoBack, GoForward, CoreDispatcher_AcceleratorKeyActivated, CoreWindow_PointerPressed가 포함되며, 페이지 네비게이션 기능을 구현해 놓았다.
Visual state switching :
StartLayoutUpdates, WindowSizeChanged, StopLayoutUpdates, DetermineVisualState, InvalidateVisualState로 구성되며, 화면의 Visual state가 변경되었을 때 어떤 처리를 할 것인지가 정의되어 있다.
* 현재 RP 버전에서는 Visual State를 알기 위해서는, WindowSizeChanged 이벤트를 사용해야 한다.
Process lifetime management :
OnNavigatedTo, OnNavigatedFrom, LoadState, SaveState로 구성되며, 각 페이지의 View State를 관리한다.
2. 전체 소스
namespace GridSampleApp.Common
{
[Windows.Foundation.Metadata.WebHostHidden]
public class LayoutAwarePage : Page
{
/// <summary>
/// DefaultViewModel 프로퍼티를 추가
/// </summary>
public static readonly DependencyProperty DefaultViewModelProperty =
DependencyProperty.Register("DefaultViewModel", typeof(IObservableMap<String, Object>),
typeof(LayoutAwarePage), null);
private List<Control> _layoutAwareControls;
/// <summary>
/// 기본 생성자
/// </summary>
public LayoutAwarePage()
{
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled) return;
// DefaultViewModel을 ObservableDictionary로 만들어 주는데 이 것을 사용하면
// View에서 디자인 타임에서 ViewModel의 프로퍼티들을 바인딩을 하기가 어렵기 때문에 간단한 용도로만 사용한다.
this.DefaultViewModel = new ObservableDictionary<String, Object>();
// Page를 상속 받은 클래스이기 때문에 Loaded 이벤트를 지정할 수 있는데,
// 페이지의 비주얼 스테이트 맵을 만들어 주고, 키보드나 마우스 네비게이션 처리를 한다.
this.Loaded += (sender, e) =>
{
// 비주얼 스테이트 관리 시작
this.StartLayoutUpdates(sender, e);
// 키보드와 마우스 이벤트 연결
if (this.ActualHeight == Window.Current.Bounds.Height &&
this.ActualWidth == Window.Current.Bounds.Width)
{
// Listen to the window directly so focus isn't required
Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated +=
CoreDispatcher_AcceleratorKeyActivated;
Window.Current.CoreWindow.PointerPressed +=
this.CoreWindow_PointerPressed;
}
};
// 페이지 언로드시 연결 이벤트 해제 - 페이지 네비게이션 발생시 원래 페이지는 Unloaded 됨
this.Unloaded += (sender, e) =>
{
// 비주얼 스테이트 관리 종료
this.StopLayoutUpdates(sender, e);
Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated -=
CoreDispatcher_AcceleratorKeyActivated;
Window.Current.CoreWindow.PointerPressed -=
this.CoreWindow_PointerPressed;
};
}
/// <summary>
/// VS 2012에서 새로 추가된 것으로 http://msdn.microsoft.com/en-us/library/windows/apps/br226050.aspx 참조
/// 딕셔너리의 확장판이라고 생각하면 될 것 같다.
/// </summary>
protected IObservableMap<String, Object> DefaultViewModel
{
get
{
return this.GetValue(DefaultViewModelProperty) as IObservableMap<String, Object>;
}
set
{
this.SetValue(DefaultViewModelProperty, value);
}
}
#region Navigation support
/// <summary>
/// 이 부분은 네비게이션 지원 부분으로 GoHome, GoBack, GoForward 처리 부분과 OnNavigatedTo, OnNavigatedFrom가 연결되는 부분을 살펴 보도록 한다.
/// </summary>
protected virtual void GoHome(object sender, RoutedEventArgs e)
{
// Use the navigation frame to return to the topmost page
if (this.Frame != null)
{
while (this.Frame.CanGoBack) this.Frame.GoBack();
}
}
/// <summary>
/// Invoked as an event handler to navigate backward in the navigation stack
/// associated with this page's <see cref="Frame"/>.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the
/// event.</param>
protected virtual void GoBack(object sender, RoutedEventArgs e)
{
// Use the navigation frame to return to the previous page
if (this.Frame != null && this.Frame.CanGoBack) this.Frame.GoBack();
}
/// <summary>
/// Invoked as an event handler to navigate forward in the navigation stack
/// associated with this page's <see cref="Frame"/>.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="e">Event data describing the conditions that led to the
/// event.</param>
protected virtual void GoForward(object sender, RoutedEventArgs e)
{
// Use the navigation frame to move to the next page
if (this.Frame != null && this.Frame.CanGoForward) this.Frame.GoForward();
}
/// <summary>
/// Invoked on every keystroke, including system keys such as Alt key combinations, when
/// this page is active and occupies the entire window. Used to detect keyboard navigation
/// between pages even when the page itself doesn't have focus.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="args">Event data describing the conditions that led to the event.</param>
private void CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher sender,
AcceleratorKeyEventArgs args)
{
//중략
}
/// <summary>
/// Invoked on every mouse click, touch screen tap, or equivalent interaction when this
/// page is active and occupies the entire window. Used to detect browser-style next and
/// previous mouse button clicks to navigate between pages.
/// </summary>
/// <param name="sender">Instance that triggered the event.</param>
/// <param name="args">Event data describing the conditions that led to the event.</param>
private void CoreWindow_PointerPressed(CoreWindow sender,
PointerEventArgs args)
{
//중략
}
#endregion
#region Visual state switching
/// <summary>
/// 비주얼 스테이트 업데이트 시작
/// </summary>
public void StartLayoutUpdates(object sender, RoutedEventArgs e)
{
// sender는 Page이다.
var control = sender as Control;
if (control == null) return;
// 윈도우 사이즈 체인지 이벤트를 연결하고, layoutAwareControls을 초기화 해준다.
if (this._layoutAwareControls == null)
{
// Start listening to view state changes when there are controls interested in updates
Window.Current.SizeChanged += this.WindowSizeChanged;
this._layoutAwareControls = new List<Control>();
}
// 컨트롤 리스트에 페이지를 넣고
this._layoutAwareControls.Add(control);
// 처음 비주얼 스테이트를 변경한다.
VisualStateManager.GoToState(control, DetermineVisualState(ApplicationView.Value), false);
}
//윈도우 사이즈 체인지 이벤트는 화면의 상태가 변경될 때 발생한다.(가로->세로, 전체->스냅드 등)
private void WindowSizeChanged(object sender, WindowSizeChangedEventArgs e)
{
this.InvalidateVisualState();
}
/// <summary>
/// 비주얼 스테이트 업데이트 종료 - Unloaded 이벤트에서 호출됨
/// </summary>
public void StopLayoutUpdates(object sender, RoutedEventArgs e)
{
var control = sender as Control;
if (control == null || this._layoutAwareControls == null) return;
this._layoutAwareControls.Remove(control);
if (this._layoutAwareControls.Count == 0)
{
// Stop listening to view state changes when no controls are interested in updates
this._layoutAwareControls = null;
Window.Current.SizeChanged -= this.WindowSizeChanged;
}
}
/// <summary>
/// 현재 뷰 상태를 문자로 반환한다.
/// </summary>
protected virtual string DetermineVisualState(ApplicationViewState viewState)
{
return viewState.ToString();
}
/// <summary>
/// 현재 뷰 상태를 컨트롤에 반영한다.
/// visual state.
/// </summary>
public void InvalidateVisualState()
{
if (this._layoutAwareControls != null)
{
string visualState = DetermineVisualState(ApplicationView.Value);
foreach (var layoutAwareControl in this._layoutAwareControls)
{
VisualStateManager.GoToState(layoutAwareControl, visualState, false);
}
}
}
#endregion
#region Process lifetime management
//SuspensionManager(SM) : 셈플에서의 사용 용도는 각 페이지의 View State를 관리하는데 사용됨
private String _pageKey;
/// <summary>
/// 네이게이션 목적지 페이지에서 발생하는 이벤트
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property provides the group to be displayed.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// 처음 페이지로 네비게이션이 되어서 들어오면 해당 페이지의 _pageKey를 만들어서 SuspensionManager(SM)에 페이지
// 에대한 스테이트를 보관하는 곳을 만들어 놓는다.
if (this._pageKey != null) return;
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
this._pageKey = "Page-" + this.Frame.BackStackDepth;
//네비게이션 모드가 신규라면
if (e.NavigationMode == NavigationMode.New)
{
// 현재 페이지를 기준으로 페이지 상태를 정리한다.
var nextPageKey = this._pageKey;
int nextPageIndex = this.Frame.BackStackDepth;
while (frameState.Remove(nextPageKey))
{
nextPageIndex++;
nextPageKey = "Page-" + nextPageIndex;
}
// LoadState를 실행한다, e.Parameter는 네비게이션 파라메터
this.LoadState(e.Parameter, null);
}
else
{
// 네비게이션 모드가 신규가 아니라면, 현재 페이지의 뷰 스테이트를 이용해서 화면을 복구 시킨다.
// (Dictionary<String, Object>)frameState[this._pageKey] 는 현재 페이지의 저장된 뷰 스테이트
this.LoadState(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey]);
}
}
/// <summary>
/// 네비게이션 출발지 페이지에서 발생하는 이벤트 - 페이지가 종료되기 전에 뷰 스테이트를 저장해 놓는다.
/// </summary>
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
var pageState = new Dictionary<String, Object>();
this.SaveState(pageState);
frameState[_pageKey] = pageState;
}
/// <summary>
/// 저장되어 있는 뷰 스테이트를 이용해서 화면을 복구 시킨다.
/// 각 페이지 내부에서 override해서 사용한다.
/// </summary>
protected virtual void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
}
/// <summary>
/// 현재 페이지의 뷰 스테이트를 저장한다. 각 페이지 내부에서 override해서 사용한다.
/// </summary>
/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
protected virtual void SaveState(Dictionary<String, Object> pageState)
{
}
#endregion
/// <summary>
/// Implementation of IObservableMap that supports reentrancy for use as a default view
/// model.
/// </summary>
private class ObservableDictionary<K, V> : IObservableMap<K, V>
{
// 중략
}
}
}
3. GroupedItemsPage 네비게이션 프로세스
1) App.xaml.cs 에서 네비게이션 시작
if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
{
throw new Exception("Failed to create initial page");
}
2) protected override void OnNavigatedTo(NavigationEventArgs e)
처음으로 들어오는 것이니 _pageKey = "Page-0"를 만들고, LoadState 호출(네비게이션 파라메터 : AllGroups)
3) protected virtual void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
각 페이지에 override되어 있는 LoadState를 호출하게 됨
네비게이션 파라메터는 : AllGroups, pageState : null
4. ItemDetailPage 네비게이션 프로세스
1) GroupedItemsPage.xaml.cs -> ItemView_ItemClick
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
//네비게이션 파라메터로 선택된 아이템의 UniqueId를 입력
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
2) LayoutAwarePage.cs -> OnNavigatedFrom
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
//현재 프래임의 상태를 저장하는 프래임 스테이트를 조회
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
//페이지 스테이트를 새로 만들고
var pageState = new Dictionary<String, Object>();
//페이지 스테이트를 저장하는 메소드 호출(이 메소드는 각 페이지에서 오버라이드해서 사용한다)
this.SaveState(pageState);
//페이지 스테이트를 프래임 스테이트에 저장
frameState[_pageKey] = pageState;
}
3) LayoutAwarePage.cs -> OnNavigatedTo
4) ItemDetailPage.xaml -> LoadState
5. 마무리
위에서 각 페이지 스테이트를 저장한 내용은 GoBack이 호출되었을 때 LoadState 메소드에서 사용하게 된다.
메트로 스타일 앱에서는 클릭 할 때 마다 계속 네비게이션이 발생하는 구조를 지향하고 있다. 하지만, 반드시 그렇게 만들어야 하는 것은 아니니 개발시 참고 사항으로 알면 될 것 같다.
다음 포스트는 각 페이지에서 어떻게 데이터를 보여주는지를 알아 보도록 하겠다.
'Previous Platforms > Samples' 카테고리의 다른 글
Using SQLite in Windows 8 RP Metro style app Part 2 (11) | 2012.07.14 |
---|---|
Using SQLite in Windows 8 RP Metro style app Part 1 (6) | 2012.07.09 |
Grid App analyze Part 1 - Windows 8 Release Preview (2) | 2012.06.04 |
Using MVVM Pattern (0) | 2012.04.23 |
WebAPI using in Portable Library (1) | 2012.04.20 |
- Total
- Today
- Yesterday
- windows 11
- Always Encrypted
- .net
- #Windows Template Studio
- Visual Studio 2022
- Bot Framework
- Windows 10
- MVVM
- ComboBox
- PRISM
- Microsoft
- Behavior
- kiosk
- XAML
- .net 5.0
- #MVVM
- Build 2016
- uno-platform
- Cross-platform
- LINQ
- dotNETconf
- #prism
- visual studio 2019
- C#
- UWP
- WPF
- #uwp
- IOT
- uno platform
- ef core
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |