Я разрабатываю свое первое приложение WPF и пытаюсь придерживаться подхода MVVM. Я использую MVVM Light. Это простое приложение, которое просто отображает список вещей под названием «Слияние» и позволяет пользователю выбрать один и отредактировать его.
У меня есть MainView:
<Window x:Class="FileMerger.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FileMerger"
xmlns:Views="clr-namespace:FileMerger.Views"
xmlns:ViewModels="clr-namespace:FileMerger.ViewModels"
mc:Ignorable="d"
Title="File Merge" Height="450" Width="800">
<Window.DataContext>
<ViewModels:MainViewModel></ViewModels:MainViewModel>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModels:MergeListViewModel}">
<Views:MergeList/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:EditMergeViewModel}">
<Views:EditMergeView/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPageViewModel}"></ContentControl>
</Window>
и MainViewModel следующим образом:
public class MainViewModel:ViewModelBase
{
private ViewModelBase _currentPageViewModel;
private MergeListViewModel _mergeListViewModel=new MergeListViewModel();
private EditMergeViewModel _editMergeViewModel = new EditMergeViewModel();
public MainViewModel()
{
// Set starting page
_mergeListViewModel.MergeSelected += navToMergeEdit;
CurrentPageViewModel = _mergeListViewModel;
}
public ViewModelBase CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
RaisePropertyChanged(nameof(CurrentPageViewModel));
}
}
}
public void navToMergeEdit(int mergeId)
{
_editMergeViewModel.MergeId = mergeId;
CurrentPageViewModel = _editMergeViewModel;
}
}
В моем первом представлении MergeListView есть это событие в ViewModel (MergeListViewModel)
public event Action<int> MergeSelected = delegate { };
Идея состоит в том, что когда выбрано «Слияние», EditMergeView загружается с ViewModel EditMergeViewModel, передавая mergeId в качестве параметра. LoadedCommand в EditMergeViewModel должен быть запущен, который получает данные для заполнения ViewModel с помощью Merge.
EventTrigger в EditMergeView должен делать следующее:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<command:EventToCommand Command="{Binding LoadedCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Я обнаружил, что LoadedCommand является не вызывается, хотя точки останова показывают, что NavToMergeEdit в MainViewModel вызывается через событие. Я установил точку останова в конструкторе EditMergeView и обнаружил, что до вызова InitializeComponent () this.DataContext имеет значение null, а после InitializeComponent () он заполняется. Я считаю, что событие Loaded запускается в InitializeComponent (), но в этот момент не задан текст данных, что вызывает проблему.
Это поддерживается в следующем окне вывода в VS, что предполагает мне, что EditMergeView не привязан к ViewModel в правильной точке
System.Windows.Data Error: 40 : BindingExpression path error: 'LoadedCommand' property not found on 'object' ''EditMergeViewModel' (HashCode=31475357)'. BindingExpression:Path=LoadedCommand; DataItem='EditMergeViewModel' (HashCode=31475357); target element is 'EventToCommand' (HashCode=41172271); target property is 'Command' (type 'ICommand')
Exception thrown: 'System.NullReferenceException' in FileMerger.exe
Exception thrown: 'System.Reflection.TargetInvocationException' in mscorlib.dll
System.Windows.Data Error: 17 : Cannot get 'MergeName' value (type 'String') from '' (type 'EditMergeViewModel'). BindingExpression:Path=MergeName; DataItem='EditMergeViewModel' (HashCode=31475357); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') TargetInvocationException:'System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at FileMerger.ViewModels.EditMergeViewModel.get_MergeName() in C:\Users\JonathanS\source\repos\FileMerger\FileMerger\ViewModels\EditMergeViewModel.cs:line 270
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at MS.Internal.Data.PropertyPathWorker.GetValue(Object item, Int32 level)
at MS.Internal.Data.PropertyPathWorker.RawValue(Int32 k)'
System.Windows.Data Error: 40 : BindingExpression path error: 'EditMergeCommand' property not found on 'object' ''EditMergeViewModel' (HashCode=31475357)'. BindingExpression:Path=EditMergeCommand; DataItem='EditMergeViewModel' (HashCode=31475357); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
Что я делаю не так? Мне нужно просто вызвать его и создать ViewModel в конструкторе View и установить там DataContext? . Может быть, менее элегантно, но я бы этого не делал.