Предложения по «смешиваемому» ViewModelLocator с Unity 2.0 - PullRequest
2 голосов
/ 01 сентября 2011

У меня есть набор существующих приложений Silverlight, использующих шаблон MVVM для разделения Views и ViewModels.Мы используем Unity 2.0 для контейнера IoC, чтобы внедрить зависимости в классы ViewModel (и поддерживающие типы).У меня есть существующий класс ViewModelLocator, который использует контейнер Unity для разрешения ViewModel.

Все это прекрасно работает во время выполнения;однако, поскольку ViewModelLocator полагается на контейнер Unity, создаваемый и настраиваемый классом Bootstrapper, который запускается из метода Application_Start в App.xaml.cs, я потерял возможность открывать представления в конструкторе или в Blend..

Я ищу предложения о том, как я могу переработать ViewModelLocator для поддержки "Blendability".

Обратите внимание, что я не хочу заставлять наши классы ViewModel реализовывать конструкторы по умолчанию без параметров только дляради смешиваемости.У нас также есть наши ViewModels, проверяющие свойство IsInDesignMode (из класса MVVM Light ViewModelBase) для предоставления данных времени разработки вместо вызовов сервисов, поэтому у нас нет разных реализаций ViewModel для времени разработки и выполнения.

Дайте мне знать, что вы думаете.

1 Ответ

2 голосов
/ 01 сентября 2011

Вы хотите, чтобы элементы пользовательского интерфейса имели хороший опыт разработки (в том числе в Blend). Это звучит как разумная цель.

Давайте посмотрим, что такое элемент пользовательского интерфейса. В остальной части этого ответа я буду называть это контролем. Ответственность элемента управления заключается в отображении пользовательского интерфейса и реагировании на пользовательские события. Поскольку оно должно иметь поведение, оно должно иметь только поведение, связанное с отображением пользовательского интерфейса и пользовательскими событиями.

Кроме того, сама структура (Silverlight, а также WPF) налагает правило: все элементы управления должны иметь конструктор по умолчанию. Кроме того, поскольку DataContext является свойством, присваивать его необязательно.

Мы должны помнить об инкапсуляции. Любой элемент управления, который мы разрабатываем, должен хорошо работать в рамках ограничений, указанных выше. Это означает, что, кроме конструктора по умолчанию, он также должен работать правильно без DataContext . Другими словами, опыт Blendability должен предоставляться самим элементом управления, а не внешним контейнером, который необходимо загрузить для присвоения DataContext.

Когда элемент управления должен реагировать на пользовательские события, я всегда находил интерфейс ICommand более чем достаточным. Присоедините ICommands к любому подходящему обработчику событий в элементе управления. ICommands определены моделью представления, но прелесть ICommand заключается в том, что это в основном пустой метод, который означает, что тривиально предоставить (по умолчанию) локальное значение по умолчанию в случае, когда DataContext равно нулю , Однако это редко требуется, поскольку команды не вызываются командами.


Вот пример из моей книги :

<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Product Management"
        Height="300"
        Width="300"
        MinHeight="300"
        MinWidth="300">
    <Window.Resources>
        <Style x:Key="ProductStyle" TargetType="{x:Type ListViewItem}">
            <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
        </Style>
    </Window.Resources>
    <DockPanel FocusManager.FocusedElement="{Binding ElementName=productsListView}">
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <Separator />
                <MenuItem Header="E_xit" Command="{Binding Path=CloseCommand}" />
            </MenuItem>
            <MenuItem Header="_Actions">
                <MenuItem Header="_Refresh" InputGestureText="F5" Command="{Binding Path=RefreshCommand}" />
                <MenuItem Header="_Add Product" InputGestureText="Ins" Command="{Binding Path=InsertProductCommand}" />
                <MenuItem Header="_Edit Product" InputGestureText="Enter" Command="{Binding Path=EditProductCommand}" />
                <MenuItem Header="_Delete Product" InputGestureText="Del" Command="{Binding Path=DeleteProductCommand}" />
            </MenuItem>
        </Menu>
        <ToolBarTray DockPanel.Dock="Top" HorizontalAlignment="Stretch">
            <ToolBar HorizontalAlignment="Stretch" HorizontalContentAlignment="Left">
                <Button Command="{Binding Path=RefreshCommand}">Refresh</Button>
                <Button Command="{Binding Path=InsertProductCommand}">Add</Button>
                <Button Command="{Binding Path=EditProductCommand}">Edit</Button>
                <Button Command="{Binding Path=DeleteProductCommand}">Delete</Button>
            </ToolBar>
        </ToolBarTray>
        <ListView x:Name="productsListView" ItemContainerStyle="{StaticResource ProductStyle}" ItemsSource="{Binding Path=Products}" SelectionMode="Single">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Path=Id}" />
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" />
                    <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=UnitPrice}" />
                </GridView>
            </ListView.View>
        </ListView>
    </DockPanel>
</Window>

и код позади:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }
}
...