C# 使用TreeView时PropertyChanged始终为空

C# 使用TreeView时PropertyChanged始终为空,c#,wpf,xaml,mvvm,treeview,C#,Wpf,Xaml,Mvvm,Treeview,我在WPF项目的树状视图中实现搜索功能时遇到问题 我曾经使用ViewModels创建树视图。我使用了TextSeachDemo并以适合我的应用程序的方式编辑了控件。(添加了正确的类、更多的层等) 一切正常,我得到了一个包含正确的孩子和家长的结构,搜索功能也可以工作,因为它可以找到正确的条目 现在的问题是:当我试图从代码中设置“IsExpanded”属性时,什么都没有发生。去边界显示RaiseProperty Changed方法中的PropertyChanged事件始终为null 在Josh Sm

我在WPF项目的树状视图中实现搜索功能时遇到问题

我曾经使用ViewModels创建树视图。我使用了TextSeachDemo并以适合我的应用程序的方式编辑了控件。(添加了正确的类、更多的层等)

一切正常,我得到了一个包含正确的孩子和家长的结构,搜索功能也可以工作,因为它可以找到正确的条目

现在的问题是:当我试图从代码中设置“IsExpanded”属性时,什么都没有发生。去边界显示RaiseProperty Changed方法中的PropertyChanged事件始终为null

在Josh Smith提供的测试项目中,一切似乎都运行良好。 我能看出的唯一显著区别是,他在代码中设置了datacontext,而我在XAML中设置了datacontext:

Josh Smith的代码:

 public TextSearchDemoControl()
    {
        InitializeComponent();

        // Get raw family tree data from a database.
        Person rootPerson = Database.GetFamilyTree();

        // Create UI-friendly wrappers around the 
        // raw data objects (i.e. the view-model).
        _familyTree = new FamilyTreeViewModel(rootPerson);

        // Let the UI bind to the view-model.
        base.DataContext = _familyTree;
    }
来自MainViewModel(处理整个窗口的ViewModel)的构造函数

List rootItems=\u machineseservice.GetFactoryItems();
FactoryTree=新FactoryTreeView模型(根项目);
其中as FactoryTree是一个公共可观察属性,我也绑定了TreeView的DataContext(而不是上面的代码):


另一方面,当我通过GUI打开一个项目时,属性上的断点会触发并引发一个事件


有什么想法吗?

此解决方案以更为mvvm友好的方式解决了这个问题。
UserControl
包含一个
TreeView
。 它使用类型
YourViewModel
作为数据上下文。 视图模型包含一个
YourDomainType
的集合,该集合本身有一个相同类型的子集合
ChildElements

在xaml中,数据绑定到视图模型的
ElementInViewModel
集合。此外,还有一个
HierarchycalDataTemplate
(适用于树状视图)

类型
YourDomainType
包含属性
IsExpanded
IsSelected
,它们以
样式绑定到
TreeItem
的相应属性。 如果在视图模型中设置了这些属性,则树视图应按预期反应(选择或展开相应的
TreeViewItem

我知道
IsExpanded
IsSelected
不属于DTO对象。
YourDomainType
可能更像是一种显示数据的类型,但它也可以将数据包装到存储在其中的对象

<UserControl>

    <UserControl.DataContext>
        <YourViewModel/>
    </UserControl.DataContext>  

    <UserControl.Resources>        
        <CollectionViewSource Source="{Binding Path=ElementsInViewModel}" x:Key="Cvs">
        </CollectionViewSource>

        <HierarchicalDataTemplate DataType="{x:Type DomainModel:YourDomainType}"
                                  ItemsSource="{Binding Path=ChildElements}">
            <TextBlock Text="{Binding Path=Name}"/>            
        </HierarchicalDataTemplate>        

        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>            
        </Style>

    </UserControl.Resources>


    <DockPanel>
        <TreeView ItemsSource="{Binding Source={StaticResource Cvs}}"/>
    </DockPanel>

</UserControl>

public class YourViewModel
{

    public ObservableCollection<YourDomainType> ElementsInViewModel 
    {
        get
        {
            return _elementsInViewModel;
        }
        set
        {
            if (_elementsInViewModel != value)
            {
                _elementsInViewModel = value;
                RaisePropertyChanged("ElementsInViewModel");
            }
        }
    }
    ObservableCollection<YourDomainType> _elementsInViewModel;


    public YourViewModel()
    {

    }
}

public class YourDomainType
{

    public ObservableCollection<YourDomainType> ChildElements
    {
        get
        {
            return _childElements;
        }
        set
        {
            if (_childElements != value)
            {
                _childElements = value;
                RaisePropertyChanged("ChildElements");
            }
        }
    }
    ObservableCollection<YourDomainType> _childElements;


    public bool IsExpanded
    {
        get
        {
            return _isExpanded;
        }
        set
        {
            if (_isExpanded != value)
            {
                _isExpanded = value;
                RaisePropertyChanged("IsExpanded");
            }
        }
    }
    bool _isExpanded;


    public bool IsSelected
    {
        get
        {
            return _isSelected;
        }
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                RaisePropertyChanged("IsSelected");
            }
        }
    }
    bool _isSelected;


    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    }
    string _name;
}

