Построение кроссплатформенного Silverlight/WPF приложения
Вместе с проектами были автоматически созданы главные окна, 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.
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>
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: }
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>
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".
Следующим этапом следует некоторое усложнение примера – размещенная на главном представлении кнопка будет отображать модальное дочернее окно, считывающее строку у пользователя. Введенные данные модифицируют исходный текст на главном представлении.