The following code works by having the combobox populated by the C# code behind at run-time. I would like to populate the user control combos declaratively from XAML, similar to how the description label is populated. This code is just for my personal use, so I won't be using complex MVVM models. I'd ideally like a solution that passes string arrays from the main XAMLcode, to the user control as I'd like to know how to do that. Thanks in advance :-)
//Main Window.xaml
<Window
x:Class="WpfApp1.MainWindow"
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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="Car Details"
SizeToContent="WidthAndHeight">
<StackPanel>
<local:myControl
x:Name="myMake"
myDescription="Make" />
<local:myControl
x:Name="myModel"
myDescription="Model" />
<local:myControl
x:Name="myYear"
myDescription="Year" />
</StackPanel>
</Window>
//MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//myMake is the name of a specific myControl
//myOption is the name of the combo within myControl
myMake.myOptions.ItemsSource = new string[] { "Ford", "Toyota" };
}
}
}
//myControl.xaml
<UserControl
x:Class="WpfApp1.myControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d">
<StackPanel>
<Label
Content="{Binding myDescription,FallbackValue=description}"
/>
<ComboBox
x:Name="myOptions"
Width="120" />
</StackPanel>
</UserControl>
//myControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace WpfApp1
{
public partial class myControl : UserControl
{
public myControl()
{
InitializeComponent();
this.DataContext = this;
}
public string myDescription
{
get { return (string)GetValue(myDescriptionProperty); }
set { SetValue(myDescriptionProperty, value); }
}
public static readonly DependencyProperty myDescriptionProperty = DependencyProperty.Register("myDescription", typeof(string), typeof(myControl), new PropertyMetadata(null));
}
}
You can declare an array of string in xaml like this:
<x:Array xmlns:s="clr-namespace:System;assembly=mscorlib" x:Key="myStringArray" Type="{x:Type s:String}">
<s:String>Ford</s:String>
<s:String>Toyota</s:String>
</x:Array>
Source: this answer
So your view becomes:
<Window x:Class="WpfApp1.MainWindow"
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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="Car Details" SizeToContent="WidthAndHeight">
<Window.Resources>
<!--Declaration of an array of string which is added to the ressources of the window-->
<x:Array xmlns:s="clr-namespace:System;assembly=mscorlib" x:Key="myStringArray" Type="{x:Type s:String}">
<s:String>Ford</s:String>
<s:String>Toyota</s:String>
</x:Array>
</Window.Resources>
<StackPanel>
<!--We get the array from the ressource by calling "StaticResource" with the key of the element to obtain-->
<local:myControl
x:Name="myMake" myDescription="Make" ArrayItemsSource="{StaticResource myStringArray}" />
<!--Do the same for the other usercontrol -->
<local:myControl
x:Name="myModel"
myDescription="Model" />
<local:myControl
x:Name="myYear"
myDescription="Year" />
</StackPanel>
In your usercontrol, you should not use this.DataContext = this;
as this is bad practice since it prevents your usercontrol from inheriting the datacontext of the view (I know you are not planning on using MVVM but I think this should still be mentioned for other readers).
So because the datacontext is not the control, the bindings in the xaml of the usercontrol have to be modified like this: {Binding ElementName=myControlName, Path=theNameOfThePropertyIwantToBind}
I created a new dependency property to pass the array of string from the view so the usercontrol becomes: Xaml:
<UserControl x:Class="WpfApp1.myControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1" x:Name="myControlName"
mc:Ignorable="d">
<StackPanel>
<Label
Content="{Binding ElementName=myControlName, Path=myDescription,FallbackValue=description}"
/>
<ComboBox
x:Name="myOptions" ItemsSource="{Binding ElementName=myControlName, Path=ArrayItemsSource}"
Width="120" />
</StackPanel>
The code behind:
public partial class myControl : UserControl
{
public myControl()
{
InitializeComponent();
//Bad practice
//this.DataContext = this;
}
public string myDescription
{
get { return (string)GetValue(myDescriptionProperty); }
set { SetValue(myDescriptionProperty, value); }
}
public string[] ArrayItemsSource
{
get { return (string[])GetValue(ArrayItemsSourceProperty); }
set { SetValue(ArrayItemsSourceProperty, value); }
}
public static readonly DependencyProperty myDescriptionProperty = DependencyProperty.Register("myDescription", typeof(string), typeof(myControl), new PropertyMetadata(null));
public static readonly DependencyProperty ArrayItemsSourceProperty = DependencyProperty.Register(nameof(ArrayItemsSource), typeof(string[]), typeof(myControl), new PropertyMetadata(null));
}