Windows2012. 12. 26. 16:25



App LifeCycle 

Metro app의 생명주기가 어떤 윈도우이벤트를 통해서 관리되어지는지에 대해 설명한다. suspend와 resume상태를 처리하는 방법과 Contract를 통해서 어떻게 윈도우8이 제공하는 UX를 활용하는지에 대해서도 설명한다.


Dealing with the Metro Application Life Cycle

App.xaml.cs에서 생명주기관련 이벤트를 다룬다. OS에서 이와 관련된 이벤트를 전달한다. 생명주기는 3가지 상태가 있다.

1. Activation

 앱 실행시의 상태이다. 모든것이 준비되면 메트로런타임은 컨텐츠를 로드하고 이 이벤트를 발생시킨다. 

2. Suspend

 메트로앱을 사용하지 않을 때이다. 메트로앱에는 종료버튼이 존재하지 않는다. 앱이 활성화되지 않은 상태를 말하며 아무 코드도 실행되지 않고 사용자와 인터렉션도 없는 상태이다.

3. Restored

 Suspend에서 복원된 상태이다. 앱은 다시 화면에 보여지고 앱도 resume된다. Suspended 앱이 반드시 restore되지는 않는다. 가령 메모리가 부족해지면 윈도우는 앱을 바로 종료한다.


Correcting the Visual Studio Event Code

Application객체의 Suspending과 Resuming에 이벤트를 추가함으로써 Suspend와 Restore이벤트의 처리가 가능하다.

Activation은 OnLaunched를 override함으로써 가능하다.

using MetroGrocer.Data;

using Windows.ApplicationModel;

using Windows.ApplicationModel.Activation;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

namespace MetroGrocer {

  sealed partial class App : Application {

    private ViewModel viewModel;

    public App() {

      this.InitializeComponent();

      viewModel = new ViewModel();

// ...test data removed for brevity...

  this.Suspending += OnSuspending;

  this.Resuming += OnResuming;

}

    protected override void OnLaunched(LaunchActivatedEventArgs args) {

      // Create a Frame to act navigation context and navigate to the first page

      var rootFrame = new Frame();

      rootFrame.Navigate(typeof(Pages.MainPage), viewModel);

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

      Window.Current.Content = rootFrame;

      Window.Current.Activate();

}

  private void OnResuming(object sender, object e) {

    viewModel.GroceryList[1].Name = "Resume";

}

  void OnSuspending(object sender, SuspendingEventArgs e) {

    viewModel.GroceryList[0].Name = "Suspend";

} }

}


Adding a Background Activity

Resuming과 Suspending이벤트의 응답을 처리하는 예제로써 백그라운드테스크를 사용하여 볼것이다. geolocation service를 이용할것인데 그러기 위해서 Location.cs를 다음과 같이 정의하였다.


The Location.cs File

using System;

using System.Net.Http;

using System.Threading.Tasks;

using Windows.Data.Json;

using Windows.Devices.Geolocation;

namespace MetroGrocer.Data {

  class Location {

    public static async Task<string> TrackLocation() {

      Geolocator geoloc = new Geolocator();

      Geoposition position = await geoloc.GetGeopositionAsync();

      HttpClient httpClient = new HttpClient();

      httpClient.BaseAddress = new Uri("http://nominatim.openstreetmap.org");

      HttpResponseMessage httpResult = await httpClient.GetAsync(

        String.Format("reverse?format=json&lat={0}&lon={1}",

        position.Coordinate.Latitude, position.Coordinate.Longitude));

      JsonObject jsonObject = JsonObject

        .Parse(await httpResult.Content.ReadAsStringAsync());

      return jsonObject.GetNamedObject("address")

        .GetNamedString("road") + DateTime.Now.ToString("‘ (‘HH:mm:ss')'");

} }

}

이 클래스는 윈도8에서 디바이스의 위치를 얻기 위해 위치정보기능을 사용하고 있다. Windows.Devices.Geolocation의 Geolocator클래스의 GetGeopositionAsync메서드를 호출하여 위치정보를 얻는다. 위치정보의 업데이트를 체크하려면 다른 방식을 사용해야 한다.

