C# 绑定时DataItem=null,can';我不知道为什么?

C# 绑定时DataItem=null,can';我不知道为什么?,c#,wpf,mvvm,data-binding,navigation,C#,Wpf,Mvvm,Data Binding,Navigation,我试图重现中的建议,以便在将WPF与MVVM模式一起使用时在视图中导航。不幸的是,当我这样做时,我得到了一个绑定错误。下面是确切的错误: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='JollyFinance.ViewModels.MainViewModel', AncestorLevel='1''.

我试图重现中的建议,以便在将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')
当我在LoginView.xaml中查看我的xaml代码时,我注意到Visual Studio告诉我它在类型为
MainViewModel
的上下文中找不到
DataContext.DisplayText
。我尝试过删除
DataContext.
,只保留
DisplayText
,但没有效果

除非谢里登的回答有错误,否则我肯定遗漏了一些东西。我该怎么做才能让它工作

MainWindow.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>
LoginView.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>

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

在应用程序范围中将
MainViewModel
定义为静态资源

<App.Resources>
    <MainViewModel x:Key="MainViewModel" />
</App.Resources>
编辑

或者尝试以下代码:

<Button Command="{Binding DisplayTest, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window), Path=DataContext}}"/>


MainViewModel
不是中的直接祖先,这就是为什么
RelativeSource={RelativeSource AncestorType={x:Type vm:MainViewModel}}
找不到它的原因

你怎么修理它?首先,请不要试图通过像这样的各种UI组件来触发命令。仅仅因为你在互联网上的某个地方看到它并不意味着它是一个理想的设计选择。这样做意味着LoginView对其他视图和视图模型有着深刻的理解,这是不好的。如果您要这样做,那么您可以将所有内容编码为一个UI类和一个viewmodel,这实际上只是一个庞大的代码隐藏类

更好(但仍然不是最佳)的方法是让MainView(或viewmodel)生成LoginView。由于它持有对该视图的引用,因此它还负责处理该视图。因此,可以显示LoginView来收集凭据,然后主视图可以在发出凭据已成功验证的信号时进行处理。或者,它可以只收集凭据,并将其留给MainView/viewmodel进行验证(这可以通过MainView/viewmodel触发后台调用以根据存储检查凭据来完成)

一个简单(粗略)的经验法则是:父视图可以了解子视图,但通常情况下不应该发生相反的情况。是关于,但你是他们。当然,所有这些都比我所说明的要复杂得多,但您仍然可以在保持实用性的同时,不过度工程化

那么,TLDR;:

  • LoginView(或其viewmodel)应该实现自己的命令来处理按钮单击
  • 触发功能
  • 努力实现代码/视图的分离和解耦
  • 使用祖先绑定时,请查找可视化/逻辑树中的内容

在我的回答中,我说你应该在
App.xaml
中声明你的视图模型
DataTemplate
s,这样每个视图都可以访问它们。将它们放入
主窗口
类是您的第一个问题

另一个错误是您的
ICommand
绑定路径。如果要从设置为
Window.DataContext
的视图模型访问某些内容,则不应使用
相对资源绑定。请尝试以下方法:

<Button Content="Create new user" Grid.Column="3" Grid.Row="3" Margin="5" Padding="5" 
    Command="{Binding DataContext.DisplayTest}, Mode=OneWay}" />

还请记住,无论出于何种原因,您选择不让您的
MainViewModel
类扩展
ViewModelNavigationBase
类。。。这也会给你带来麻烦


不管怎样,如果这不能解决你的问题,就告诉我。另外,如果您想随时通知用户堆栈溢出,只需在其名称前面放置一个
@
符号,他们就会收到通知。如果您这样做了,您可以直接问我这个问题。

您的第一个解决方案是在每次启动应用程序时弹出一个新窗口(另一个主窗口),但也不能解决问题。第二个解决方案不编译。我尝试过修复它,但它显示路径设置2次。第一个解决方案在每次应用程序启动时创建
MainViewModel
实例。非
main窗口
。第二种解决方案必须适合您的代码。复制粘贴不起作用。另外,第二种解决方案只有在主页面中显示子窗口时才有效。给出应该适合我的项目的代码和给出错误代码使其无法编译是有区别的。首先,
Path=“DataContext”
应该是
Path=DataContext
Mode=findancestorancestortype=…
甚至在语法上都是无效的,等等。上面的代码是我自己写的。在发布评论或回答之前,编译每段代码将花费太多时间。不管怎样,如果你不能修改代码,就问吧我已经编辑了我的答案。这是已经发生的事情,我的
LoginViewModel
(和视图)在创建时由我的
MainViewModel
生成。我的
LoginViewModel
或查看如何向
MainViewModel
发出凭证已验证的信号?这就是为什么我在链接问题的答案中使用这种方法。另外,你提到我所做的事情是不好的,因为它把所有的事情结合在一起。没有使用
Page
s,还有什么其他方法可以在视图中导航?在找到链接问题的答案之前,这就是我最初寻找的。在MVVM风格中,ViewModels使用直接事件处理(如果一个VM创建另一个VM)或使用
Messenger
类(如果VM是独立的)相互交互。Messenger具有在方法
Messenger.Send(eventArgs)
中激发的公共事件。每个视图模型为Messenger的公共事件注册事件处理程序。查看MVVM light toolkit.To navi
<Button Command="{Binding Source={StaticResource MainViewModel}, Path=DisplayTest}" />
<Button Command="{Binding DisplayTest, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window), Path=DataContext}}"/>
<Button Content="Create new user" Grid.Column="3" Grid.Row="3" Margin="5" Padding="5" 
    Command="{Binding DataContext.DisplayTest}, Mode=OneWay}" />