티스토리 뷰

반응형

Page navigation part2

 

지난번 소스에서 많은 부분을 수정해서 실제 프로젝트에서 사용이 가능할 정도까지 만들었고, 소스는 모두 ReSharper를 이용해서 정리했다. 또한, 이 소스를 이용해서 개발을 시작 할 수 있도록 좀 자세하게 설명을 추가 하니 좀 길더라도 이해하기 바란다.

 

0. 환경

 

Visual Studio 2013 Update 2

Windows 8.1

지난번 사용한 UniversalSample 소스 - 참고 정도?

 

1. 기본

프로젝트는 UniversalSample.Windows(Windows 8.1 store app), UniversalSample.WindowsPhone(Windows Phone 8.1 app), UniversalSample.Shared 3개의 프로젝트로 구성된다.

 

1-1. UniversalSample.Shared

W8.1과 WP8.1 프로젝트에서 공통으로 사용되는 대부분의 코드가 이 프로젝트에 위치하고 있다.

 

. Commons folder

- BindableBase.cs : 바인딩 클래스

- FrameHelper.cs : 프레임 헬퍼 클래스, 네비게이션을 담당한다.

- RelayCommand.cs : 커맨드 클래스(ICommand를 상속받은 커맨드 생성용 클래스)

- SuspensionManager.cs : 서스팬션 메니저로 앱이 서스팬드 모드로 돌입하거나 나올때 필요한 데이터를 저장하고 복구시킨다.

- ViewModelBase.cs : 뷰모델 베이스 클래스

 

. Design folder : 디자인 데이터 폴더

. Models folder : 모델 폴더

. ViewModels folder : 뷰모델 폴더

App.xaml : 앱 시작시에 필요한 내용이 들어있음

 

1-2. UniversalSample.Windows

. Assets folder : 이미지 리소스가 포함되어 있다.

. Views : 뷰 폴더

 

1-3. UniversalSample.WindowsPhone

. Assets folder : 이미지 리소스가 포함되어 있다.

. Views : 뷰 폴더

 

* 주의할 사항은 Windows프로젝트와 WindowsPhone 프로젝트는 모두 동일한 폴더 구조와 동일한 뷰 이름을 가져야 한다.

 

2. App.xaml과 App.xaml.cs를 살펴 본다.

앱이 시작될 때 어떤 동작들이 실행되는지 살펴 보도록 하자.

 

App.xaml

 

<Application
    x:Class="UniversalSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:viewModel="using:UniversalSample.ViewModels">

    <Application.Resources>
        <x:String x:Key="AppName">Universal Sample</x:String>
        <viewModel:ViewModelLocator x:Key="Locator" />
    </Application.Resources>
</Application> 

 

앱이 시작 될 때 앱 전체에서 사용할 리소스를 이곳에 정의한다.

AppName이라는 문자열 리소스를 하나 만들고, ViewModelLocator라는 클래스를 생성해서 리소스로 사용한다.

뷰모델로케이터는 두가지 용도로 사용되는데, 하나는 앱 전체에서 하나의 뷰모델만 유지 하기 위함이고, 또 하나는 템플릿 내부에서 뷰모델에 있는 내용을 바인딩(주로 커맨드)할 때 사용한다.

 

App.xaml.cs

 

        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            var rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame {CacheSize = 1};

                // TODO: change this value to a cache size that is appropriate for your application

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    // TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
                // 루트 프레임을 FrameHelper에 등록한다.
                FrameHelper.Instance.RegisterFrame(rootFrame);
            }

            //루트 프레임의 컨텐츠가 null이면 MainPage로 네비게이션한다.
            if (rootFrame.Content == null)
            {
#if WINDOWS_PHONE_APP
                // Removes the turnstile navigation for startup.
                if (rootFrame.ContentTransitions != null)
                {
                    this.transitions = new TransitionCollection();
                    foreach (var c in rootFrame.ContentTransitions)
                    {
                        this.transitions.Add(c);
                    }
                }

                rootFrame.ContentTransitions = null;
                rootFrame.Navigated += this.RootFrame_FirstNavigated;
#endif

                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                if (!FrameHelper.Instance.Navigation(typeof(MainPage), e.Arguments))
                {
                    throw new Exception("Failed to create initial page");
                }

            }

            // Ensure the current window is active
            Window.Current.Activate();
        }

 