주) await키워드는 Task Parallel Library 기술이며 백그라운드 테스크를 생성하여 다루는 고급기술이다. C#4.5부터 지원하며 비동기태스크가 끝날때까지 기다리라는 뜻이다.

위치정보를 얻어온 후 reverse geocoding service요청을 만들어서 요청하면 위,경도좌표정보로 주소기반의 정보를 얻어준다. 이 정보는 JSON string이며 C# object로 파싱하여 거리정보를 읽을 수 있다. 리턴되는 값은 거리정보리스트와 타임스탬프정보를 문자열로 만들어 리턴한다.

여기서 OpenStreetMap geocoding service를 사용하였는데 이는 어카운트를 요구하지 않기 때문이다. 이는 어카운트를 요구하는 구글맵이나 빙맵을 사용할수도 있다는 것이다.


package.appxmanifest에서 location service를 사용한다고 명시적으로 체크해주어야 한다. Capabilityes탭에서 Location capability를 체크한 후 저장한다.



백그라운드테스크관련하여 수정된 App.xaml.cs파일


using System.Threading;

using System.Threading.Tasks;

using MetroGrocer.Data;

using Windows.ApplicationModel;

using Windows.ApplicationModel.Activation;

using Windows.UI.Core;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

namespace MetroGrocer {

  sealed partial class App : Application {

    private ViewModel viewModel;

  private Task locationTask;

  private CancellationTokenSource locationTokenSource;

  private Frame rootFrame;

    public App() {

      this.InitializeComponent();

viewModel = new ViewModel();
// ...test data removed for brevity...

      this.Suspending += OnSuspending;

      this.Resuming += OnResuming;

    StartLocationTracking();

    }

    protected override void OnLaunched(LaunchActivatedEventArgs args) {

      // Create a Frame to act navigation context and navigate to the first page

      rootFrame = new Frame();

      rootFrame.Navigate(typeof(Pages.MainPage), viewModel);

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

      Window.Current.Content = rootFrame;

      Window.Current.Activate();

}

    private void OnResuming(object sender, object e) {

  viewModel.Location = "Unknown";

  StartLocationTracking();

    }

    void OnSuspending(object sender, SuspendingEventArgs e) {

      StopLocationTracking();

      SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral();

      locationTask.Wait();

      deferral.Complete();

    }

    private void StartLocationTracking() {

      locationTokenSource = new CancellationTokenSource();

      CancellationToken token = locationTokenSource.Token;

      locationTask = new Task(async () => {

        while (!token.IsCancellationRequested) {

          string locationMsg = await Location.TrackLocation();

          rootFrame.Dispatcher.Invoke(CoreDispatcherPriority.Normal,

            (sender, context) => {

              viewModel.Location = locationMsg;

            }, this, locationMsg);

          token.WaitHandle.WaitOne(5000);

        }

});

      locationTask.Start();

    }

    private void StopLocationTracking() {

      locationTokenSource.Cancel();

}
}

}


두가지 수정된 동작이 있다.  StartLocationTracking과 StopLocationTracking이다.

이 함수에 대해서는 블랙박스오 여겨주길 바란다. TPL컨셉과 특성에 대해서 잘몰라서리..

중요한건 StartLocationTracking은 위치정보를 5초마다 수집하는것을 시작하는 메서드고 StopLocationTracking은 그 작업을 중단시킨다는 것이다.

여기서 중요한건 생명주기 이벤트와 어떻게 연결하였느냐이다.앱이 시작될때 또는 resume될때 StartLocationTracking을 호출하고 Suspending이벤트때 백그라운드테스크가 앱이 중단되기전에 끝내도록 해야 한다. 그러지 않으면 예외가 발생할 수 있는데 앱이 resume되었을때 잘못된 데이터에 접근할 수 있고 suspended상태에서 네트웍이 끊겼을 경우에 에러가 발생할 수 있다.

이런 문제들을 방지하기 위해 SuspendingEventArgs.SuspendingOperation.GetDeferral메서드가 있는데 이를 통해 아직 suspended가 될수 있는 상황이 아니고 좀더 기다려야 함을  윈도우런타임에 알릴수 있다. GetDeferrel메소드는 SuspendingDeferral 객체를 리턴하는데 Suspended로 변경할 수 있는 시점이 되면 Complete메소드를 호출한다.

