Разработка
27
Сен
7

Графики на Silverlight при помощью службы WCF

Продолжаем изучение Silverlight. Сегодня будем строить графики… опят! ну, сколько можно 🙂

Нам потребуется следующие компоненты (скачайте и установите их, если у Вас их еще нет 🙂 ):

  • Visual Studio 2010 (а вместе с ним и .Net 4.0);
  • Silverlight 4 Tools for Visual Studio 2010 — добавляет в Toolbox VS набор стандартных Silverlight-контролов;
  • Silverlight Toolkit — это набор дополнительных элементов управления и тем для Silverlight, распространяемые бесплатно, с открытым кодом и тестами по лицензии Ms-Pl. Silverlight Toolkit разрабатывается вместе с сообществом, причем отдельно от собственно создания Silverlight. Помимо прочего включает в себя и графики. На специальной страничке Вы можете увидить их в действии 🙂

Начнем…

Подготовка

Итак, концептуально решение будет состоять из следующийх элиемнтов:

  • Cлужба WCF — т.к. Silverlight упаковывает свои сборки в архив и отправляет клинту, где они и выполняются, то C#-код реализующий графики не сможет напрямую получить доступ к SQL серверу. Для этого мы создадим службу WCF (более крутая служба, нежели обычные ASP.Net службы), которая и будет общаться с SQL сервером и по запросу клиентов (в данном случае нашего решения на Silverlight) будет возвращать определенный набор данных;
  • Страница ASPX — будет хостировать Silverlight-приложение и ее же мы подставим в iFramу расположенный на форме (Бизнес-партнера, в данном примере);
  • Silverlight-приложение — само клиентское приложение разработанное по технологии Silverlight, включающее в себя графики (три штуки) распространяемые посредством Silverlight Toolkit.

Данные для диаграмок будут поставляться такими SQL-запросами (в самом приложении GUID’ы в них будут подставляться динамически):

  • Сумма продаж за последние пол-года (по какому-либо Бизнес-партнеру) в разрезе Возможных сделок, Предложений, Заказов:
    SELECT
    	'mnth' =
    	CASE
    		WHEN tmp.month = '1' THEN 'Январь'
    		WHEN tmp.month = '2' THEN 'Февраль'
    		WHEN tmp.month = '3' THEN 'Март'
    		WHEN tmp.month = '4' THEN 'Апрель'
    		WHEN tmp.month = '5' THEN 'Май'
    		WHEN tmp.month = '6' THEN 'Июнь'
    		WHEN tmp.month = '7' THEN 'Июль'
    		WHEN tmp.month = '8' THEN 'Август'
    		WHEN tmp.month = '9' THEN 'Сентябрь'
    		WHEN tmp.month = '10' THEN 'Октябрь'
    		WHEN tmp.month = '11' THEN 'Ноябрь'
    		WHEN tmp.month = '12' THEN 'Декабрь'
    	END,
    	SUM(tmp.opp) [opp],
    	SUM(tmp.qt) [qt],
    	SUM(tmp.ord) [ord]
    FROM (
    	SELECT
    		MONTH(createdon) [month],
    		SUM(estimatedvalue) [opp],
    		'' [qt],
    		'' [ord]
    	FROM
    		FilteredOpportunity
    	WHERE
    		(MONTH(GETDATE()) - MONTH(createdon)) < 6
    		and customerid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'
    	GROUP BY
    		MONTH(createdon)
    
    	UNION
    
    	SELECT
    		MONTH(createdon) [month],
    		'' [opp],
    		SUM(totalamount) [qt],
    		'' [ord]
    	FROM
    		FilteredQuote
    	WHERE
    		(MONTH(GETDATE()) - MONTH(createdon)) < 6
    		and customerid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'
    	GROUP BY
    		MONTH(createdon)
    
    	UNION
    
    	SELECT
    		MONTH(createdon) [month],
    		'' [opp],
    		'' [qt],
    		SUM(totalamount) [ord]
    	FROM
    		FilteredSalesOrder
    	WHERE
    		(MONTH(GETDATE()) - MONTH(createdon)) < 6
    		and customerid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'
    	GROUP BY
    		MONTH(createdon)
    ) AS tmp
    GROUP BY
    	tmp.month
    ORDER BY
    	tmp.month
    
  • Количество действий, у которых поле В отношении указан текущий Бизнес-партнер:
    select
    	CRMAF_ap.activitytypecodename as tip,
    	COUNT(*) as kolvo
    from
    	dbo.FilteredActivityPointer as CRMAF_ap
    where
    	CRMAF_ap.regardingobjectid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'
    group by CRMAF_ap.activitytypecodename
    
  • Количество Возможных сделок, Предложений, Заказов заведенных в отношении текущего Бизнес-партнера:
    select
    	'Возможные сделки' as tip,
    	COUNT(*) as kolvo
    from
          dbo.FilteredOpportunity as CRMAF_opp
    where
    	customerid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'
    UNION
    select
    	'Предложения' as tip,
    	COUNT(*) as kolvo
    from
    	dbo.FilteredQuote as CRMAF_quo
    where
    	customerid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'	
    UNION
    select
    	'Заказы' as tip,
    	COUNT(*) as kolvo
    from
    	dbo.FilteredSalesOrder as CRMAF_ord
    where
    	customerid = 'E85253EE-C1C5-DF11-82C7-000C29F6B309'
    

