【问题标题】:WPF Databinding searching in wrong DataContextWPF数据绑定在错误的DataContext中搜索
【发布时间】:2019-10-28 10:18:43
【问题描述】:

我正在学习 MVVM 模式,但在数据绑定方面遇到了一些问题。我了解它的工作原理,但在我的示例中,它使用了错误的 DataContext 来搜索绑定。

我没有找到适合我的问题的另一个问题。

所以我有这个观点:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterChoiceView"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <ItemsControl ItemsSource="{Binding CashRegisters}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel></WrapPanel>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</UserControl>

使用以下 ViewModel:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kenshinaro.CashRegister.UI.View.CashRegister;

namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
    public class CashRegisterChoiceViewModel : MVVM.ViewModel
    {
        private ObservableCollection<CashRegisterItemsControlView> _cashRegisters = new ObservableCollection<CashRegisterItemsControlView>()
        {
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "1"
                            }
                        }
                    }
                }
            },
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "2"
                            }
                        }
                    }
                }
            },
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "3"
                            }
                        }
                    }
                }
            }
        };

        public ObservableCollection<CashRegisterItemsControlView> CashRegisters
        {
            get => _cashRegisters;
            set => SetProperty(ref _cashRegisters, value);
        }
    }
}

(我的基础 ViewModel 类正在实现 INotifyPropertyChanged 接口)

所以这个视图显示了这个视图的列表:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
             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:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <md:Card Margin="10">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <local:CashRegisterCardView Padding="5"/>
            <Button Margin="20" Grid.Row="1" Content="Pick me"/>
        </Grid>
    </md:Card>
</UserControl>

视图模型:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
    public class CashRegisterItemsControlViewModel : MVVM.ViewModel
    {
        private View.CashRegister.CashRegisterCardView _view;

        public View.CashRegister.CashRegisterCardView View
        {
            get => _view;
            set => SetProperty(ref _view, value);
        }
    }
}

这个视图也显示了这个视图:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterCardView"
             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:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="White" >
    <Grid>
        <TextBlock Text="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=local:CashRegisterCardView}}"/>
    </Grid>
</UserControl>

视图模型:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Kenshinaro.CashRegister.Model;

namespace Kenshinaro.CashRegister.UI.ViewModel.CashRegister
{
    public class CashRegisterCardViewModel : MVVM.ViewModel
    {
        private Model.CashRegister _model;

        public Model.CashRegister Model
        {
            get => _model;
            set => SetProperty(ref _model, value);
        }

        public String Name
        {
            get => _model.Name;
            set => _model.Name = value;
        }
    }
}

现在,如果我启动应用程序,最后一个视图中的 TextBlock 上的绑定不会出现。 VisualStudio 的输出显示:

System.Windows.Data 错误:40:BindingExpression 路径错误:在“对象”“CashRegisterItemsControlViewModel”(HashCode=35528341)上找不到“名称”属性。 BindingExpression:Path=DataContext.Name; DataItem='CashRegisterCardView' (名称='');目标元素是'TextBlock'(名称='');目标属性是“文本”(类型“字符串”)

我可以摆脱它,它正在 CashRegisterItemsControlView 的 DataContext 中搜索属性,但我只是不明白为什么,因为我在集合初始化时手动设置了 DataContext(仅用于测试):

private ObservableCollection<CashRegisterItemsControlView> _cashRegisters = new ObservableCollection<CashRegisterItemsControlView>()
        {
            new CashRegisterItemsControlView()
            {
                DataContext = new CashRegisterItemsControlViewModel()
                {
                    View = new CashRegisterCardView()
                    {
                        DataContext = new CashRegisterCardViewModel()
                        {
                            Model = new Model.CashRegister()
                            {
                                Name = "1"
                            }
                        }
                    }
                }
            },
...

那么为什么它仍然使用错误的 DataContext?

现在,当我将绑定更改为 View.DataContext.Name 时,它​​正在手动设置的 DataContext 中搜索:

System.Windows.Data 错误:40:BindingExpression 路径错误:在“对象”“CashRegisterCardView”(名称=“”)上找不到“视图”属性。 BindingExpression:Path=View.DataContext.Name; DataItem='CashRegisterCardView' (名称='');目标元素是'TextBlock'(名称='');目标属性是“文本”(类型“字符串”)

我真的很困惑..希望你能帮助我。

谢谢!

如果您需要有关其他课程的信息,请询问。我不想在问题中添加更多代码,因为它已经足够长了。

【问题讨论】:

  • 首先,永远不要在视图模型中创建视图元素。这不是 MVVM 的意义所在。通常,为某些视图模型类型声明 DataTemplates。然后视图元素将由这些模板自动生成。从这里开始阅读:Data Templating Overview.
  • @Clemens 正如我提到的集合初始化只是为了测试。绑定问题解决后,将彻底重做。我正在使用 ItemsControl 测试 MVVM。不过谢谢你的建议。
  • 作为一般规则,永远不要显式设置任何 DataContext,Window 或 Page 除外。具体来说,对于 ItemsControls,各个项目容器的 DataContext 会(由框架)自动设置为 ItemsSource 集合的适当项目。

标签: c# wpf xaml mvvm data-binding


【解决方案1】:

视图模型具有对视图的引用。这违反了 MVVM 模式。

问题的根源在于 CashRegisterItemsControlView 的实现:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
             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:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <md:Card Margin="10">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <local:CashRegisterCardView Padding="5"/>
            <Button Margin="20" Grid.Row="1" Content="Pick me"/>
        </Grid>
    </md:Card>
</UserControl>

它创建了一个CashRegisterCardView (&lt;local:CashRegisterCardView Padding="5"/&gt;) 的实例,它没有自己的DataContext,并从父级继承DataContext。未使用来自CashRegisterItemsControlViewModelView

可以对其进行更改以使其正常工作,如下所示:

<UserControl x:Class="Kenshinaro.CashRegister.UI.View.CashRegister.CashRegisterItemsControlView"
             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:local="clr-namespace:Kenshinaro.CashRegister.UI.View.CashRegister"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <md:Card Margin="10">
        <Grid >
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <ContentControl Content="{Binding View}" Padding="5"/>
            <Button Margin="20" Grid.Row="1" Content="Pick me"/>
        </Grid>
    </md:Card>
</UserControl>

但最好重新设计您的应用架构

【讨论】:

  • 还有你说它对我不起作用的变化。在这种情况下,他正在 CashRegisterCardView DataContext 中搜索 View 属性
  • @Kenshinaro,唯一的变化是 ContentControl 代替了 local:CashRegisterCardView 。我复制了整个示例,并按预期显示了修复卡
  • 但是正如你提到的,最好重做这个。你能给我一点启动帮助吗?我只是不知道如何实现这种行为:我想要一个视图,其中包含他们的模型的多个视图(在我的示例中是一个包含多个视图的视图,其中包含模型的视图)。
  • 用这个答案解决了问题。完全重做。
猜你喜欢
  • 1970-01-01
  • 2021-07-19
  • 1970-01-01
  • 1970-01-01
  • 2015-01-18
  • 2012-01-19
  • 2012-12-25
  • 2013-11-16
  • 2011-03-16
相关资源
最近更新 更多