정보
요청한 주제가 아래에 표시됩니다. 그러나 이 주제는 이 라이브러리에 포함되지 않습니다.

연습: Windows Phone 8의 OData(MVVM 포함) 사용

2014-06-18

적용 대상: Windows Phone 8 및 Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

 

이 연습에서는 OData(Open Data Protocol)를 구현하는 데이터 서비스에 액세스하기 위해 MVVM(Model-View-ViewModel) 디자인 패턴을 구현하는 간단한 앱을 빌드합니다. MVVM은 사용자 인터페이스에서 데이터를 분리하는 한 방법으로, OData 피드를 사용하는 앱에 중요합니다. 이 샘플에서 데이터 모델은 샘플 Northwind 데이터 서비스를 사용하여 생성된 프록시 클래스 집합이고 뷰는 XAML 사용자 컨트롤입니다. 데이터 모델과 사용자 인터페이스 간의 연결인 ViewModel은 Windows Phone 의 OData 클라이언트 라이브러리를 사용하는 C# 클래스입니다. MVVM 디자인 패턴에 대한 자세한 내용은 Windows Phone 8의 Model-View-ViewModel 패턴 구현을 참조하세요. Windows Phone의 OData 클라이언트는 Windows Phone SDK에 포함되지 않으므로 WCF Data Services Tools for Visual Studio에서 별도로 다운로드해야 합니다.

이 연습에서 만드는 앱을 사용하면 샘플 Northwind 데이터 서비스의 Northwind 데이터를 볼 수 있습니다. Windows Phone 데이터 바인딩된 응용프로그램 템플릿을 시작 지점으로 사용하여 이 앱을 만들고 템플릿에서 제공된 샘플 데이터를 OData 서비스의 Northwind 데이터로 바꿉니다. 이 앱의 완성된 버전은 이 MSDN 샘플 갤러리 페이지에서 다운로드할 수 있습니다.

이 연습에서는 다음 작업을 수행합니다.

이 프로세스에서는 다음 코드 파일을 수정하거나 만듭니다.

  1. ViewModels\MainViewModel.cs: 앱의 기본 ViewModel을 수정하여 Northwind 샘플 데이터 서비스의 데이터에 액세스합니다. ViewModel은 앱 상태를 저장 및 복원하고 읽기/쓰기 데이터 서비스에 변경 사항을 저장하는 메서드도 제공합니다.

  2. MainPage.xaml: 앱의 기본 페이지를 수정하여 기존 StackPanel 및 Grid 컨트롤을 업데이트합니다.

  3. DetailsPage.xaml: 앱의 세부 정보 페이지를 수정하여 페이지의 레이아웃 루트인 기존 Grid 컨트롤을 업데이트합니다.

  4. App.xaml.cs: 이 파일을 수정하여 앱 전체에서 액세스할 수 있는 정적 ViewModel을 만듭니다. SaveState 및 RestoreState 메서드를 호출하여 앱 상태 변경 중에 앱 상태를 유지하고 복원합니다.

  5. MainPage.xaml.cs: 이 페이지를 수정하여 취소새로 고침 버튼 클릭을 처리합니다.

  6. DetailsPage.xaml.cs: 이 페이지를 수정하여 저장 버튼 클릭을 처리합니다.

이 연습을 완료하려면 Windows Phone SDK 가 설치되어 있어야 합니다. 자세한 내용은 SDK 다운로드를 참조하세요.

Windows Phone의 OData 클라이언트도 설치해야 합니다. 이 클라이언트는 WCF Data Services Tools for Visual Studio에 있습니다.

이 연습에서는 OData 웹 사이트에 게시된 Northwind 샘플 데이터 서비스를 사용합니다. 이 샘플 데이터 서비스는 읽기 전용이므로 변경 내용을 저장하려고 하면 오류가 반환됩니다. Northwind 데이터 서비스에 변경 사항을 저장하는 이 연습의 최종 섹션을 완료하려면 Northwind 데이터 서비스의 고유한 읽기/쓰기 버전을 구현해야 합니다. 이렇게 하려면 방법: Northwind 데이터 서비스 만들기(WCF 데이터 서비스) 항목의 절차를 완료합니다.

