第一次绑定不适用于视图 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 注册关闭延迟加载。