MVVM View против проблем привязки ViewModel
У меня возникла проблема при создании экземпляров viewModels.
Я использую ViewModelLocator по большей части, так как мне приходится вводить зависимости в большинстве случаев. Однако есть случаи, когда мне нужно передать аргументы в ViewModel. Насколько я понимаю, для этого мне нужно использовать подход ViweModel-First. Это означает, что мне нужно создать DataTemplate для ViewModel, который связан с View во время выполнения. Обязательно включите конструктор с аргументами, которые я хочу передать.
У меня проблема в том, что когда я создаю ViewModel и передаю свои аргументы, вызывается правильный конструктор. Однако, поскольку ViewModel привязан к представлению, представление вызывает конструктор по умолчанию без параметров для модели представления.
Вот как выглядит XAML для UserControl, с которым я связываю ViewModel:
<UserControl x:Class="MyView">
<UserControl.DataContext>
<viewModels:MyViewModel></viewModels:MyViewModel>
</UserControl.DataContext>
</UserControl>
Шаблон данных выглядит так:
<DataTemplate DataType="{x:Type viewModels:MyViewModel}">
<views:MyView></views:MyView>
</DataTemplate>
Вот пример ViewModel:
public class MyViewModel : ViewModelBase
{
private MyModel _myModel;
public MyViewModel()
{
}
public MyViewModel(MyModel myModel)
{
_myModel = myModel;
}
}
Как только я создаю свою viewModel через код, используя правильный конструктор для передачи аргументов, viewModel снова создается представлением, используя конструктор по умолчанию без параметров viewModel.
Может кто-нибудь объяснить, почему это происходит, и пролить некоторый свет на то, как настроить подход viewmodel-first, чтобы он работал правильно? Я в растерянности и работаю над этим весь день.
Спасибо Тим
1 ответ
Если вы удалите следующий фрагмент из вашего UserControl
и следуйте моим остальным инструкциям, я думаю, у вас будет то, что вы хотите:
Удали это
<UserControl.DataContext>
<viewModels:MyViewModel></viewModels:MyViewModel>
</UserControl.DataContext>
Теперь допустим, что у вас есть UserControl
или же Window
привязан к ViewModel
что вы хотите представить в вашем UserControl
или же Window
, То, как вы могли бы сделать это, это использовать ContentControl
внутри вашего UserControl
или же Window
в сочетании с DataTemplate
указано в ResourceDictionary
вот так:
.xaml:
<Window x:Class="WPF_Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
Title="MainWindow"
x:Name="ThisControl">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<DockPanel LastChildFill="True">
<ContentControl DockPanel.Dock="Left" Content="{Binding NavigationRegion}"/>
<ContentControl DockPanel.Dock="Left" Content="{Binding ContentRegion}"/>
</DockPanel>
</Window>
ContentControl
будет неявно искать DataTemplate
(в иерархии ResourceDictionary
объекты), связанные с ViewModel
что связано с его Content
имущество. Итак, давайте скажем, что мы установили ContentRegion
недвижимость в MainWindowViewModel
к примеру вашего MyViewModel
вот так:
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfApplication1.ViewModels
{
public class MainWindowViewModel : ViewModel
{
private object _navigationRegion;
private object _contentRegion;
public object NavigationRegion
{
get
{
return _navigationRegion;
}
set
{
_navigationRegion = value;
OnPropertyChanged(nameof(NavigationRegion));
}
}
public object ContentRegion
{
get
{
return _contentRegion;
}
set
{
_contentRegion = value;
OnPropertyChanged(nameof(ContentRegion));
}
}
public MainWindowViewModel()
{
ContentRegion = new MyViewModel(new MyModel());
}
}
}
И у вас есть ResourceDictionary
указано так:
MyResourceDictionary.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:views="clr-namespace:WpfApplication1.Views">
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<views:MyView/>
</DataTemplate>
</ResourceDictionary>
И вы объединили ResourceDictionary
с вашим Application.Resources
в вашем файле App.xaml так:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Framework будет неявно искать DataTemplate
для типа данных MyViewModel
, Рамки найдут DataTemplate
за MyViewModel
в ResourceDictionary
мы указали и видим, что он должен быть представлен MyView
пользовательский контроль. Именно в этот момент Framework будет оказывать MyView
пользовательский контроль.
И для потомков:
ViewModel.cs
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected string _title;
protected string Title
{
get
{
return _title;
}
set
{
_title = value;
OnPropertyChanged(nameof(Title));
}
}
protected void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}