mgphall
mgphall

Reputation: 87

Passing objects to UserControl

I'm trying to create a CustomControl to which I can pass an "Person" object. How can I pass the person object to the custom control like this PersonDetails="{Binding Path=Person}" to the CusomControl ?

instead of passing Address="{Binding Path=Person.Address}"Address="{Binding Path=Person.FirstName}" ect

Upvotes: 3

Views: 2805

Answers (2)

Mark Davich
Mark Davich

Reputation: 626

There are several requirements to pass an object to a UserControl

  1. UserControl.xaml.cs
    • DependencyProperty
    • Binding Property
      • DependencyObject Getter
      • DependencyObject Setter
    • InitializeComponent() in constructor
  2. UserControl.xaml
    • DataContext attribute in the <UserControl DataContext="..." tag
  3. MainWindow.xaml.cs
    • Public property for getting and setting the data
    • InitializeComponent() in constructor
  4. MainWindow.xaml
    • Name attribute in the <Window Name="..." tag
    • Namespace attribute in the <Window xmlns:... tag for the
      UserControl that will be consumed
    • Bind using ElementName

Example

In this example, ColorList is the UserControl that will be consumed by the MainWindow. The ColorInfo model is used for demonstrating data binding with an object.

Project Structure
Visusal Studio Project
GitHub Repository

ColorInfo Model

using System.Windows.Media;

namespace WpfProject.Models;

public class ColorInfo
{
    public string Name { get; set; } = string.Empty;
    public SolidColorBrush Value { get; set; } = default!;
}

ColorList.xaml.cs

using System.Windows;
using System.Windows.Controls;
using WpfProject.Models;

namespace WpfProject.UserControls;

public partial class ColorList : UserControl
{
    // ✶ Binding Dependency Property
    public static readonly DependencyProperty ColorsProperty =
        DependencyProperty.Register(
            name: nameof(Colors),                         // The "Name" of the property to register
            propertyType: typeof(IEnumerable<ColorInfo>), // The "Type" of the property to register
            ownerType: typeof(ColorList),                 // The "Type" of object that owns the property to register
            typeMetadata: new FrameworkPropertyMetadata(
                defaultValue: Enumerable.Empty<ColorInfo>(),
                flags: FrameworkPropertyMetadataOptions.None
            )
        );

    // ✶ Binding Property
    public IEnumerable<ColorInfo> Colors
    {
        get => (IEnumerable<ColorInfo>)GetValue(ColorsProperty); // ✶ Binding Getter 
        set => SetValue(ColorsProperty, value); // ✶ Binding Setter
    }

    public ColorList()
    {
        InitializeComponent();
    }
}

ColorList.xaml

<UserControl
    x:Class="WpfProject.UserControls.ColorList"
    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:WpfProject"
    mc:Ignorable="d" 
    d:DesignHeight="450" d:DesignWidth="800"
    
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
    <ListBox Name="ColorsListBox" ItemsSource="{Binding Path=Colors}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                    <Rectangle Width="23" Height="23">
                        <Rectangle.Fill>
                            <SolidColorBrush Color="{Binding Path=Value.Color}" />
                        </Rectangle.Fill>
                    </Rectangle>

                    <TextBlock 
                        Text="{Binding Path=Value}" 
                        FontFamily="Consolas" 
                        FontSize="16"
                        VerticalAlignment="Center"
                        Margin="5 0 0 0" />

                    <TextBlock 
                        Text="{Binding Path=Name}" 
                        FontFamily="Consolas" 
                        FontSize="16"
                        FontWeight="Bold"
                        VerticalAlignment="Center"
                        Margin="5 0 0 0" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</UserControl>

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Media;
using WpfProject.Models;

namespace WpfProject;

public partial class MainWindow : Window
{
    public IEnumerable<ColorInfo> Colors => typeof(Brushes)
        .GetProperties()
        .Where(info => info.PropertyType == typeof(SolidColorBrush))
        .Select(info => 
            new ColorInfo()
            {
                Name = info.Name,
                Value = (SolidColorBrush)info.GetValue(info, null)!
            }
        ).ToList()! ?? Enumerable.Empty<ColorInfo>();

    public MainWindow()
    {
        InitializeComponent();
    }
}

MainWindow.xaml

<Window
    Name="Main"
    x:Class="WpfProject.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:WpfProject"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800"
    
    xmlns:UserControls="clr-namespace:WpfProject.UserControls"
>
    <Grid>
        <UserControls:ColorList Colors="{Binding ElementName=Main, Path=Colors}" />
    </Grid>
</Window>

Key Points (9 Things to Remember)

📃 ColorList.xaml.cs
✅ public static readonly DependencyProperty // For registering Colors
✅ public IEnumerable<ColorInfo> Colors // Implements DependencyObject GetValue and SetValue
✅ InitializeComponent(); // In the constructor

📃 ColorList.xaml
// Set the "Data Context" of the "User Control" to itself (The code behind)
// This is the equivalent of calling DataContext = this; in the constructor
// in the code behind
✅ <UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}" ...

📃 MainWindow.xaml.cs
✅ public IEnumerable<ColorInfo> Colors // This is the property that is "passed" to the user control
✅ InitializeComponent(); // In the constructor

📃 MainWindow.xaml
✅ <Window Name="Main" ... // This is used as the ElementName in the data binding
✅ <Window xmlns:UserControls="clr-namespace:WpfProject.UserControls" ... // Registers the user control in the xaml
✅ <UserControls:ColorList Colors="{Binding ElementName=Main, Path=Colors}" /> // Use "ElementName" for binding

Upvotes: 0

Matěj Z&#225;bsk&#253;
Matěj Z&#225;bsk&#253;

Reputation: 17272

You have to declare PersonDetails as a dependency property.

Then you can use it as any other WPF control property - both in XAML (including data binding) and in code.

Upvotes: 1

Related Questions