먼저 이름이 MVVMODataTestApp인 새 Windows Phone 앱 프로젝트를 만듭니다. 이후 단계에서 앱 이름을 MVVMODataTestApp으로 가정하는 코드를 앱에 추가합니다. 앱에 다른 이름을 선택한 경우 코드에서 네임스페이스 참조를 변경해야 합니다.

앱 프로젝트를 만들려면

  1. Visual Studio의 파일 메뉴에서 새로 만들기를 가리킨 다음 프로젝트를 클릭합니다.

    새 프로젝트 대화 상자가 나타납니다.

  2. 왼쪽 창에서 템플릿 항목이 확장되어 있는지 확인하고 Visual C# 또는 Visual Basic을 선택합니다. Visual Basic은 기타 언어 아래에 있습니다.

  3. 프로젝트 형식 목록에서 Windows Phone 데이터 바인딩된 앱을 클릭합니다.

  4. 이름 상자에 MVVMODataTestApp를 입력합니다.

  5. 확인을 클릭합니다.

    Windows Phone 플랫폼 선택 대화 상자가 나타납니다.

  6. 대상 Windows Phone OS 버전을 확장하고 Windows Phone OS 8.0을 클릭한 다음 확인을 클릭합니다.

    새 앱 프로젝트가 생성되고 Visual Studio에서 열립니다.

  7. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

    참고참고:

    Windows Phone 데이터 바인딩된 앱 프로젝트 템플릿에는 샘플 데이터를 포함하는 XAML 파일이 포함되어 있으므로 이 프로젝트는 변경하기 전에 실행됩니다. 샘플 데이터를 사용하여 앱의 동작을 확인하려면 디버그 메뉴에서 디버깅 시작을 클릭합니다(F5).

  8. 솔루션 탐색기에서 SampleData 폴더를 마우스 오른쪽 버튼으로 클릭하고 프로젝트에서 제외를 클릭합니다.

    그러면 프로젝트에서 샘플 데이터 폴더가 제거됩니다. 이 폴더를 데이터 서비스의 Northwind 데이터로 바꾸겠습니다.

이 절차에서는 Northwind 샘플 데이터 서비스에 참조를 추가합니다. Visual Studio의 서비스 참조 추가 도구를 사용하는 경우 이 도구에서 Northwind 데이터 서비스의 데이터 모델을 나타내는 클라이언트 데이터 클래스 집합을 생성합니다. 이러한 클래스는 이 앱에 대한 MVVM 패턴의 모델입니다.

프로젝트에 데이터 서비스 참조를 추가하려면

  1. MVVMODataTestApp 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 서비스 참조 추가를 클릭합니다.

    이렇게 하면 서비스 참조 추가 대화 상자가 표시됩니다.

  2. 주소 텍스트 상자에 다음 URI 값을 입력하고 이동을 클릭합니다.

    http://services.odata.org/Northwind/Northwind.svc/
    

    그러면 읽기 전용 공용 Northwind 샘플 데이터 서비스에서 메타데이터 문서가 다운로드됩니다.

    Northwind 데이터 서비스의 고유한 읽기/쓰기 구현을 사용하는 경우 대신 이 서비스에 URI 값을 제공해야 합니다. 사용자 지정 Northwind 데이터 서비스에 대한 프로젝트가 MVVMODataTestApp 샘플 프로젝트와 동일한 솔루션에 있는 경우 검색 버튼을 클릭하여 Northwind 데이터 서비스의 URI를 검색합니다.

  3. 네임스페이스 텍스트 상자에 Northwind를 입력한 다음 확인을 클릭합니다.

    그러면 데이터 서비스 리소스에 액세스하고 상호 작용하는 데 사용되는 데이터 클래스가 포함된 새 코드 파일이 프로젝트에 개체로 추가됩니다. 데이터 클래스는 MVVMODataTestApp.Northwind 네임스페이스에 생성됩니다. System.Data.Services.Client.WP80.dll 어셈블리에 대한 참조도 추가됩니다.

  4. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