* 기존 유니버셜앱 템플릿에 있는 NavigationHelper는 MVVM 패턴에서는 사용하기가 좀 그렇다, 그래서 ViewModel에서 네비게이션 관련 처리를 모두 할 수 있도록 수정한 것이 이 셈플이다.

 

위에서 앱이 시작되면 MainPage로 네비게이션을 시키도록 되어있다. 이제 MainPage.xaml.cs를 보도록 하자.

 

3. MainPage.xaml.cs

 

using Windows.ApplicationModel;
using Windows.UI.Xaml.Controls;
using UniversalSample.ViewModels;

namespace UniversalSample.Views
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            InitializeComponent();

            if (DesignMode.DesignModeEnabled == false)
            {
                var locator = App.Current.Resources["Locator"] as ViewModelLocator;
                if (locator != null) DataContext = DataContext ?? locator.MainPageVM;
            }

        }
    }
}

 

간단하게 코딩 했다. 런타임이라면 앱의 리소스에서 ViewModelLocator를 찾고, 뷰모델로케이터에 있는 MainPageVM을 가지고 와서 DataContext에 넣어 준다. 그렇다면 ViewModelLocator는 어떤 내용이 있는지 보아야 할 때다.

 

4. ViewModelLocator.cs

 

namespace UniversalSample.ViewModels
{
    public class ViewModelLocator
    {
        private ClassDetailPageVM _classDetailPageVM;
        private MainPageVM _mainPageVM;

        public MainPageVM MainPageVM
        {
            get { return _mainPageVM = _mainPageVM ?? new MainPageVM(); }
        }

        public ClassDetailPageVM ClassDetailPageVM
        {
            get { return _classDetailPageVM = _classDetailPageVM ?? new ClassDetailPageVM(); }
        }
    }
}

 

ViewModelLocator는 2개의 뷰모델을 프로퍼티로 가지고 있으며, 각 프로퍼티가 사용이 되는 시점에 인스턴스가 되도록 되어 있다. 아주 특별한 내용은 없다. 그러면, MainPageVM.cs의 내용을 살펴보자

 

5. MainPageVM.cs

 

using System.Collections.Generic;
using Windows.ApplicationModel;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using UniversalSample.Commons;
using UniversalSample.Design;
using UniversalSample.Models;
using UniversalSample.Views;

 

namespace UniversalSample.ViewModels
{
    /// <summary>
    ///     MainPage ViewModel
    /// </summary>
    public class MainPageVM : ViewModelBase
    {
        private IList<ClassM> _classList;
        private RelayCommand _itemClickCommand;
        private IList<PersonM> _people;

        /// <summary>
        ///     Constructor
        /// </summary>
        public MainPageVM()
        {
            Title = "Kaki education center";

            ClassList = new List<ClassM>();
            People = new List<PersonM>();

            if (DesignMode.DesignModeEnabled)
            {
                //디자인 타임 데이터
                ClassList = DesignDatas.GetClassList();
                People = DesignDatas.GetPeople();
            }
        }

 

        /// <summary>
        ///     Class List
        /// </summary>
        public IList<ClassM> ClassList
        {
            get { return _classList; }
            set
            {
                _classList = value;
                OnPropertyChanged();
            }
        }

 

        /// <summary>
        ///     People
        /// </summary>
        public IList<PersonM> People
        {
            get { return _people; }
            set
            {
                _people = value;
                OnPropertyChanged();
            }
        }

 

        /// <summary>
        ///     ItemClick Command
        /// </summary>
        public RelayCommand ItemClickCommand
        {
            get
            {
                return _itemClickCommand = _itemClickCommand ?? new RelayCommand(
                    args =>
                    {
                        if (!(args is ItemClickEventArgs)) return;

                        var item = ((ItemClickEventArgs) args).ClickedItem as ClassM;
                        if (item == null) return;

                        FrameHelper.Instance.Navigation(typeof (ClassDetailPage), item);
                    });
            }
        }

 

