绑定时 DataItem=null,找不到原因?

Cho*_*890 6 c# navigation data-binding wpf mvvm

我试图重现Sheridan 对这个问题的回答中建议的内容,以便在将 WPF 与 MVVM 模式一起使用时浏览我的视图。不幸的是,当我这样做时,我遇到了绑定错误。这是确切的错误:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='JollyFinance.ViewModels.MainViewModel', AncestorLevel='1''. BindingExpression:Path=DataContext.DisplayTest; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
Run Code Online (Sandbox Code Playgroud)

当我查看 LoginView.xaml 中的 xaml 代码时,我注意到 Visual Studio 告诉我它无法DataContext.DisplayText在 type 上下文中找到MainViewModel。我尝试过删除DataContext.并保留DisplayText,但无济于事。

除非谢里登的答案有错误,否则我肯定在这里遗漏了一些东西。我应该做什么才能让它发挥作用?

主窗口.xaml:

<Window x:Class="JollyFinance.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:JollyFinance.ViewModels"
        xmlns:views="clr-namespace:JollyFinance.Views"
        Title="JollyFinance!" Height="720" Width="1280">

    <Window.Resources>
        <!-- Different pages -->
        <DataTemplate DataType="{x:Type vm:LoginViewModel}">
            <views:LoginView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:TestViewModel}">
            <views:Test/>
        </DataTemplate>
    </Window.Resources>

    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>

    <Grid>
        <ContentControl Content="{Binding CurrentViewModel}"/>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

MainViewModel.cs:

public class MainViewModel : BindableObject
{
    private ViewModelNavigationBase _currentViewModel;

    public MainViewModel()
    {
        CurrentViewModel = new LoginViewModel();
    }

    public ICommand DisplayTest
    {
        get
        {
            // This is added just to see if the ICommand is actually called when I press the
            // Create New User button
            Window popup = new Window();
            popup.ShowDialog();

            // View model that doesn't contain anything for now
            return new RelayCommand(action => CurrentViewModel = new TestViewModel());
        }
    }

    public ViewModelNavigationBase CurrentViewModel
    {
        get { return _currentViewModel; }
        set
        {
            if (_currentViewModel != value)
            {
                _currentViewModel = value;
                RaisePropertyChanged("CurrentViewModel");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

登录视图.xaml:

<UserControl x:Class="JollyFinance.Views.LoginView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vm="clr-namespace:JollyFinance.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.DataContext>
        <vm:LoginViewModel/>
    </UserControl.DataContext>

    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <TextBlock Text="Username: " Grid.Column="1" Grid.Row="1" Margin="5"/>
        <TextBox Text="{Binding Path=Username}" Grid.Column="2" Grid.Row="1" Grid.ColumnSpan="2" Margin="5"/>

        <TextBlock Text="Password: " Grid.Column="1" Grid.Row="2" Margin="5"/>
        <PasswordBox x:Name="PasswordBox" PasswordChar="*" Grid.Column="2" Grid.ColumnSpan="2" Grid.Row="2" Margin="5"/>

        <Button Content="Log In" Grid.Column="2" Grid.Row="3" Margin="5" Padding="5" Command="{Binding LoginCommand}"/>
        <Button Content="Create new user" Grid.Column="3" Grid.Row="3" Margin="5" Padding="5" 
                Command="{Binding DataContext.DisplayTest, RelativeSource={RelativeSource AncestorType={x:Type vm:MainViewModel}}, 
            Mode=OneWay}"/>

    </Grid>

</UserControl>
Run Code Online (Sandbox Code Playgroud)

LoginViewModel.cs:

public class LoginViewModel : ViewModelNavigationBase
{
    public LoginViewModel()
    {
        LoginCommand = new RelayCommand(Login);
    }

    private void Login(object param)
    {
        // Just there to make sure the ICommand is actually called when I press the
        // Login button             
        Window popup = new Window();
        popup.ShowDialog();
    }

    public String Username { get; set; }

    public String Password { get; set; }

    public ICommand LoginCommand { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

ViewModelNavigationBase只是一个实现INotifyPropertyChanged接口的类,Test.xaml 和 TestViewModel.cs 只是用于测试目的的虚拟视图模型/视图。

She*_*dan 1

在我的回答中,我指出您应该声明您的视图模型DataTemplateApp.xaml以便每个视图都可以访问它们。将它们放入课堂MainWindow是您的第一个问题。

另一个错误是你Binding PathICommand。如果您想从设置为 的视图模型中访问某些内容Window.DataContext,那么您应该使用RelativeSource Binding。试试这个:

<Button Content="Create new user" Grid.Column="3" Grid.Row="3" Margin="5" Padding="5" 
    Command="{Binding DataContext.DisplayTest}, Mode=OneWay}" />
Run Code Online (Sandbox Code Playgroud)

还要记住,无论出于何种原因,您选择不让您的MainViewModel班级扩展ViewModelNavigationBase班级......这也可能会给您带来问题。

无论如何,如果这不能解决您的问题,请告诉我。此外,如果您想随时在 Stack Overflow 上通知用户,只需@在用户姓名前添加一个符号,他们就会收到通知。如果你这样做的话,你可以直接问我这个问题。