이 절차에서는 프로젝트 템플릿에서 생성된 MainViewModel 클래스를 정의하는 코드를 형식화된 DataServiceCollection을 노출하는 버전으로 바꿉니다. ObservableCollection<T>에서 상속되는 이 컬렉션을 사용하면 컬렉션의 Customer 개체가 변경될 때 ViewModel에서 해당 뷰에 알릴 수 있으므로 변경 사항에 따라 뷰가 사용자 인터페이스를 업데이트할 수 있습니다.

MainViewModel에는 Customer 데이터를 컬렉션에 로드하기 위해 데이터 서비스를 쿼리하는 코드도 포함되어 있습니다. 이후 절차에서는 변경 사항을 저장하고, 요청을 취소하고, 앱 상태를 저장하는 데 필요한 OData 클라이언트 라이브러리의 기능을 노출하는 다른 메서드를 추가합니다.

ViewModel을 업데이트하려면

  1. 솔루션 탐색기에서 ViewModels 폴더를 확장하고 MainViewModel.cs 또는 MainViewModel.vb 파일을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

    코드 파일이 표시됩니다.

  2. MainViewModel 클래스의 코드를 다음 코드로 바꿉니다.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data.Services.Client;
    using System.Linq;
    using System.Windows;
    using MVVMODataTestApp.Northwind;
    
    namespace MVVMODataTestApp
    {
        public class MainViewModel : INotifyPropertyChanged
        {
    
            // This is the URI of the public, read-only Northwind data service. 
            // To make updates and save changes, replace this URI 
            // with your own Northwind service implementation.
            private static readonly Uri _rootUri = 
                new Uri("http://services.odata.org/Northwind/Northwind.svc/");
    
            // Define the typed DataServiceContext.
            private NorthwindEntities _context;
    
            // Define the binding collection for Customers.
            private DataServiceCollection<Customer> _customers;
    
            // Gets and sets the collection of Customer objects from the feed.
            // This collection is used to bind to the UI (View).
            public DataServiceCollection<Customer> Customers
            {
                get { return _customers; }
    
                private set
                {
                    // Set the Titles collection.
                    _customers = value;
    
                    // Register a handler for the LoadCompleted callback.
                    _customers.LoadCompleted += OnCustomersLoaded;
    
                    // Raise the PropertyChanged events.
                    NotifyPropertyChanged("Customers");
                }
            }
    
            // Used to determine whether the data is loaded.
            public bool IsDataLoaded { get; private set; }
    
            // Loads data when the application is initialized.
            public void LoadData()
            {
                // Instantiate the context and binding collection.
                _context = new NorthwindEntities(_rootUri);
                Customers = new DataServiceCollection<Customer>(_context);
    
                // Specify an OData query that returns all customers.
                var query = from cust in _context.Customers
                            select cust;
    
                // Load the customer data.
                Customers.LoadAsync(query);
            }
    
            // Displays data from the stored data context and binding collection 
            public void LoadData(NorthwindEntities context,
                DataServiceCollection<Customer> _customers)
            {
                _context = context;
                Customers = _customers;
    
                IsDataLoaded = true;
            }
    
            // Handles the DataServiceCollection<T>.LoadCompleted event.
            private void OnCustomersLoaded(object sender, LoadCompletedEventArgs e)
            {
                // Make sure that we load all pages of the Customers feed.
                if (Customers.Continuation != null)
                {
                    Customers.LoadNextPartialSetAsync();
                }
                IsDataLoaded = true;
            }
    
            // Declare a PropertyChanged for the UI to register 
            // to get updates from the ViewModel.
            public event PropertyChangedEventHandler PropertyChanged;
    
            // Notifies the binding about a changed property value.
            private void NotifyPropertyChanged(string propertyName)
            {
                var propertyChanged = PropertyChanged;
                if (propertyChanged != null)
                {
                    // Raise the PropertyChanged event.
                    propertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    
    
  3. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

이 절차에서는 프로젝트 템플릿에서 생성된 MainPage.xaml 파일을 업데이트하여 ListBox 컨트롤인 이 페이지의 뷰가 MainViewModel에서 노출된 Customers 속성에 바인딩되도록 합니다.

MainPage.xaml 파일을 업데이트하려면

  1. 솔루션 탐색기에서 MainPage.xaml 파일을 마우스 오른쪽 버튼으로 클릭한 다음 뷰 디자이너를 클릭합니다.

    그러면 파일이 열려 디자인하고 편집할 수 있습니다.

  2. 최상위 phone 요소에서 d:DataContext 요소를 제거합니다.

    MainViewModelSampleData.xaml 샘플 데이터 파일은 디자인하는 동안 바인딩에 더 이상 사용할 수 없으므로 이 작업을 수행해야 합니다.

  3. TitlePanel이라는 StackPanel 요소를 찾아 다음 코드로 바꿉니다.

    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
                <TextBlock x:Name="ApplicationTitle" Text="NORTHWIND TRADERS" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="Customers" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
    
  4. ContentPanel이라는 Grid 요소를 찾아 다음 코드로 바꿉니다.

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <ListBox x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Customers}" SelectionChanged="MainLongListSelector_SelectionChanged">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="0,0,0,17" Width="432" Height="100">
                        <TextBlock Text="{Binding CompanyName}" TextWrapping="NoWrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                        <TextBlock Text="{Binding ContactName}" TextWrapping="NoWrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                        <TextBlock Text="{Binding Phone}" TextWrapping="NoWrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
    
    

    ListBox(뷰)는 MainViewModel 클래스의 Customers 속성에 바인딩되고, ItemTemplate의 개별 컨트롤은 컬렉션의 Customers 개체 속성에 바인딩됩니다. 최상위 바인딩은 프로젝트 템플릿에 있는 MainPage.xaml 파일의 코드 숨김 페이지에서 다음 코드 줄로 정의됩니다.

    // Set the data context of the listbox control to the sample data.
    DataContext = App.ViewModel;
    
  5. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

MainPage.xaml.cs 코드 숨김 페이지를 업데이트하려면

  • MainLongListSelector_SelectionChanged 메서드를 다음 코드로 업데이트합니다.

    // Handle selection changed on LongListSelector
    private void MainLongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        // If selected item is null (no selection) do nothing
        if (MainLongListSelector.SelectedItem == null)
        return;
    
        // Navigate to the new page
        NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainLongListSelector.SelectedIndex , UriKind.Relative));
    
        // Reset selected item to null (no selection)
        MainLongListSelector.SelectedItem = null;
    }
    

    여기서는 이 샘플의 ItemViewModel 클래스를 사용하지 않으므로 ItemViewModel의 ID가 아닌 선택한 항목의 인덱스를 사용하도록 NavigationService.Navigate 호출을 수정합니다.

