2017-02-16 1 views
0

Как я могу переключаться между представлениями/ViewModels, когда виртуальные машины (и, следовательно, представления) должны принимать параметры конструктора, объекты которых асинхронно инициализируются. Или, альтернативно, есть ли лучший способ предоставить Views/VM эти объекты?Как я могу переключаться между Views/ViewModels, которые предоставляют объекты, которые асинхронно инициализируются раньше времени?

Это моя предыдущая реализация, когда View/VM не использовал параметры конструктора.

MainWindow.xaml

Использует DataTemplates для переключения между просмотрами при изменении ContentControl.

<Window.DataContext> 
    <viewModels:MainWindowViewModel /> 
</Window.DataContext> 
<Window.Resources> 
    <DataTemplate DataType="{x:Type viewModels:InitializerViewModel}"> 
     <userControls:InitializerUserControl /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type viewModels:GameListingViewModel}"> 
     <views:GameListingView /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type viewModels:StreamListingViewModel}"> 
     <views:StreamListingView /> 
    </DataTemplate> 
</Window.Resources> 
<ContentControl x:Name="ContentSection" Content="{Binding CurrentViewModel, Mode=OneWay}"> 
    <interactivity:Interaction.Triggers> 
     <interactivity:EventTrigger EventName="Loaded"> 
      <interactivity:InvokeCommandAction Command="{Binding StartInitializationCommand}"/> 
     </interactivity:EventTrigger> 
    </interactivity:Interaction.Triggers> 
</ContentControl> 

MainWindowViewModel.cs

Создает новые ViewModels, а затем устанавливает CurrentViewModel к соответствующему ViewModel. Когда это изменяется, DataTemplate переключает представление на основе вышеуказанных DataTemplates.

public class MainWindowViewModel : BaseViewModel 
{ 
    private InitializerViewModel _initializerViewModel; 
    public ICommand StartInitializationCommand { get; set; }  

    public MainWindowViewModel() 
    { 
     StartInitializationCommand = new AwaitableDelegateCommand(InitializeAsync); 
    } 

    private async Task InitializeAsync() 
    { 
     _initializerViewModel = new InitializerViewModel(); 
     CurrentViewModel = _initializerViewModel; 
     var initializationResult = await _initializerViewModel.InitializeAsync(); 
     if (initializationResult) 
     { 
      GameListingViewModel = new GameListingViewModel(_initializerViewModel.MyObject1, _initializerViewModel.MyObject2); 
      CurrentViewModel = GameListingViewModel; 
     } 
    } 
} 

GameListingView.xaml (это один из виртуальных машин я переключаюсь в и):

Ранее определено DataContext в XAML через:

<UserControl.DataContext> 
    <viewModels:GameListingViewModel /> 
</UserControl.DataContext>--> 

Все вышеперечисленные работы как и ожидалось.


Теперь, благодаря новой функциональности, эти ViewModels нужно получить 1 или несколько объектов, чтобы функционировать, поэтому я хочу, чтобы передать их в конструктор. В результате я больше не могу определять DataContext в xaml, но я могу использовать код для их создания.

public GameListingView(ConfigSettings configSettings, GameBoxHelper gameBoxHelper) 
{ 
    InitializeComponent(); 
    DataContext = new GameListingViewModel(configSettings, gameBoxHelper); 
} 

Проблема заключается в это требует конструктора зрения, чтобы также принимать в соответствующих объектах (в противном случае, где бы я получить эти объекты из?), Что делает DataTemplate назад в MainWindow.xaml больше не работает.

Xaml сообщения об ошибке: «. Тип„GameListingView“не может использоваться в качестве элемента объекта, поскольку она не является общедоступной или не определяет открытый конструктор без параметров или преобразователь типа конструктора по умолчанию не найден»

Важно, что объекты, которые будут переданы в эти ViewModels, не будут готовы немедленно; MainWindowViewModel инициализирует их заранее, что делается асинхронно. В приведенном выше сообщении об ошибке, я думаю, можно использовать TypeConverter (привязка подлежащих передаче объектов к параметрам и назначение их в xaml в качестве параметров для TypeConverter), но я не вижу, как это будет работать, поскольку они не сразу готово (асинхронно инициализировано).

