티스토리 뷰

반응형

처음 시작은 매우 간단했지만, 이제 본격적으로 작업을 시작해 보려고 한다. 처음 시작이 네비게이션인데 작업을 하고 보니, 추가된 코드나 개념들에 대한 이해가 필요할 것 같다. 하지만, MVVM 패턴이나 여기 작성된 모든 내용을 반드시 알아야지만 앱을 만들 수 있는 것은 아님을 미리 이야기한다.

 

0. 환경

 

Visual Studio 2013 Update 2

Windows 8.1

지난번 사용한 UniversalSample 소스

 

1. 기본 템플릿에 존재하는 NavigationHelper를 직접 구현

기본 템플릿에 있는 NavigationHelper는 뷰모델에서 사용하기에 적합하지 않은 구조를 가지고 있다. 그래서 난 이전부터 만들어서 사용하고 있던 FrameHelper를 수정해서 구현했다.

 

기초적인 내용만 구현해 놓았다. 다음에는 여기에서 더 수정을 해야 완성된다.

 

    /// <summary>
    /// 네비게이션 관련 헬퍼
    /// </summary>
    public class FrameHelper
    {
        private static FrameHelper _instance;
        /// <summary>
        /// Singleton
        /// </summary>
        public static FrameHelper Instance
        {
            get
            {
                return _instance = _instance ?? new FrameHelper();
            }
        }

        /// <summary>
        /// Frame object
        /// </summary>
        internal Frame _frame;

        /// <summary>
        /// Navigated Command
        /// </summary>
        public ICommand NavigatedCommand { get; set; }
   
        /// <summary>
        /// Navigating Command
        /// </summary>
        public ICommand NavigatingCommand { get; set; }

        /// <summary>
        /// RegisterFrame
        /// </summary>
        /// <param name="frame"></param>
        public void RegisterFrame(Frame frame)
        {
            _frame = frame;
            if (_frame == null) return;

            _frame.Navigated -= Frame_Navigated;
            _frame.Navigating -= Frame_Navigating;

            _frame.Navigated += Frame_Navigated;
            _frame.Navigating += Frame_Navigating;
        }

        /// <summary>
        /// Navigation Type
        /// </summary>
        /// <param name="navigationType"></param>
        /// <param name="navigationParameter"></param>
        /// <returns></returns>
        public bool Navigation(Type navigationType, object navigationParameter)
        {
            var returnValue = false;
            if (_frame == null) return false;

            returnValue = _frame.Navigate(navigationType, navigationParameter);

            return returnValue;
        }

        /// <summary>
        /// GoBack
        /// </summary>
        /// <returns></returns>
        public bool GoBack()
        {
            if (_frame.CanGoBack == true)
            {
                _frame.GoBack();
            }

            return true;
        }

        /// <summary>
        /// Navigation From
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Frame_Navigating(object sender, NavigatingCancelEventArgs e)
        {
            if(NavigatingCommand != null)
            {
                NavigatingCommand.Execute(e);
            }
           
        }

        /// <summary>
        /// Navigation To
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Frame_Navigated(object sender, NavigationEventArgs e)
        {
            if (NavigatedCommand != null)
            {
                NavigatedCommand.Execute(e);
            }
        }

    }

 

2. 프레임 등록

윈8.1이나 윈폰8.1이나 모두 페이지 네비게이션은 Frame위에서 이루어 진다. 그래서 앱이 생성될 때 App.xaml.cs를 보면 Frame을 생성하는 곳이 존재한다.

 

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;

 

                FrameHelper.Instance.RegisterFrame(rootFrame);

 

위의 코드를 추가해서 프레임을 FrameHelper에 등록한다.

 

3. ViewModel 추가

이전 소스는 MainPage.xaml, MainPage.xaml.cs 2개의 파일을 사용했지만, MVVM 패턴을 사용하기 위해서 MainPageVM.cs 파일을 추가하고 작업을 진행한다.

 

ViewModel을 추가하기 위해서 ViewModelBase.cs를 추가하고 여러가지 클래스들을 추가했다.

 

Commons 폴더에 추가된 내용(더 자세한 내용은 소스 파일을 참고한다.)

* BindableBase.cs

* RelayCommand.cs

* ViewModelBase.cs : 기본적으로 사용하는 여러가지 커맨드를 가지고 있다.

여기에 적지 않는 내용들은 차후에 사용 예정이거나, 참고용으로 추가해 놓았다.

 

4. MainPageVM.cs