        /// <summary>
        ///     OnNavigated
        /// </summary>
        /// <param name="e"></param>
        protected override void OnNavigated(NavigationEventArgs e)
        {
            //네비게이션이 완료 된 후 실행되는 부분, 지금은 디자인 타임과 동일한 데이터를 보여준다.
            ClassList = DesignDatas.GetClassList();
            People = DesignDatas.GetPeople();
        }
    }
}

 

public RelayCommand ItemClickCommand

커맨드 프로퍼티로 ListView의 ItemClick이벤트를 처리하며, args에는 ItemClick 이벤트의 아규먼트인 ItemClickEventArgs가 반환되는데, 이 것은 InvokeCommandAction에서 CommandParameter를 지정하지 않으면 반환되는 것이다.

 

FrameHelper.Instance.Navigation(typeof (ClassDetailPage), item);
ClassDetailPage로 item object를 가지고 네비게이션 한다. 네비게이션 파라메터는 기본적으로는 string을 넘기도록 하지만, object를 넘겨도 큰 문제는 없다. 대신 네비게이션 파라메터를 serialization해서 저장을 하는 것은 할 수 없다. 만약 그렇게 해야만 한다면, string만을 넘기도록 한다.

 

protected override void OnNavigated(NavigationEventArgs e)

MainPage가 Navigated가 된후 실행되는 가상 메소드이다. 네비게이션과 관련된 가상 메소드들은 ViewModelBase.cs에 선언되어 있다. 네비게이션 과정이 어떻체 처리되는지 더 자세하게 알려면, FrameHelper.cs와 ViewModelBase.cs를 자세히 보면 된다.

 

