2

我有一套現有Silverlight應用程序使用MVVM模式來分離Views和ViewModels。我們使用Unity 2.0作爲IoC容器,將依賴性注入到ViewModel類(和支持類型)中。我有一個現有的ViewModelLocator類,它使用Unity容器來解析ViewModel。關於使用Unity 2.0的「Blendable」ViewModelLocator的建議

所有這些在運行時都很好用;然而,因爲ViewModelLocator依賴於由App.xaml.cs的Application_Start方法中的「運行」類的Bootstrapper類創建和配置Unity容器,所以我失去了在設計器或Blend中打開視圖的能力。

我在尋找建議,我可以如何重做ViewModelLocator以支持「可混合性」。

請注意,我不願意強迫我們的ViewModel類實現默認的無參數構造函數,只是爲了Blendability。我們也有我們的ViewModels檢查IsInDesignMode屬性(來自MVVM Light ViewModelBase類)以提供設計時數據與進行服務調用,因此我們沒有針對設計時和運行時的不同ViewModel實現。

讓我知道你在想什麼。

回答

2

您希望UI元素具有良好的設計時體驗(包括在Blend中)。這聽起來像是一個合理的目標。

讓我們來看看UI元素是什麼。對於這個答案的其餘部分,我將稱之爲控制。控件的責任是呈現UI並響應用戶事件。只要它應該具有行爲,它應該只具有與UI渲染和用戶事件相關的行爲。除此之外,框架本身(Silverlight以及WPF)強加一條規則:所有控件都必須具有默認構造函數。此外,由於DataContext是屬性,因此可以將其分配。

我們應該牢記封裝。我們開發的任何控制應該在上面給出的約束內很好地工作。這意味着,除了具有默認構造函數外,也應該在沒有DataContext的情況下正常工作。換句話說,Blendability的體驗應該由Control本身提供,而不是任何需要引導的外部容器來指定DataContext

當控件必須響應用戶事件時,我總是發現接口綽綽有餘。將ICommands附加到控件中的任何適用的事件處理程序。 ICommands由視圖模型定義,但ICommand的美妙之處在於它基本上是一種無效的方法,這意味着在DataContext爲空的情況下提供(無操作)本地默認值是微不足道的。然而,這並不是必須的,因爲命令不是由設計者調用的。


下面是my book一個例子:

<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(); 
    } 
} 
+0

雖然這是一個偉大的,正確的答案 - 我使用的技術自己 - 我認爲這是用代碼短小的代碼解釋你的意思更詳細,或者指向完成的地方。 –

+0

如何顯示*缺少代碼? –

+0

雖然這很好,但提供設計時數據的重點在於,控件在設計器中的渲染方式與在運行時顯示的方式相似。這允許設計師以更準確的方式提供樣式作爲網格,例如,將具有在運行時呈現的行。沒有設計時數據,網格將呈現,列將出現,但不會有數據(人,沒有行)。這不僅會影響設計控件,還會影響UI元素基於其內容流動的佈局。不過,我很欣賞這種詳細的迴應。 – SonOfPirate