xmldataprovider在代码后面使用元素值

时间:2014-12-23 21:09:27

标签: c# xml wpf code-behind xmldataprovider

我有一个wpf窗口,我通过XMLDataProvider使用xml数据。屏幕基于网格,所有数据都正确显示,已定义xml如下...

<Grid.DataContext>
    <XmlDataProvider x:Name="Data" XPath="Book/Page" />
</Grid.DataContext>

将xml源设置在代码后面,如下所示......

InitializeComponent();

string appPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
Data.Source = new Uri(appPath + @"\SNG.xml");

到目前为止一切都那么好。但是现在我需要在后面的代码中读取xml文件中的一个元素。我所有的搜索和我发现的唯一方法是将它绑定到一个不可见的控件然后从控件中读取数据。例如从xml中读取BookRef我在xaml中有以下内容......

TextBlock Name="BookRefTextBox" Visibility="Hidden" Text="{Binding XPath=@BookRef}"/>

然后在后面的代码中......

string bookRef = BookRefTextBox.Text;

这样可行,然后我可以使用来自xml文件的数据......但它真的感觉就像一个软糖。有没有更好的方法从代码隐藏部分中获取xml文件的各个部分的值。

编辑:

忘了说我已经尝试将XmlDataProvider放在Windows.Resources而不是Grid.DataContext中作为我已经发现的一些例子。

然而,我无法找到在后面的代码中设置xml文件路径的方法。添加到将其放入Windows.Resource并不容易找到如何从Xml文件中访问数据。

EDIT2: 以下是XML文件的示例。请注意,有多本书。

<Books>
  <Book Id="1" BookRef="12345" Name="My Book Name" Author="Author" Pages="2" >
    <Page PageId="1"/>
    <Page PageId="2"/>
  </Book>
  <Book Id="1" BookRef="67890" Name="My Second Book Name" Author="Author 2" Pages="1" >
    <Page PageId="1"/>
  </Book>
</Books>

2 个答案:

答案 0 :(得分:2)

好的,这是另一种方式,但更复杂。

您是正确的,您需要将当前显示的页面与BookRefTextBox的文本同步。因此,在XmlDataProvider之上,我定义了一个CollectionViewSource,可用于跟踪当前显示的页面。

在下面的XAML代码中,BookRefTextBoxlistBox1(我使用ListBox显示页面)绑定到相同的CollectionViewSource,并通过设置 IsSynchronizedWithCurrentItem =“True “,当所选页面发生变化时,当前项目会更新。

有趣的是BookRefTextBox.Test的XPath表达式,XPath=../@BookRef表示BookRefTextBox的Text为Find the parent element of the current page- which is Book, and display its BookRef attribute"

窗口的整个XAML。

<Window x:Class="MyNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <XmlDataProvider x:Key="userDataXmlDataProvider1" Source="/Data/XMLFile1.xml" XPath="Books/Book/Page"/>
        <CollectionViewSource x:Key="userDataCollectionViewSource1" Source="{StaticResource userDataXmlDataProvider1}"/>
    </Window.Resources>
    <Grid DataContext="{StaticResource userDataXmlDataProvider1}">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <TextBlock x:Name="BookRefTextBox" Grid.Row="0" Text="{Binding XPath=../@BookRef}" />
        <ListBox x:Name="listBox1" Grid.Row="1" 
                 IsSynchronizedWithCurrentItem="True"
                 ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="8,0,8,0">
                        <Label Content="{Binding XPath=@PageId}" Width="100" Margin="5" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

从后面的代码中,您可以获得BookRefTextBox的文本。

编辑:

为了从后面的代码设置源代码:

如果在XmlDataProvider中声明了Window.Resources,它具有x:Key属性,您可以通过密钥访问资源,而不是名称。

XmlDataProvider xdp = this.Resources["userDataXmlDataProvider1"]  as XmlDataProvider; 
xdp.Source = new Uri(...);

答案 1 :(得分:0)

我相信我终于找到了避免使用隐藏控件的答案。首先,非常感谢kennyzx的回答,虽然它仍然使用了隐藏的控件,这对我来说是非常宝贵的。

不是将XmlDataProvider放在Grid.Context中,而是将其移动到Window.Resources并添加一个CollectionViewSource。

<Window.Resources>        
    <XmlDataProvider x:Name="books" x:Key="bookData" XPath="Books/Book/Page"/>
    <CollectionViewSource x:Key="collBookData" Source="{StaticResource bookData}"/>
</Window.Resources>

在后面的代码中定义了一个新的XmlDataProvider,并且在窗口的构造函数中将其设置为对XAML Windows.Resources中定义的引用。

    XmlDataProvider bookData;

    public BookPage()
    {
        InitializeComponent();

        string appPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
        bookData = (XmlDataProvider)this.Resources["bookData"];
        bookData.Source = new Uri(appPath + @"\SNG.xml");
    }

Grid的DataContext设置为CollectionViewSource。

    <Grid.DataContext>
        <Binding Source="{StaticResource collBookData}"/>
    </Grid.DataContext>

以上并非100%必要,因为它可以在每个控件上指定,但这种方式可以更简单地绑定表单上的每个控件。 (此解决方案中没有隐藏的控件,只有我想要实际显示的控件)。例如......

    <TextBlock Name="myTextBlockName" Style="{StaticResource MyTextBlockStyle}" Text="{Binding XPath=../@BookRef}" />

最后在代码后面的XML中读取数据。

        XmlNode currentXmlNode = bookData.Document.SelectNodes("Books/Book/Page").Item(collBookData.View.CurrentPosition);
        string currentBookRef = currentXmlNode.ParentNode.Attributes["BookRef"].Value;

除此之外,这个解决方案还允许我对collBookData.View使用MoveCurrentToPrevious和MoveCurrentToNext来更改当前正在显示的页面(之前有一个隐藏的列表框控件来执行此操作并且对该解决方案不满意)。