이 절차에서는 프로젝트 템플릿에서 생성된 DetailsPage.xaml 및 코드 숨김 파일을 모두 업데이트하여 이 페이지의 요소가 MainPage.xml의 ListView 컨트롤에서 선택한 Customers 개체에 바인딩되도록 합니다.

DetailsPage.xaml 및 코드 숨김 페이지를 업데이트하려면

  1. 솔루션 탐색기에서 DetailsPage.xaml 파일을 두 번 클릭합니다.

    그러면 파일이 열려 디자인하고 편집할 수 있습니다.

  2. 최상위 phone 요소에서 d:DataContext 요소를 제거합니다.

    MainViewModelSampleData.xaml 샘플 데이터 파일은 디자인하는 동안 바인딩에 더 이상 사용할 수 없으므로 이 작업을 수행해야 합니다.

  3. LayoutRoot라는 Grid 요소를 찾아 다음 코드로 바꿉니다.

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="PageTitle" Text="NORTHWIND TRADERS" 
                        Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="ListTitle" Text="{Binding CompanyName}" Margin="9,-7,0,0" 
                        Style="{StaticResource PhoneTextTitle1Style}" />
        </StackPanel>
        <ScrollViewer Grid.Row="1">
            <Grid x:Name="ContentPanel" Margin="12,0,12,0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="165"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <TextBlock Text="Contact name:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="0" Grid.Column="0"/>
                <TextBlock Text="Contact title:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="1" Grid.Column="0"/>
                <TextBlock Text="Phone number:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="2" Grid.Column="0"/>
                <TextBlock Text="Address:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="3" Grid.Column="0"/>
                <TextBlock Text="City:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="4" Grid.Column="0"/>
                <TextBlock Text="Region:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="5" Grid.Column="0"/>
                <TextBlock Text="Postal code:" Style="{StaticResource PhoneTextNormalStyle}" 
                        VerticalAlignment="Center" Grid.Row="6" Grid.Column="0"/>
                <TextBox Text="{Binding ContactName, Mode=TwoWay}" Grid.Row="0" Grid.Column="1"/>
                <TextBox Text="{Binding ContactTitle, Mode=TwoWay}" Grid.Row="1" Grid.Column="1"/>
                <TextBox Text="{Binding Phone, Mode=TwoWay}" Grid.Row="2" Grid.Column="1"/>
                <TextBox Text="{Binding Address, Mode=TwoWay}" Grid.Row="3" Grid.Column="1"/>
                <TextBox Text="{Binding City, Mode=TwoWay}" Grid.Row="4" Grid.Column="1"/>
                <TextBox Text="{Binding Region, Mode=TwoWay}" Grid.Row="5" Grid.Column="1"/>
                <TextBox Text="{Binding PostalCode, Mode=TwoWay}" Grid.Row="6" Grid.Column="1"/>
            </Grid>
        </ScrollViewer>
    </Grid>
    
    
    

    이 코드에는 MainViewModel에서 선택한 Customer 개체의 속성에 바인딩된 TextBox 컨트롤(뷰)이 포함되어 있습니다. 바인딩은 양방향이므로 읽기/쓰기 버전의 Northwind 데이터 서비스에 액세스하는 경우 업데이트가 가능합니다.

  4. 솔루션 탐색기에서 DetailsPage.xaml.cs 또는 DetailsPage.xaml.vb 파일을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

    그러면 파일이 열려 편집할 수 있습니다.

  5. OnNavigatedTo 메서드 구현을 찾아 다음 코드로 바꿉니다.

    // When the page is navigated to, set the data context 
    // to the selected item in the list.
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        
     if (DataContext == null)
                {
                    string selectedIndex = "";
                    if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
                    {
                        int index = int.Parse(selectedIndex);
                        DataContext = App.ViewModel.Customers[index];
                    }
                }
    
    
    
    
    
    }
    
    

    뷰의 바인딩 컨텍스트가 MainViewModel에서 노출된 현재 선택한 Customer로 설정됩니다.

  6. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

  7. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