6. MainPage.xaml - Windows 8.1

 

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ViewModels="using:UniversalSample.ViewModels"
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
    x:Class="UniversalSample.Views.MainPage"
    mc:Ignorable="d">


    <Page.Resources>
        <!--ListViewItem의 가로 너비를 Stretch하도록 한다-->
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>
    </Page.Resources>

    <!--디자인타임 뷰모델, Blend를 이용해서 작업하면 이렇게 저장된다. Visual Studio에서는 <d:Page.DataContext>로 사용하면 된다. -->
    <d:DataContext>
        <ViewModels:MainPageVM />
    </d:DataContext>

 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition />
            </TransitionCollection>
        </Grid.ChildrenTransitions>

 

        <Hub>
            <Hub.Header>
                <!-- Back button and page title -->
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="80" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <!--GoBackCommand로 변경, ViewModelBase에 있는 커맨드-->
                    <Button Margin="-1,-1,39,0"
                            Command="{Binding GoBackCommand, Mode=OneWay}"
                            Style="{StaticResource NavigationBackButtonNormalStyle}"
                            VerticalAlignment="Top"
                            AutomationProperties.Name="Back"
                            AutomationProperties.AutomationId="BackButton"
                            AutomationProperties.ItemType="Navigation Button" />
                    <!--App.xaml에서 추가해 놓았던 AppName을 여기서 사용-->
                    <TextBlock
                        Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
                        IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Top"
                        Text="{StaticResource AppName}" />
                </Grid>
            </Hub.Header>

 

            <!--앱에서 사용하던 이미지를 셈플로 사용-->
            <HubSection Width="500" Margin="0,0,80,0">
                <HubSection.Background>
                    <ImageBrush Stretch="UniformToFill" ImageSource="ms-appx:///Assets/storelogo558_756.png" />
                </HubSection.Background>
            </HubSection>

 

            <!--클래스 목록-->
            <HubSection Header="Class List" MinWidth="400" HorizontalContentAlignment="Stretch">
                <DataTemplate>
                    <Grid>
                        <!--ItemClick이벤트를 사용하기 위해서는 IsItemClickEnabled가 True여야 한다-->
                        <ListView ItemsSource="{Binding ClassList}" HorizontalContentAlignment="Stretch"
                                  SelectionMode="None" IsItemClickEnabled="True">
                            <!--리스트뷰에 헤더 템플릿을 이용-->
                            <ListView.HeaderTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.4*" />
                                            <ColumnDefinition Width="0.6*" />
                                            <ColumnDefinition Width="60" />
                                            <ColumnDefinition Width="15" />
                                        </Grid.ColumnDefinitions>
                                        <TextBlock Style="{StaticResource BodyTextBlockStyle}">
                                            <Run Text="Name" />
                                        </TextBlock>
                                        <TextBlock Grid.Column="1" Style="{StaticResource BodyTextBlockStyle}">
                                            <Run Text="Description" />
                                        </TextBlock>
                                        <TextBlock Grid.Column="2" Style="{StaticResource BodyTextBlockStyle}">
                                            <Run Text="Count" />
                                        </TextBlock>
                                    </Grid>
                                </DataTemplate>
                            </ListView.HeaderTemplate>
                            <!--아이템 템플릿-->
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.4*" />
                                            <ColumnDefinition Width="0.6*" />
                                            <ColumnDefinition Width="60" />
                                        </Grid.ColumnDefinitions>

                                        <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}"
                                                   Margin="0,0,4,0" />
                                        <TextBlock Grid.Column="1" Text="{Binding Description}"
                                                   Style="{StaticResource BodyTextBlockStyle}" />
                                        <TextBlock Grid.Column="2" Text="{Binding People.Count, Mode=OneWay}"
                                                   Style="{StaticResource BodyTextBlockStyle}"
                                                   HorizontalAlignment="Center" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>

                            <!--인터렉티비티-->
                            <Interactivity:Interaction.Behaviors>
                                <!--아이템클릭이벤트에 반응-->

                                <Core:EventTriggerBehavior EventName="ItemClick">
                                    <!--ItemClickCommand를 실행시키는데 CommandParameter는 ItemClickEventArgs가 자동으로 입력된다.-->
                                    <Core:InvokeCommandAction Command="{Binding ItemClickCommand, Mode=OneWay}" />
                                </Core:EventTriggerBehavior>
                            </Interactivity:Interaction.Behaviors>

                        </ListView>
                    </Grid>
                </DataTemplate>
            </HubSection>

 

            <!--People 프로퍼티 내용 출력용으로 중요한 부분은 없다-->
            <HubSection MinWidth="400" Header="People" HorizontalAlignment="Stretch"
                        HorizontalContentAlignment="Stretch">
                <DataTemplate>
                    <Grid>
                        <ListView ItemsSource="{Binding People}" HorizontalContentAlignment="Stretch"
                                  SelectionMode="None">
                            <ListView.HeaderTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="15" />
                                        </Grid.ColumnDefinitions>
                                        <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Name" />
                                        <TextBlock Grid.Column="1" Style="{StaticResource BodyTextBlockStyle}"
                                                   Text="Age" />
                                        <TextBlock Grid.Column="2" Style="{StaticResource BodyTextBlockStyle}"
                                                   Text="Sex" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.HeaderTemplate>
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                        </Grid.ColumnDefinitions>

                                        <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}"
                                                   Margin="0,0,4,0" />
                                        <TextBlock Grid.Column="1" Text="{Binding Age}"
                                                   Style="{StaticResource BodyTextBlockStyle}" />
                                        <TextBlock Grid.Column="2" Text="{Binding Sex}"
                                                   Style="{StaticResource BodyTextBlockStyle}" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </Grid>
                </DataTemplate>
            </HubSection>
        </Hub>
    </Grid>
</Page>

 

