Używam projektanta Visual Studio 2013 do tworzenia kontroli użytkownika w WPF i używam podejścia MVVM.Ustawianie czasu projektowania ViewModel
Próbuję znaleźć najlepszy sposób na ustawienie "Design-Time" mojego viewmodelu, tak aby od razu zobaczyć efekt w projektancie zmiany wartości właściwości na przykład. Użyłem różnych projektów i technik, aby to wspierać, ale nic nie jest dokładnie tym, czego chcę. Zastanawiam się, czy ktoś ma lepsze pomysły ...
Sytuacja (uproszczona): Mam więc "urządzenie", które chcę, aby UserControl pokazywał stany i operacje. Od góry do dołu:
- Mam IDeviceModel który ma pole
bool IsConnected {get;}
(i właściwego powiadomienia o zmianach stanu) - Mam FakeDeviceModel który implementuje IDeviceModel, a tym samym pozwala mi nie polegać na rzeczywistym urządzeniu czas projektowania i testowanie:
- Urządzenie DeviceViewModel, które zawiera IDeviceModel i enkapsuluje właściwości modelu. (Tak, że ma odpowiednie powiadomienia INotifyPropertyChanged w nim)
- Moje UserControl które będą miały DataContext typu DeviceViewModel i musiałby niestandardowego stylu CheckBox który
IsChecked={Binding IsConnected, Mode=OneWay
- Mój cel: chcę obejrzeć na czasie projektowania jak robi stan IsConnected modelu wpływają mojego UserControl (więc może to mieć wpływ na inne rzeczy niż tylko IsChecked)
ramowa:
- używam pomysł MVVM światła ViewModelLocator, wracając pola non-statyczne (tak nowy ja właściwości ViewModels). W czasie wykonywania prawdziwy datacontext zostaną podane przez jednego instanciating ten UserControl
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 Próbowałem:
rozwiązanie kompilacji
Wystarczy Kodeksu konfiguracja ViewModel w ViewModelLocator.
var deviceViewModel = new DeviceViewModel(fakeDeviceModel);
var fakeDeviceModel = new FakeDeviceModel();
fakeDeviceModel.IsConnected = true;
deviceViewModel.AddDevice(fakeDeviceModel);
Plusy: Proste
Wady: To dłuższe iteracji zawsze zmieni wartość w kodzie, rekompilacji, wróć do projektanta widoku czekać na wynik
Instancji w zasobach i przechowywane static w ViewModelLocator
Tak więc tworzę instancję w XAML i próbuję ją wepchnąć do bieżącego ViewModel używanego przez projektanta.Nie najczystszy sposób, ale pracował przez jakiś czas w prostych sytuacji (tak jest jakaś wierdness z kolekcji, ale z myślą, że mogę mieć wiele urządzeń i stare)
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...]
I 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();
}
}
}
}
Plusy:
- Pracował bardzo dobrze na jednym projec t. instancja, którą miałem w XAML, mogłem zmodyfikować booleans i otrzymywałbym natychmiastową informację zwrotną o tym, jak wpływa ona na moją UserControl. Tak więc w prostej sytuacji „sprawdzone” stan pole wyboru byłby zmienić i mogę zmodyfikować moje stylizacji w czasie rzeczywistym, bez konieczności rekompilacji
Wady:
przestał działać w innym projekcie, a to Sam nie mogłem znaleźć przyczyny. Ale po ponownym skompilowaniu i zmianie rzeczy, projektant dałby mi wyjątki wyglądające jak "Nie mogę rzucić" FakeDeviceModel "na" FakeDeviceModel "" !! Domyślam się, że Designer wewnętrznie kompiluje i wykorzystuje pamięci podręczne dla tych typów (C: \ Users \ firstname.lastname \ AppData \ Local \ Microsoft \ VisualStudio \ 12.0 \ Designer \ ShadowCache). I w moim rozwiązaniu, w zależności od porządku rzeczy, tworzyłem "FakeDeviceModel", który został przypisany statycznym instancjom i "później", następnym razem, gdy ViewModelLocator zostanie poproszony o ViewModel, użyłby tego instancja. Jeśli jednak w międzyczasie "przekompiluje" lub użyje innej pamięci podręcznej, to nie jest "dokładnie" tego samego typu. Musiałem więc zabić projektanta (XDescProc) i przekompilować go, aby działał, a następnie ponowić awarię kilka minut później. Jeśli ktoś może mnie poprawić, byłoby wspaniale.
Wielu Oprawa do d: DataContext i niestandardową konwertera
Problem poprzedniego rozwiązania była wskazując mnie fakt, że ViewModel i FakeDeviceModel powstały w innym momencie w czasie (podając typ/Problem cast) i go rozwiązać, to trzeba utworzyć je w tym samym czasie
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();
}
}
Plusy: PRACE super miły! Gdy wiążąca dla DataContext prosi o ViewModel, mogę skorzystać z konwertera do modyfikowania że ViewModel i wstrzyknąć moje urządzenie przed wpuszczeniem go
Wady:
Tracimy intelissense (z ReSharper), ponieważ on nie robi” t Wiesz, jaki typ jest zwracany przez konwerter
Jakieś inne pomysły lub modyfikacje, które mogłem wprowadzić, aby rozwiązać ten problem?
Czy próbowałeś tego w Blend? W Visual Studio 2010 łatwiej było pracować z tym wzorcem w Blend niż Visual Studio, ponieważ projektant był bardziej wytrzymały. Nie jestem pewien, jak Blend porównuje się do VS2013. –
Jak sobie z tym radziłeś w Blend? – FrankyB
VS i Blend dzielą tego samego projektanta, ale nie wszystkie takie same funkcje w jakikolwiek sposób. Chociaż VS nie używa już projektanta cydru, nadal możesz zrobić dużo tego, co możesz/może mieszać w VS teraz. –