뷰모델이 생성되면서 프레임헬퍼에 NavigatingCommand, NavigatedCommand 두개의 커맨드에 뷰모델베이스에 있는 커맨드를 연결한다.

 

    public class MainPageVM : ViewModelBase
    {
        public MainPageVM()
        {
            Title = "Hello World First page";

            FrameHelper.Instance.NavigatingCommand = NavigatingCommand;
            FrameHelper.Instance.NavigatedCommand = NavigatedCommand;
        }

        /// <summary>
        /// Navigating
        /// </summary>
        /// <param name="args"></param>
        protected override void Navigating(object args)
        {
            System.Diagnostics.Debug.WriteLine(Title + " Navigating");
        }

        /// <summary>
        /// Navigated
        /// </summary>
        /// <param name="arg"></param>
        protected override void Navigated(object arg)
        {
            System.Diagnostics.Debug.WriteLine(Title + " Navigated");
        }

        private RelayCommand _nextPageCommand;
        /// <summary>
        /// 다음 페이지로 이동
        /// </summary>
        public RelayCommand NextPageCommand
        {
            get { return _nextPageCommand = _nextPageCommand ?? new RelayCommand(
                args =>
                {
                    if (FrameHelper.Instance.Navigation(typeof (SecondPage), null) == false)
                    {
                        var msg = new MessageDialog("Error");
                        msg.ShowAsync();
                    };
                }); }
        }

    }
}

 

5. MainPage.xaml.cs

 

앱에서 사용하는 네비게이션 방식이 Type네비게이션이므로, 페이지로 네비게이션이나 네비게이션백이 되었을 때 항상 새로 페이지를 생성한다.

 

    public sealed partial class MainPage : Page
    {
        public MainPageVM ViewModel
        {
            get { return DataContext as MainPageVM; }
            set { DataContext = value; }
        }

        public MainPage()
        {
            this.InitializeComponent();

            ViewModel = ViewModel ?? new MainPageVM();        //페이지가 생성되면 뷰모델도 생성한다. 다음에 개선할 부분이다.
        }
    }

 

6. MainPage.xaml

 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center"
       Style="{StaticResource HeaderTextBlockStyle}" FontFamily="Global User Interface" TextWrapping="Wrap"/>

            <Button Content="Navigation NextPage" HorizontalAlignment="Center" Margin="0,20,0,0" Command="{Binding NextPageCommand, Mode=OneWay}"/>
        </StackPanel>
    </Grid>

 

첫번째 페이지의 수정이 완료되었다. 두번째 페이지에 대한 작업도 진행해보자

 

7. SecondPage.xaml

Page를 추가하고, 아래와 같이 작업한다. 첫번째 페이지와 크게 다르지 않다.

 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center"
    Style="{StaticResource HeaderTextBlockStyle}" FontFamily="Global User Interface" TextWrapping="Wrap"/>
            <Button Content="Navigation Back" HorizontalAlignment="Center" Margin="0,20,0,0" Command="{Binding GoBackCommand, Mode=OneWay}"/>
        </StackPanel>
    </Grid>

 

8. SecondPage.xaml.cs

    public sealed partial class SecondPage : Page
    {
        public SecondPageVM ViewModel
        {
            get { return DataContext as SecondPageVM; }
            set { DataContext = value; }
        }

        public SecondPage()
        {
            this.InitializeComponent();

            ViewModel = ViewModel ?? new SecondPageVM();
        }
    }

 

9. SecondPageVM.cs

 

    public class SecondPageVM : ViewModelBase
    {
        public SecondPageVM()
        {
            Title = "Hello World, Second page";

            FrameHelper.Instance.NavigatingCommand = NavigatingCommand;
            FrameHelper.Instance.NavigatedCommand = NavigatedCommand;

        }

        protected override void Navigating(object args)
        {
            System.Diagnostics.Debug.WriteLine(Title + " Navigating");
        }

        protected override void Navigated(object arg)
        {
            System.Diagnostics.Debug.WriteLine(Title + " Navigated");
        }

    }

 

10. 실행

 

 

 

 

* Debug - Output창 내용

Hello World Fist page Navigated

 

첫 페이지로 Navigated되어서 표시가 된다.

Navigation Next Page 버튼을 클릭해보자

 

 

* Debug - Output창 내용

Hello World Fist page Navigating
Hello World, Second page Navigated

 

첫번째 페이지에서 Navigating이 발생하고, 두번째 페이지에서 Navigated가 발생한다.

 

Navigation Back버튼을 클릭해보자

 

Hello World, Second page Navigating
Hello World Fist page Navigated

 

두번째 페이지에서 Navigating이 발생하고, 첫번째 페이지에서 Navigated가 발생한다. 이 경우 네비게이션 파라메터의 Mode는 Back이 된다.

 

폰 에물레이터에서도 확인 결과 동일한 동작을한다. 그런데 Back키를 누른 경우에 앱 전환이 일어나는데 이부분은 추가 작업이 필요한 것 같다.

 

11. 매우 간단한 작업이지만, 기본적인 내용이기 때문에 네비게이션의 동작 원리를 눈여겨서 보아야 한다.

 

12. 소스 

UniversalSample_20140519.zip

 

 

 

반응형
댓글