【问题标题】:XML data binding in WPFWPF 中的 XML 数据绑定
【发布时间】:2011-03-24 01:46:22
【问题描述】:

我正在尝试编写一个对一些无符号长值进行解码的应用程序。每个值的格式在 XML 中表示为:

<Project Name="Project1">
 <Message Name="a">
  <BitField high="31" low="28">
   <value>0001</value>
   <value>1010</value>
  </Bitfield>
  <BitField high="27" low="17">
   <value>000111101</value>
  </BitField>
  <BitField high="16" low="0">100h</BitField>
 </Message>
</Project>

现在项目值出现在组合框中。当用户在组合框中选择一个值时,消息类型必须显示在列表框中。然后,当用户在列表框中选择消息类型时,必须显示位域和它们可以保存的值。现在,当用户为每个位域选择一个值时,最终的 dword 值必须显示在一个文本框中。

我遇到过解析完整 xml 但与选择无关的示例。在这里需要你们的帮助。

还有一件事是用户可以在文本框中输入一个 dword 值。现在我怎样才能在文本框中解码 dword 并使用上面解释的 UI 显示相应的消息 + 值的反向绑定?

更新:现在我已经完成了显示项目值的组合框和显示消息的列表框之间的绑定。接下来我要做的是,当用户在列表框中选择一条消息时,位域必须显示为带有“high”、“low”、“value/@name”的行(此处未显示),然后值(绑定到值/@name)作为列。值/@name 必须显示为组合框。我确信我可以在 dataGrid 中做到这一点,但我使用的是 .net 3.5,所以在这里寻找替代方案。此外,值文本块必须是可编辑的,以防 节点不存在于 xml 中。最后,我必须将“值”列中的条目打包到 DWORD 中。我可以在没有数据网格的情况下在 XAML 中执行此操作吗? .Net 3.5 的数据网格有什么替代品?

【问题讨论】:

  • 什么是 C# WPF?这与 VB.NET WPF 有什么不同?
  • 致 John:WPF 与公共语言运行时 (CLR) 代码集成。 AFAIK 这可以从任何 .NET 语言生成,但肯定是由 C#.NET 和 VB.NET 生成的,因此基本上无论使用哪种语言,都可以使用相同的功能。

标签: c# wpf xml data-binding


【解决方案1】:

这里有两个不同的问题——一个 XML 解析问题和一个转换问题。

要使绑定起作用,您需要将 XML 解析为一个表示其值的类。对于转换问题,我指出TypeConverters 的概念,它使用起来相当简单。对于 XML 解析问题,我想强调 LINQ-to-XML,我认为这是蜜蜂的膝盖。它的几行甚至可以从 XML 源重构最复杂的对象图。对于您的 Project/Message/BitField 示例,我已经部分实现了一个可以为您提供模式的解决方案。

注意“LoadFromXml”方法。这使用填充对象图的 linq-to-xml 表达式。此查询在您的 XML 源中更改的一件事是它期望集合节点(即 或 )被集合节点(即 )包围

   public class Project
    {
        public string Name { get; set; }
        public List<Message> MessageCollection = new List<Message>();

        public void LoadFromXml(string xmlString)
        {
            // From System.Xml.Linq
            XDocument xDocument = XDocument.Parse(xmlString);

            // The following assumes your XML is well formed, is not missing attributes and has no type conversion problems. In
            // other words, there is (almost) zero error checking.
            if (xDocument.Document != null)
            {
                XElement projectElement = xDocument.Element("Project");

                if (projectElement != null)
                {
                    Name = projectElement.Attribute("Name") == null ? "Untitled Project" : projectElement.Attribute("Name").Value;

                    MessageCollection = new List<Message>
                        (from message in xDocument.Element("Project").Elements("Messages")
                         select new Message()
                                {
                                    Name = message.Attribute("Name").Value,
                                    BitfieldCollection = new List<BitField>
                                        (from bitField in message.Elements("Bitfields")
                                            select new BitField() {High = int.Parse(bitField.Attribute("High").Value), Low = int.Parse(bitField.Attribute("Low").Value)})
                                }
                        );
                }
            }
        }
    }

    public class Message
    {
        public string Name { get; set; }
        public List<BitField> BitfieldCollection = new List<BitField>();
    }

    public class BitField
    {
        public int High { get; set; }
        public int Low { get; set; }
        public List<string> ValueCollection = new List<string>();
    }

