Search code examples
wpftemplatesbindinguser-controlsdependency-properties

Bind ItemsControl ItemSource to UserControl Dependency Property


this is my very first shot at trying to create a User Control with Dependency Properties. So excuse my lack of knowledge over the subject. I created a general design on one of my pages that I would like to bring over to a reusable user control.

Original Control On Page

So this is the control I am trying to port into a reusable UserControl

<ToggleButton x:Name="filterButton" Background="{StaticResource BackgroundLightBrush}" BorderThickness="0">
    <fa:ImageAwesome x:Name="border"
                     Height="15"
                     Foreground="{StaticResource MediumBlueBrush}"
                     Icon="Filter"/>
</ToggleButton>

<Popup x:Name="popup" 
       AllowsTransparency="True"
       StaysOpen="False"
       PlacementTarget="{Binding ElementName=filterButton}"
       IsOpen="{Binding ElementName=filterButton,Path=IsChecked,Mode=TwoWay}">
    <Border BorderThickness="2" BorderBrush="{StaticResource MediumBlueBrush}" Background="{StaticResource BackgroundLightBrush}" CornerRadius="5">
        <ItemsControl ItemsSource="{Binding HeaderList}"
                      Background="{StaticResource BackgroundLightBrush}"
                      Margin="1">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" Margin="5">
                        <CheckBox IsChecked="{Binding IsChecked}"/>
                        <TextBlock Text="{Binding HeaderName}"/>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Border>
</Popup>

Here is a picture of what this control looks like

enter image description here

This is my Dependency Property Code

Here you can see I created an ObservableCollection of type RulesColumnHeader. This is the item source I am trying to set my UserControl to.

public partial class FilterDropDown : UserControl
{
    public ObservableCollection<RulesColumnHeader> ItemSource
    {
        get => (ObservableCollection<RulesColumnHeader>)GetValue(ItemSourceProperty);
        set => SetValue(ItemSourceProperty, value);
    }
    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ItemSourceProperty =
        DependencyProperty.Register("ItemSource", typeof(ObservableCollection<RulesColumnHeader>), typeof(FilterDropDown), new FrameworkPropertyMetadata(null));

    public FilterDropDown()
    {
        InitializeComponent();
    }
}

UserControl

Here is my "attempt" at creating a user control and binding the item source to the dependency property I created.

<UserControl x:Class="YAI.BomConfigurator.Desktop.Control.FilterDropDown"
         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:YAI.BomConfigurator.Desktop.Control"
         xmlns:fa="http://schemas.fontawesome.io/icons/"
         mc:Ignorable="d" 
         d:DesignHeight="50" d:DesignWidth="50">
<Grid>
    <ToggleButton x:Name="filterButton" Background="Transparent" BorderThickness="0">
        <fa:ImageAwesome x:Name="border"
                         Height="15"
                         Foreground="{StaticResource MediumBlueBrush}"
                         Icon="Filter"/>
    </ToggleButton>

    <Popup x:Name="popup" 
           AllowsTransparency="True"
           StaysOpen="False"
           PlacementTarget="{Binding ElementName=filterButton}"
           IsOpen="{Binding ElementName=filterButton,Path=IsChecked,Mode=TwoWay}">

        <Border BorderThickness="2" BorderBrush="{StaticResource MediumBlueBrush}" Background="{StaticResource BackgroundLightBrush}" CornerRadius="5">
            <ItemsControl ItemsSource="{Binding ItemSource, Source={local:FilterDropDown}}"
                          Background="{StaticResource BackgroundLightBrush}"
                          Margin="1">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal" Margin="5">
                            <CheckBox IsChecked="{Binding IsChecked}"/>
                            <TextBlock Text="{Binding HeaderName}"/>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Border>
    </Popup>
</Grid>

On my ItemSource binding on ItemsControl for the, the source= portion throws an error saying "Invalid Markup Extension, expected type is 'object' actual is FilterDropDown".

So this is basically where I am at now. I am not sure how to move forward or what to do from here. I am trying to figure out how to bind the UserControl item source to the dependency property. I don't know if its my syntax or I am doing this whole thing incorrectly. If anyone could help guide me along that would be great.

Thank you,


Solution

  • Setting the Source property of the ItemsSource Binding is wrong.

    Replace

    ItemsSource="{Binding ItemSource, Source={local:FilterDropDown}}"
    

    with

    ItemsSource="{Binding ItemSource,
                          RelativeSource={RelativeSource AncestorType=UserControl}}"
    

    Besides that, it is unnecessary or even wrong to declare the ItemsSource as an ObservableCollection. Use a more general type like IEnumerable:

    public IEnumerable ItemSource
    {
        get => (IEnumerable)GetValue(ItemSourceProperty);
        set => SetValue(ItemSourceProperty, value);
    }
    
    public static readonly DependencyProperty ItemSourceProperty =
        DependencyProperty.Register(
            nameof(ItemSource), typeof(IEnumerable), typeof(FilterDropDown));