【问题标题】:WPF Treeview Not Updating After Rearchitecting to MVVM重新架构到 MVVM 后 WPF 树视图未更新
【发布时间】:2019-02-27 09:50:41
【问题描述】:

我创建了一个应用程序来加载特定格式的 XML 文件并通过 WPF GUI 编辑其字段。但是,作为 C# 的新手,我在编写 MVVM 模式时并不了解它,并且我采取了在代码隐藏中简单地编写所有内容的快速而肮脏的路线。但是,在重新架构后,我似乎无法再更新树视图(之前的工作)。

这是相关的 XAML:

<UserControl x:Class="TableBuilder.Views.XmlView"
             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:views="clr-namespace:TableBuilder.Views"
             xmlns:viewmodels="clr-namespace:TableBuilder.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.DataContext>
        <viewmodels:XmlViewModel />
    </UserControl.DataContext>
    <DockPanel>
        <TreeView x:Name="XmlTree" SelectedItemChanged="XmlView_SelectedItemChanged">
            <TreeView.Resources>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="HeaderTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <Image Width="20" Margin="2" Source="../Images/Red_Bullet.png" />
                                    <TextBlock VerticalAlignment="Center" Text="{Binding}" />
                                </StackPanel>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TreeView.Resources>
        </TreeView>
    </DockPanel>
</UserControl>

这是代码隐藏:

using System;
using System.Windows;
using System.Windows.Controls;
using TableBuilder.Helpers;

namespace TableBuilder.Views
{
    /// <summary>
    /// Interaction logic for XmlView.xaml
    /// </summary>
    public partial class XmlView : UserControl
    {
        RelayCommand selectedItemChanged { get; set; }
        public XmlView()
        {
            InitializeComponent();
        }

        private void XmlView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            //((MainViewModel)this.DataContext).SelectedItemChangedCommand = (RelayCommand)sender;
            Console.WriteLine("XmlView selected item changed event triggered");
        }
    }
}

这是视图模型:

using System;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Xml;
using TableBuilder.Helpers;
using TableBuilder.Models;

namespace TableBuilder.ViewModels
{
    class XmlViewModel : ViewModelBase
    {
        private TreeView _xmlTree;
        public TreeView XmlTree
        {
            get { return _xmlTree; }
            set
            {
                _xmlTree = value;
                OnPropertyChanged("XmlTree");
            }
        }

        public XmlViewModel()
        {
            xmlModel = new XmlModel();

            TreeView XmlTree = new TreeView();
            TreeViewItem node = new TreeViewItem();
            node.Header = "Test";
            XmlTree.Items.Add(node);

            xmlModel.PropertyChanged += XmlModel_CurrentChanged;
        }

        private void XmlModel_CurrentChanged(object sender, EventArgs e)
        {
            TreeViewItem xmlTreeItems = GetTreeViewItems();
            TreeView XmlTree = new TreeView();
            XmlTree.Items.Add(xmlTreeItems);

        }

        private TreeViewItem GetTreeViewItems()
        {
            // Find the filename of the XML document
            Uri uri = new Uri(xmlModel.XmlDoc.BaseURI);
            string filename = "";
            if (uri.IsFile)
            {
                filename = System.IO.Path.GetFileName(uri.LocalPath);
            }

            // Populate the TreeView
            TreeViewItem TitleNode = new TreeViewItem()
            {
                Header = filename,
                Tag = "Title Node",
            };
            PopulateTree(TitleNode, xmlModel.XmlDoc.ChildNodes);
            return TitleNode;
        }

        private void PopulateTree(TreeViewItem parent, XmlNodeList nodes)
        {
            TreeViewItem item;
            Dictionary<string, string> dict;
            bool isSmallestChild;
            foreach (XmlNode node in nodes)
            {

                // Ignore all comments in the XML document
                if (!node.Name.Contains("#comment"))
                {
                    // Add to TreeView
                    item = toTreeViewItem(node);

                    isSmallestChild = false;
                    dict = new Dictionary<string, string>();
                    foreach (XmlNode child in node.ChildNodes)
                    {
                        if (child.Name.Contains("type") ||
                            child.Name.Contains("value") ||
                            child.Name.Contains("units"))
                        {
                            isSmallestChild = true;
                        }
                        if (isSmallestChild)
                        {
                            dict.Add(child.Name, child.InnerText);
                        }
                    }
                    if (!isSmallestChild)
                    {
                        PopulateTree(item, node.ChildNodes);
                    }
                    else
                    {
                        item.Tag = dict;
                    }
                    parent.Items.Add(item);
                }
            }
        }

