There are lots of ways you could do this. But as hinted at by this comment:
There is a bigger question: Why would you want to bind them in code? Usually trying soemthing like this indicates you are on the wrong track. WPF is designed with the MVVM pattern in mind and if you use it, you will never need to do this.
…you should go ahead and use regular binding. WPF makes this relatively simple, and it can be implemented entirely in XAML:
<Window x:Class="TestSO57299808PropertyBrowser.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:properties="clr-namespace:TestSO57299808PropertyBrowser.Properties"
xmlns:configuration="clr-namespace:System.Configuration;assembly=System"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<ListBox ItemsSource="{Binding PropertyValues, Source={x:Static properties:Settings.Default}}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type configuration:SettingsPropertyValue}">
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Padding="5,0" Grid.Column="0"/>
<TextBlock Text="{Binding PropertyValue}" Padding="5,0" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
The above puts a ListBox
into the window, sets its ItemsSource
property to reference the Settings.Default.PropertyValues
collection, and declares a DataTemplate
to use to display the property name and value for each SettingsPropertyValue
object contained in that collection.
Actually, I lied. There is one line of code-behind required, because the .NET Settings
object has a little bug. The PropertyValues
collection will be empty until the first time any property value is retrieved. So you need to make sure to retrieve a property value during initialization (any property will do). For example, in the window constructor:
public MainWindow()
{
var _ = Properties.Settings.Default.Property1;
InitializeComponent();
}
See Why does Settings PropertyValues have 0 items? for some discussion of that bug.
Keep in mind that whether you copy values in code-behind or use binding as above, you will need to handle your own updates if/when a property values changes, because the collection itself doesn't implement INotifyCollectionChanged
and so WPF has no way to know if a property value has updated. You can use BindingOperations
and BindingExpression
to force the collection binding to update, if you use data binding as shown here.
However, doing so would be a bit of a kludge, since you'd be telling WPF to update the entire collection every time just one property changed. If you expect the property values to be changing and want that reflected in the UI, it would be better to implement a proxy that can provide a proper collection of property name/value pairs along with property-change notification. For example:
class ViewModel
{
public IReadOnlyList<SettingsPropertyValueProxy> Values { get; } = Array.AsReadOnly(
Properties.Settings.Default.Properties
.Cast<SettingsProperty>()
.Select(p => new SettingsPropertyValueProxy(p.Name))
.OrderBy(p => p.Name)
.ToArray());
}
class SettingsPropertyValueProxy : INotifyPropertyChanged
{
public string Name { get; }
public object PropertyValue => Properties.Settings.Default[Name];
public SettingsPropertyValueProxy(string name)
{
Name = name;
Properties.Settings.Default.PropertyChanged += (sender, e) => _OnPropertyChanged(e.PropertyName);
}
private void _OnPropertyChanged(string propertyName)
{
if (propertyName == Name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PropertyValue)));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then in the XAML, bind to the view model instead of the settings object directly:
<Window x:Class="TestSO57299808PropertyBrowser.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:l="clr-namespace:TestSO57299808PropertyBrowser"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:ViewModel/>
</Window.DataContext>
<ListBox ItemsSource="{Binding Values}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type l:SettingsPropertyValueProxy}">
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Padding="5,0" Grid.Column="0"/>
<TextBlock Text="{Binding PropertyValue}" Padding="5,0" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
The only difference above from the earlier example is that the data context is set to an instance of the view model, and then the view model's Values
property is used for the ItemsSource
instead of the Settings.Default.PropertyValues
property.
Note that in this approach, there's no need for the little hack to trigger population of the PropertyValues
collection, as this version doesn't rely on that collection at all.
This is a far better approach than writing code-behind to create UI objects and manually implement their bindings. Doing this in XAML, with just the barest of code-behind plumbing to wrap the settings in a WPF-friendly way, ensures proper separation of UI from the underlying data, and makes it far easier to make changes to the UI itself as desired.
Finally, if this is more than just an academic exercise, note that there are already existing solutions to display the properties of an object. See e.g. wpf propertyGrid and Analogous WinForms Propertygrid in WPF?