Для справки: намерение заключается в том, что при загрузке приложения вся инициализация выполняется спереди (считывание параметров конфигурации из файловой системы, извлечение некоторых данных из веб-интерфейсов и т. Д.), Так что она сразу же готова, когда это необходимо остальное приложение более отзывчивое. Я бы предпочел не перемещать асинхронную инициализацию объектов на виртуальные машины, которые их используют.

Я не использую рамки MVVM.

Учитывая все это, мой вопрос заключается в том, как я могу продолжать переключать свои представления/виртуальные машины, предоставляя им объекты, которые асинхронно инициализируются раньше времени?


+0

Я думаю, что вы можете использовать DependancyProperty в представлении и обновить вид модель, когда они изменяют. – Ron

+0

Можете ли вы привести пример, как это будет работать? – JBC

+0

Рассмотрите дизайн, ориентированный на viewmodel, где ваша основная модель просмотра имеет дочерние режимы просмотра (или коллекции дочерних моделей) в качестве свойств, а дочерние модели просмотра могут иметь собственные собственные свойства VM. Вместо представлений, создающих свои собственные режимы просмотра, используйте 'DataTemplate' для неявного назначения представлений в viewmodels. '' - любой тип 'BlahVM', если для него есть неявный' DataTemplate', вы получите это в пользовательском интерфейсе. «BlahVM» может быть объявлен как объект, его родитель может в течение дня переключать разные дочерние виртуальные машины. –

ответ

1

Не можете создавать зависимости в вашем App.xaml.cs или классе загрузчика при запуске приложения, а затем передавать их в MainViewModel в качестве аргументов конструктора?

Удалить атрибут StartupUri из Application корневого элемента в App.xaml и переопределить метод OnStartup в App.xaml.cs:

public partial class App : Application 
{ 
    protected async override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     var yourDependency = await YourClass.CreateAsync(); 

     MainWindowViewModel vm = new MainWindowViewModel(yourDependency); 
     MainWindow mainWindow = new WpfApplication4.MainWindow() { DataContext = vm }; 
     mainWindow.Show(); 
    } 
} 

MainViewModel класса будет нести ответственность за инъекционные другие модели просмотра, который он создает, с зависимостями, которые он вводит классом App.

Если вы предпочитаете инициализировать зависимости внутри класса MainViewModel, вы можете просто инициализировать этот, то есть MainViewModel, в классе App.

Пожалуйста, обратитесь к сообщению в блоге Стивена Клири для получения дополнительной информации о методах фабричных асинхронных и асинхронном строительстве: http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

+0

Я чувствую, что это действительно не решает мою проблему. Одна вещь, о которой я не указывал ранее, заключается в том, что, хотя все инициализируется, я обновляю пользовательский интерфейс со статусами каждой части (используя «InitializerViewModel» и элемент управления, увиденный в первом фрагменте кода). Если я перемещаю init за пределами MainVM и в App, я теряю эту визуализацию, и если я отделяю его от MainVM на что-то еще, мне все равно нужно переключать представления/виртуальные машины обратно в MainVM из того, что обрабатывает init при передаче параметров. Кажется, с таким подходом у вас будет пустой экран во время инициализации. – JBC

+0

Кроме того, с этим подходом, как бы я взял инициализированные параметры и передал их дочерней VM (учитывая данные DataTemplates, которые я использую в настоящее время для обмена между представлениями при изменении VM 'ContentControl'? Я могу просто передать это в MainVM и теперь у MainVM есть это, но как же MainVM передает его собственным дочерним виртуальным машинам таким образом, чтобы облегчить обмен? – JBC

+0

MainVM создает модели дочернего представления, правильно? Тогда вы просто вводите их любыми зависимостями, которые они необходимо, когда вы их создаете. Перестановка - это просто вопрос об установке свойства CurrentViewModel экземпляру инициализированной модели дочернего представления. И если модель представления должна быть асинхронно инициализирована, вам, конечно же, нужно ее инициализировать. способ сделать это - использовать метод async factory. – mm8

Смежные вопросы