deferrel은 suspending을 준비하기 위해 추가로 5초를 더 보장한다. 이는 별로 좋지않게 느껴질 수 있으나 시스템 리소스를 절약하는 면에서는 중요하다.



메트로의 UI control들은 하나로 설계된 스레드내에서 업데이트될것이다. 이는 Application객체에서 사용하는 스레드이다. 백그라운드테스크에서 view model데이터를 업데이트 한다면 그 이벤트는 생략될것이다. 그러면 정상적으로 데이터 바인딩도 일어나지 않을 것이고 그 결과는 예외를 발생시킬것이다.

Dispatcher를 통해서 정확한 스레드에 업데이트이벤트를 전달해야 한다. 하지만 Application class에는 유효한 Dispatcher가 없다. 이를 해결하기 위해 Frame control의 Dispatcher를 이용하였다. 이를 위해 rrotFrame을 로컬이 아닌 인스턴스변수로 선언한것이다.


...

rootFrame.Dispatcher.Invoke(CoreDispatcherPriority.Normal,

  (sender, context) => {

    viewModel.Location = locationMsg;

   }, this, locationMsg);

...



Posted by 삼스
Windows2012. 12. 26. 15:34

Using Tiles and Badges


타일은 시작메뉴상의 앱을 의미한다. 단순히 앱의 아이콘일뿐이다. 하지만 약간의 노력으로 앱의 상태나 어떤 동작을 의미하는 그림을 표시한다든가하는 작업을 타일에 추가해줄수 있다.

이 장에서는 어떻게 타일에 정보를 표시하는지에 대해 기술할것이다.

동적타일을 통해서 사용자가 앱을 실행하게 또는 만류(?)하게 할수 있다. 이는 서로 상충되는 의미이다. 재믿네..

사용자를 유인하기 위해 광고나 컨텐츠등을 표시되게 할 수 있고 이는 엔터테인먼트앱이나 뉴스같은 컨텐츠를 연계하는 앱에 적당하다.

실행중인 앱을 만류(?)하게 하는 동작은 물론 어색하겠지만 이는 UX면에서 중대한 요소이다.

내가 약속이나 해야할 일을 확인하기 위해 앱을 실행해야 한다는 것은 어떤 면에서는 굉장히 귀찮은 일이다. 중요한것은 내가 멀해야 하는가이다. 이는 사용자에게 중요한 정보만 타일에 노출함으로써 불필요하게 앱을 로딩하는 번거로움을 줄여준다.


이 두가지 목표는 아주 신중하게 선택되어야 한다. Metro app은 작고, 단순하며 직관적(불필요한작업을 중임)이다. 알아서 잘 기획해서 선택하여 사용하길... 주저리 주저리...


Improving Static Tiles


앱의 타일을 변경하면 가장 쉽게 시작메뉴에서 보여지는 애플리케이션의 모습을 개선할 수 있다. 타일의 다른 막강한 기능을 사용하지 않는다 하더라도 이작업은 하는것이 좋다.

이 작업은 3가지 사이즈의 이미지가 필요하다.


  30x30, 150x150, 310x150


로고와 텍스트를 포함하고 반투명하면 안된다.

VisualStudio project의 Asset폴더에 3가지 이미지를 작업하여 추가한다.

이미지를 적용하려면 package.appxmanifest 파일을 열어서 편집한다. Application UI탭의 Tile섹션에서 logo, wide logo 그리고 small logo에 이미지파일을 지정한다.


Creating Live Tiles


라이브타일은 사용자에 추가적인 정보를 제공한다. 데모에서는 grocery list의 몇개의 아이템에 대한 정보를 표시할것이다. 이 데모가 모든 라이브타일의 기능을 보여주지는 못하겠지만 데모수준은 된다.

타일업데이트는 미리설정된 템플릿기준으로 이루어진다. 템플릿은 그래픽과 텍스트의 조합으로 이루어져 있는데 표준과 와이드모드로 디자인되어 있다. 첫번째로 해야 하는것은 템플릿의 선택이다. Windows.UI.Notifications.TileTemplateType enum상수를 참조해서 선택하면 된다. 템플릿은 XML fragment로 작성되었고 XML구조도 확인할 수 있다. 여기서는 TileSquareText03 템플릿을 선택하였다. 이 템플릿은 사각형타일에 4라인의 nonwrapping text로 이미지는 없는 구성이다. XML fragment는 다음과 같다.