이 절차에서는 페이지 상태를 유지하는 코드를 추가합니다. 사용자가 앱을 벗어난 부분을 탐색할 때 일반적으로 앱이 유휴 상태로 전환됩니다. 이 상태에서는 앱이 메모리에 유지되므로 사용자가 앱으로 돌아가면 거의 즉시 계속될 수 있습니다. 이 빠른 앱 전환은 자동으로 사용할 수 있습니다. 그러나 앱이 유휴 상태인 동안 종료될 수 있습니다. 이러한 상태 변경을 처리하도록 앱을 디자인하는 것이 중요합니다. 자세한 내용은 Windows Phone 8의 앱 활성화 및 비활성화를 참조하세요.

Windows Phone 용 OData 클라이언트에는 이러한 상태 전환을 관리하는 데 사용되는 DataServiceState 클래스가 있습니다. ViewModel은 앱이 이 기능에 액세스하여 ViewModel에서 데이터 상태를 유지할 수 있도록 하는 메서드도 노출해야 합니다.

페이지 상태를 유지하려면

  1. 솔루션 탐색기에서 ViewModels 폴더를 확장하고 MainViewModel.cs 또는 MainViewModel.vb 파일을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

  2. MainViewModel 클래스에서 다음 SaveState 메서드를 추가합니다.

    // Return a string serialization of the application state.
    public string SaveState()
    {
        if (App.ViewModel.IsDataLoaded)
        {
            // Create a new dictionary to store binding collections. 
            var collections = new Dictionary<string, object>();
    
            // Add the current Customers binding collection.
            collections["Customers"] = Customers;
    
            // Return the serialized context and binding collections.
            return DataServiceState.Serialize(_context, collections);
        }
        else
        {
            return string.Empty;
        }
    }
    
    
  3. MainViewModel 클래스에서 다음 RestoreState 메서드를 추가합니다.

    // Restores the view model state from the supplied state serialization.
    public void RestoreState(string appState)
    {
        // Create a dictionary to hold any stored binding collections.
        Dictionary<string, object> collections;
    
        if (!string.IsNullOrEmpty(appState))
        {
            // Deserialize the DataServiceState object.
            DataServiceState state
                = DataServiceState.Deserialize(appState);
    
            // Restore the context and binding collections.
            var context = state.Context as NorthwindEntities;
            collections = state.RootCollections;
    
            // Get the binding collection of Customer objects.
            DataServiceCollection<Customer> customers
                = collections["Customers"] as DataServiceCollection<Customer>;
    
            // Initialize the application with stored data. 
            App.ViewModel.LoadData(context, customers);
        }
    }
    
    
  4. 솔루션 탐색기에서 App.xaml을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

    코드 숨김 파일이 코드 편집기에 열립니다.

  5. Application_Deactivated 메서드를 찾아 다음 코드로 바꿉니다.

    // Code to execute when the application is deactivated (sent to the background).
    // This code will not execute when the application is closing.
    private void Application_Deactivated(object sender, DeactivatedEventArgs e)
    {
        if (App.ViewModel.IsDataLoaded)
        {
            // Store application state in the state dictionary.              
            PhoneApplicationService.Current.State["ApplicationState"]
                = ViewModel.SaveState();
        }
    }
    
    

    이 코드는 ViewModel에서 SaveState 메서드를 호출하며, 이 메서드는 DataServiceState 개체의 직렬화인 문자열을 반환합니다.

  6. Application_Activated 메서드를 찾아 다음 코드로 바꿉니다.

    // Code to execute when the application is activated (brought to the foreground).
    // This code will not execute when the application is first launched.
    private void Application_Activated(object sender, ActivatedEventArgs e)
    {
        // If data is not still loaded, try to get it from the state store.
        if (!ViewModel.IsDataLoaded)
        {
            if (PhoneApplicationService.Current.State.ContainsKey("ApplicationState"))
            {
                // Get back the serialized data service state from the dictionary.
                string appState =
                    PhoneApplicationService.Current.State["ApplicationState"]
                    as string;
    
                // Use the returned dictionary to restore the state of the data service.
                App.ViewModel.RestoreState(appState);
            }
            else
            {
                // Load the data since it is not persisted in the state dictionary. 
                App.ViewModel.LoadData();
            }
        }
    }
    
    

    이 코드는 상태 사전의 직렬화된 DataServiceState 개체를 사용하여 ViewModel에서 RestoreState 메서드를 호출합니다. 상태 사전에서 이 문자열을 사용할 수 없는 경우 대신 데이터 서비스에서 데이터가 로드됩니다.

  7. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

  8. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

