Creating and Binding UI objects in C# instead of XAML (WPF)
-
he application that I'm creating has a menu comprised of labels/headers. When you click on a menu item (a xaml Label) a drop down appears with subheaders related to that item. However, I need to make this menu customizable via XML. The idea is this, the XML outlines the header names as well as the subheader names for each menu item.
NestedHeader1 NestedHeader2 NestedHeader1 NestedHeader2
I am able to read the XML using XDocument and bind a Dependency Property to the label context. The problem is that I do not know how many menu items might be in the XML file. I wanted to avoid creating a ton of labels and dependency properties for the label content to bind to. Instead of creating labels in xaml and binding to a property in C#, can I create UI items in C# and populate them with elements from a list at run time? There's some code below of how I'm getting and binding the data to a preexisting label.
C#:
public string HeaderName { get => (string)GetValue(HeaderNameProperty); set => SetValue(HeaderNameProperty, value); } public static readonly DependencyProperty HeaderNameProperty = DependencyProperty.Register("HeaderName", typeof(string), typeof(MainWindow)); private void LoadHeaders() { var msg = new List(0); XDocument doc = null; try { doc = XDocument.Load("MenuHeaders.xml"); } catch (Exception e) { msg.Add(string.Format("Unable to open file:{0}", "")); msg.Add(e.Message); } if(doc != null) { var allHeaders = doc.Descendants("Header").ToList(); List headers = new List(); foreach(XNode node in allHeaders) { XElement element = node as XElement; foreach(XAttribute attribute in element.Attributes()) { headers.Add(attribute.Value); } } HeaderName = headers\[0\]; } }
XAML:
(As you may have n
-
he application that I'm creating has a menu comprised of labels/headers. When you click on a menu item (a xaml Label) a drop down appears with subheaders related to that item. However, I need to make this menu customizable via XML. The idea is this, the XML outlines the header names as well as the subheader names for each menu item.
NestedHeader1 NestedHeader2 NestedHeader1 NestedHeader2
I am able to read the XML using XDocument and bind a Dependency Property to the label context. The problem is that I do not know how many menu items might be in the XML file. I wanted to avoid creating a ton of labels and dependency properties for the label content to bind to. Instead of creating labels in xaml and binding to a property in C#, can I create UI items in C# and populate them with elements from a list at run time? There's some code below of how I'm getting and binding the data to a preexisting label.
C#:
public string HeaderName { get => (string)GetValue(HeaderNameProperty); set => SetValue(HeaderNameProperty, value); } public static readonly DependencyProperty HeaderNameProperty = DependencyProperty.Register("HeaderName", typeof(string), typeof(MainWindow)); private void LoadHeaders() { var msg = new List(0); XDocument doc = null; try { doc = XDocument.Load("MenuHeaders.xml"); } catch (Exception e) { msg.Add(string.Format("Unable to open file:{0}", "")); msg.Add(e.Message); } if(doc != null) { var allHeaders = doc.Descendants("Header").ToList(); List headers = new List(); foreach(XNode node in allHeaders) { XElement element = node as XElement; foreach(XAttribute attribute in element.Attributes()) { headers.Add(attribute.Value); } } HeaderName = headers\[0\]; } }
XAML:
(As you may have n
You need to put a secondary loop into the Header loop to get the children of the header. How do you intend to bind the actions to the menu item?
Never underestimate the power of human stupidity - RAH I'm old. I know stuff - JSOP
-
he application that I'm creating has a menu comprised of labels/headers. When you click on a menu item (a xaml Label) a drop down appears with subheaders related to that item. However, I need to make this menu customizable via XML. The idea is this, the XML outlines the header names as well as the subheader names for each menu item.
NestedHeader1 NestedHeader2 NestedHeader1 NestedHeader2
I am able to read the XML using XDocument and bind a Dependency Property to the label context. The problem is that I do not know how many menu items might be in the XML file. I wanted to avoid creating a ton of labels and dependency properties for the label content to bind to. Instead of creating labels in xaml and binding to a property in C#, can I create UI items in C# and populate them with elements from a list at run time? There's some code below of how I'm getting and binding the data to a preexisting label.
C#:
public string HeaderName { get => (string)GetValue(HeaderNameProperty); set => SetValue(HeaderNameProperty, value); } public static readonly DependencyProperty HeaderNameProperty = DependencyProperty.Register("HeaderName", typeof(string), typeof(MainWindow)); private void LoadHeaders() { var msg = new List(0); XDocument doc = null; try { doc = XDocument.Load("MenuHeaders.xml"); } catch (Exception e) { msg.Add(string.Format("Unable to open file:{0}", "")); msg.Add(e.Message); } if(doc != null) { var allHeaders = doc.Descendants("Header").ToList(); List headers = new List(); foreach(XNode node in allHeaders) { XElement element = node as XElement; foreach(XAttribute attribute in element.Attributes()) { headers.Add(attribute.Value); } } HeaderName = headers\[0\]; } }
XAML:
(As you may have n
The
XMLDataProvider
might be a simpler option: How to: Bind to XML Data Using an XMLDataProvider and XPath Queries - WPF | Microsoft Docs[^] If you want to stick with code, you're going to need a class to represent the menu item. For example:public class MenuHeaderItem
{
public string HeaderName { get; set; }
public IReadOnlyList<MenuHeaderItem> Children { get; set; }
}public static IReadOnlyList<MenuHeaderItem> LoadMenu(XDocument document)
{
var result = new List<MenuHeaderItem>();
var allHeaders = document.Descendants("Header");
foreach (XElement node in allHeaders)
{
result.Add(new MenuHeaderItem
{
HeaderName = (string)node.Attribute("Value"),
Children = node.Elements().Select(el => new MenuHeaderItem
{
HeaderName = (string)el,
}).ToList(),
});
}return result;
}
Bind the loaded values to a read-only dependency property:
private static readonly DependencyPropertyKey MenuHeadersKey = DependencyProperty.RegisterReadOnly(
"MenuHeaders",
typeof(IReadOnlyList<MenuHeaderItem>),
typeof(YourContainingClass),
new PropertyMetadata(null)
);public static readonly DependencyProperty MenuHeadersProperty = MenuHeadersKey.DependencyProperty;
public IReadOnlyList<MenuHeaderItem> MenuHeaders
{
get { return (IReadOnlyList<MenuHeaderItem>)GetValue(MenuHeadersProperty); }
private set { SetValue(MenuHeadersKey, value); }
}private void LoadHeaders()
{
if (File.Exists("MenuHeaders.xml"))
{
XDocument document = XDocument.Load("MenuHeaders.xml");
MenuHeaders = LoadMenu(document);
}
}Then bind the
MenuHeaders
collection to your menu.<Menu ItemsSource="{Binding Path=MenuHeaders}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding Path=HeaderName}" />
<Setter Property="ItemsSource" Value="{Binding Path=Children}" />