티스토리 뷰

반응형

이 포스트는 Windows 8 Release Preview 버전의 Grid App template를 분석하는 포스트이다.

Windows 8 RP

Visual Studio 12 Beta 버전에서 가능하다.

 

이번에 버전이 올라가면서 여러부분이 변경되어서, 기존 CP버전에서 만들었던 앱은 오류가 발생한다. 변경된 부분에 대해서는 다른 포스트를 참조해서 수정을 하던지, 새로 프로젝트를 만들어서 하나하나 붙여나가야 한다.

 

1. 템플릿을 이용해서 프로젝트를 만들어보자

File -> New -> Project를 선택

 

Grid App(XAML)을 선택하고 Name은 GridSampleApp으로 입력하고 OK를 누른다.

 

 

Grid를 사용하는 기본 템플릿으로 프로젝트를 생성하고, 실행한 결과이다. 이전과 다르게 마우스 휠이 동작하는 것을 쉽게 알 수 있으며, 좀 더 다듬어 졌다는 인상을 준다. 앞으로 이 프로젝트의 여러 부분에 대해서 알아보도록 하겠다.

 

2. App.xaml, App.xaml.cs

앱이 실행이 되었을 때 가장 먼저 실행하는 곳으로 매우 중요한 역할을 한다.

 

App.xaml

 

<Application
    x:Class="GridSampleApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GridSampleApp"
    xmlns:localData="using:GridSampleApp.Data">

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <!--
                    Styles that define common aspects of the platform look and feel
                    Required by Visual Studio project and item templates

                    리소스 딕셔너리를 머지하는 곳으로, 지금은 StandardStyles.xaml 파일을 포함하고 있다.

                    일반적으로 xaml에서 Static Resource를 바인딩하면 대부분은 이 파일 내부에서 찾을 수 있다.
                 -->
                <ResourceDictionary Source="Common/StandardStyles.xaml"/>
            </ResourceDictionary.MergedDictionaries>

            <!-- Application-specific resources 특정 리소스를 선언하는 곳으로 여기서는 앱의 이름을 리소스로 정의해 놓았다. -->

            <x:String x:Key="AppName">GridSampleApp</x:String>
        </ResourceDictionary>
    </Application.Resources>
</Application>

 

App.xaml.cs

 

namespace GridSampleApp
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton Application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().

        /// 기본 생성자로 여기서는 Suspending 이벤트 처리를 하기 위한 연결만 해주고 있다.
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used when the application is launched to open a specific file, to display
        /// search results, and so forth.

        /// 기본적으로 사용자가 런치를 시키면 실행되는 곳으로, 다른 방법으로는 연결된 파일을 실행 시킬 때나, 검색결과를 표시하는 경우가 있다.
        /// </summary>
        /// <param name="args">Details about the launch request and process.</param>
        protected override async void OnLaunched(LaunchActivatedEventArgs args)
        {

            // Do not repeat app initialization when already running, just ensure that
            // the window is active

            // 이미 실행 중인데 다시 처음 시작시 실행되는 코드의 재실행을 방지하기 위한 코드

            if (args.PreviousExecutionState == ApplicationExecutionState.Running)
            {
                Window.Current.Activate();
                return;
            }

            // Create a Frame to act as the navigation context and associate it with
            // a SuspensionManager key

            // 새로운 프레임을 만들고, SuspensionManager(SM)에 프레임을 등록해 놓는다. SM은 각 페이지의 상태를 관리하고, 서스팬드 모드가 될 때 상태들을 모두 저장해 놓아서 다시 복귀했을 때에도 상태를 유지 하도록 지원하는 역할을 한다.
            var rootFrame = new Frame();
            SuspensionManager.RegisterFrame(rootFrame, "AppFrame");

            if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
            {
                // Restore the saved session state only when appropriate

                // SM에 저장되어 있는 각 페이지 상태를 복구시킨다.
                await SuspensionManager.RestoreAsync();
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter

                // 프레임에 아무런 내용이 없다면, 첫번째 페이지로 네비게이션 한다. 파라메터로는 AllGroups이라고 넘긴다.
                if (!rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups"))
                {
                    throw new Exception("Failed to create initial page");
                }
            }

            // Place the frame in the current Window and ensure that it is active

            // 프레임을 윈도우의 현재 컨텐츠로 등록하고, Activate해준다.
            Window.Current.Content = rootFrame;
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.

        /// 서스팬드모드로 돌입할 때 실행되는 곳으로, 현재 상태 데이터를 SM을 이용해서 저장한다.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private async void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            await SuspensionManager.SaveAsync();
            deferral.Complete();
        }
    }
}

서스팬드라는 것이 나오는데, 이 것은 앱의 기본 상태 중에 하나로, Running, Suspended, NotRunning이란 상태를가질 수 있는데, 각 상태가 변경 될 때 발생하는 이벤트를 이야기한다.