이 앱에서 사용자는 취소 버튼을 탭하여 LoadAsync() 작업을 취소하거나 새로 고침 버튼을 탭하여 데이터 서비스에서 데이터를 다시 로드할 수 있습니다. 또한 사용자는 저장 버튼을 탭하여 고객 세부 정보 데이터에 대한 업데이트를 읽기/쓰기 데이터 서비스로 보낼 시기를 선택합니다. 이 절차에서는 취소새로 고침 버튼을 MainPage의 앱 바에 추가하고 저장 버튼을 DetailPage의 앱 바에 추가합니다. DataServiceContext 클래스의 이러한 기능은 MainViewModel의 메서드로 노출됩니다.

중요중요:

변경 사항을 저장하면 읽기 전용 공용 Northwind 샘플 데이터 서비스에 액세스할 때 오류가 발생합니다. 데이터 서비스에 변경 사항을 저장하려면 고유한 읽기/쓰기 버전의 Northwind 샘플 데이터 서비스를 만들어야 합니다. 또한 고객 Northwind 데이터 서비스의 Customers 피드가 쓰기 가능한지 확인해야 합니다. 이렇게 하려면 SetEntitySetAccessRule 데이터 서비스 구성 메서드에 전달된 EntitySetRights 값을 EntitySetRights.AllRead로 변경합니다.