Для размщения ASPX-странички создадим новый сайт:

  • Откройте IIS Manager;
  • Создайте новый сайт с именем SilverlightChart и разместите его в папке C:\inetpub\SilverlightChart (которую предварительно создайте) и задайте порт 5555 (если он еще не занят);
  • Перейдите к узлу Application Pools и переведите пул SilverlightChart на работу с .Net 4.0 в режиме Integrated;
  • Откройте расширенные настройки пула и задайте его запуск от имени администратора (или того кто может свободно подключаться к скулю используя Windows-аутентификацию и выполнять любые запросы в отношении БД CRM);




Разработка

  • Создайте в VS 2010 новый Silverlight проект: File — New — Project — выберите шаблон Silverlight Application и поставьте для него .Net Framework 4.0 — Ok- в открывшемся диалоговом окне:
    • Поставьте галку (по умолчания она и так установлена) Host the Silverlight application in a new Web site;
    • В качестве типа проекта выберите ASP.NET Web Application Project;
    • Версию Silverlight укажите четвертую;
    • И поставьте галку Enable WCF RIA Services.
  • Щелкните правой кнопкой мыши по заголовку веб-проекта в Solution Explorer (именно веб-проекта, т.к. в Solution Explorer будут присутствовать и веб- и Silverlight-проекты) и в контекстном меню выберите Add — New Item — выберите шаблон Silverlight-enabled WCF Service, тем самым мы добавим в текущий проект WCF-сервис;
  • Откроется на редактирование файл Service1.svc.cs, в котором:
    • Добавьте три пространства имен:
      using System.Collections.ObjectModel;
      using System.Data.SqlClient;
      using System.Data;
      
    • Замените внитри класса Service1 код на следующий:
      // Общие переменные для работы SQL-класса
              DataTable oTable = new DataTable();
              SqlDataAdapter da;
              string query;
      
              // Функция подключается к БД CRM и выполняет переданный в параметре SQL-запрос
              public DataTable getSQL(string Query)
              {
                  // Подключаемся к БД
                  SqlConnection myConnection = new SqlConnection("Initial Catalog=superfirma_MSCRM;Data Source=win-n22hj23d1b1;Integrated Security=SSPI;");
                  myConnection.Open();
      
                  // Выполняем запрос
                  da = new SqlDataAdapter(Query, myConnection);
                  oTable.Clear();
                  da.Fill(oTable);
      
                  // Закрываем соединение с SQL-сервером и возвращаем результат в виде объекта DataTable
                  myConnection.Close();
                  return oTable;
              }
      
              // Метод сервиса, возвращающий данные в виде коллекции объектов Sales
              [OperationContract]
              public ObservableCollection<Sales> GetSales(string AccGUID)
              {
                  // Создаем экземпляр класса Sales
                  ObservableCollection<Sales> Sales = new ObservableCollection<Sales> { };
      
                  // Формируем SQL-запрос
                  query =
                  "SELECT " +
                      "'mnth' = " +
                      "CASE " +
                          "WHEN tmp.month = '1' THEN 'Январь' " +
                          "WHEN tmp.month = '2' THEN 'Февраль' " +
                          "WHEN tmp.month = '3' THEN 'Март' " +
                          "WHEN tmp.month = '4' THEN 'Апрель' " +
                          "WHEN tmp.month = '5' THEN 'Май' " +
                          "WHEN tmp.month = '6' THEN 'Июнь' " +
                          "WHEN tmp.month = '7' THEN 'Июль' " +
                          "WHEN tmp.month = '8' THEN 'Август' " +
                          "WHEN tmp.month = '9' THEN 'Сентябрь' " +
                          "WHEN tmp.month = '10' THEN 'Октябрь' " +
                          "WHEN tmp.month = '11' THEN 'Ноябрь' " +
                          "WHEN tmp.month = '12' THEN 'Декабрь' " +
                      "END, " +
                      "SUM(tmp.opp) [opp], " +
                      "SUM(tmp.qt) [qt], " +
                      "SUM(tmp.ord) [ord] " +
                  "FROM ( " +
                      "SELECT " +
                          "MONTH(createdon) [month], " +
                          "SUM(estimatedvalue) [opp], " +
                          "'' [qt], " +
                          "'' [ord] " +
                      "FROM " +
                          "FilteredOpportunity " +
                      "WHERE " +
                          "(MONTH(GETDATE()) - MONTH(createdon)) < 6 " +
                          "and customerid = '" + AccGUID + "' " +
                      "GROUP BY " +
                          "MONTH(createdon) " +
                      "UNION " +
                      "SELECT " +
                          "MONTH(createdon) [month], " +
                          "'' [opp], " +
                          "SUM(totalamount) [qt], " +
                          "'' [ord] " +
                      "FROM " +
                          "FilteredQuote " +
                      "WHERE " +
                          "(MONTH(GETDATE()) - MONTH(createdon)) < 6 " +
                          "and customerid = '" + AccGUID + "' " +
                      "GROUP BY " +
                          "MONTH(createdon) " +
                      "UNION " +
                      "SELECT " +
                          "MONTH(createdon) [month], " +
                          "'' [opp], " +
                          "'' [qt], " +
                          "SUM(totalamount) [ord] " +
                      "FROM " +
                          "FilteredSalesOrder " +
                      "WHERE " +
                          "(MONTH(GETDATE()) - MONTH(createdon)) < 6 " +
                          "and customerid = '" + AccGUID + "' " +
                      "GROUP BY " +
                          "MONTH(createdon) " +
                  ") AS tmp " +
                  "GROUP BY " +
                      "tmp.month " +
                  "ORDER BY " +
                      "tmp.month";
      
                  // Отправляем SQL-запрос в функцию getSQL и просматриваем полученный ответ
                  foreach (DataRow oRow in getSQL(query).Rows)
                  {
                      // Каждую строку ответа заносим в объект Sales
                      Sales.Add(new Sales(oRow["mnth"].ToString(), Convert.ToInt32(oRow["opp"]), Convert.ToInt32(oRow["qt"]), Convert.ToInt32(oRow["ord"])));
                  }
      
                  // Возвращаем сформированный объект Sales
                  return Sales;
              }
      
              // Метод сервиса, возвращающий данные в виде коллекции объектов Sales
              [OperationContract]
              public ObservableCollection<Count> GetActivityCount(string AccGUID)
              {
                  // Создаем экземпляр класса Count
                  ObservableCollection<Count> ActivityCount = new ObservableCollection<Count> { };
      
                  // Формируем SQL-запрос  
                  query =
                  "select " +
                      "CRMAF_ap.activitytypecodename as tip, " +
                      "COUNT(*) as kolvo " +
                  "from " +
                      "dbo.FilteredActivityPointer as CRMAF_ap " +
                  "where " +
                      "CRMAF_ap.regardingobjectid = '" + AccGUID + "' " +
                  "group by CRMAF_ap.activitytypecodename";
      
                  // Отправляем SQL-запрос в функцию getSQL и просматриваем полученный ответ
                  foreach (DataRow oRow in getSQL(query).Rows)
                  {
                      // Каждую строку ответа заносим в объект ActivityCount
                      ActivityCount.Add(new Count(oRow["tip"].ToString(), Convert.ToInt32(oRow["kolvo"])));
                  }
      
                  // Возвращаем сформированный объект ActivityCount
                  return ActivityCount;
              }
      
              // Метод сервиса, возвращающий данные в виде коллекции объектов SalesCount
              [OperationContract]
              public ObservableCollection<Count> GetSalesCount(string AccGUID)
              {
                  // Создаем экземпляр класса SalesCount
                  ObservableCollection<Count> SalesCount = new ObservableCollection<Count> { };
      
                  // Формируем SQL-запрос
                  query =
                  "select " +
                      "'Возможные сделки' as tip, " +
                      "COUNT(*) as kolvo " +
                  "from " +
                      "dbo.FilteredOpportunity as CRMAF_opp " +
                  "where " +
                      "customerid = '" + AccGUID + "' " +
                  "UNION " +
                  "select " +
                      "'Предложения' as tip, " +
                      "COUNT(*) as kolvo " +
                  "from " +
                      "dbo.FilteredQuote as CRMAF_quo " +
                  "where " +
                      "customerid = '" + AccGUID + "' " +
                  "UNION " +
                  "select " +
                      "'Заказы' as tip, " +
                      "COUNT(*) as kolvo " +
                  "from " +
                      "dbo.FilteredSalesOrder as CRMAF_ord " +
                  "where " +
                      "customerid = '" + AccGUID + "'";
      
                  // Отправляем SQL-запрос в функцию getSQL и просматриваем полученный ответ
                  foreach (DataRow oRow in getSQL(query).Rows)
                  {
                      // Каждую строку ответа заносим в объект SalesCount
                      SalesCount.Add(new Count(oRow["tip"].ToString(), Convert.ToInt32(oRow["kolvo"])));
                  }
      
                  // Возвращаем сформированный объект SalesCount
                  return SalesCount;
              }
      
              // Класс, в котором кранятся данные для первой диаграммы
              public class Sales
              {
                  public Sales() { }
                  public Sales(string Month, int Opp, int Qt, int Ord)
                  {
                      month = Month;
                      opp = Opp;
                      qt = Qt;
                      ord = Ord;
                  }
      
                  public string month { get; set; }
                  public int opp { get; set; }
                  public int qt { get; set; }
                  public int ord { get; set; }
              }
      
              // Класс, в котором кранятся данные для второй и третей диаграммы
              public class Count
              {
                  public Count() { }
                  public Count(string Type, int Count)
                  {
                      type = Type;
                      count = Count;
                  }
      
                  public string type { get; set; }
                  public int count { get; set; }
              }
      

      Что тут у нас есть:

      • Во-первых, определены два пользовательских класса (Sales и Count), экземпляры которых, мы упакуем в коллекцию ObservableCollection и поместим в диаграммы;
      • Во-вторых, функция getSQL, подключающаяся к БД MS CRM и выполняющая в отношении нее SQL-запрос (который передан ему в качестве параметра);
      • Далее определеные три метода самого WCF-сервис (обозначены атрибутом [OperationContract]). В эти методы передается GUID Бизнес-партнера, который подставляется в SQL-запрос. SQL-запрос передается функции getSQL, возвращенный результат просматривается и каждая его строка в заностися в коллекцию ObservableCollection в виде экземпляров объектов классов Sales или Count. Коллекция ObservableCollection возвращается клиенту.
  • Щелкните правой кнопкой мыши по заголовку веб-проекта в Solution Explorer и в контекстном меню выберите Add — New Item — добавьте файл с именем clientaccesspolicy.xml. После добавления, файл откроется на редактирование — поместите в него следующий код:
    <?xml version="1.0" encoding="utf-8" ?>
    <access-policy>
      <cross-domain-access>
        <policy>
          <allow-from>
            <domain uri="*"/>
          </allow-from>
          <grant-to>
            <resource path="/" include-subpaths="true"/>
          </grant-to>
        </policy>
      </cross-domain-access>
    </access-policy>
    

    Этот код разрешает обращаться к сервису кому угодно (т.е. из любого домена);

  • WCF-сервис готов — тепреь опубликуем проект. Хотя это и не целиком готовый проект, это нужно для того, чтобы мы могли в Silverlight части добавить на сервис ссылку. Выделите веб-проект в Solution Explorer — перейдите Build — Publish <название проекта> — в диалоговом окне укажите путь к папку созданного выше сайта (в этом примере это C:\inetpub\SilverlightChart) — Ок. Введите в адресной строке браузера http://<имя_сервера>:5555/Service1.svc, чтобы убедиться что сервис работает;





  • Теперь займемся программирование самого Silverlight-приложения:
    • Добавьте в Silverlight-проект следующие dll’ки:
      • System.Windows.Controls;
      • System.Windows.Controls.DataVisualization.Toolkit;
      • System.Windows.Controls.Toolkit.
    • В файле MainPage.xaml из узла UserControl удалите такую строчку:
      d:DesignHeight="300" d:DesignWidth="400"
      

      Тем самым мы заставим Silverlight-приложение разворачиваться на всю доступную область.
      И в него же добавьте такую:

      xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
      

      Это мы просто добавляем пространство имен диаграмм (подключенных выше), через которые диаграммы потом будут добавляться в проект;

    • Весь узел Grid (созданный по умолчанию) замените на такой:
      <Grid x:Name="LayoutRoot" Background="#FFEAF3FF">
      	<!-- Определяем строки и столбцы диаграммы -->
      	<Grid.RowDefinitions>
      		<RowDefinition></RowDefinition>
      		<RowDefinition></RowDefinition>
      	</Grid.RowDefinitions>
      	<Grid.ColumnDefinitions>
      		<ColumnDefinition></ColumnDefinition>
      		<ColumnDefinition></ColumnDefinition>
      	</Grid.ColumnDefinitions>
      	<!-- Первая диграмма (линейная) -->
      	<toolkit:Chart  x:Name="SalesChart" Title="Объемы продажи за полгода" Grid.Row="0" Grid.ColumnSpan="2" BorderThickness="0">
      		<toolkit:Chart.Series>
      			<toolkit:LineSeries ItemsSource="{Binding}" IndependentValuePath="month" DependentValuePath="opp" Title="Возможные сделки"></toolkit:LineSeries>
      			<toolkit:LineSeries ItemsSource="{Binding}" IndependentValuePath="month" DependentValuePath="qt" Title="Предложения"></toolkit:LineSeries>
      			<toolkit:LineSeries ItemsSource="{Binding}" IndependentValuePath="month" DependentValuePath="ord" Title="Заказы"></toolkit:LineSeries>
      		</toolkit:Chart.Series>
      	</toolkit:Chart>
      	<!-- Вторая диграмма (круговая) -->
      	<toolkit:Chart x:Name="CountActivityChart" Title="Количество действий" Grid.Row="1" Grid.Column="0"  BorderThickness="0" >
      		<toolkit:PieSeries ItemsSource ="{Binding}" IndependentValuePath="type" DependentValuePath="count"></toolkit:PieSeries>
      	</toolkit:Chart>
      	<!-- Третья диграмма (столбчатая) -->
      	<toolkit:Chart  x:Name="CountSalesChart" Title="Количество сделок" Grid.Row="1" Grid.Column="1"  BorderThickness="0">
      		<toolkit:Chart.LegendStyle>
      			<Style TargetType="toolkit:Legend" >
      				<Setter Property="Width" Value="0" />
      				<Setter Property="Height" Value="0" />
      			</Style>
      		</toolkit:Chart.LegendStyle>
      		<toolkit:ColumnSeries ItemsSource="{Binding}" IndependentValuePath="type" DependentValuePath="count"></toolkit:ColumnSeries>
      	</toolkit:Chart>
      </Grid>
      

      Тут определена сетка Grid в которую включены три диаграммы: линейная (занимает две верхние ячейки), круговая и столбчатая (занимают по одной нижней ячейки, соответственно);

    • Добавьте в проект ссылку на сервис, который мы опубликовали ранее: правой кнопкой на References — Add Service Reference — введите URL сервиса (если все как в примере делаете, то должно быть http://<имя_сервера>:5555/Service1.svc) — Go — Ok;
    • Далее… откройте файл MainPage.xaml.cs:
      • Добавьте два пространства имен:
        Второе зависит от названия проекта и от имени пространства имен, которое Вы присвоили сервису при создании ссылки на него;
      • После конструктора MainPage() добавьте такие строчки:
        А после конструктора MainPage() добавьте три обработчика:
        Тут определены три обработчика событий, каждый из которых подключаться к событию завершения вызова WCF-сервиса и возвращению результатов. Полученные результаты (в виде колекции ObservableCollection объектов Sales или Count) помещаются в диаграммы.
    • Снова опубликуйте проект как было описано выше;





Развертывание

Откройте CRM и на карточке Бизнес-партнера добавьте новую вкладку, а на нее новый iFrame с такими параметрами:

  • Имя: любое;
  • URL: http://<имя_сервера>:5555/SilverlightApplication3TestPage.aspx (имя ASPX-страницы может у Вас отличаться);
  • Поставьте галку «Отправить код типа объекта и уникальный идентификатор как параметры»;
  • Снимите галку ограничивающую использование сценариев.

Готово 🙂 Идем смотреть кавайную картинку 🙂


Дополниельно

Помимо Silverlight Toolkit существуют еще несколько Silverlight-аддонов позволяющих рисовать диаграммы (некоторые из них значительно превосходя Silverlight Toolkit в плане рисования графиков). Вот парочка самых интересных:

  • Visifire — это набор компонентов для визуализации данных, созданный с использованием технологий Microsoft Silverlight. Visifire позволяет вам создавать красочные анимированные диаграммы, которые можно интегрировать на web-страницы. Visifire прост в использовании и не зависит от серверного программного обеспечения, поскольку может использоваться с ASP, ASP.Net, PHP, JSP, ColdFusion, Ruby on Rails или простым HTML.Visifire распространяется на бесплатной основе, в рамках лицензии Open Source GPL 3.0. В случае внедрения Visifire в коммерческий продукт со своей собственной схемой лицензирования – уже придётся раскошеливаться на определённую сумму (и получит к этому проффесиональную поддержку разработчиков) 🙂Галлерея, где можно скачать и онлайн документация 🙂
  • amCharts — представляет собой набор для построения графиков. amCharts может извлекать данные из простых XML или CSV файл, или же может читать динамические данные, получаемые из PHP, .NET, Java, Ruby On Rails, Perl, ColdFusion, а также многих других языков программирования.Бесплатная редакция включает в себя ссылку (сбоку на самих диаграммах) на сайт amCharts.

    Инсталяционники и документация для amCharts for Silverlight.

Комментарии (7)
  • Legendary 27.09.2010

    SqlConnection myConnection = new SqlConnection(«Initial Catalog=superfirma_MSCRM;Data Source=win-n22hj23d1b1;Integrated Security=SSPI;»);

    Тут мы задаем начальный каталог. А что если я создаю приложение на одном сервере а в последствии готовый файл решения перенесу на другой сервер, мне потребуется обновлять SqlConnection?

  • slivka_83 27.09.2010

    Да 🙂

  • Endymion 27.09.2010

    А можно ли как то как-то эту строку
    SqlConnection myConnection = new SqlConnection(«Initial Catalog=superfirma_MSCRM;Data Source=win-n22hj23d1b1;Integrated Security=SSPI;»);
    сделать динамической.
    Мне нужно создать решение, которое работает на слиьверлайте, устанавливается на crm и подключается к той базе, к которой подключена текущаяя crm.

  • Juerry 27.09.2010

    Ссылка на amCharts битая

  • Juerry 27.09.2010

    Также отсутствует код файла MainPage.xaml.cs

  • slivka_83 27.09.2010

    Зря Вы взялись за Silverlight. Это уже полумертвая технология 🙂

  • Juerry 27.09.2010

    Меня не столько Silverlight интересует, сколько сама возможность ухода от .rdl, т.к. отчеты для срм в vs bi имеют весьма ограниченный функционал ( сделай график, потом сделай еще один отчет который покажет такие же данные только в табличном варианте, установи ссылку на действие), ранее (не в срм) использовал библиотеку amchart, но система была на php и принципы создания страницы мне были более понятны. С aspx только начинаю, почти ничего непонятно, IIS, сервисиы и еще куча всего, если у Вас есть возможность доукомплектовать статью (код файла MainPage.xaml.cs), буду весьма благодарен.

*

code