http://msdn.microsoft.com/en-us/library/windows/apps/hh464925.aspx  페이지를 참고하면 더 자세한 내용을 할 수 있다.

 

3. GroupedItemsPage.xaml, GroupedItemsPage.xaml.cs

그룹된 데이터가 보이는 기본 페이지이다.

 

GroupedItemsPage.xaml

 

<common:LayoutAwarePage
    x:Name="pageRoot"
    x:Class="GridSampleApp.GroupedItemsPage"

    <!--데이터컨텍스트에 DefaultViewModel을 바인딩하고 있다.-->
    DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    IsTabStop="false"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:GridSampleApp"
    xmlns:data="using:GridSampleApp.Data"
    xmlns:common="using:GridSampleApp.Common"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>

        <!--
            Collection of grouped items displayed by this page, bound to a subset
            of the complete item list because items in groups cannot be virtualized

            그룹데이터를 표시하기 위해서는 CollectionViewSource를 사용하는 것이 좋다.
        -->
        <CollectionViewSource
            x:Name="groupedItemsViewSource"
            Source="{Binding Groups}"
            IsSourceGrouped="true"
            ItemsPath="TopItems"
            d:Source="{Binding AllGroups, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}"/>
    </Page.Resources>

    <!--
        This grid acts as a root panel for the page that defines two rows:
        * Row 0 contains the back button and page title
        * Row 1 contains the rest of the page layout

        페이지의 기본 구성은 첫번째 줄에는 백버튼과 타이틀이 들어가고, 두번째 줄에는 컨텐츠가 들어가는데, 이 메트로 디자인 스타일은 반드시 유지를 해야한다.
    -->
    <Grid Style="{StaticResource LayoutRootStyle}">
        <Grid.RowDefinitions>
            <RowDefinition Height="140"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- Back button and page title -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
            <TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Grid.Column="1" Style="{StaticResource PageHeaderTextStyle}"/>
        </Grid>

        <!-- Horizontal scrolling grid used in most view states

         기본 뷰인 그리드 뷰이다. 이전 버전이랑 다른 점 중에 하나로 그리드 뷰하나만으로 메인 화면을 처리한다. 중점적으로 보아야 하는 부분에 대해서만 굻게 표시하겠다. -->
        <GridView
            x:Name="itemGridView"
            AutomationProperties.AutomationId="ItemGridView"
            AutomationProperties.Name="Grouped Items"
            Grid.Row="1"
            Margin="0,-3,0,0"
            Padding="116,0,40,46"
            ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
            ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
            SelectionMode="None"
            IsItemClickEnabled="True"
            ItemClick="ItemView_ItemClick">

            <GridView.ItemsPanel>
                <ItemsPanelTemplate>                       
                    <VirtualizingStackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </GridView.ItemsPanel>
            <GridView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <Grid Margin="1,0,0,6">
                                <Button
                                    AutomationProperties.Name="Group Title"
                                    Content="{Binding Title}"
                                    Click="Header_Click"
                                    Style="{StaticResource TextButtonStyle}"/>
                            </Grid>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                    <GroupStyle.Panel>
                        <ItemsPanelTemplate>
                            <VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0"/>
                        </ItemsPanelTemplate>
                    </GroupStyle.Panel>
                </GroupStyle>
            </GridView.GroupStyle>
        </GridView>

        <!-- Vertical scrolling list only used when snapped

        스냅드 화면에서 사용하는 리스트뷰 컨트롤로, 각 화면 전환은 VisualState에서 관리한다.-->
        <ListView
            x:Name="itemListView"
            AutomationProperties.AutomationId="ItemListView"
            AutomationProperties.Name="Grouped Items"
            Grid.Row="1"
            Visibility="Collapsed"
            Margin="0,-10,0,0"
            Padding="10,0,0,60"
            ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
            ItemTemplate="{StaticResource Standard80ItemTemplate}"
            SelectionMode="None"
            IsItemClickEnabled="True"
            ItemClick="ItemView_ItemClick">

            <ListView.GroupStyle>
                <GroupStyle>
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate>
                            <Grid Margin="7,7,0,0">
                                <Button
                                    AutomationProperties.Name="Group Title"
                                    Content="{Binding Title}"
                                    Click="Header_Click"
                                    Style="{StaticResource TextButtonStyle}"/>
                            </Grid>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </ListView.GroupStyle>
        </ListView>

 

        <VisualStateManager.VisualStateGroups>

            <!-- Visual states reflect the application's view state -->
            <VisualStateGroup x:Name="ApplicationViewStates">
                <VisualState x:Name="FullScreenLandscape"/>
                <VisualState x:Name="Filled"/>

                <!-- The entire page respects the narrower 100-pixel margin convention for portrait 기본화면 상태 -->
                <VisualState x:Name="FullScreenPortrait">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>

                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView" Storyboard.TargetProperty="Padding">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="96,0,10,56"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>

                <!--
                    The back button and title have different styles when snapped, and the list representation is substituted
                    for the grid displayed in all other view states

                    스냅드 화면 상태 - 모든 화면에서 스냅드 상태가 지원이되어야 한다.
                -->
                <VisualState x:Name="Snapped">
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedBackButtonStyle}"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageTitle" Storyboard.TargetProperty="Style">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource SnappedPageHeaderTextStyle}"/>
                        </ObjectAnimationUsingKeyFrames>

                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemListView" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                        </ObjectAnimationUsingKeyFrames>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView" Storyboard.TargetProperty="Visibility">
                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                        </ObjectAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Grid>