【讨论】:

    【解决方案2】:

    您绝对需要牢记模型和视图的分离以及模型的外部表示。根据您的实际 XML 的大小以及您对这种 API 的感觉是否满意,您可以使用几种完全不同的方式来读取您的 XML 并将其转换为实际的对象,然后这些对象将用作您的 UI 的模型。记住这种分离很重要。

    我会假设您的 XML 是来自其他一些处理的实际数据——这意味着会有很多项目标签,有很多消息标签,并且位域跨度因消息而异。如果这个假设是错误的,并且位域跨度对于整个应用程序或一个大文件中的所有消息都是固定的,那么您可以节省一些时间和空间。

    所以假设一切都是可变的,从标签中获取值的最快方法可能是创建 XPathDocumen 并通过 XPath 选择,您可以将其保留为字符串常量,甚至保存在配置中。例如,这将为您提供所有值标签(现在称为节点)

    //Project/Message/BitField/Value
    

    并且由于节点的对象具有父指针,因此您可以收集共享父对象的所有值(到列表或数组中),然后为父对象(BitField)创建一个对象,保留它并确保它具有来自的父指针xml 节点。一旦你拥有了所有的 BitField 对象,你就可以进行类似的扫描,然后将它们分组到它们的父级(消息)中,然后再次获取 Project-s。这只是一种方式,您可以使用较短的 XPath 来表示开始从 BitField 收集并自己获取他们的孩子。

    您还可以创建一些简单的类来逐字表示您的 XML 标记(每个字段一个数据成员,每个标记一个类),然后为 XmlSerialization 放置属性。这将需要一些试验才能获得正确的名称,但是如果您拥有它,您就可以使用函数调用“反序列化”整个文件并从 .NET 接收数组或数组的数组:-)。

    如果您的文件非常非常大,那么您可能必须使用 XmlTextReader 并从上到下扫描文件 - 与 XPath 选择完全相反(XmlTextReader 是最简单的解析方式,不会产生大量额外的对象,但是您必须以几次外观对其进行迭代,并且始终从父级“步行”到子级,因为它是单向的)。

    在 UI 方面,如果您有时间尝试一下,可能值得尝试构建一个具有 3 个组合框和一个文本字段的边界框,然后是这些的列表 - 这样您就有更大的灵活性,即您不需要'不必符合网格的机制。

    哦,如果您对 XML 格式有任何影响,请尝试让 ppl 始终将该值放入一个值标记中,即使它只是一个。 XML 文件会有点长,但您的处理会得到简化。

    【讨论】:

      【解决方案3】:

      这可以使用数据绑定和 XMLReaders 来完成。首先创建您的 UI,最好在父面板中创建(这样我们可以设置面板的数据上下文,子控件都将继承此上下文)。

      接下来创建一个自定义类(我们称之为项目)。确保此类中的每个字段都更新 PropertyDependencyInfo,否则数据绑定将不起作用。

      然后将 UI 控件的 Content 或 Itemssource 属性绑定到 Project 类的字段。

      然后,使用 XMLReader 类或 XPathReader 类创建代码以读取 XML。然后,当您访问文件中的数据后,设置新 Project 对象的字段以匹配您要读取的数据。

      一旦 Project 对象被实例化并且 XML 中的值被正确分配给它,将父 Panel 从 UI 的 DataContext 属性设置为您的 Project 对象。然后所有的 UI 字段都应该更新。

      如果您随后需要加载不同的文件,您所要做的就是使用不同的文件名重复 XML 读取。

      至于您的更新:可以使用以下任一方式管理行和列:

      一个 Grid/UniformGrid 控件,其 RowDefinition 和 ColumnDefinition 子项在运行时创建和修改,以便管理不断增加和减少的字段数

      或:一组 StackPanel,每列一个(因为列数永远不会改变)。您可以直接为您的 value/@name 列添加 Comboboxes 到 StackPanel,或者您可以修改 StackPanel 的 ItemsTemplate(如果有,我目前无法访问机器进行检查)。

      这个问题可能应该分成几个子问题,因为它涵盖了一系列主题,如果有专门针对这些问题量身定制的问题,您更有可能获得问题各个方面的详细答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-16
        • 1970-01-01
        • 1970-01-01
        • 2011-03-07
        • 1970-01-01
        相关资源
        最近更新 更多