Опубликован: 13.12.2011 | Доступ: свободный | Студентов: 1021 / 34 | Оценка: 4.29 / 4.57 | Длительность: 13:56:00
Самостоятельная работа 6:

Построение кроссплатформенного Silverlight/WPF приложения

< Лекция 12 || Самостоятельная работа 6: 123

Вместе с проектами были автоматически созданы главные окна, MainWindow.xaml и MainPage.xml, для WPF и Silverlight соответственно. Так как эти сущности различны в двух версиях (в случае WPF это окно, в Silverlight – UserControl), их невозможно повторно использовать. Однако в них можно поместить приведенный в "Реализация паттерна MVVM с использованием IoC-контейнера, как метод избавления от зависимости между компонентами системы" элемент управления ViewModelPresenter, который будет делегировать создание внутренней разметки представлению, соответствующему находящейся в DataContext модели представления.

MainWindow.xaml:

  1: <Window x:Class="CrossPlatformApplication.MainWindow"
  2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4: xmlns:view="clr-namespace:CrossPlatformApplication"
  5: Title="{Binding Title}" SizeToContent="WidthAndHeight">
  6:     
  7:     <view:ViewModelPresenter ViewModel="{Binding}" />
  8: </Window>

MainPage.xaml:

  1: <UserControl x:Class="SilverlightApplication.MainPage"
  2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4: xmlns:view="clr-namespace:CrossPlatformApplication">
  5:
  6:     <view:ViewModelPresenter ViewModel="{Binding}" />
  7: </UserControl>

Согласно принципам MVVM главному представлению приложения должна соответствовать модель представления. Для начала достаточно пустого класса, общего для WPF и Silverlight:

  1: [Export]
  2: [PartCreationPolicy(CreationPolicy.NonShared)]
  3: [ExportMetadata(AopExtensions.AspectMetadata,
  4:     Aspects.NotifyPropertyChanged)]
  5: public class MainViewModel : IEntitledViewModel
  6: {
  7:     public string Title
  8:     {
  9:         get { return "Test Application"; }
 10:     }
 11: }

Затем для корректного запуска приложения необходимо добавить логику инициализации IoC контейнера MEF и MVVM окружения, а также модифицировать логику отображения главных окон. Как в WPF, так и в Silverlight приложении модифицируется код App.xaml и App.xaml.cs.

App.xaml в WPF:

  1: <Application x:Class="WpfApplication.App"
  2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  4:     <Application.Resources>
  5:          
  6:     </Application.Resources>
  7: </Application>

App.xaml.cs в WPF:

  1: public partial class App
  2: {
  3:     #region Injected properties
  4:
  5:     [Import]
  6:     public ChildViewManager ChildViewManager
  7:        { private get; set; }
  8:
  9:     #endregion
 10:
 11:     protected override void OnStartup(StartupEventArgs e)
 12:     {
 13:         base.OnStartup(e);
 14:
 15:         // Create interception configuration
 16:         var cfg = new InterceptionConfiguration()
 17:             .AddAopInterception();
 18:
 19:         var container = new CompositionContainer(
 20:            new InterceptingCatalog(new AggregateCatalog(
 21:            new DirectoryCatalog(".", "*.exe"),
 22:            new DirectoryCatalog(".", "*.dll")), cfg));
 23:
 24:         var locator = new MefServiceLocator(container);
 25:         ServiceLocator.SetLocatorProvider(() => locator);
 26:         container.ComposeExportedValue<IServiceLocator>(locator);
 27:         container.ComposeParts(this);
 28:
 29:         new MainWindow { DataContext =
 30:                   locator.GetInstance<MainViewModel>() }.Show();
 31:     }
 32: }

App.xaml в Silverlight:

  1: <Application x:Class="SilverlightApplication.App"
  2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  4:     <Application.Resources>
  5:         
  6:     </Application.Resources>
  7: </Application>

App.xaml.cs в Silverlight:

  1: public partial class App
  2: {
  3:     public App()
  4:     {
  5:         Startup += Application_Startup;
  6:         Exit += Application_Exit;
  7:         UnhandledException += Application_UnhandledException;
  8:
  9:         InitializeComponent();
 10:     }
 11:
 12:     #region Injected properties
 13:
 14:     [Import]
 15:     public ChildViewManager ChildViewManager
 16:        { private get; set; }
 17:
 18:     #endregion
 19:
 20:     private void Application_Startup
 21:        (object sender, StartupEventArgs e)
 22:     {
 23:         // Create interception configuration
 24:         var cfg = new InterceptionConfiguration()
 25:             .AddAopInterception();
 26:
 27:         var container = new CompositionContainer(
 28:                         new InterceptingCatalog(
 29:                         new DeploymentCatalog(), cfg));
 30:         var locator = new MefServiceLocator(container);
 31:         ServiceLocator.SetLocatorProvider(() => locator);
 32:         container.ComposeExportedValue<IServiceLocator>(locator);
 33:         container.ComposeParts(this);
 34:
 35:         RootVisual = new MainPage
 36:           { DataContext = locator.GetInstance<MainViewModel>() };
 37:     }
 38:
 39:     private void Application_Exit(object sender, EventArgs e)
 40:     {
 41:
 42:     }
 43:
 44:     private void Application_UnhandledException
 46:        (object sender, ApplicationUnhandledExceptionEventArgs e)
 47:     {
 48:         if (!System.Diagnostics.Debugger.IsAttached)
 49:         {
 50:             e.Handled = true;
 51:             Deployment.Current.Dispatcher.BeginInvoke(
 52:                delegate { ReportErrorToDOM(e); });
 53:         }
 54:     }
 55:
 56:     private void ReportErrorToDOM
 57:        (ApplicationUnhandledExceptionEventArgs e)
 44:     {
 45:         try
 46:         {
 47:             string errorMsg = e.ExceptionObject.Message
 48:                + e.ExceptionObject.StackTrace;
 49:             errorMsg = errorMsg.Replace('"',
 50:                '\'').Replace("\r\n", @"\n");
 51:
 52:             System.Windows.Browser.HtmlPage.Window.Eval(
 53:   "throw new Error(\"Unhandled Error in Silverlight Application "
 54:   + errorMsg + "\");");
 55:         }
 56:         catch (Exception)
 57:         {
 58:         }
 59:     }
 60: }

Как видно из кода, главные окна приложения будут отображать представление MainView, соответствующее модели представления MainViewModel. Пусть это представление будет содержать простой текст "Hello world":

  1: <UserControl x:Class="CrossPlatformApplication.MainView"
  2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5: xmlns:mc
  6:    ="http://schemas.openxmlformats.org/markup-compatibility/2006"
  7: mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
  8:     
  9:     <Grid x:Name="LayoutRoot" Background="White" MinHeight="150"
 10:           MinWidth="200">
 11:         <TextBlock Text="Hello world"
 12:                    HorizontalAlignment="Center"
 13:                    VerticalAlignment="Center" />
 14:     </Grid>
 15: </UserControl>

Естественно, как говорилось в "Особенности отображения диалоговых окон в WPF и Silverlight версиях приложения" , необходимо создать представление в Silverlight проекте и добавить ссылку на него в WPF проект. Теперь оба приложения готовы к запуску и отображают на своем главном окне сообщение "Hello world".

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

< Лекция 12 || Самостоятельная работа 6: 123