<tile>

 <visual lang="en-US">

   <binding template="TileSquareText03">

     <text id="1">Text Field 1</text>

     <text id="2">Text Field 2</text>

     <text id="3">Text Field 3</text>

     <text id="4">Text Field 4</text>

   </binding>

 </visual>

</tile>

원리는 애플리케이션에서 정보를 텍스트로 구성하고 그 결과를 Metro Tile notifications system에 전달하는 것이다. 나는 MainPage 클래스에서 타일업데이트를 설정하기를 원한다. 이는 앞에서 작성했던 코드의 리펙토링을 요구한다. ListPage에서 ViewModel을 다루었는데 이를 MainPage에서 하도록 변경해야 한다.

다음은 리팩토링된 MainPage 클래스코드이다.


using System;

using MetroGrocer.Data;

using Windows.Data.Xml.Dom;

using Windows.UI.Notifications;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

using Windows.UI.Xaml.Controls.Primitives;

using Windows.UI.Xaml.Navigation;

namespace MetroGrocer.Pages {

  public sealed partial class MainPage : Page {

    private ViewModel viewModel;

    public MainPage() {

      this.InitializeComponent();

      viewModel = new ViewModel();

      // ...test data removed for brevity

      this.DataContext = viewModel;

      MainFrame.Navigate(typeof(ListPage), viewModel);

  viewModel.GroceryList.CollectionChanged += (sender, args) => {

     UpdateTile();

   };

   UpdateTile();

}


private void UpdateTile() {

  XmlDocument tileXml = TileUpdateManager.

    GetTemplateContent(TileTemplateType.TileSquareText03);

  XmlNodeList textNodes =

    tileXml.GetElementsByTagName("text");

  for (int i = 0; i < textNodes.Length &&

    i < viewModel.GroceryList.Count; i++) {

  textNodes[i].InnerText = viewModel.GroceryList[i].Name;

 }

  for (int i = 0; i < 5; i++) {

    TileUpdateManager.CreateTileUpdaterForApplication()

      .Update(new TileNotification(tileXml));

 }

}

protected override void OnNavigatedTo(NavigationEventArgs e) {

}

private void NavBarButtonPress(object sender, RoutedEventArgs e) {

  Boolean isListView = (ToggleButton)sender == ListViewButton;

  MainFrame.Navigate(isListView ? typeof(ListPage)

      : typeof(DetailPage), viewModel);

  ListViewButton.IsChecked = isListView;

  DetailViewButton.IsChecked = !isListView;

} }

}

Populating the XML Template


XML fragment template을 얻으려면 TileUpdateManager.GetTemplateContent메서드를 호출한다. 이 때 템플릿타입 상수로 타입을 결정한다. Windows.Data.Xml.Dom.XmlDocument객체를 얻을 수 있으며 DOM 메소드로 "text" 엘리먼트의 값을 변경할 수 있다. sort를 위해 GetElementById는 동작하지 않으며 그래서 GetElementByTagName을 사용하였다.


...