公共类视图模型
{
公共可观测集合元素视图模型
{
得到
{
返回_元素视图模型;
}
设置
{
if(_elementsInViewModel!=值)
{
_elementsInViewModel=值;
RaisePropertyChanged(“ElementsInViewModel”);
}
}
}
可观测采集元素视图模型;
公共视图模型()
{
}
}
公共类YourDomainType
{
公共可观测集合子元素
{
得到
{
返回子元素;
}
设置
{
if(_childElements!=值)
{
_子元素=值;
RaisePropertyChanged(“子元素”);
}
}
}
可观察的集合元素;
公共图书馆被扩展了
{
得到
{
返回已扩展;
}
设置
{
如果(_isExpanded!=值)
{
_isExpanded=值;
RaisePropertyChanged(“IsExpanded”);
}
}
}
布尔(u)被扩张;;
公选学校
{
得到
{
返回(未选);;
}
设置
{
如果(_isSelected!=值)
{
_isSelected=值;
RaisePropertyChanged(“IsSelected”);
}
}
}
布尔乌当选;
公共字符串名
{
得到
{
返回_name;
}
设置
{
如果(_name!=值)
{
_名称=值;
RaiseProperty变更(“名称”);
}
}
}
字符串\u名称;
}

这是问题的症结所在——您遵循了一个示例,作者专门为他的用户控件创建了一个ViewModel。每次都会把你搞砸的。如果你搜索我的答案,你会发现我像疯子一样咆哮。用户控件应该像控件一样设计。想想文本框是如何工作的。没有TextBoxViewModel。它公开了VM绑定到的文本属性。TextSearchDemoControl也应该如此。您需要从控件的用户那里得到什么?公开表面上的属性并绑定到内部。因此,如果不将所有内容重写到控件中,就不可能修复问题?可以修复所有内容。你可以接受一个糟糕的设计,让它发挥作用。但这需要两倍的时间和四倍的努力。史密斯的密码太糟糕了
Person-rootPerson=Database.GetFamilyTree()在*UserControl`?这根本不是MVVM。你有没有一篇文章介绍如何正确处理这个问题?我认为为列表中的每一项都有一个自己的虚拟机并不是一个坏主意。
   <TreeView DataContext="{Binding FactoryTree}" ItemsSource="{Binding FirstGeneration}">
<UserControl>

    <UserControl.DataContext>
        <YourViewModel/>
    </UserControl.DataContext>  

    <UserControl.Resources>        
        <CollectionViewSource Source="{Binding Path=ElementsInViewModel}" x:Key="Cvs">
        </CollectionViewSource>

        <HierarchicalDataTemplate DataType="{x:Type DomainModel:YourDomainType}"
                                  ItemsSource="{Binding Path=ChildElements}">
            <TextBlock Text="{Binding Path=Name}"/>            
        </HierarchicalDataTemplate>        

        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            </Setter>            
        </Style>

    </UserControl.Resources>


    <DockPanel>
        <TreeView ItemsSource="{Binding Source={StaticResource Cvs}}"/>
    </DockPanel>

</UserControl>

public class YourViewModel
{

    public ObservableCollection<YourDomainType> ElementsInViewModel 
    {
        get
        {
            return _elementsInViewModel;
        }
        set
        {
            if (_elementsInViewModel != value)
            {
                _elementsInViewModel = value;
                RaisePropertyChanged("ElementsInViewModel");
            }
        }
    }
    ObservableCollection<YourDomainType> _elementsInViewModel;


    public YourViewModel()
    {

    }
}

public class YourDomainType
{

    public ObservableCollection<YourDomainType> ChildElements
    {
        get
        {
            return _childElements;
        }
        set
        {
            if (_childElements != value)
            {
                _childElements = value;
                RaisePropertyChanged("ChildElements");
            }
        }
    }
    ObservableCollection<YourDomainType> _childElements;


    public bool IsExpanded
    {
        get
        {
            return _isExpanded;
        }
        set
        {
            if (_isExpanded != value)
            {
                _isExpanded = value;
                RaisePropertyChanged("IsExpanded");
            }
        }
    }
    bool _isExpanded;


    public bool IsSelected
    {
        get
        {
            return _isSelected;
        }
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                RaisePropertyChanged("IsSelected");
            }
        }
    }
    bool _isSelected;


    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    }
    string _name;
}