버튼 아이콘에는 표준 Windows Phone 아이콘 중 하나를 사용합니다. 자세한 내용은 Windows Phone의 응용프로그램 모음을 참조하세요.

아이콘 파일을 추가하려면

  1. 솔루션 탐색기에서 MVVMTestApp 프로젝트를 마우스 오른쪽 버튼으로 클릭하고 추가를 가리킨 다음 기존 항목을 클릭합니다.

    그러면 기존 항목 추가 대화 상자가 나타납니다.

  2. 다음 위치로 이동하여 표준 아이콘을 찾습니다.

    C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Icons\dark

  3. 저장 아이콘을 선택한 다음 추가를 클릭합니다.

  4. 1-3단계를 반복하여 취소새로 고침 아이콘도 추가합니다.

  5. 솔루션 탐색기에서 새 파일 save.png를 선택합니다.

  6. 속성 창에서 새 파일에 대해 다음 속성을 설정합니다.

    속성

    빌드 작업

    Content

    출력 디렉터리로 복사

    변경된 내용만 복사

    파일 이름

    AppBarSave.png

  7. 5-6단계를 반복하여 새 파일 cancel.png에 대해 다음 속성을 설정합니다.

    속성

    빌드 작업

    Content

    출력 디렉터리로 복사

    변경된 내용만 복사

    파일 이름

    AppBarCancel.png

  8. 5-6단계를 반복하여 새 파일 refresh.png에 대해 다음 속성을 설정합니다.

    속성

    빌드 작업

    Content

    출력 디렉터리로 복사

    변경된 내용만 복사

    파일 이름

    AppBarRefresh.png

  9. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

  10. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

MainViewModel에서 취소, 새로 고침 및 변경 사항 저장 기능을 노출하려면

  1. 솔루션 탐색기에서 ViewModels 폴더를 확장하고 MainViewModel.cs 또는 MainViewModel.vb 파일을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

  2. MainViewModel 클래스에서 LoadAsync() 작업을 취소하고, 데이터 서비스에서 데이터를 다시 로드하고, 비동기적으로 변경 사항을 데이터 서비스로 보내는 다음 메서드를 추가합니다.

    public void CancelCustomersAsyncLoad()
    {
        // Call the CancelAsyncLoad method on the binding collection.
        this.Customers.CancelAsyncLoad();
    }
    
    
    public void SaveChanges()
    {
        // Start the save changes operation. 
        this._context.BeginSaveChanges(OnChangesSaved, this._context);
    }
    
    private void OnChangesSaved(IAsyncResult result)
    {
        // Use the Dispatcher to ensure that the 
        // asynchronous call returns in the correct thread.
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            this._context = result.AsyncState as NorthwindEntities;
    
            try
            {
                // Complete the save changes operation.
                this._context.EndSaveChanges(result);
            }
            catch (DataServiceRequestException ex)
            {
                // Ideally, we should not create a UI element 
                // from the ViewModel. A better way is to use a 
                // service or event to report exceptions to the view.
                MessageBox.Show(string.Format(
                        "{0} The target Northwind data service ('{1}') is read-only.",
                        ex.Message, this._context.BaseUri));
            }
        }
        );
    }
    
    
    중요중요:

    변경 사항을 저장하면 읽기 전용 공용 Northwind 샘플 데이터 서비스에 액세스할 때 오류가 발생합니다. 정상적으로 실행되는 동안 클라이언트 라이브러리는 메시지 상자를 표시하기 위해 처리되는 DataServiceRequestException을 발생시킵니다. 디버깅 도중 읽기 전용 데이터 서비스에 변경 사항을 저장하려고 하면 클라이언트 라이브러리의 버그 때문에 처리되지 않은 예외가 발생합니다. 고유한 읽기/쓰기 버전의 Northwind 데이터 서비스를 구현하려면 방법: Northwind 데이터 서비스 만들기(WCF 데이터 서비스) 항목의 절차를 완료합니다.

    public void Refresh()
    {
        // Cache the current merge option and change 
        // it to MergeOption.OverwriteChanges.
        MergeOption cachedOption = _context.MergeOption;
        _context.MergeOption = MergeOption.OverwriteChanges;
    
        // Reload data from the data service.
        this.LoadData();
    
        // Reset the merge option.
        _context.MergeOption = cachedOption;
    }
    
    
  3. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

  4. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