7. MainPage.xaml - Windows Phone 8.1

 

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ViewModels="using:UniversalSample.ViewModels"
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
    x:Class="UniversalSample.Views.MainPage"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">


    <Page.Resources>
        <!--ListViewItem의 가로 너비를 Stretch하도록 한다-->
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>

    </Page.Resources>

 

    <!--디자인타임 뷰모델, Blend를 이용해서 작업하면 이렇게 저장된다.-->
    <d:DataContext>
        <ViewModels:MainPageVM />
    </d:DataContext>

 

    <Grid>
        <!--App.xaml에서 추가해 놓았던 AppName을 여기서 사용-->
        <Hub Header="{StaticResource AppName}">
            <!--앱에서 사용하던 이미지를 셈플로 사용, 이미지는 Windows 8.1 프로젝트에 있는 이미지의 링크이미지임-->
            <HubSection>
                <HubSection.Background>
                    <ImageBrush Stretch="UniformToFill" ImageSource="ms-appx:///Assets/storelogo558_756.png" />
                </HubSection.Background>
            </HubSection>

 

            <!--클래스 목록, 너비를 지정하지 않는다-->
            <HubSection Header="Class List">
                <DataTemplate>
                    <Grid>
                        <!--ItemClick이벤트를 사용하기 위해서는 IsItemClickEnabled가 True여야 한다-->
                        <ListView ItemsSource="{Binding ClassList}" HorizontalContentAlignment="Stretch"
                                  SelectionMode="None" IsItemClickEnabled="True">
                            <!--리스트뷰에 헤더 템플릿을 이용-->
                            <ListView.HeaderTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.4*" />
                                            <ColumnDefinition Width="0.6*" />
                                            <ColumnDefinition Width="60" />
                                        </Grid.ColumnDefinitions>
                                        <TextBlock Style="{StaticResource BodyTextBlockStyle}">
                                            <Run Text="Name" />
                                        </TextBlock>
                                        <TextBlock Grid.Column="1" Style="{StaticResource BodyTextBlockStyle}">
                                            <Run Text="Description" />
                                        </TextBlock>
                                        <TextBlock Grid.Column="2" Style="{StaticResource BodyTextBlockStyle}">
                                            <Run Text="Count" />
                                        </TextBlock>
                                    </Grid>
                                </DataTemplate>
                            </ListView.HeaderTemplate>
                            <!--아이템 템플릿-->
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.4*" />
                                            <ColumnDefinition Width="0.6*" />
                                            <ColumnDefinition Width="60" />
                                        </Grid.ColumnDefinitions>

                                        <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}"
                                                   Margin="0,0,4,0" />
                                        <TextBlock Grid.Column="1" Text="{Binding Description}"
                                                   Style="{StaticResource BodyTextBlockStyle}" />
                                        <TextBlock Grid.Column="2" Text="{Binding People.Count, Mode=OneWay}"
                                                   Style="{StaticResource BodyTextBlockStyle}"
                                                   HorizontalAlignment="Center" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                            <!--인터렉티비티-->
                            <Interactivity:Interaction.Behaviors>
                                <!--아이템클릭이벤트에 반응-->
                                <Core:EventTriggerBehavior EventName="ItemClick">
                                    <!--ItemClickCommand를 실행시키는데 CommandParameter는 ItemClickEventArgs가 자동으로 입력된다.-->
                                    <Core:InvokeCommandAction Command="{Binding ItemClickCommand, Mode=OneWay}" />
                                </Core:EventTriggerBehavior>
                            </Interactivity:Interaction.Behaviors>

                        </ListView>
                    </Grid>
                </DataTemplate>
            </HubSection>

 

            <!--People 프로퍼티 내용 출력용으로 중요한 부분은 없다-->
            <HubSection Header="People">
                <DataTemplate>
                    <Grid>
                        <ListView ItemsSource="{Binding People}" HorizontalContentAlignment="Stretch"
                                  SelectionMode="None">
                            <ListView.HeaderTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                        </Grid.ColumnDefinitions>
                                        <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Name" />
                                        <TextBlock Grid.Column="1" Style="{StaticResource BodyTextBlockStyle}"
                                                   Text="Age" />
                                        <TextBlock Grid.Column="2" Style="{StaticResource BodyTextBlockStyle}"
                                                   Text="Sex" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.HeaderTemplate>
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                            <ColumnDefinition Width="0.3*" />
                                        </Grid.ColumnDefinitions>

                                        <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}"
                                                   Margin="0,0,4,0" />
                                        <TextBlock Grid.Column="1" Text="{Binding Age}"
                                                   Style="{StaticResource BodyTextBlockStyle}" />
                                        <TextBlock Grid.Column="2" Text="{Binding Sex}"
                                                   Style="{StaticResource BodyTextBlockStyle}" />
                                    </Grid>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </Grid>
                </DataTemplate>
            </HubSection>
        </Hub>
    </Grid>
</Page>

 

