Search code examples
c#xmlwpfcode-behindxmldataprovider

xmldataprovider using element values in code behind


I have a wpf window where I am using xml data through an XMLDataProvider. The screen is based on a grid and all the data is being displayed correctly, having defined the xml as follows...

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

With the xml source being set in code behind as follows...

InitializeComponent();

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

All so good so far. But now I have a need to read one of the elements from the xml file in the code behind. All my searching and the only way I've found to do it is to bind it to an invisible control then read the data out of the control. e.g. to read the BookRef from the xml I have the following in the xaml...

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

Then in the code behind...

string bookRef = BookRefTextBox.Text;

This works, I can then use the data that came from the xml file... but it really feels like a fudge. Is there a better way to get the value of parts of the xml file from within the code behind section.

EDIT:

Forgot to say that I've also tried putting the XmlDataProvider in Windows.Resources instead of in Grid.DataContext as some examples I've found do.

However I then can't find a way to set the path to the xml file in code behind. Added to which putting it in Windows.Resource does not make it any easier to find how to access the data from the Xml file.

EDIT2: Here is an example of the XML file. Note there are multiple books.

<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>

Solution

  • I believe I have finally found the answer that avoids the use of a hidden control. First off many thanks to kennyzx for his answer which while it still used a hidden control was invaluable in leading me to this answer.

    Instead of putting the XmlDataProvider in the Grid.Context it has been moved to the Window.Resources and a CollectionViewSource added to accompany it.

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

    A new XmlDataProvider is defined in the code behind and in the constructor of the window is set to be a reference to the one defined in the 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");
        }
    

    The DataContext of the Grid is set to be the CollectionViewSource.

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

    The above is not 100% necessary as it could be specified on each control instead, but this way makes for simpler binding on each control on the form. (No hidden controls in this solution, only the ones I want to actually show). For example...

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

    Finally the bit to read the data from the XML in code behind.

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

    Just as an aside, this solution also allows me to use MoveCurrentToPrevious and MoveCurrentToNext against collBookData.View to change the current page being displayed (previously had a hidden listbox control to do that and wasn't happy with that solution either).