Utilizzo la finestra di progettazione di Visual Studio 2013 per creare il controllo utente in WPF e sto utilizzando un approccio MVVM.Configurazione in fase di progettazione di un ViewModel
Sto cercando di trovare il modo migliore di avere l'impostazione "Design-Time" del mio viewmodel in modo tale da vedere immediatamente l'effetto nel progettista di modificare un valore di una proprietà, ad esempio. Ho usato diversi disegni e tecniche per supportare questo, ma nulla è esattamente quello che voglio. Mi chiedo se qualcuno ha idee migliori ...
Situazione (semplificata): Quindi ho un "Dispositivo" che voglio che un UserControl mostri stati e operazioni. Dall'alto verso il basso:
- Ho un IDeviceModel, che ha un campo
bool IsConnected {get;}
(e la corretta notifica delle modifiche di stato) - Ho un FakeDeviceModel che implementa IDeviceModel, e quindi mi permette di non fare affidamento su un dispositivo reale per la fase di progettazione e test
- Un DeviceViewModel, che contiene un IDeviceModel e incapsula le proprietà del modello. (Sì, ha corretto le notifiche INotifyPropertyChanged in esso)
- mio UserControl che avrà un DataContext di tipo DeviceViewModel, e avrebbe un CheckBox personalizzato in stile che è
IsChecked={Binding IsConnected, Mode=OneWay
- mio obiettivo: voglio vedere in anteprima sulla fase di progettazione come fa del modello di stato IsConnected influisce sul mio UserControl (così potrebbe influenzare le altre cose che solo IsChecked)
quadro:
- io uso l'idea del MVVM Luce ViewModelLocator, tornando campi non statici (così nuovo io istanze di ViewModels). In fase di esecuzione, il vero DataContext sarà dato da colui instanciating questo 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 ho provato:
soluzione Compile-Time
semplicemente il codice configurazione di ViewModel in ViewModelLocator.
var deviceViewModel = new DeviceViewModel(fakeDeviceModel);
var fakeDeviceModel = new FakeDeviceModel();
fakeDeviceModel.IsConnected = true;
deviceViewModel.AddDevice(fakeDeviceModel);
Pro: semplici
Contro: Ecco iterazioni più lunghe di sempre andando a cambiare il valore nel codice, ricompilare, tornare alla visualizzazione progettista, attendere risultato
istanza di risorse e conservato static in ViewModelLocator
Così creo un'istanza in XAML e provo a inserirla nell'attuale ViewModel utilizzato dal progettista.Non il modo più pulito, ma ha lavorato per qualche tempo in una situazione semplice (sì c'è qualche wierdness con la raccolta, ma è stato con l'idea che avrei potuto avere più dispositivi e una quella attuale)
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...]
E 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();
}
}
}
}
Pro:
- Ha lavorato super-grande su uno projec t. l'istanza che ho avuto in XAML, ho potuto modificare i booleani e ottenere un feedback immediato su come influenza il mio UserControl. Quindi, nella semplice situazione, stato "controllato" del CheckBox cambierebbe e ho potuto modificare la mia styling in tempo reale, senza la necessità di ricompilare
Contro:
ha smesso di funzionare in un altro progetto, e questo da solo non ho trovato il motivo per cui. Ma dopo aver ricompilato e cambiato roba, il progettista mi avrebbe dato delle eccezioni come "Non posso lanciare" FakeDeviceModel "su" FakeDeviceModel "" !! La mia ipotesi è che il Designer compila internamente e utilizza cache per questi tipi (C: \ Users \ firstname.lastname \ AppData \ Local \ Microsoft \ VisualStudio \ 12.0 \ Designer \ ShadowCache). E che nella mia soluzione, a seconda dell'ordine delle cose, stavo creando un "FakeDeviceModel" che è stato assegnato a istanze statiche, e "più tardi", la prossima volta che ViewModelLocator sarebbe stato richiesto per un ViewModel, lo userebbe esempio. Tuttavia, nel frattempo "ricompila" o utilizza una cache diversa, quindi non è "esattamente" lo stesso tipo. Quindi ho dovuto uccidere il progettista (XDescProc) e ricompilarlo per farlo funzionare, e poi fallire di nuovo pochi minuti dopo. Se qualcuno può correggermi su questo sarebbe fantastico.
Multi-Binding per d: DataContext e personalizzato convertitore
problema della soluzione precedente mi stava indicando il fatto che il ViewModel e FakeDeviceModel sono stati creati al momento diverso nel tempo (che indica il tipo/problema pressofuso) e per risolverlo, avrei bisogno di creare loro allo stesso tempo
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();
}
}
Pro: -Funziona super bello! Quando il vincolante per il DataContext chiede il ViewModel, approfitto del convertitore per modificare il ViewModel e iniettare il mio dispositivo prima di restituirlo
Contro:
Perdiamo intelissense (con ReSharper), dal momento che doesn' so quale tipo viene restituito dal convertitore
Altre idee o modifiche che potrei apportare per risolvere questo problema?
Hai provato questo in Blend? Con Visual Studio 2010, è stato più semplice lavorare con questo modello in Blend rispetto a Visual Studio perché il designer era più robusto. Non sono sicuro di come la miscela si paragona a VS2013. –
Come stavi gestendo questo in Blend? – FrankyB
VS e Blend condividono lo stesso designer ora, anche se non tutte le stesse funzionalità in alcun modo. Sebbene VS non utilizzi più il designer di sidro, puoi ancora fare molto di ciò che puoi/potresti combinare in VS adesso. –