XmlNodeList textNodes = tileXml.GetElementsByTagName("text”);

...


텍스트 노드는 순서대로 리턴된다. 따라서 순서대로 다음과 같이 초기화 가능하다.


...

for (int i = 0; i < textNodes.Length && i < viewModel.GroceryList.Count; i++)

{

  textNodes[i].InnerText = viewModel.GroceryList[i].Name;

}

...


Applying the Tile Update


XML Document에 데이터를 초기화한 후 Application Tile을 위해 Update를 생성한 후 TimeUpdater 객체의 Update메소드에 인자로 넘긴다.


.

for (int i = 0; i < 5; i++) {

  TileUpdateManager.CreateTileUpdaterForApplication()

    .Update(new TileNotification(tileXml));

} ...


Calling the Tile Update Method


두가지 상태에서 UpdateTile 메소드를 호출하였다. 먼저 생성자에서 호출하여 앱구동시 데이터를 전달한다. 그리고 컨텐츠가 변경되었을 때 이다.


...

viewModel.GroceryList.CollectionChanged += (sender, args) => {

   UpdateTile();

};

...


CollectionChanged 이벤트는 item이 추가, 삭제, 변경될경우 호출된다.


Testing the Tile Update


시뮬레이터에서는 타입업데이트가 동작하지 않으므로 타겟을 Local Machine으로 맞춘 후 테스트해야 한다. 그렇지 않으면 테스트가 불가하다.



Updating Wide Tiles

사용자가 square 또는 wide tile중 멀 선택할지 모르기 때문에 두가지 다 지원하는 것이 좋다. 그를라믄 두개의 XML fragment를 하나의 fragment로 합쳐줄 필요가 있다. TileSquareText03과 TileWideBlockAndText01템플릿을 합쳐볼것이다. 다음은 그 예이다.


<tile>

   <visual lang="en-US">

     <binding template="TileSquareText03">

       <text id="1">Apples</text>

       <text id="2">Hotdogs</text>

       <text id="3">Soda</text>

       <text id="4"></text>

     </binding>

     <binding template="TileWideBlockAndText01">

       <text id="1">Apples (Whole Foods)</text>

       <text id="2">Hotdogs (Costco)</text>

       <text id="3">Soda (Costco)</text>

       <text id="4"></text>

       <text id="5">2</text>

       <text id="6">Stores</text>

     </binding>

   </visual>

</tile>


합쳐진 template을 다루는 API는 없다. 여기서 선택한 방법은 따로 핸들링하고 처리의 마지막에 합치는 방법이다. 아래 코드를 참조하라.


.

private void UpdateTile() {

    int storeCount = 0;

    List<string> storeNames = new List<string>();

    for (int i = 0; i < viewModel.GroceryList.Count; i++) {

      if (!storeNames.Contains(viewModel.GroceryList[i].Store)) {

        storeCount++;

        storeNames.Add(viewModel.GroceryList[i].Store);

      }

}

    XmlDocument wideTileXml = TileUpdateManager

      .GetTemplateContent(TileTemplateType.TileWideBlockAndText01);

      XmlNodeList narrowTextNodes = narrowTileXml.GetElementsByTagName("text");

      XmlNodeList wideTextNodes = wideTileXml.GetElementsByTagName("text");

      for (int i = 0; i < narrowTextNodes.Length

        && i < viewModel.GroceryList.Count; i++) {

        GroceryItem item = viewModel.GroceryList[i];

        narrowTextNodes[i].InnerText = item.Name;

    wideTextNodes[i].InnerText = String.Format("{0} ({1})", item.Name, item.Store);

    }

XmlDocument narrowTileXml = TileUpdateManager

      .GetTemplateContent(TileTemplateType.TileSquareText03);

} }

...

wideTextNodes[4].InnerText = storeCount.ToString();

wideTextNodes[5].InnerText = "Stores";

var wideBindingElement = wideTileXml.GetElementsByTagName("binding")[0];

var importedNode = narrowTileXml.ImportNode(wideBindingElement, true);

narrowTileXml.GetElementsByTagName("visual")[0].AppendChild(importedNode);

  for (int i = 0; i < 5; i++) {

      TileUpdateManager.CreateTileUpdaterForApplication()

.Update(new TileNotification(narrowTileXml));

}

}

...


Posted by 삼스
Windows2012. 12. 17. 10:58



