Осторожно, Modern UI


    Каждый разработчик Windows Store приложений должен знать гайдлайны.
    Guideline в переводе с английского — рекомендации, руководящие указания. Для того, чтобы приложение попало в Store не обязательно следовать рекомендациям, но следовать им желательно. Есть несколько особенностей, или иноземно выражаясь фич (англ. feature), которые желательно иметь каждому приложению. Я решил рассмотреть эти особенности, а заодно сделать шаблон C#/XAML приложения Windows Store, с которого можно начинать разработку. Ведь, так или иначе, часто приходится заходить в разработанные ранее приложения или MSDN, чтобы скопировать код сниппета для определенной функции.
    Описанные далее возможности не только сделают ваше приложение соответствующим гайдлайнам, но и украсят его дополнительным функционалом, привлекающим внимание пользователей, а также помогут в продвижении.

    Контракт Share

    Для того, чтобы пользователь мог отправить e-mail друзьям или поделиться информацией о приложении/игре в социальной сети, в код приложения нужно добавить контракт share. Для того чтобы это сделать следуем следующим действиям.
    Добавляем пространство имен:
    using Windows.ApplicationModel.DataTransfer;
    

    И в классе страницы добавляем объявление переменной:
    private DataTransferManager dataTransferManager;       
    

    Далее переопределяем события захода на страницу и выхода с нее:
    protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                    //регистрируем страницу как источник share
                    this.dataTransferManager = DataTransferManager.GetForCurrentView();
                    this.dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.DataRequested);
            }
       protected override void OnNavigatedFrom(NavigationEventArgs e)
            {
                    // удаляем регистрацию
                    this.dataTransferManager.DataRequested -= new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(this.DataRequested);
            }
    

    Простейший вариант контента, которым можно поделиться это заголовок, текст и ссылка:
        private void DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
            {
     Uri dataPackageUri = new Uri("http://apps.microsoft.com/windows/ru-ru/app/mp3/0122ffd9-585f-4b3d-a2ad-571f74231d14");
     DataPackage requestData = e.Request.Data;
     requestData.Properties.Title = "Прикольная игра";
     requestData.SetWebLink(dataPackageUri);
     requestData.Properties.Description = "И здесь добавить какое-то описание.Как правило, текст содержит рекомендацию установить приложение.";
            }
    

    Также можно отправить файл или изображение. Есть вариант, при котором сообщение может быть отправлено в формате HTML.
    Поделиться из Windows 8.1 можно вызвав волшебную charm панельку и выбрав в ней вот такой значок:

    В Windows 10 поделиться контентом можно нажав такой же значок в верхнем левом углу приложения:

    Для того, чтобы открыть окно программно из кода, можно воспользоваться следующей строчкой:
    Windows.ApplicationModel.DataTransfer.DataTransferManager.ShowShareUI();
    

    Официальное руководство доступно по ссылке:
    Краткое руководство: общий доступ к содержимому (XAML)

    Live tile

    Наверняка вы замечали, что некоторые плитки на стартовом экране изменяются со временем. Взять, к примеру, тайлы новостных сайтов или же тайл приложения «Финансы», который сам обновляет сводки. Некоторые тайлы меняются каждые несколько секунд, привлекая тем самым внимание пользователя. Это так называемые «живые» плитки/тайлы.
    Для того, чтобы приложение изменило тайл ему можно послать push уведомление, но можно обновить тайл и из самого приложения.
    Для того, чтобы обновить тайл из приложения, первым делом нужно добавить пространство имен
    using Windows.UI.Notifications;  
    

    Далее напишем простой метод, который изменит вид и текст тайла:
    void UpdateTileWithText()
            {
                //    #  обновление квадратного тайла
                var tileXml = TileUpdateManager.GetTemplateContent(TileTemplateType.TileSquare150x150Text04);
                var tileAttributes = tileXml.GetElementsByTagName("text");
                string tiletext = "Какой - либо текст для отображения на тайле";
                tileAttributes[0].AppendChild(tileXml.CreateTextNode(tiletext));
                var tileNotification = new TileNotification(tileXml);
                tileNotification.ExpirationTime = DateTimeOffset.UtcNow.AddHours(1);
                TileUpdateManager.CreateTileUpdaterForApplication().Update(tileNotification);
            }
    

    В примере, в качестве шаблона использован тайл с четырьмя строками текста. Все шаблоны доступны по ссылке: Каталог шаблонов плиток

    По гайдлайнам очень рекомендуется, чтобы у приложения были плитки и квадратного и широкого размера, поэтому желательно этот же код дублировать, указав в виде шаблона тайла уже не квадратный, а широкий шаблон. То есть в примере заменить TileTemplateType.TileSquare150x150Text04 на TileTemplateType.TileWide310x150Text04.

    У себя в приложении я обновлял тайл при событии App_Suspending, для того, чтобы напомнить пользователям на чем они остановились, но можно делать это в любой другой момент.
    Если нужно очистить изменения, которые были совершены и вернуть тайл к первоначальному виду, то сделать это можно из кода строчкой:
    TileUpdateManager.CreateTileUpdaterForApplication().Clear();
    

    Официальная, хоть и краткая, но чуть более полная инструкция доступна по ссылке:
    Краткое руководство: отправка обновления плитки (XAML)

    Rate and Review

    Рекомендуется ненавязчиво предлагать пользователям оценить ваше приложение. Хорошие оценки и отзывы помогут повысить популярность вашего предложения. Кроме того отзывы пользователей позволяют вам своевременно и с пользой делать изменения функционала и дизайна приложения. Для того, чтобы открыть окно Windows Store с оценкой достаточно создать и открыть URI. Для этого достаточно двух строчек:
     var uri = new Uri("ms-windows-store:Review?PFN=9cc1e6dd-9d82-4736-aee5-acb9a01d9c39_1dv79ndb6c382");
     await Windows.System.Launcher.LaunchUriAsync(uri);
    

    Обратите внимание на то, что открытие страницы происходит асинхронно с использованием await. Раз так, то к процедуре нажатия кнопки необходимо будет добавить предикат async.
    Сформировать URI относительно просто. К тексту «ms-windows-store:Review?PFN=» нужно добавить Package Family Name или сокращенно PFN. Его значение можно узнать, открыв манифест приложения.

    Ссылка на официальное руководство как сформировать ссылку на приложение в Store:
    Linking to your app
    Ссылку указал на английском, так как в русском переводе перевели по ошибке еще и ключевые слова.

    Так как шаблон мы пишем под 8.1 а не под 10, то есть одна небольшая путаница. Способ формирования URI для WP 8.1 немного отличается от способа формирования URI для Windows Store приложений. Для WP 8.1 URI выглядит вот так:
    ms-windows-store:reviewapp?appid=cq23k522-7u59-1193-h4aa-3t56e4633c24
    Официальное руководство (на русском):
    Ссылка на ваше приложение в Магазине

    Довольно хорошим способом получить оценку и отзыв пользователя может быть предложение оценить приложение после определенного количества запусков. Будем считать, что раз пользователь запустил приложение, скажем, 5 раз, то ему есть что сказать о приложении.
    Количество запусков приложения можно сохранять с помощью настроек приложения. Заодно и разберемся с тем, как сохранять настройки.

    Settings

    Простой сниппет, с помощью которого можно считать сколько раз приложение было запущено:
    В объявления переменных добавим:
    int appRunCount = 0;
    Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
    

    В конструктор класса страницы после this.InitializeComponent(); добавим:
                try
                {
                    appRunCount = (int)localSettings.Values["AppRunCount"];
                }
                catch { }
    
                appRunCount++;
    
                try
                {
                    localSettings.Values["AppRunCount"] = appRunCount;
                }
                catch { }
    

    Таким образом мы получим в переменной appRunCount количество запусков приложения.
    Вместо Windows.Storage.ApplicationData.Current.LocalSettings мы можем использовать Windows.Storage.ApplicationData.Current.RoamingSettings. Если сделать так, то мы сможем сохранять настройки не локально на устройстве, а в роуминге, т.е. в сети. Они сохраняются в пользовательском аккаунте Microsoft. RoamingSettings будут доступны с любого устройства, на котором был совершен вход из одного и того же аккаунта.
    Простой код, которым выдается сообщение в случае, если приложение запущено 5-ый раз и в случае согласия пользователя поставить оценку открывается окошко магазина с предложением оставить отзыв:
      if (appRunCount == 5)
                {
       var messageDialog = new Windows.UI.Popups.MessageDialog("Оставьте, пожалуйста, отзыв о приложении", "Проголосуйте за нас!");
         // можем добавить не больше трех команд/вариантов выбора 
       messageDialog.Commands.Add(new Windows.UI.Popups.UICommand("Поставить оценку", new Windows.UI.Popups.UICommandInvokedHandler(CommandHandler)));
       messageDialog.Commands.Add(new Windows.UI.Popups.UICommand("Я уже ставил оценку", new Windows.UI.Popups.UICommandInvokedHandler(CommandHandler)));
       messageDialog.Commands.Add(new Windows.UI.Popups.UICommand("В другой раз", new Windows.UI.Popups.UICommandInvokedHandler(CommandHandler)));
    
       messageDialog.DefaultCommandIndex = 0;
       messageDialog.CancelCommandIndex = 2;
       await messageDialog.ShowAsync();
                }
    
           private async void CommandHandler(IUICommand command)
            {
                if (command.Label=="Поставить оценку")
                {
         var uri = new Uri("ms-windows-store:PDP?PFN=9cc1e6dd-9d82-4736-aee5-acb9a01d9c39_1dv79ndb6c382");
         await Windows.System.Launcher.LaunchUriAsync(uri);
                }
            }
    

    Окошко с вопросом выглядит стандартным и слишком обычным. В идеале, лучше сделать свой PopUp, который будет обладать дизайном, вписывающимся в дизайн приложения. Но это уже выходит за рамки, рассматриваемые этой статьей.
    Как сохранить настройки приложения мы разобрались.

    Вот где можно почитать официальное руководство:
    Загрузка и сохранение параметров

    Для того, чтобы пользователь мог изменить какие-то настройки в Modern UI приложениях есть возможность создать «вылетающую» панельку – Flyout

    Settings Flyout

    Такая возможность доступна только для приложений Windows 8.1 и 10. В Windows 8 при желании создать аналогичную панельку было возможно, но необходимо было создавать свой user control.
    Для того, чтобы добавить в приложение «вылетающую» панельку с настройками в проект необходимо добавить новый элемент управления под названием Settings Flyout (в русскоязычном варианте «Всплывающий элемент параметров»). Порядок действий следующий. Заходим в меню:
    Project – Add New Item… — Visual C# — XAML – Settings Flyout
    или
    Проект — Добавить новый элемент — Visual C# — XAML – Всплывающий элемент параметров
    Назовем новый элемент, например, так: CustomSettingsFlyout.xaml
    Добавляем в App.xaml.cs приложения ссылки на пространства имен:
    using Windows.UI.ApplicationSettings;
    using Windows.UI.Popups;
    

    И следующий код:
            protected override void OnWindowCreated(WindowCreatedEventArgs args)
            {
                SettingsPane.GetForCurrentView().CommandsRequested += OnCommandsRequested;
            }
    
            private void OnCommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
            {
                SettingsCommand settingsCommand = new SettingsCommand("SettingsPage", "Open settings",new UICommandInvokedHandler(onSettingsCommand));
                args.Request.ApplicationCommands.Add(settingsCommand);
            }
    
            private void onSettingsCommand(IUICommand command)
            {
                CustomSettingsFlyout settingsFlyout = new CustomSettingsFlyout();
                settingsFlyout.Show();
            }
    

    Теперь при вызове настроек приложения у нас будет отображено наше окно. Можем править код XAML и C# нашего элемента CustomSettingsFlyout так как нам нужно.
    Сохранять настройки мы только что научились с помощью класса Windows.Storage.ApplicationData.Current.LocalSettings

    Официальный мануал:
    Краткое руководство: добавление параметров приложения (XAML)

    Всплывающие уведомления

    Для того, чтобы уведомить пользователя о чем-либо, можно отобразить toast уведомление. Это небольшой прямоугольник с картинкой и текстом, которые выплывает справа, подобно Flyout. Toast уведомление не блокирует интерфейс приложения в отличие от MessageBox-а. Довольно удобно будет, если у нас в коде вызов уведомления будет заключен в отдельном методе. Добавляем еще 2 пространства имен:
    using Windows.Data.Xml.Dom;
    using Windows.UI.Notifications;
    

    И добавим сам метод, после вызова которого с текстом в качестве параметра мы получим уведомление:
            void ShowToast(string whattext)
            {
                XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01);
                XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
                stringElements[0].AppendChild(toastXml.CreateTextNode(whattext));
                ToastNotification toast = new ToastNotification(toastXml);
    
                toast.Activated += ToastActivated;
                toast.Dismissed += ToastDismissed;
                toast.Failed += ToastFailed;
    
                ToastNotificationManager.CreateToastNotifier().Show(toast);
            }
    

    Первой строкой мы задаем значением xml шаблон ToastText01. Это довольно простой шаблон, который содержит в себе только текст, занимающий 3 строки.
    Все возможные шаблоны здесь: Каталог шаблонов всплывающих уведомлений
    Еще вы можете заметить, что мы подписываемся на 3 события: активацию уведомления, скрытие уведомления и ошибку уведомления. Теперь на обязательно нужно реализовать обработчики этих событий (пусть даже и без кода):
            private void ToastFailed(ToastNotification sender, ToastFailedEventArgs args) { }
            private void ToastDismissed(ToastNotification sender, ToastDismissedEventArgs args) { }
            private void ToastActivated(ToastNotification sender, object args) { }
    

    Официальное руководство:
    Краткое руководство: отправка всплывающего уведомления (XAML)

    AppBar и CommandBar

    Напоследок, добавим в приложение панель со стандартными элементами управления.
    Панель может быть верхней или нижней. Кнопки команд и инструментов принято размещать на нижней панели.
    Панель можно создать в XAML следующими тэгами:
    <Page.TopAppBar>
            <!-- content -->
    </Page.TopAppBar>
    

    Или
    <Page.BottomAppBar>
            <!-- content -->
    </Page.BottomAppBar>
    

    Содержимым панели может быть элемент AppBar (для различных контролов, кои ваша душенька пожелает разместить) или элемент CommandBar (только для кнопок – XAML элементов AppBarButton, AppBarToggleButton и AppBarSeparator). Для обычного приложения вполне достаточно обычных кнопок, поэтому ограничимся CommandBar-ом. Код такой:
        <Page.BottomAppBar>
            <CommandBar IsOpen="True">
                <AppBarButton Label=" Vote and review" Icon="Like" Click="ReviewButton_Click"/>
                <AppBarButton x:Name="btnSecTile"  Label="Add to Start" Click="btnSecTile_Click" Icon="Pin"/>
    
                <CommandBar.SecondaryCommands>
                    <AppBarButton Label="Share" Icon="OtherUser" Click="ShareButton_Click"/>
                </CommandBar.SecondaryCommands>
            </CommandBar>
        </Page.BottomAppBar>
    

    Обратите внимание на атрибут IsOpen=”True”. При открытии приложения желательно отобразить панель, дабы пользователь был в курсе ее наличия и соответственно знал какие возможности есть у приложения. Также вы можете заметить вложенный тег CommandBar.SecondaryCommands которые содержит в себе кнопки, которые будут отражены слева панели. Они как бы второстепенные.

    Официальное руководство:
    Краткое руководство: добавление панелей приложения (XAML)

    Secondary tile

    Есть возможность разрешить пользователю закрепить вспомогательную плитку/тайл на стартовый экран Windows. По нажатию этой плитки приложение может открываться с определенными параметрами, также вспомогательный тайл может быть «живым» (т.е. live тайлом) и отображать какие-либо специальные оповещения.
    Сперва, чтобы добавить secondary tile, мы добавим необходимое отсутствующее пространство имен:
    using Windows.UI.StartScreen;
    

    в конструктор страницы после InitializeComponent(); добавляем вызов метода с параметром:
    ToggleAppBarButton(!SecondaryTile.Exists("MyUnicTileID"));
    

    Здесь MyUnicTileID это уникальный идентификатор тайла
    Далее нам нужно добавить сам метод ToggleAppBarButton, который будет отображать значок прикрепить или открепить в зависимости от текущего состояния вспомогательной плитки.
       public void ToggleAppBarButton(bool showPinButton)
            {
                if (showPinButton)
                {
                    btnSecTile.Label = "Прикрепить";
                    btnSecTile.Icon = new SymbolIcon(Symbol.Pin);
                }
                else
                {
                    btnSecTile.Label = "Открепить";
                    btnSecTile.Icon = new SymbolIcon(Symbol.UnPin);
                }
                this.btnSecTile.UpdateLayout();
            }
    

    Здесь showPinButton это булева переменная, которая хранит значение передаваемое !SecondaryTile.Exists(«MyUnicTileID»), т.е. прикреплен ли вспомогательный тайл приложения к стартовому экрану или нет.
    Теперь добавим код в событие нажатия кнопки. Этот код будет прикреплять плитку к начальному экрану или откреплять ее:
        private async void btnSecTile_Click(object sender, RoutedEventArgs e)
            {
        Windows.Foundation.Rect rect = GetElementRect((FrameworkElement)sender);
               
                if (SecondaryTile.Exists("MyUnicTileID"))
                {
                    SecondaryTile secondaryTile = new SecondaryTile("MyUnicTileID");
    
                    bool isUnpinned = await secondaryTile.RequestDeleteForSelectionAsync(rect, Windows.UI.Popups.Placement.Above);
                    ToggleAppBarButton(isUnpinned);
                }
                else
                {
                    // Pin
                    Uri square150x150Logo = new Uri("ms-appx:///Assets/Logo.scale-100.png");
                    string tileActivationArguments = "Secondary tile was pinned at = " + DateTime.Now.ToLocalTime().ToString();
                    string displayName = "Application name";
    
                    TileSize newTileDesiredSize = TileSize.Square150x150;
                    SecondaryTile secondaryTile = new SecondaryTile("MyUnicTileID",
                                                                    displayName,
                                                                    tileActivationArguments,
                                                                    square150x150Logo,
                                                                    newTileDesiredSize);
    
                    secondaryTile.VisualElements.Square30x30Logo = new Uri("ms-appx:///Assets/SmallLogo.scale-100.png");
                    secondaryTile.VisualElements.ShowNameOnSquare150x150Logo = true;
                    secondaryTile.VisualElements.ForegroundText = ForegroundText.Light;
                    bool isPinned = await secondaryTile.RequestCreateForSelectionAsync(rect, Windows.UI.Popups.Placement.Above);
                    ToggleAppBarButton(!isPinned);
                }
            }
    

    Также вам нужно добавить функцию
            public static Rect GetElementRect(FrameworkElement element)
            {
                GeneralTransform buttonTransform = element.TransformToVisual(null);
                Point point = buttonTransform.TransformPoint(new Point());
                return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
            }
    

    Примерно такое вот предупреждение вы должны получить при закреплении плитки на начальный экран:

    «App Template» — это название приложения. Вы можете или закрепить плитку или отказаться, кликнув где-либо свободном месте приложения и закрыв тем самым pop-up.
    Как обычно, ссылка на официальное руководство:
    Краткое руководство. Закрепление вспомогательной плитки (XAML)

    Это были некоторые из возможностей Windows Store приложений. Очень рекомендую их использовать.
    Получившийся шаблон вы можете скачать по ссылке с GitHub:
    https://github.com/programmersommer/WindowsStoreAppTemplate
    или в качестве архива zip (версия с русскими комментариями):
    OneDrive

    Комментарии 21

      +5
      Проблема только в том, что в таком виде Share, Settings несколько Depricated.
      Так что сейчас актуальнее реализовать настройки внутри приложения.

      А ещё полезно воздержаться от использования свернутых appbar.
        0
        Дима, что именно устарело в Settings? Шаблон делал на 10-ке и VS 2015 RC с последними обновлениями. Как выглядит вызов Share в 10-ке я показал.
          +7
          просто попробуйте в планшетном режиме добраться до настроек, это мало того, что неочевидно, так ещё и хрен попадёшь пальцем.

          Ну и в последнем sdk (для 10158) он помечен как deprecated.
            –3
            Вообще, по ссылке в MSDN упомянуты требования для Windows 10 — Requirements (Windows 10 device family):
            https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.applicationsettings.settingspane
            Но, если говоришь в последнем sdk, который вышел буквально за последние сутки, то верю. Может быть.
            В любом случае остается Windows 8.1 разработку под которую еще никто не делал depricated))
              0
              В любом случае остается Windows 8.1 разработку под которую еще никто не делал depricated))

              Да, но если это приложение запустят в windows 10, то настройками пользоваться будет очень неудобно. В планшетном режиме это:
              1) свайп через верхний край экрана, чтобы получить заголовок окна.
              2) прицеливание и тап в кнопку меню
              3) тап в кнопку настроек.

              А теперь представьте, что после этого случайно ткнули не в панель настроек, а в приложение, то панель закроется и операцию придётся повторять сначала.
                0
                Это да. Но для планшетного режима удобен <Page.BottomAppBar>, который в 10-ке пока что работает. Можно дублировать там.
                  0
                  он и будет работать, главное его не сворачивать до трёх точек, а то это не удобно ни для пальцев, ни для мыши.
              0
              До настроек и в мобильном режиме не очень то. Была уже куча концептов с логичным расположением элементов. Надеюсь хоть в 10ке до ума доведут
                0
                >До настроек и в мобильном режиме не очень то.
                В каком режиме? мы точно про десктопную ОС?

                > Была уже куча концептов с логичным расположением элементов.
                В настольной 10ке UI за ближайший месяц принципиально не изменится. Будет то, что сейчас есть в 10159.
              +1
              А share, на самом деле на настолько deprecated, да. Приложение Фотографии его и использует. просто кнопку вызова контракта придётся для 10ки добавлять внутрь приложения.
            +3
            Чем обоснованы «глухие» try {...} catch {}?
              0
              Вы про то, почему в catch не обрабатываются различные типы исключений? В процессе тестирования это важно, а также в случае отправки feedback-а разработчику продукта. Здесь такой отправки нет.
                0
                Я про то, что что бы ни произошло внутри вашего приложения, ни пользователь, ни вы об этом не узнаете. У пользователя пропал шаринг — ну бывает, какая разница, почему? Не смогли отписаться от события, оставили утечку — пофиг. Не смогли записать число запусков, и в следующий раз приложение опять будет считать, что оно запущено в первый раз? Какая мелочь.

                Кончилась память? NRE? Не важно, будем продолжать выполнение.
                  –1
                  Цель статьи не в том, чтобы создать приложение готовое к выпуску, а в том, чтобы показать какие есть возможности.
                  Вы бы еще возмутились почему в приложении пустой экран и никакого контента)
                    +7
                    Вы это предлагаете как шаблон и пример использования. Уж лучше не иметь никакого try/catch, чем такой.
                      0
                      Касательно share соглашусь — можно убрать. А вот в setting-ах я оставлю.
                      Fork-айте и делайте на свой вкус.
              +2
              Код ужасен
                +2
                После заглавного фото я уже ничему не удивлялся.
                  0
                  Мне кажется (поправьте минусами, если не прав), наиболее правильный шаблон приложения Windows Store уже дан самой Microsoft. Они над ним думали, зная свою внутреннюю кухню. Судя по заголовку я ожидал увидеть пост о неочевидных, подводных камнях в разработке современного интерфейса. Понятно, автор хотел заинтересовать, хотел как лучше, но выходит как всегда.
                  Да, напоминание о гайдлайнах полезно, но автор сам признаёт, что не обязательно им следовать. Здесь, в посте, показаны только «типичные» куски кода для более правильного приложения. И хорошо, что автор даёт ссылки на источники. Да, как полновесный шаблон это явно не используешь, но писалось это (судя и по явно черновому коду) для внутренних нужд, а уже из лучших побуждений было показано здесь.
                  У меня тут всего два поста. Один как раз про что-то вроде черновой работы и с претензиями к питону. Нахватал минусов, конечно. Ценный опыт. Сообщество, насколько я успел понять, ждёт кристально чистых постов. Информативных, чётких, красивых, однозначно полезных; в конечном итоге именно таких, которых не на'RTFM'ить. Такие посты здесь редки, но ради них здесь стоит быть, как стоит рыть и вымывать тонны песка ради граммов золота. Я хочу, чтобы здесь было больше золота, поэтому мой комментарий именно такой.
                  0
                  Мне кажется (поправьте минусами, если не прав), наиболее правильный шаблон приложения Windows Store уже дан самой Microsoft.

                  Боюсь, это далеко от истины. В 8ке приложения от МС не всегда оптимальны, а с 10кой они вообще не сочетаются.
                  В 10ке приложения продуманы, но такой интерфейс не подходит для 8ки.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое