第一次绑定不适用于视图 MvvmLight
Bindings not working for views first time MvvmLight
我一直在使用一种 MVVM 导航方法,在该方法中,我为视图模型创建了一个数据模板,其中包含视图。
主要 window 是这样设置的
<Window x:Class="VaultPrez.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:ignore="http://www.ignore.com"
xmlns:v="clr-namespace:VaultPrez.Views"
xmlns:vm="clr-namespace:VaultPrez.ViewModel"
mc:Ignorable="d ignore"
Height="720"
Width="1280"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type vm:MainMenuViewModel}">
<v:MainMenu />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:HeaderViewModel}">
<v:Header />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MediaViewModel}">
<v:MediaViewer />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Content="{Binding Header}" />
<ContentPresenter Grid.Row="1" Content="{Binding CurrentView}" />
</Grid>
名称 space v 引用实际视图,vm 是视图模型。
这是此视图的主视图模型
public class MainViewModel : ViewModelBase
{
private readonly DataAccessService dataService;
private ViewModelBase currentView;
private ViewModelBase header;
...
public Dictionary<string, ViewModelBase> Views { get; set; }
public ViewModelBase CurrentView
{
get { return currentView; }
set
{
if (currentView == null)
currentView = value;
else if (currentView != value)
{
ViewStates.Push(currentView);
currentView = value;
RaisePropertyChanged("CurrentView");
}
}
}
public ViewModelBase Header
{
get { return header; }
set
{
header = value;
RaisePropertyChanged("Header");
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataAccessService dataService)
{
this.dataService = dataService as DataAccessService;
Views = new Dictionary<string, ViewModelBase>();
ViewStates = new ObservableStack<ViewModelBase>();
ViewStates.CollectionChanged += ViewStates_CollectionChanged;
Views.Add("MainMenu", new MainMenuViewModel(dataService));
Views.Add("MediaViewer", new MediaViewModel(dataService));
CurrentView = Views["MainMenu"];
Header = new HeaderViewModel(dataService);
registerMessages();
}
private void registerMessages()
{
MessengerInstance.Register<BackMessage>(this, (message) =>
{
onBack();
});
MessengerInstance.Register<ChangeViewMessage>(this, (message) =>
{
CurrentView = Views[message.View];
});
MessengerInstance.Register<CancelBackMessage>(this, (message) =>
{
cancelBack = true;
waiting = false;
});
MessengerInstance.Register<SaveMediaMessage>(this, (message) =>
{
if (message.Media != null)
dataService.SaveMedia();
waiting = false;
});
}
void ViewStates_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (((ObservableStack<ViewModelBase>)sender).Count < 1)
MessengerInstance.Send<SetBackVisibilityMessage>(new SetBackVisibilityMessage() { Visibility = Visibility.Hidden });
else
MessengerInstance.Send<SetBackVisibilityMessage>(new SetBackVisibilityMessage() { Visibility = Visibility.Visible });
}
private void onBack()
{
//MessageBox.Show("Back");
if (currentView == Views["MediaViewer"])
{
waiting = true;
MessengerInstance.Send<AskSaveMediaMessage>(new AskSaveMediaMessage());
Task t = Task.Factory.StartNew(() =>
{
while (waiting)
if (!waiting) break;
});
t.Wait();
}
if(!cancelBack)
{
currentView = ViewStates.Pop();
RaisePropertyChanged("CurrentView");
}
cancelBack = false;
}
但相关的部分是视图模型存储在字典中,当主视图收到一条消息以更改视图时,它会将其移动到指定的视图
MessengerInstance.Register<ChangeViewMessage>(this, (message) =>
{
CurrentView = Views[message.View];
});
理论上,当绑定 CurrentView 的视图类型为 MainMenuViewModel 时,MainMenu 显示等有效,但在您更改为其他视图时它第一次不起作用,视图上的所有绑定都是空的但是,当您返回主菜单时,再次尝试一切正常,一切都已正确绑定。
ViewModel 也有数据,第一次尝试时没有任何内容为 null。
如果它只是一个 3 或 4 个视图的程序,我会在加载期间遍历每个视图,但我计划有大约 20 个视图,这可能会非常耗时。
我做错了什么吗?
编辑:这里是 ViewModelLocator.cs
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
SimpleIoc.Default.Register<IDataAccessService, DataAccessService>();
}
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<MainMenuViewModel>();
SimpleIoc.Default.Register<HeaderViewModel>();
SimpleIoc.Default.Register<MediaViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get { return ServiceLocator.Current.GetInstance<MainViewModel>(); }
}
public MainMenuViewModel Menu
{
get { return ServiceLocator.Current.GetInstance<MainMenuViewModel>(); }
}
public HeaderViewModel Header
{
get { return ServiceLocator.Current.GetInstance<HeaderViewModel>(); }
}
public MediaViewModel Media
{
get { return ServiceLocator.Current.GetInstance<MediaViewModel>(); }
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
}
}
据我所知,您的 View 非常好。我使用完全相同的机制,没有任何问题。我认为问题可能出在您的 ViewModel 此处:'Views.Add("MainMenu", new MainMenuViewModel(dataService));'。更新您的 ViewModels 的实例并没有像您期望的那样工作。添加 (true) 到您的 SimpleIoc 注册的末尾 - SimpleIoc.Default.Register(true);。默认情况下,ViewModel 是延迟加载的单例。将 true 参数传入 SimpleIoc 注册关闭延迟加载。
我一直在使用一种 MVVM 导航方法,在该方法中,我为视图模型创建了一个数据模板,其中包含视图。 主要 window 是这样设置的
<Window x:Class="VaultPrez.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:ignore="http://www.ignore.com"
xmlns:v="clr-namespace:VaultPrez.Views"
xmlns:vm="clr-namespace:VaultPrez.ViewModel"
mc:Ignorable="d ignore"
Height="720"
Width="1280"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type vm:MainMenuViewModel}">
<v:MainMenu />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:HeaderViewModel}">
<v:Header />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MediaViewModel}">
<v:MediaViewer />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Content="{Binding Header}" />
<ContentPresenter Grid.Row="1" Content="{Binding CurrentView}" />
</Grid>
名称 space v 引用实际视图,vm 是视图模型。 这是此视图的主视图模型
public class MainViewModel : ViewModelBase
{
private readonly DataAccessService dataService;
private ViewModelBase currentView;
private ViewModelBase header;
...
public Dictionary<string, ViewModelBase> Views { get; set; }
public ViewModelBase CurrentView
{
get { return currentView; }
set
{
if (currentView == null)
currentView = value;
else if (currentView != value)
{
ViewStates.Push(currentView);
currentView = value;
RaisePropertyChanged("CurrentView");
}
}
}
public ViewModelBase Header
{
get { return header; }
set
{
header = value;
RaisePropertyChanged("Header");
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataAccessService dataService)
{
this.dataService = dataService as DataAccessService;
Views = new Dictionary<string, ViewModelBase>();
ViewStates = new ObservableStack<ViewModelBase>();
ViewStates.CollectionChanged += ViewStates_CollectionChanged;
Views.Add("MainMenu", new MainMenuViewModel(dataService));
Views.Add("MediaViewer", new MediaViewModel(dataService));
CurrentView = Views["MainMenu"];
Header = new HeaderViewModel(dataService);
registerMessages();
}
private void registerMessages()
{
MessengerInstance.Register<BackMessage>(this, (message) =>
{
onBack();
});
MessengerInstance.Register<ChangeViewMessage>(this, (message) =>
{
CurrentView = Views[message.View];
});
MessengerInstance.Register<CancelBackMessage>(this, (message) =>
{
cancelBack = true;
waiting = false;
});
MessengerInstance.Register<SaveMediaMessage>(this, (message) =>
{
if (message.Media != null)
dataService.SaveMedia();
waiting = false;
});
}
void ViewStates_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (((ObservableStack<ViewModelBase>)sender).Count < 1)
MessengerInstance.Send<SetBackVisibilityMessage>(new SetBackVisibilityMessage() { Visibility = Visibility.Hidden });
else
MessengerInstance.Send<SetBackVisibilityMessage>(new SetBackVisibilityMessage() { Visibility = Visibility.Visible });
}
private void onBack()
{
//MessageBox.Show("Back");
if (currentView == Views["MediaViewer"])
{
waiting = true;
MessengerInstance.Send<AskSaveMediaMessage>(new AskSaveMediaMessage());
Task t = Task.Factory.StartNew(() =>
{
while (waiting)
if (!waiting) break;
});
t.Wait();
}
if(!cancelBack)
{
currentView = ViewStates.Pop();
RaisePropertyChanged("CurrentView");
}
cancelBack = false;
}
但相关的部分是视图模型存储在字典中,当主视图收到一条消息以更改视图时,它会将其移动到指定的视图
MessengerInstance.Register<ChangeViewMessage>(this, (message) =>
{
CurrentView = Views[message.View];
});
理论上,当绑定 CurrentView 的视图类型为 MainMenuViewModel 时,MainMenu 显示等有效,但在您更改为其他视图时它第一次不起作用,视图上的所有绑定都是空的但是,当您返回主菜单时,再次尝试一切正常,一切都已正确绑定。
ViewModel 也有数据,第一次尝试时没有任何内容为 null。
如果它只是一个 3 或 4 个视图的程序,我会在加载期间遍历每个视图,但我计划有大约 20 个视图,这可能会非常耗时。
我做错了什么吗?
编辑:这里是 ViewModelLocator.cs
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
SimpleIoc.Default.Register<IDataAccessService, DataAccessService>();
}
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<MainMenuViewModel>();
SimpleIoc.Default.Register<HeaderViewModel>();
SimpleIoc.Default.Register<MediaViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get { return ServiceLocator.Current.GetInstance<MainViewModel>(); }
}
public MainMenuViewModel Menu
{
get { return ServiceLocator.Current.GetInstance<MainMenuViewModel>(); }
}
public HeaderViewModel Header
{
get { return ServiceLocator.Current.GetInstance<HeaderViewModel>(); }
}
public MediaViewModel Media
{
get { return ServiceLocator.Current.GetInstance<MediaViewModel>(); }
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
}
}
据我所知,您的 View 非常好。我使用完全相同的机制,没有任何问题。我认为问题可能出在您的 ViewModel 此处:'Views.Add("MainMenu", new MainMenuViewModel(dataService));'。更新您的 ViewModels 的实例并没有像您期望的那样工作。添加 (true) 到您的 SimpleIoc 注册的末尾 - SimpleIoc.Default.Register(true);。默认情况下,ViewModel 是延迟加载的单例。将 true 参数传入 SimpleIoc 注册关闭延迟加载。