Binding to XML Node Name - c#

I'm trying to bind to some XML data from my WPF application. I've set up the data context so that the XmlElement I'm trying to bind to ends up looking like this:
<Item name="Potion" classes="Healing Item" value="200">
<Classes>
<Class value="HealingItem" />
</Classes>
<Description value="A low quality potion, it restores a small amount of health" />
<Components>
<HealingComponent>
<BattleHealingComponent>
<HPHealingComponent value="500" type="Absolute"/>
</BattleHealingComponent>
</HealingComponent>
</Components>
</Item>
Now here's the problem. I can't figure out an XPath query I can bind with that will return only the Component Subnodes.
I know it'll go something like this:
ItemsSource="{Binding XPath=Components/*/????}"
I'm stuck on what to use for ????
The result of this query should display "HealingComponent" I've tried playing around with various different parameters on an online XPath visualizer, but I can't figure this one out. I ready about name(), but I can't seem to get that to work.
Any help would be appreciated

In addition to the ItemsSource you probably need an ItemTemplate, this should work:
<ListBox ItemsSource="{Binding XPath=Components/*}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
If you do not intend to do anything fancy you can also use the DisplayMemberPath, though in this case the binding ensures that Name is not interpreted as an XPath, you may not have that kind of control with DisplayMemberPath.

Related

Binding fails without error

I have a CustomListView that has controls like TextBoxes, HyperLinks and Buttons.
I can't share the exact Code, but please bear with me.
I have a list, say of type person with attributes like personHeight, personWidth, etc.
The binding to the list is done at the list level.
The issue is, that all values are fetched from the database, but only one is populated, the remaining are blank.
Also I didn't get any binding error in the Output Window. What could be the issue?
The general syntax of the XAML (where personHeight is one of the properties of the list object):
<CustomGridViewColumn SortOnProperty="personHeight">
<CustomGridViewColumn.SortableHeader>
<TextBlock Text="Person Height" />
</CustomGridViewColumn.SortableHeader>
<CustomGridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="personHeightText" Text="{Binding personHeight}" />
</DataTemplate>
</CustomGridViewColumn.CellTemplate>
<CustomGridViewColumn>
The binding is done as follows:
<CustomListView ItemsSource="{Binding Path=Persons}"
ItemContainerStyle="{StaticResource MyExpandableListViewItemStyle}"
ExpandedDataTemplate="{StaticResource ExpandedDataTemplate}"
ScrollViewer.CanContentScroll="False"
x:Name="GridList">

How do you implement custom inline searching in an ItemsControl?

