2014-09-26 15 views
12

Tôi đang sử dụng trình thiết kế của Visual Studio 2013 để tạo Điều khiển người dùng trong WPF và tôi đang sử dụng phương pháp MVVM.Thiết lập thời gian thiết kế của ViewModel

Tôi đang cố gắng tìm cách tốt nhất để có thiết lập "Thiết kế-thời gian" của chế độ xem của tôi để tôi ngay lập tức thấy hiệu ứng trong nhà thiết kế thay đổi giá trị của thuộc tính chẳng hạn. Tôi đã sử dụng các thiết kế và kỹ thuật khác nhau để hỗ trợ điều này, nhưng không có gì là chính xác những gì tôi muốn. Tôi tự hỏi nếu ai đó có ý tưởng tốt hơn ...

Tình huống (đơn giản): Vì vậy, tôi có một "Thiết bị" mà tôi muốn UserControl hiển thị trạng thái và hoạt động. Từ trên xuống dưới:

  • Tôi có một IDeviceModel trong đó có một lĩnh vực bool IsConnected {get;} (và thông báo thích hợp thay đổi trạng thái)
  • Tôi có một FakeDeviceModel mà thực hiện IDeviceModel, và do đó cho phép tôi để không dựa trên một thiết bị thực cho thiết kế và thử nghiệm
  • DeviceViewModel, chứa IDeviceModel và đóng gói các thuộc tính của mô hình. (Vâng nó có thông báo INotifyPropertyChanged thích hợp trong nó)
  • UserControl của tôi mà sẽ có một DataContext của loại DeviceViewModel, và sẽ có một CheckBox tùy chỉnh theo kiểu đó là IsChecked={Binding IsConnected, Mode=OneWay
  • My Goal: Tôi muốn xem trước trên thời gian thiết kế như thế nào nhà nước IsConnected của mẫu ảnh hưởng đến UserControl của tôi (vì vậy, nó có thể ảnh hưởng đến những thứ khác hơn là chỉ IsChecked)

Khung:

  • tôi sử dụng ý tưởng của MVVM nhẹ ViewModelLocator, trở về các lĩnh vực không tĩnh (vì vậy mới tôi nstances của ViewModels). Khi chạy, các DataContext sản sẽ được đưa ra bởi một instanciating UserControl này

d:DataContext="{Binding DeviceViewModelDesignTime, Source={StaticResource ViewModelLocator}}"

public class ViewModelLocator 
{ 
    private static MainWindowViewModel _mainWindowViewModel; 
    public MainWindowViewModel MainWindowViewModelMainInstance 
    { 
     get 
     { 
      if (_mainWindowViewModel == null) 
      { 
       _mainWindowViewModel = new MainWindowViewModel(); 
      } 
      return _mainWindowViewModel; 
     } 
    } 

    public DeviceViewModel DeviceViewModelDesignTime 
    { 
     get 
     { 
      //Custom initialization of the dependencies here 
      //Could be to create a FakeDeviceModel and assign to constructor 
      var deviceViewModel = new DeviceViewModel(); 

      //Custom setup of the ViewModel possible here 
      //Could be: deviceViewModel.Model = new FakeDeviceModel(); 

      return deviceViewModel; 
     } 
    } 

Solutions tôi đã cố gắng:

giải pháp thời gian biên dịch

Đơn giản chỉ cần mã hoá thiết lập của ViewModel trong ViewModelLocator.

var deviceViewModel = new DeviceViewModel(fakeDeviceModel); 
var fakeDeviceModel = new FakeDeviceModel(); 
fakeDeviceModel.IsConnected = true; 
deviceViewModel.AddDevice(fakeDeviceModel); 

Ưu điểm: Đơn giản

Nhược điểm: Đó là sự lặp lại lâu hơn luôn luôn thay đổi giá trị trong mã, biên dịch lại, trở về chế độ thiết kế, chờ đợi kết quả

Instance tài nguyên và giữ tĩnh trong ViewModelLocator

Vì vậy, tôi tạo một thể hiện trong XAML và tôi cố gắng đẩy nó trong ViewModel hiện tại được nhà thiết kế sử dụng.Không phải là cách sạch nhất, nhưng làm việc trong một thời gian trong tình huống đơn giản (có có một số wierdness với bộ sưu tập, nhưng với ý tưởng rằng tôi có thể có nhiều thiết bị và một hiện tại)

XAML:

<UserControl x:Class="Views.StepExecuteView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DataContext="{Binding DeviceViewModelDesignTime, Source={StaticResource ViewModelLocator}}"> 
<UserControl.Resources> 
    <viewModels:DesignTimeDeviceManager x:Key="DesignTimeDeviceManager"> 
     <viewModels:DesignTimeDeviceManager.DesignTimeDevices> 
      <device:FakeDeviceModel IsConnected="True" 
            IsBusy="False" 
            IsTrayOpen="True" 
            NumberOfChipSlots="4" 
            /> 
     </viewModels:DesignTimeDeviceManager.DesignTimeDevices> 

[... CheckBox binding to datacontext and so on...] 

Và ViewModelLocator.cs:

public class ViewModelLocator 
{ 
    private static MainWindowViewModel _mainWindowViewModel; 
    public MainWindowViewModel MainWindowViewModelMainInstance 
    { 
     get 
     { 
      if (_mainWindowViewModel == null) 
      { 
       _mainWindowViewModel = new MainWindowViewModel(); 
      } 
      return _mainWindowViewModel; 
     } 
    } 

    public static FakeDeviceModel DeviceModelToAddInDesignTime; 
    public DeviceViewModel DeviceViewModelDesignTime 
    { 
     get 
     { 
      var deviceViewModel = new DeviceViewModel(); 
      if (DeviceModelToAddInDesignTime != null) 
       deviceViewModel.AddDevice(DeviceModelToAddInDesignTime); 

      return deviceViewModel; 
     } 
    } 
} 

public class DesignTimeDeviceManager 
{ 
    private ObservableCollection<FakeDeviceModel> _DesignTimeDevices; 
    public ObservableCollection<FakeDeviceModel> DesignTimeDevices 
    { 
     get { return _DesignTimeDevices; } 
     set 
     { 
      if (_DesignTimeDevices != value) 
      { 
       _DesignTimeDevices = value; 
       ViewModelLocator.DeviceModelToAddInDesignTime = value.FirstOrDefault(); 
      } 
     } 
    } 
} 

Ưu điểm:

  • Làm việc siêu tuyệt vời trên một projec t. ví dụ mà tôi đã có trong XAML, tôi có thể sửa đổi các boolean và tôi sẽ nhận được phản hồi ngay lập tức về cách nó ảnh hưởng đến UserControl của tôi. Vì vậy, trong các tình huống đơn giản, "Checked" nhà nước của CheckBox sẽ thay đổi và tôi có thể thay đổi phong cách của tôi trong thời gian thực, mà không cần phải biên dịch lại

Nhược điểm:

Nó ngừng làm việc trong một dự án khác, và điều này tôi không thể tìm ra lý do tại sao. Nhưng sau khi biên dịch lại và thay đổi nội dung, nhà thiết kế sẽ cho tôi ngoại lệ giống như "Không thể truyền" FakeDeviceModel "tới" FakeDeviceModel "" !! Tôi đoán là nhà thiết kế biên dịch nội bộ và sử dụng bộ nhớ cache cho các loại đó (C: \ Users \ firstname.lastname \ AppData \ Local \ Microsoft \ VisualStudio \ 12.0 \ Designer \ ShadowCache). Và trong giải pháp của tôi, tùy thuộc vào thứ tự của mọi thứ, tôi đã tạo một "FakeDeviceModel" được gán cho một cá thể tĩnh và "sau này", lần tiếp theo ViewModelLocator sẽ được hỏi cho ViewModel, nó sẽ sử dụng ví dụ. Tuy nhiên, nếu trong khi chờ đợi anh ta "biên dịch lại" hoặc sử dụng một bộ nhớ cache khác nhau, sau đó nó không phải là "chính xác" cùng loại. Vì vậy, tôi đã phải giết các nhà thiết kế (XDescProc) và biên dịch lại cho nó để làm việc, và sau đó thất bại một lần nữa một vài phút sau đó. Nếu ai đó có thể sửa tôi thì điều này thật tuyệt.

Multi-Binding cho d: DataContext và tùy chỉnh chuyển đổi

Vấn đề giải pháp trước đã chỉ cho tôi đến một thực tế rằng các ViewModel và FakeDeviceModel được tạo ra ở thời điểm khác nhau trong thời gian (đưa ra các loại/đúc vấn đề) và để giải quyết nó, tôi sẽ cần phải tạo ra chúng cùng một lúc

XAML:

<UserControl x:Class="MeltingControl.Views.DeviceTabView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d"> 
<d:UserControl.DataContext> 
    <MultiBinding Converter="{StaticResource DeviceDataContextConverter}"> 
     <Binding Path="DeviceViewModelDesignTime" Source="{StaticResource ViewModelLocator}" /> 
     <Binding> 
      <Binding.Source> 
       <device:FakeDeviceModel IsConnected="False" 
            IsBusy="False" 
            IsTrayOpen="False" 
            SerialNumber="DesignTimeSerie" 
            /> 
      </Binding.Source> 
     </Binding> 
    </MultiBinding> 
</d:UserControl.DataContext> 

public class DeviceDataContextConverter: IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values == null || values.Length == 0) 
      return null; 

     var vm = (DeviceViewModel)values[0]; 
     if (values.Length >= 2) 
     { 
      var device = (IDeviceModel)values[1]; 
      vm.AddDevice(device); 
     } 

     return vm; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Ưu điểm: -Works siêu đẹp! Khi bắt buộc đối với DataContext yêu cầu ViewModel, tôi tận dụng lợi thế của các Converter để chỉnh sửa mà ViewModel và tiêm điện thoại của tôi trước khi trở về nó

Nhược điểm:

Chúng tôi mất intelissense (với ReSharper), kể từ khi ông doesn' Tôi biết loại chuyển đổi nào được trả về bởi bộ chuyển đổi

Bất kỳ ý tưởng hoặc sửa đổi nào khác mà tôi có thể thực hiện để giải quyết vấn đề này?

+0

Bạn đã thử điều này trong Blend chưa? Với Visual Studio 2010, nó đã được dễ dàng hơn để làm việc với mô hình này trong Blend hơn Visual Studio bởi vì các nhà thiết kế đã được mạnh mẽ hơn. Tôi không chắc chắn Blend so sánh với VS2013 như thế nào. –

+0

Bạn xử lý điều này như thế nào trong Blend? – FrankyB

+0

VS và Blend chia sẻ cùng một nhà thiết kế ngay bây giờ, mặc dù không phải tất cả các chức năng giống nhau bằng bất kỳ phương tiện nào. Mặc dù VS không còn sử dụng các nhà thiết kế rượu táo bạn vẫn có thể làm được rất nhiều những gì bạn có thể/có thể pha trộn trong VS bây giờ. –

Trả lời

4

Bạn có thể tạo một ViewModel thời gian thiết kế mà trả IsConnected = true (FakeDeviceViewModel) và thiết lập nó như là một thời gian thiết kế bối cảnh dữ liệu:

d:DataContext="{d:DesignInstance viewModels:FakeDeviceViewModel, 
IsDesignTimeCreatable=True}" 
0
  1. tôi sẽ tạo ra một thể hiện của Fake Xem các mẫu trong một XAML riêng biệt ví dụ DeviceViewModelDataSample.xaml (xem ví dụ dưới đây)

  2. Set Build Action để DesignData

  3. tham khảo các tập tin như vậy

    <UserControl x:Class="YourNameSpace.YourControl" 
           xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
           mc:Ignorable="d" 
           d:DataContext="{d:DesignData Source=/DataSample/DeviceViewModelDataSample.xaml}"> 
    <!-- Skiped details for brevity --> 
    </UserControl> 
    