* 6번 MainPage.xaml과 7번 MainPage.xaml은 작은 차이가 몇가지가 있다. 우선은 헤더 부분이 다르고, 허브 Section에 너비를 지정하지 않아야 하고, ItemTemplate이 약간 다르다. 이런 작은 차이가 있기 때문에 하나의 xaml을 두 프로젝트에서 공통으로 사용하기에는 문제가 있을 것 같아 그냥 2개로 분리하는 방향으로 했다. 이 방법말고 VisualState를 이용하는 방법도 있기는 한데 파일은 하나지만 관리하기가 더 어렵지 않을까 생각된다.

 

이제 두번째 페이지를 살펴보자

 

8. ClassDetailPageVM.cs

 

using System.Linq;
using Windows.ApplicationModel;
using Windows.UI.Xaml.Navigation;
using UniversalSample.Commons;
using UniversalSample.Design;
using UniversalSample.Models;

 

namespace UniversalSample.ViewModels
{
    /// <summary>
    ///     ClassDetailPage ViewModel
    /// </summary>
    public class ClassDetailPageVM : ViewModelBase
    {
        private ClassM _currentClass;

        /// <summary>
        ///     Constructor
        /// </summary>
        public ClassDetailPageVM()
        {
            Title = "Class detail page";

            if (DesignMode.DesignModeEnabled)
            {
                //디자인 타임에서는 첫번째 데이터를 이용한다.
                CurrentClass = DesignDatas.GetClassList().First();
            }
        }

        /// <summary>
        ///     Current Class
        /// </summary>
        public ClassM CurrentClass
        {
            get { return _currentClass; }
            set
            {
                _currentClass = value;
                OnPropertyChanged();
            }
        }

        /// <summary>
        ///     Navigating
        /// </summary>
        /// <param name="e"></param>
        protected override void OnNavigating(NavigatingCancelEventArgs e)
        {
            CurrentClass = null;
        }

        /// <summary>
        ///     Navigated
        /// </summary>
        /// <param name="e"></param>
        protected override void OnNavigated(NavigationEventArgs e)
        {
            if (e.NavigationMode == NavigationMode.New)
            {
                CurrentClass = e.Parameter as ClassM;
            }
        }
    }
}

 

protected override void OnNavigated(NavigationEventArgs e)

네비게이션이 완료된 후 호출되는 가상 메소드로 NavigationMode가 New인 경우에 CurrentClass에 네비게이션 파라메터를 입력한다.

 

protected override void OnNavigating(NavigatingCancelEventArgs e)

네비게이션이 발생하기 전에 호출되는 가상 메소드로(여기서는 GoBack) CurrentClass = null을 실행한다. 특별히 네비게이션이 되기전에 처리할 내용이 있으면 이곳에서 처리하면 된다. 위의 내용은 예로 넣어 놓은 것 뿐이다.

 

뷰를 살펴보자.

 