앱 바를 추가하려면

  1. 솔루션 탐색기에서 MainPage.xaml을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

    코드 숨김 파일이 코드 편집기에 열립니다.

  2. MainPage 클래스에서 다음 메서드를 추가합니다.

    private void AppBarCancel_Click(object sender, EventArgs e)
    {
        App.ViewModel.CancelCustomersAsyncLoad();
    }
    
    
    private void AppBarRefresh_Click(object sender, EventArgs e)
    {
        // Reload the data from the OData service.
        App.ViewModel.Refresh();
    }
    
    
  3. 솔루션 탐색기에서 DetailsPage.xaml을 마우스 오른쪽 버튼으로 클릭한 다음 코드 보기를 클릭합니다.

    코드 숨김 파일이 코드 편집기에 열립니다.

  4. DetailsPage 클래스에서 다음 메서드를 추가합니다.

    private void AppBarSave_Click(object sender, EventArgs e)
    {
        App.ViewModel.SaveChanges();
    }
    
  5. 솔루션 탐색기에서 MainPage.xaml을 두 번 클릭하여 디자이너에서 XAML을 엽니다.

  6. LayoutRoot</Grid> 태그 뒤에 다음 앱 바 요소 추가

    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" >
                <shell:ApplicationBarIconButton IconUri="AppBarRefresh.png" 
                            Text="Refresh" Click="AppBarRefresh_Click" />
                <shell:ApplicationBarIconButton IconUri="AppBarCancel.png" 
                            Text="Cancel" Click="AppBarCancel_Click" />
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
    
  7. 솔루션 탐색기에서 DetailsPage.xaml을 두 번 클릭하여 디자이너에서 XAML을 엽니다.

  8. LayoutRoot</Grid> 태그 뒤에 다음 앱 바 요소 추가

    <phone:PhoneApplicationPage.ApplicationBar>
        <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" >
            <shell:ApplicationBarIconButton IconUri="AppBarSave.png" 
                        Text="Save" Click="AppBarSave_Click" />
        </shell:ApplicationBar>
    </phone:PhoneApplicationPage.ApplicationBar>
    
  9. 파일 메뉴에서 모두 저장을 클릭합니다(Ctrl+Shift+S).

  10. 빌드 메뉴에서 솔루션 빌드를 클릭합니다(Ctrl+Shift+B).

다음 절차에서는 앱을 테스트합니다.

에뮬레이터에서 앱을 실행하려면

  1. 표준 도구 모음에서 앱의 배포 대상을 Windows Phone 단말기가 아니라 Windows Phone 에뮬레이터로 설정합니다.

    Target on Standard Toolbar selecting emulator

  2. 디버그 메뉴에서 디버깅하지 않고 시작을 클릭합니다.

    에뮬레이터가 열리고 앱이 시작됩니다.

표시: