xmldataprovider using element values in code behind - c#

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>

OK, here is another way, more complicated, though.
You are correct that you need to synchronize the currently displayed page with the text of the BookRefTextBox. So on top of XmlDataProvider, I define a CollectionViewSource, which can be used to keep track of the current displayed page.
In the XAML code below, both BookRefTextBox and listBox1 (I use ListBox to display pages) are bound to the same CollectionViewSource, and by setting IsSynchronizedWithCurrentItem="True", the current item is updated when the selected page is changed.
The interesting point is the XPath expression for BookRefTextBox.Test, XPath=../#BookRef means the Text of BookRefTextBox is Find the parent element of the current page- which is Book, and display its BookRef attribute".
The whole XAML of the window.
<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>
And from code behind, you can get the text of the BookRefTextBox.
Edit:
In order to set the source from code behind:
If XmlDataProvider is declared in Window.Resources, it has a x:Key attribute, you access a resource via Key, not Name.
XmlDataProvider xdp = this.Resources["userDataXmlDataProvider1"] as XmlDataProvider;
xdp.Source = new Uri(...);

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

Related

UWP+Prism MVVM - How do I set the value of a view model property via XAML?

I have a property in a view model which I would like to be able to set via the XAML but I can't figure out how to do it.
I have a pretty basic user control (containing a list of items), two of which are to be placed on a page and I would like to be able to set one to be a 'Source' (defined by an enum) and one to be a 'Target'.
[The code below has been stripped down quite a bit so apologies if I've accidentally made some mistakes or missed something out.]
My enumeration is:
public enum ConversionSide
{
Source, // Convert something FROM whatever is here.
Target // Convert something TO whatever is here.
}
I have a page which looks like this:
<Page
x:Class="MyApp.Views.ConverterPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:MyApp.Models"
xmlns:my="using:MyApp.Controls"
xmlns:prismMvvm="using:Prism.Windows.Mvvm"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
Style="{StaticResource PageStyle}"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<my:SelectorPage Name="SourceSelector" Grid.Column="0" />
<my:SelectorPage Name="TargetSelector" Grid.Column="1" />
</Grid>
</Page>
...where SelectorPage is a user control (I've called it a 'Page' to make the Prism AutoWire work but that's not the issue here) containing a list of items (all working fine) which looks like this...
<UserControl
x:Class="MyApp.Controls.SelectorPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:MyApp.Models"
xmlns:my="using:MyApp.Controls"
xmlns:prismMvvm="using:Prism.Windows.Mvvm"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<ListView
Grid.Column="0"
ItemsSource="{x:Bind ViewModel.MyList, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.MySelectedItem, Mode=TwoWay}">
<ListView.Header>
<TextBlock Margin="0,8,0,8" HorizontalAlignment="Center" FontStyle="Italic" Text="Header Text" />
</ListView.Header>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:MyListItem">
<my:MyListItemTemplate />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
..with code behind as...
public sealed partial class SelectorPage : UserControl
{
private SelectorViewModel ViewModel => DataContext as SelectorViewModel;
public SelectorPage()
{
this.InitializeComponent();
}
}
SelectorViewModel looks like this...
public class SelectorViewModel : ViewModelBase
{
private ConversionSide _side;
public ConversionSide Side
{
get { return _side; }
set { SetProperty(ref _side, value); }
}
// Many lines have been omitted for 'clarity'.
}
I would like to be able to set the Side property of SelectorViewModel in XAML like this...
<my:SelectorPage Name="SourceSelector" Grid.Column="0" Side="Source" />
<my:SelectorPage Name="TargetSelector" Grid.Column="1" Side="Target" />
(Once Side has been set, I do not expect it to ever change.)
How can I do this?
I've looked at using a dependency property but I can't get it to change the property in SelectorViewModel. When I add one in SelectorPage it's visible in the XAML and I can set it but it doesn't actually do anything so I'm probably not using it right. Putting a dependency property in the view model doesn't sound right to me but I could be wrong.
I've had a look around the web - Microsoft documentation, blogs, articles, stack overflow, etc. - but I can't find anything that explains things well enough for me to figure out what I'm supposed to do. The writings I've found seem to be exclusively about getting information from a bound property - which I'm okay with - but what I'm after is setting a property from the XAML.
Can anyone give my any clues please? I don't know if I'm just a tiny step away from getting what I want or if I'm miles away.
This would set the Side property of the SelectorPage control to Source:
A view sets the property of a view model by two-way bind to it. For example, the following TextBox sets the string property of a view model called Test when you change the text in the TextBox:
<TextBox Text="{Binding Test, Mode=TwoWay}" />
So setting the property of a view model from the view typically applies to controls that handles some kind of input. Any default value of a source property should be defined in the view model:
private ConversionSide _side = ConversionSide.Source;
You shouldn't define the default values in the view.

Calibrun.Micro Sample/Feature/UWP : What is "cm:Bind.Model="{Binding}"

Here is Calibrun.Micro example with UWP.
in Bubbling sample, in this file,
there is this line
<Grid cm:Bind.Model="{Binding}">
What is mean ? Why does it necessary ?
I thought Next line is enough for send $dataContext
<Button x:Name="Message" cm:Message.Attach="SelectPhrase($dataContext)" Margin="0,12" />
Please advice me....
Let's analyze the code as quite some things are a bit obscure in Caliburn.Micro as it works through convention:
<Page
x:Class="Features.CrossPlatform.Views.BubblingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cm="using:Caliburn.Micro"
mc:Ignorable="d">
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="actions" Style="{StaticResource SubheaderTextBlockStyle}" Margin="40,10,40,0"/>
<StackPanel Margin="40,20">
<ItemsControl x:Name="Phrases">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid cm:Bind.Model="{Binding}">
<Button x:Name="Message" cm:Message.Attach="SelectPhrase($dataContext)" Margin="0,12" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Page>
First of all, the page's datacontext (viewmodel) is set through naming convetion. This viewmodel can be found here. As you can see, this viewmodel has a Phrases property and a SelectPhrase(MessageActivityViewModel phrase) method.
If we go down the tree, we have an <ItemsControl x:Name="Phrases"> which binds to the Phrases property, which is a collection of MessageActivityViewModel.
An ItemsControl needs a way to present the items, which is defined in the DataTemplate. This template has a Grid as root object. If the Grid would not use {Binding}, it would inherit the datacontext from the template, which is the current element being rendered (a single MessageActivityViewModel). However, we want to call the SelectPhrase method on the BubblingViewModel and not on the MessageActivityViewModel. To be able to do that, we use {Binding} to tell the Grid binds to the page's viewmodel instead of to the single rendered MessageActivityViewModel.
What the Button does, is sending the datacontext object (being the rendered MessageActivityViewModel item) back to your viewmodel.
$dataContext:
Passes the DataContext of the element that the ActionMessage is attached to. This is very useful in Master/Detail scenarios where the ActionMessage may bubble to a parent VM but needs to carry with it the child instance to be acted upon.
Source: http://caliburnmicro.com/documentation/cheat-sheet

Access template properties which are stored in resources

I have a derived devexpress grid control which sets some template for the indicator row:
<dxg:GridControl.Resources>
<sys:Double x:Key="{dxgt:TableViewThemeKey ResourceKey=IndicatorWidth, ThemeName=Office2016White}">300</sys:Double>
<DataTemplate x:Key="{dxgt:RowIndicatorThemeKey ResourceKey=RowTemplate, ThemeName=Office2016White}">
<Grid Name="IndicatorGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35" Name="IndicatorColumnRowNumber" SharedSizeGroup="RowNumberGroup" />
<ColumnDefinition Width="200" Name="IndicatorColumnDescription" SharedSizeGroup="DescriptionGroup" />
<ColumnDefinition Width="200" Name="IndicatorColumnSource" SharedSizeGroup="SourceGroup" />
</Grid.ColumnDefinitions>
//...
</DataTemplate>
</dxg:GridControl.Resources>
Right now the width of the indicator column is fixed to 300. Now I want the width of the row being calculated by the columns defined in the second part. I know I can access the indicator width of the view via the code behind as well but I'm not able to access the template controls in the code behind
var view = ((TableView)this.View);
view.IndicatorWidth = IndicatorColumnRowNumber.Width /* can not be found */ + ...
Since they live in the resources in the xaml. As I understand this is also not supposed to happen. What is the best way to implement this? Maybe extract the definitions in the code behind?
Edit:
Here is a picture of the table
I am not 100% sure what you are trying to achieve but if you want to access your resource in code behind you can go about it like this:
XAML:
<Grid x:Name="MyGrid">
<Grid.Resources>
<DataTemplate x:Key="MyResource">
<TextBlock Text="Hello"></TextBlock>
</DataTemplate>
</Grid.Resources>
</Grid>
Code behind:
public MainWindow()
{
InitializeComponent();
var resource = MyGrid.Resources["MyResource"];
var dataTemplate = (DataTemplate) resource;
}
You have additional tools to obtain any defined resources.
The methods FrameworkElement.FindResource and FrameworkElement.TryFindResource will search for resources from the element you specified and up the visual tree until the main application and including any themes you have setup. From the MS reference:
FrameworkElement.FindResource documentation
Also notice that the Resources dictionary and the FindResource methods accept an object key (not a string). This suits your case since your key is a RowIndicatorThemeKey object. You can instantiate such an object and set the ResourceKey property to the "RowTemplate" value and search for your resource like that.
In general solutions for problems such as yours can be achieved without requiring code behind though, if you provide some more information perhaps we can find a solution based on bindings.

How to refresh xmlDataProvider when xml document changes at runtime in WPF?

I am trying to make a image viewer/album creator in visual studio, wpf. The image paths for each album is stored in an xml document which i bind to to show the images from each album in a listbox.
The problem is when i add a image or an album at runtime and write it to the xml document. I can't seem to make the bindings to the xml document update so they show the new images and albums aswell.
Calling Refresh() on the XmlDataProvider doesn't change anything.
I don't wish to redo the binding of the XmlDataProvider, just make it read from the same source again.
XAML:
...
<Grid.DataContext>
<XmlDataProvider x:Name="Images" Source="Data/images.xml" XPath="/albums/album[#name='no album']/image" />
</Grid.DataContext>
...
<Label Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Padding="0" Margin="0,0,0,5" Content="{x:Static resx:Resource.AddImageLabel}"/>
<TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Name="newImagePath" Margin="0" />
<Button Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Bottom" Name="newImagePathButton" Content="{x:Static resx:Resource.BrowseImageButton}" Click="newImagePathButton_Click" />
...
<ListBox Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="3" HorizontalAlignment="Stretch" Name="thumbnailList" VerticalAlignment="Bottom" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding BindingGroupName=Images}" SelectedIndex="0" Background="#FFE0E0E0" Height="110">
...
Code behind:
private void newImagePathButton_Click(object sender, RoutedEventArgs e)
{
string imagePath = newImagePath.Text;
albumCreator.addImage(imagePath, null);
//Reset import image elements to default
newImagePath.Text = "";
//Refresh thumbnail listbox
Images.Refresh();
Console.WriteLine("Image added!");
}
public void addImage(string source, XmlElement parent)
{
if (parent == null)
{
//Use default album
parent = (XmlElement)root.FirstChild;
}
//Create image element with source element within
XmlElement newImage = xmlDoc.CreateElement(null, "image", null);
XmlElement newSource = xmlDoc.CreateElement(null, "source", null);
newSource.InnerText = source;
newImage.AppendChild(newSource);
//Add image element to parent
parent.AppendChild(newImage);
xmlDoc.Save(xmlFile);
}
Thank you very much for any help!
The right way in this situation I beleive is to use ObservableCollection and bind it to ItemsSource property of your ListView. So, just play with objects and no tricks with XML files.
Edit:
Entire concept is work with Refresh(). Next sample is works. Check if Refresh() call is made after document saving.
<ListView x:Name="uiList" ItemsSource="{Binding}">
<ListView.DataContext>
<XmlDataProvider x:Name="DataSource" Source="c:\XMLFile.xml" XPath="/root/item" />
</ListView.DataContext>
<ListView.ItemTemplate>
<DataTemplate>
<Border Width="40" Height="40" Background="Gray">
<Label Content="{Binding Attributes[0]}" />
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
public MainWindow()
{
InitializeComponent();
uiList.SelectionChanged += new SelectionChangedEventHandler(uiList_SelectionChanged);
}
void uiList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string sFile = #"c:\XMLFile.xml";
XDocument oDoc = XDocument.Load(sFile);
oDoc.Root.Add(
new XElement("item", new XAttribute("name", "test3"))
);
oDoc.Save(sFile);
XmlDataProvider oProv = uiList.DataContext as XmlDataProvider;
oProv.Refresh();
}
Possible Problem and Solution #1
Do you have this XmlDataProvider resource declared in the Application.Resources block as well?
If you do, the XAML UI ListBox element "thumbnailList" refers to the Grid panel's instance of the XmlDataProvider. I'm guessing, since I can't see the code in your Window CS file constructor, that you refer to the Application-level instance of the XmlDataProvider when you address the XmlDataProvider there as in
XmlDataProvider xmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider;
XmlDocument xDoc = xmlDataProvider.Document;
If this is the case, remove the XmlDataProvider resource from the Grid element. Now when your code-behind updates the XML file the UI will automatically update.
Possible Problem and Solution #2
I see from your addImage() method that you refer to an instance variable named "xDoc".
The other possibility is that you are creating a NEW XmlDocument in your Window constructor, instead of referencing the XAML created XmlDocument object. If so, get the instance of the current XmlDocument instead of creating a new instance. Make sure to declare the resource
at the Application level and remove the resource declaration from the Grid element
XmlDataProvider xmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider;
Or reference the resource at the Grid element (you will need to add a Name to the Grid) and do not declare the resource in the Application.Resources block
XmlDataProvider xmlDataProvider = grid.FindResource("Images") as XmlDataProvider;
XmlDocument xDoc = xmlDataProvider.Document;
Now when your code-behind updates the XML file the UI will automatically update.
Conclusions
If you declare these two class instance variables in your code-behind
XmlDataProvider xmlDataProvider;
XmlDataProvider gridXmlDataProvider;
and have this code in your Window constructor
xmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider;
gridXmlDataProvider = grid.FindResource("Images") as XmlDataProvider;
Put a stop in the addImage event handler right you add a Node and save the XML Document changes. Assuming you originally loaded oDoc from xmlDataProvider as shown above. Run in Debug Mode and open a Watch window and inspect the contents of xmlDataProvider and gridXmlDataProvider. Open the Document property on each, and compare the contents of the InnerXml property. On the xmlDataProvider (the Application-level's resource) you will find
the latest node changes to the XML file are reflected. Not so on the gridXmlDataProvider (the XAML UI element's resource). InnerXml property shows no changes. No changes, not need to update the UI.
FYI I had Problem #1 - the same XmlDataProvider resource declared in the Application.Resources block AND in the Window.Resources block. I started out with the latter declaration, ran into an exception error after I referred to the XmlDataProvider instance via Application.Current.FindResource("name"), copy and pasted the declaration into the Application.Resources block, LEAVING the resource declared in the Window.Resources block, creating a TWO REFERENCE problem. The XAML UI used the Window data context, while my code-behind updated the XML file with the Application data context! Whenever I added or removed nodes from the XML file the UI (ListBox) did not get updated!
BTW XmlDataProvider already implements its own notification mechanism, no need use an ObservableCollection. oProv.Refresh() does not cause the refresh of the bound UI because it may point to a different instance of the XmlDataProvider (the Grid element's), and as far as that instance is concerned, no changes have happened.
This answer probably comes too late for you, but I just found this stuff out, thought I share it.
in xml
<XmlDataProvider Source="XMLFile1.xml" XPath="Data" DataChanged="XmlDataProvider_DataChanged"></XmlDataProvider>
</Window.DataContext>
in cs
private void XmlDataProvider_DataChanged(object sender, EventArgs e)
{
Dispatcher.BeginInvoke((Action)(() =>
{
XmlDataProvider oProv = this.DataContext as XmlDataProvider;
oProv.Refresh();
}));
}
Taken from..http://www.infosysblogs.com/microsoft/2008/03/wpf_updating_xmldataprovider_w.html
Ive used below;
XmlDataProvider xdp = this.Resources["userDataXmlDataProvider1"] as XmlDataProvider;
xdp.Source = new Uri(MyPath + #"\Projects.xml");
FileSystemWatcher watcher = new FileSystemWatcher();
//set the path of the XML file appropriately as per your requirements
watcher.Path = MyPath;
//name of the file i am watching
watcher.Filter = "Projects.xml";
//watch for file changed events so that we can refresh the data provider
watcher.Changed += new FileSystemEventHandler(file_Changed);
//finally, don't forget to enable watching, else the events won't fire
watcher.EnableRaisingEvents = true;
and
void file_Changed(object sender, FileSystemEventArgs e)
{
XmlDataProvider xdp = this.Resources["userDataXmlDataProvider1"] as XmlDataProvider;
xdp.Refresh();
}
and in my UserControl;
<UserControl.Resources>
<XmlDataProvider x:Key="userDataXmlDataProvider1" XPath="Projects/Project" IsAsynchronous="True" />
<CollectionViewSource x:Key="userDataCollectionViewSource1" Source="{StaticResource userDataXmlDataProvider1}"/>
</UserControl.Resources>
<Grid DataContext="{StaticResource userDataXmlDataProvider1}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<ListBox x:Name="listBox1" Grid.Row="1"
ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="8,0,8,0">
<Label Content="{Binding XPath=ProjectName}" Width="100" Margin="5" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>

Dynamically generated XAML

I am working towards producing a small application that parses XML from a URL and populates a Grid panel based on the contents of the XML. Currently, I have many other elements working properly, but still lack the knowledge needed to hide or show certain columns within the table and having it resize properly. Here's the basic structure of my XAML thus far.
Currently, I feel as though my solution is very poor. I have hard coded each coulmn and row within the Grid and tied their Visibility to a code behind Converter. Under certain conditions, this Converter will return a Visibility of Hidden, but under other conditions it returns the value to display within the table. This feels very sloppy to me, so I assume I've designed this system incorrectly.
My question is more about the proper way to setup this type of system. I am much more familiar with generating the document structure itself within some business logic and then token swapping that generated structure with a token inside the raw document itself. What is a best way to accomplish the goal I'm pursuing?
You could provide the XDocument or XElement retrieved from the web service as the DataContext of an ItemsControl with a Grid. You would then use a DataTemplate to display the information.
XML:
<Entities>
<Person Name="Ted" Age="42" />
<Person Name="Sam" Age="19" />
<Person Name="Bob" Age="25" />
<Person Name="Angie" Age="38" />
</Entities>
Code Behind:
this.DataContext = xdoc;
XAML:
<ItemsControl ItemsSource="{Binding Root.Elements[Person]}"
Grid.IsSharedSizeGroup="True">
<ItemsControl.Resources>
<DataTemplate DataType="Person">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="NameColumn"/>
<ColumnDefinition SharedSizeGroup="AgeColumn" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Path=Attribute[Name].Value}" />
<TextBox Text="{Binding Path=Attribute[Age].Value}"
Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Linq to XML resources:
WPF Data Binding with LINQ to XML Overview
WPF Data Binding Using LINQ to XML Example
How to: Bind to XDocument, XElement, or LINQ for XML Query Results

Categories