9. ClassDetailPage.xaml - Windows 8.1

 

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ViewModels="using:UniversalSample.ViewModels"
    x:Class="UniversalSample.Views.ClassDetailPage"
    mc:Ignorable="d">

 

    <Page.Resources>
        <!--디자인 리소스들-->
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>

        <Style x:Key="HeadBorderStyle" TargetType="Border">
            <Setter Property="Background" Value="Gray" />
            <Setter Property="BorderBrush" Value="White" />
            <Setter Property="BorderThickness" Value="2" />
        </Style>
        <Style x:Key="DetailBorderStyle" TargetType="Border">
            <Setter Property="BorderBrush" Value="DarkGray" />
            <Setter Property="BorderThickness" Value="2" />
            <Setter Property="Padding" Value="10" />
        </Style>
    </Page.Resources>

 

    <!--디자인타임 뷰모델-->
    <d:Page.DataContext>
        <ViewModels:ClassDetailPageVM />
    </d:Page.DataContext>

 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <!--에니메이션 효과-->
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition />
            </TransitionCollection>
        </Grid.ChildrenTransitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="140" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

 

        <Grid Grid.Row="1" Margin="120,0,120,90">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.5*" />
                <ColumnDefinition Width="0.5*" />
            </Grid.ColumnDefinitions>

            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="193*" />
                    <ColumnDefinition Width="490*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="31*" />
                    <RowDefinition Height="126*" />
                </Grid.RowDefinitions>

                <Border Style="{StaticResource HeadBorderStyle}">
                    <TextBlock Text="Name" Style="{StaticResource TitleTextBlockStyle}" HorizontalAlignment="Center"
                               VerticalAlignment="Center" />
                </Border>
                <Border Grid.Row="1" Grid.Column="0" Style="{StaticResource HeadBorderStyle}">
                    <TextBlock Text="Description" Style="{StaticResource TitleTextBlockStyle}"
                               VerticalAlignment="Center" HorizontalAlignment="Center" />
                </Border>

                <!--CurrentClass.Name 프로퍼티 바인딩-->
                <Border Grid.Row="0" Grid.Column="1" Style="{StaticResource DetailBorderStyle}">
                    <TextBlock Text="{Binding CurrentClass.Name}" Style="{StaticResource BodyTextBlockStyle}"
                               VerticalAlignment="Center" />
                </Border>
                <!--CurrentClass.Description 프로퍼티 바인딩-->
                <Border Grid.Row="1" Grid.Column="1" Style="{StaticResource DetailBorderStyle}">
                    <TextBlock Text="{Binding CurrentClass.Description}" Style="{StaticResource BodyTextBlockStyle}"
                               VerticalAlignment="Center" />
                </Border>
            </Grid>

 

            <!--CurrentClass.People 프로퍼티 바인딩 HeaderTemplate, ItemTemplate는 MainPage에 있는 내용과 동일하다-->
            <ListView Grid.Column="1" ItemsSource="{Binding CurrentClass.People}" HorizontalContentAlignment="Stretch"
                      BorderBrush="White" BorderThickness="2" Margin="20,0,0,0">
                <ListView.HeaderTemplate>
                    <DataTemplate>
                        <Grid Height="40">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="15" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Name" />
                            <TextBlock Grid.Column="1" Style="{StaticResource BodyTextBlockStyle}" Text="Age" />
                            <TextBlock Grid.Column="2" Style="{StaticResource BodyTextBlockStyle}" Text="Sex" />
                        </Grid>
                    </DataTemplate>
                </ListView.HeaderTemplate>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                            </Grid.ColumnDefinitions>

                            <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}"
                                       Margin="0,0,4,0" />
                            <TextBlock Grid.Column="1" Text="{Binding Age}" Style="{StaticResource BodyTextBlockStyle}" />
                            <TextBlock Grid.Column="2" Text="{Binding Sex}" Style="{StaticResource BodyTextBlockStyle}" />
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

        </Grid>

        <!-- Back button and page title -->
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="120" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <!--GoBackCommand로 변경, ViewModelBase에 있는 커맨드-->
            <Button Margin="39,59,39,0"
                    Command="{Binding GoBackCommand, Mode=OneWay}"
                    Style="{StaticResource NavigationBackButtonNormalStyle}"
                    VerticalAlignment="Top"
                    AutomationProperties.Name="Back"
                    AutomationProperties.AutomationId="BackButton"
                    AutomationProperties.ItemType="Navigation Button" />
            <TextBlock Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}"
                       Grid.Column="1"
                       IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" />
        </Grid>
    </Grid>
</Page>

 