원문(Metro Revealed - Build Windows 8 apps with XAML and C# )의 내맘대로 정리임

아직 국내에는 메트로스타일앱에 대한 적당한 책을 발견하지 못하여 원서를 하나 선정하여 내 맘대로 의역하여 정리해보았다. 잘못된 내용이 있을 수 있음. ㅋㅋ.

================================================================

시작한다~

메트로앱은 윈도우8에서 아주 중요한 기능중 하나인데 데스크탑, 태블릿, 스마트폰간에 동일한 상호작용을 제공한다. 메트로앱은 이전 윈도우앱과는 완전히 다른 경험을 제공한다.

이전윈도우앱과 차별되며 완전히 새로운 API들, 새로운 control들, 그리고 완전히 다른 애플리케이션라이프사이클을 제공한다.

메트로스타일앱은 몇가지 개발언어로 개발이 가능한데, JavaScript, VisualBasic, C++그리고 C#이다. 기존에 개발에 사용되던 XAML과 C#으로 풍부한 메트로앱을 개발하고 광범위한 윈도우플랫폼상에서 통합가능하다. 

설명을 위해 데모앱을 만들것인데 아래와 같은 실행화면을 가진다.



프로젝트시작은 File -> New Project 하여 프로젝트팝업에서 'Visual C# form the Templates'섹션을 선택, 그리고 'Blank Application'을 선택한다. 프로젝트명을 MetroGrocer로 하고 OK버튼을 눌러서 프로젝트를 만든다. VisualStudio는 몇가지 형태의 템플릿을 지원하며 기본적인 코드를 생성하여 프로젝트를 만들어준다.


만들어진 프로젝트의 솔루션탐색기는 위와 같다. App.xaml과 App.xaml.cs는 메트로앱을 시작하는데 사용된다. 다음 코드를 보면 StandardStyles.xaml파일을 Common폴더로부터 참조하도록 되어있는것을 볼 수 있다.

App.xaml

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

  <Application.Resources>

    <ResourceDictionary>

       <ResourceDictionary.MergedDictionaries>
       <ResourceDictionary Source="Common/StandardStyles.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application> 

App.xaml.cs

using Windows.ApplicationModel; 

using Windows.ApplicationModel.Activation;

using Windows.UI.Xaml; 

using Windows.UI.Xaml.Controls;

namespace MetroGrocer {  

sealed partial class App : Application {

 public App() {
      this.InitializeComponent();
      this.Suspending += OnSuspending;
    }
    protected override void OnLaunched(LaunchActivatedEventArgs args) {
      if (args.PreviousExecutionState == ApplicationExecutionState.Terminated) {
        //TODO: Load state from previously suspended application
      }
      // Create a Frame to act navigation context and navigate to the first page
   var rootFrame = new Frame();   rootFrame.Navigate(typeof(BlankPage));
   // Place the frame in the current Window and ensure that it is active
   Window.Current.Content = rootFrame;
   Window.Current.Activate();
    }
    void OnSuspending(object sender, SuspendingEventArgs e) {
      //TODO: Save application state and stop any background activity
    }
  } 
}

메트로앱은 자체 생명주기모델을 가지고있으며 App.xaml.cs에서 처리된다. OnLaunched에서 앱이 시작될때의 처리를, OnSuspending은 앱이 백그라운드로모드로 전환될때 호출된다. 더 자세한 사항은 생명주기를 설명할때 하도록 하겠다.

BlankPage.xaml파일이 처음에 표시되는 화면의 디자인을 결정짓는 파일이다.

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

  <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">

  </Grid>

 </Page>

Grid가 하나로 표현된 빈 페이지에 백그라운드속성에 ApplicationPageBackgroundBrush로 설정되어 있다.

그리고 다음은 BlankPage.xaml.cs의 내용은 다음과 같다.

using Windows.UI.Xaml.Controls;

using Windows.UI.Xaml.Navigation;

namespace MetroGrocer {

  public sealed partial class BlankPage : Page {

    public BlankPage() {

      this.InitializeComponent();

    }

    protected override void OnNavigatedTo(NavigationEventArgs e) {

    }

  }

}


StandardStyles.xaml 파일

common폴더는 VS project template들이 담겨져 있다. 여기서는 App.xml에서 참조하는 스타일들을 모두 정의한 StandardStyles.xaml이 있다. 여기에는 메트로앱스타일의 룩앤필을 제공하는 다양한 스타일들이 정의되어 있다.

<Style x:Key="HeaderTextStyle" TargetType="TextBlock"

    BasedOn="{StaticResource BaselineTextStyle}">

  <Setter Property="FontSize" Value="56"/>

  <Setter Property="FontWeight" Value="Light"/>

  <Setter Property="LineHeight" Value="40"/>

  <Setter Property="RenderTransform">

    <Setter.Value>

      <TranslateTransform X="-2" Y="8"/>

    </Setter.Value>

  </Setter>

</Style> …


Package.appminifest파일

애플리케이션 메니페이스파일이다. 메트로앱에 대한 다양한 정보들을 기술한다. xml파일이매 VS에서 쉽게 편집이 가능하도록 편집화면을 제공한다. 나중에 더 자세히 설명할게요!



Posted by 삼스