        private TreeViewItem toTreeViewItem(XmlNode node)
        {
            TreeViewItem item = new TreeViewItem()
            {
                Header = node.Name.Contains("#") ? node.InnerText : node.Name,
            };
            return item;
        }
    }
}

这是模型:

using System.Xml;
using TableBuilder.Helpers;

namespace TableBuilder.Models
{
    public class XmlModel : ModelBase
    {
        private XmlDocument _xmlDoc;
        public XmlDocument XmlDoc
        {
            get { return _xmlDoc; }
            set
            {
                _xmlDoc = value;
                OnPropertyChanged("XmlDoc");
            }
        }

        public XmlModel()
        {
            XmlDoc = new XmlDocument();
        }
    }
}

这是视图模型基类:

using System.ComponentModel;
using TableBuilder.Models;

namespace TableBuilder.Helpers
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        static protected RedModel redModel { get; set; }
        static protected XmlModel xmlModel { get; set; }
        static protected MainModel mainModel { get; set; }

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

最后,这是模型基类:

using System.ComponentModel;

namespace TableBuilder.Helpers
{
    public class ModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我知道代码中有很多草率的做法;这是我的第一个 C#/WPF 应用程序。为了帮助限制这个问题的范围,我只想知道为什么我不能从我的视图模型中设置树视图数据。我曾尝试在 Google 上闲逛,但没有成功。我应该注意到,我能够用文本框替换树视图,并将数据从视图模型设置到文本框而没有问题。

【问题讨论】:

  • TreeView 是一个 ItemsControl。您应该将一组项目绑定到它,对吗?其次,在您的视图模型中,您的 XmlTree 属性是 TreeView 类型。不要那样做;视图模型不直接创建和操作 UIElements(视图模型不是视图...(: ) 我猜,如果这是您的第一个 WPF 应用程序(使用数据绑定和 MVVM),请不要从 TreeView 开始,而是更简单的东西,例如一个 ListBox 或一个 ComboBox(如果你得到一个使用数据绑定的列表框,那么就开始攻击分层 TreeViews...)
  • (我不知道您在 Google 中具体搜索什么,但有很多关于 WPF 基础/数据绑定、MVVM 以及如何使用 TreeView 的教程和博客文章。我相信您如果您尝试一下,可以通过 Google 找到它们...)
  • 是的,我想您对项目集合的看法是正确的。我将如何转换此代码来做到这一点?
  • 我很抱歉,但老实说(我很抱歉),看到你上面的代码示例(这是完全没有数据绑定和对 MVVM 的反论),我目前不相信你了解 MVVM 基础知识以及如何正确使用数据绑定 :(((
  • 错误的地方 :( Stack Overflow 是一个问答网站。你需要花一些时间来学习其中的技巧,这不是一个单一答案可以提供给你(或任何人)的东西. 这就是教程的内容,而 StackOverflow 不是教程网站...

标签: c# xml wpf mvvm treeview


【解决方案1】:

有关解决方案,请参阅上述评论部分。 elgonzo 指出了在重新架构过程中发生的主要架构问题:视图模型不直接与 UI 元素交互,它们与绑定到 XAML 中的 UI 元素的数据交互。

另一个缺失的部分是由于我在我的主视图模型中再次实例化我的视图模型以创建适当的美学,但是,这会破坏子窗口的数据上下文,这意味着我只能在构造函数。解决方案是在更改 UI 的各个方面时实例化 VIEWS,而不是视图模型。这样做会保留数据上下文。

编辑: 我的 XmlPane 没有发送正在更新的通知。实例化我对视图模型的视图是由此产生的结果,而不是我上面提到的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 2011-06-19
    • 1970-01-01
    • 2020-03-29
    相关资源
    最近更新 更多