</common:LayoutAwarePage>


 

GroupedItemsPage.xaml.cs

 

namespace GridSampleApp
{
    /// <summary>
    /// A page that displays a grouped collection of items.

    /// 그룹컬렉션을 보여주는 화면으로 주목해야하는 부분은 LayoutAwarePage를 상속 받는다는 점이다.
    /// </summary>
    public sealed partial class GroupedItemsPage : GridSampleApp.Common.LayoutAwarePage
    {
        public GroupedItemsPage()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.

        /// 네비게이션이 되었을때 실행되는 페이지로 저장된 상태가 있다면, 복구하는 코드의 추가가 필요하다.
        /// </summary>
        /// <param name="navigationParameter">The parameter value passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
        /// </param>
        /// <param name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            // TODO: Create an appropriate data model for your problem domain to replace the sample data

            // 이 예제에서는 저장된 상태가 존재하는지 여부를 확인하지 않고 무조건 새로 만들어 준다.
            var sampleDataGroups = SampleDataSource.GetGroups((String)navigationParameter);
            this.DefaultViewModel["Groups"] = sampleDataGroups;
        }

        /// <summary>
        /// Invoked when a group header is clicked.

        /// 그룹 헤더를 클릭했을 때 이벤트 처리
        /// </summary>
        /// <param name="sender">The Button used as a group header for the selected group.</param>
        /// <param name="e">Event data that describes how the click was initiated.</param>
        void Header_Click(object sender, RoutedEventArgs e)
        {
            // Determine what group the Button instance represents

            // 이 예제에서는 이벤트를 발생시킨 엘리먼트를 찾아서 그 엘리먼트의 데이터컨텍스트 부분을 가지고 와서
            var group = (sender as FrameworkElement).DataContext;

            // Navigate to the appropriate destination page, configuring the new page
            // by passing required information as a navigation parameter

            // 데이터컨텍스트를 SampleDataGroup으로 형변환시키고, UniqueId값을 얻어서 GroupDetailPage로 네비게이션 시키면서, 파라메터로 UniqueId값을 넘긴다.
            this.Frame.Navigate(typeof(GroupDetailPage), ((SampleDataGroup)group).UniqueId);
        }

        /// <summary>
        /// Invoked when an item within a group is clicked.

        /// 그룹의 아이템을 클릭했을 때 이벤트 처리
        /// </summary>
        /// <param name="sender">The GridView (or ListView when the application is snapped)
        /// displaying the item clicked.</param>
        /// <param name="e">Event data that describes the item clicked.</param>
        void ItemView_ItemClick(object sender, ItemClickEventArgs e)
        {
            // Navigate to the appropriate destination page, configuring the new page
            // by passing required information as a navigation parameter

            // 클릭한 아이템을 SampleDataItem으로 형변환한 후 UniqueId를 구한 후 ItemDetailpage로 네비게이션을 하면서, UniqueId를 넘겨준다.
            var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
            this.Frame.Navigate(typeof(ItemDetailPage), itemId);
        }
    }
}

App에서는 Navigate가 되나, Back으로 돌아오나 둘다 새로운 인스턴스 페이지가 만들어 진다는 점을 유의해서 보아야한다. 즉, Back으로 돌아온다고 하더라도, 그전 상태를 유지하고 있는 페이지가 아닌 새로운 페이지가 만들어 진다는 것이다. 그래서, 매번 페이지를 이동할때마다, 이전 상태로 복구를 시키는 부분에 대해서 고민해야한다.

 

4. 이번에는 여기까지만 작성하겠다.

다음에는 LayoutAwarePage.cs에 대해서 알아보기로 하겠다. 이 곳에는 기본 cs페이지에는 나타나지 않는 여러가지 기능들을 포함하고 있으며, 알게 모르게 사용이 되고 있기 때문에 흐름을 파악하는데 매우 중요한 역할을 하기 때문이다.

반응형
댓글