This is a two-parter.
First, in WPF the standard ListBox control automatically supports inline searching of its items. It does this via using the items' ToString function, meaning if you have the focus inside a listbox and just start typing, it will do a left-most search, highlighting any items whose ToString matches what you've typed. Subsequent key presses within a short period of time add to the search string (i.e. typing 'A' followed by 'S' will left-search for 'AS' whereas typign 'A' then pausing, then typing 'S' will instead left-search for 'S'.
The problem is this mechanism seems to be dependent solely on the value returned by ToString, which in some cases is something we can't rely on. Is there something else we can use instead of ToString?
The second part of this is that behavior only seems to be present in a ListBox, but no other ItemsControl objects (or hierarchical ones like a TreeView.) Without having to re-write that functionality from scratch, is there an easy way to add it to an ItemsControl?
You can control what is searched with TextSearch.Text or TextSearch.TextPath attached properties. (See http://msdn.microsoft.com/en-us/library/system.windows.controls.textsearch(v=vs.110).aspx)
You can apply TextSearch.TextPath to your ListBox instance (so it searches this property instead of ToString) or you can apply TextSearch.Text to individual ListBoxItem child (so you can set individual search text for individual elements).
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<XmlDataProvider x:Key="Items" XPath="People">
<x:XData>
<People xmlns="">
<Person Name="John" Surname="Smith" />
<Person Name="Andrew" Surname="Johnson" />
<Person Name="Otis" Surname="Everett" />
<Person Name="Jesus" Surname="Osborn" />
</People>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<StackPanel>
<TextBlock Text="Searches by a property (Name):" />
<ListBox ItemsSource="{Binding Source={StaticResource Items}, XPath=*}"
TextSearch.TextPath="#Name">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding XPath=#Name}" /> <Run Text="{Binding XPath=#Surname}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock>Searches by a individual value (number in english):</TextBlock>
<ListBox>
<ListBoxItem TextSearch.Text="One">1</ListBoxItem>
<ListBoxItem TextSearch.Text="Two">2</ListBoxItem>
<ListBoxItem TextSearch.Text="Three">3</ListBoxItem>
<ListBoxItem TextSearch.Text="Four">4</ListBoxItem>
</ListBox>
</StackPanel>
</Window>
This behavior is implemented in the ItemsControl class (and you can find other examples of ItemsControl descendants with search: ComboBox, DataGrid). You can set IsTextSearchEnabled property to true to make it work. (See http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.istextsearchenabled(v=vs.110).aspx)
The single level search works for TreeView. I think you should implement the search programmatically if you want to perform multi level search. (see http://social.msdn.microsoft.com/Forums/vstudio/en-US/e6d58fcc-4eaa-4bdc-8621-ce24c8efd330/treeview-textsearch)

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

WPF binding XML data to GUI items

I'm new to WPF and don't yet have a solid grasp of how things should be done...
I have an xml file that stores config data and I want the data that is in this xml file to be displayed on the gui front end.
I'm currently using an XmlDataProvider that reads in a set of data
The data is roughly like:
<Items>
<Item name="item01">
<Position x="0", y="0"/>
</Item>
<Item name="item02">
<Position x="0", y="0"/>
</Item>
<Item name="item03">
<Position x="0", y="0"/>
</Item>
</Items>
The XmlDataProvider is declared as a resource as follows
<Window.Resources>
<XmlDataProvider x:Key="SiteConfigFile" XPath="SiteConfig" >
</XmlDataProvider>
</Window.Resources>
I then enable a combobox to show each item in the Xml file via a drop down menu using:
<ComboBox Name="ButtonMapping" ItemsSource="{Binding Source={StaticResource SiteConfigFile}, XPath=Items/Item}" DisplayMemberPath="#name" SelectedIndex="0">
This all works fine.
The problem I want to solve now is... depending on which item from the combo box is selected, the corresponding Position element with its 2 attributes need to be shown in atextbox on the gui... do i i need to generate dynamic XPath, that seems a bit messy... what is the best way to do this, I'm out of ideas :(
How about wrapping the TextBox within a couple of panels? See example below. I used an outer panel (Border) whose DataContext is bound to the ComboBox.SelectedItem property. Then, another inner panel (StackPanel) is bound to the element in the XML. Finally within this inner panel, I placed the TextBox control whose Text is bound to #x and #y.
<Border DataContext="{Binding ElementName=ButtonMapping, Path=SelectedItem}">
<StackPanel DataContext="{Binding XPath=Position}">
<TextBlock Text="x:"/>
<TextBox Text="{Binding XPath=#x}"/>
<TextBlock Text="y:"/>
<TextBox Text="{Binding XPath=#y}"/>
</StackPanel>
</Border>
Note though that I used two TextBoxes to display the x and y attributes. If you want to use just one, you'll have to use an IValueConverter to correctly "format" the string you want to show.

WPF, XML databinding into dependent/cascading ComboBoxes

I have an XML file with the following structure:
<Products>
<Product name="MyProduct1">
<Components>
<Component name="MyComponent1">
<SubComponents>
<SubComponent name="MySubComponent1"/>
<SubComponent name="MySubComponent2"/>
...more SubComponent nodes...
</SubComponents>
</Component>
...more Component nodes...
</Components>
</Product>
...more Product nodes...
</Products>
I'm trying to create a WPF app that has a ComboBox with the Product names in it. I am completely new to WPF so I don't know if I'm going about things the right way. When a Product is chosen, a second ComboBox should be populated with all the Components for that Product. And when a Component is chosen, a third ComboBox should be populated with all the SubComponents for that Component.
I don't know how to set up a dependency between ComboBoxes except to populate the dependent ComboBox inside an event handler triggered by the independent ComboBox. This seems to imply I need to be able to read the XML in C#, so I have [Serializable] classes for Products, Product, Component, and SubComponent. However, I was trying to do XML databinding in my XAML:
<Window.Resources>
<XmlDataProvider Source="Products.xml"
XPath="Products/Product"
x:Key="productsXml"/>
</Window.Resources>
I'm currently not seeing a list of Product names in my first ComboBox, whose XAML is the following:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="138,116,0,0"
Name="cbo_product" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Source=productsXml, XPath=#name}"
SelectionChanged="product_SelectionChanged"/>
The Products XML should be read-only--the user won't be able to change any values in the XML from the app. I just want to read the XML data and display it in the app.
I have a few questions:
Am I going about this correctly? Having a standalone XML file from which my WPF app reads, having [Serializable] classes representing the nodes in the XML file for the purpose of extracting data from those nodes in C#, using an event handler to code dependencies between ComboBoxes, etc.
Why don't my product names, e.g., MyProduct1, show up in my ComboBox? Currently it just shows up empty.
It seems almost like having [Serializable] classes for representing my XML nodes is redundant/unnecessary since XAML already has the XmlDataProvider/XPath stuff. Is this the case?
Edit:
Updated my ComboBox XAML to the following and now I see my list of Product names in the ComboBox, thanks to decyclone's answer:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="138,116,0,0"
Name="cbo_product" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Source={StaticResource productsXml}}"
DisplayMemberPath="#name"
SelectionChanged="product_SelectionChanged"/>
The ItemsSource property should be set to a collection of items that are to be displayed in the list which is XmlDataProvider in your case. User StaticResource to locate it since it is defined as a resource. The DisplayMemberPath property should be used to select what property should be used to display text in combobox.
Regarding your 1st (& 3ed) question, I personally like create classes than using raw XML. It gives me few benefits like
Adding wrapper properties. For example, FullName = FirstName + " " + LastName property.
I dont have to check for empty values and type safety everytime I want to access the values (which are always strings).
I can add my own behaviors as methods which can be really useful for doing little tasks.
Control the serialization method and inject custom logic in it using attributes.
The point is, is it worth it? Do you really care for these benefits? It's just like choosing between DataReader and DataSet. For readonly and displayonly purpose, use raw XML and if you are going to play with it a lot, use classes.
Okay, since I found an answer to my more specific question, I think I know the answer to this question, too. I don't need [Serializable] classes for the different nodes in my XML, because I can just use XAML and XPath to create cascading/dependent ComboBoxes:
<!-- Independent -->
<ComboBox Height="23" HorizontalAlignment="Left" Margin="138,116,0,0"
Name="cbo_product" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Source={StaticResource productsXml}}"
DisplayMemberPath="#name"/>
<!-- Dependent -->
<ComboBox Height="23" HorizontalAlignment="Left" Margin="138,151,0,0"
Name="cbo_component" VerticalAlignment="Top" Width="201"
DataContext="{Binding ElementName=cbo_product, Path=SelectedItem}"
ItemsSource="{Binding XPath=Components/Component}"
DisplayMemberPath="#name"/>

Categories