10. ClassDetailPage.xaml - Windows Phone 8.1

 

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ViewModels="using:UniversalSample.ViewModels"
    x:Class="UniversalSample.Views.ClassDetailPage"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

 

    <Page.Transitions>
        <TransitionCollection>
            <NavigationThemeTransition>
                <ContinuumNavigationTransitionInfo />
            </NavigationThemeTransition>
        </TransitionCollection>
    </Page.Transitions>

 

    <Page.Resources>
        <!--디자인 리소스들-->
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        </Style>

        <Style x:Key="HeadBorderStyle" TargetType="Border">
            <Setter Property="Background" Value="Gray" />
            <Setter Property="BorderBrush" Value="White" />
            <Setter Property="BorderThickness" Value="2" />
        </Style>
        <Style x:Key="DetailBorderStyle" TargetType="Border">
            <Setter Property="BorderBrush" Value="DarkGray" />
            <Setter Property="BorderThickness" Value="2" />
            <Setter Property="Padding" Value="10" />
        </Style>
    </Page.Resources>

 

    <!--디자인타임 뷰모델-->
    <d:Page.DataContext>
        <ViewModels:ClassDetailPageVM />
    </d:Page.DataContext>

 

    <Grid>
        <!--에니메이션 효과-->
        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition />
            </TransitionCollection>
        </Grid.ChildrenTransitions>


        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

 

        <!-- TitlePanel -->
        <StackPanel Grid.Row="0" Margin="24,17,0,28">
            <TextBlock x:Uid="Header" Text="{StaticResource AppName}" Style="{ThemeResource TitleTextBlockStyle}"
                       Typography.Capitals="SmallCaps" />
            <TextBlock Text="{Binding Title}" Margin="0,12,0,0" Style="{ThemeResource HeaderTextBlockStyle}" />
        </StackPanel>

 

        <Grid Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>

            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="193*" />
                    <ColumnDefinition Width="490*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Border Style="{StaticResource HeadBorderStyle}">
                    <TextBlock Text="Name" Style="{StaticResource TitleTextBlockStyle}" HorizontalAlignment="Center"
                               VerticalAlignment="Center" />
                </Border>
                <Border Grid.Row="1" Grid.Column="0" Style="{StaticResource HeadBorderStyle}">
                    <TextBlock Text="Description" Style="{StaticResource TitleTextBlockStyle}"
                               VerticalAlignment="Center" HorizontalAlignment="Center" />
                </Border>

                <!--CurrentClass.Name 프로퍼티 바인딩-->
                <Border Grid.Row="0" Grid.Column="1" Style="{StaticResource DetailBorderStyle}">
                    <TextBlock Text="{Binding CurrentClass.Name}" Style="{StaticResource BodyTextBlockStyle}"
                               VerticalAlignment="Center" />
                </Border>

                <!--CurrentClass.Description 프로퍼티 바인딩-->
                <Border Grid.Row="1" Grid.Column="1" Style="{StaticResource DetailBorderStyle}">
                    <TextBlock Text="{Binding CurrentClass.Description}" Style="{StaticResource BodyTextBlockStyle}"
                               VerticalAlignment="Center" />
                </Border>

            </Grid>

            <!--CurrentClass.People 프로퍼티 바인딩 HeaderTemplate, ItemTemplate는 MainPage에 있는 내용과 동일하다-->
            <ListView Grid.Row="1" ItemsSource="{Binding CurrentClass.People}" HorizontalContentAlignment="Stretch"
                      BorderBrush="White" BorderThickness="2" Margin="0,10,0,0">
                <ListView.HeaderTemplate>
                    <DataTemplate>
                        <Grid Height="40">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Name" />
                            <TextBlock Grid.Column="1" Style="{StaticResource BodyTextBlockStyle}" Text="Age" />
                            <TextBlock Grid.Column="2" Style="{StaticResource BodyTextBlockStyle}" Text="Sex" />
                        </Grid>
                    </DataTemplate>
                </ListView.HeaderTemplate>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                                <ColumnDefinition Width="0.3*" />
                            </Grid.ColumnDefinitions>

                            <TextBlock Text="{Binding Name}" Style="{StaticResource BodyTextBlockStyle}"
                                       Margin="0,0,4,0" />
                            <TextBlock Grid.Column="1" Text="{Binding Age}" Style="{StaticResource BodyTextBlockStyle}" />
                            <TextBlock Grid.Column="2" Text="{Binding Sex}" Style="{StaticResource BodyTextBlockStyle}" />
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
    </Grid>
</Page>

 

9번과 10번도 몇개의 차이점이 있는데, 우선 타이틀 부분이 버튼의 존재 여부 때문에 다르고, 컨텐츠의 배치를 윈폰은 세로로 만들었다.

 

 

 

 

이렇게 기본 셈플을 살펴보았다. 전체 소스를 받아서 살펴보면 더 쉽게 차이점을 알 수 있을 것이다.

 

전체 소스 

UniversalSample_20140617.zip

 

 

반응형
댓글