DeviceViewModelDataSample.xaml

<vm:DeviceViewModel xmlns:dm="clr-namespace:YourNameSpace.DataModel" 
       xmlns:vm="clr-namespace:YourNameSpace.ViewModel"> 
    <vm:DeviceViewModel.DeviceManager> <!-- Assuming this is a collection --> 
     <dm:DeviceModel DeviceName="Fake Device" IsConnected ="true" /> <!-- This creates an instance at design time --> 
    </vm:DeviceViewModel.DeviceManager>  
</vm:DeviceViewModel> 
0

Tôi muốn đề xuất giải pháp thay thế.

Bạn có thể sử dụng cùng một kiểu xem cho dữ liệu thời gian thiết kế và thời gian chạy bình thường và kiểm tra chế độ xem (duy nhất) của bạn xem nhà thiết kế có đang hoạt động không và sau đó tải dữ liệu thời gian thiết kế ở đó.

Trong mô hình quan điểm của bạn, bạn sẽ làm điều gì đó như thế này:

public class ExampleViewModel : ViewModelBase 
{ 
    public ExampleViewModel() 
    { 
     if (IsInDesignMode == true) 
     { 
      LoadDesignTimeData(); 
     } 
    } 

    private void LoadDesignTimeData() 
    { 
     // Load design time data here 
    }  
} 

Thuộc tính IsInDesignMode có thể được đặt theo quan điểm của lớp mô hình cơ sở của bạn - nếu bạn có một - và trông như thế này:

DesignerProperties.GetIsInDesignMode(new DependencyObject()); 

Vui lòng xem câu trả lời của tôi here

Các vấn đề liên quan