Search code examples
wpflistviewcomboboxbindinguser-controls

WPF Bindings of a Custom User Control with a ComboBox containing a DataTemplate


I am trying to create a Custom User Control with a ListView in it that contains a Data Template looking like this:

        <ComboBox  ItemsSource="{Binding ItemsSource, ElementName=root}" 
               SelectedItem="{Binding SelectedItem, ElementName=root, Mode=TwoWay}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">                        
                    
                    <!-- This is what I tried:
                    
Working but not what I want                 <TextBox Text="{Binding Name}"/>    
Returns the List only the word "FallBack"   <TextBox Text="{Binding ItemText, ElementName=root}" />
Returns the LIst empty                      <TextBox Text="{Binding ItemText}" />
                    -->
                    
                </StackPanel>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>

In the code behind I have created the necessary dependency properties for this case (so I assume) the only relevant one is regarding the Item Text and it looks like that:

    #region ItemText

    public string ItemText
    {
        get { return (string)GetValue(ItemTextProperty); }
        set { SetValue(ItemTextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for ItemText.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ItemTextProperty =
        DependencyProperty.Register("ItemText", typeof(string), typeof(CardComboBox), new PropertyMetadata("FallBack"));

    #endregion

What I would like to do is to add the User Control like that

  <local:CardComboBox ItemsSource="{Binding Persons}" SelectedItem="{Binding SelectedPerson}" ItemText="{Binding Name}" IsEnabled="True" />

Option one:

<TextBox Text="{Binding Name}"/>

works fine because the PropertyName of the Class Person is Name, but I obviously do not want to HardCode it. I want to bind it to whichever property I like.

Option 2:

<TextBox Text="{Binding ItemText, ElementName=root}" />

Gives me the list of 7 items (as per list) but just displays the word "Fallback" because of the DependencyPropertyMetadata.

Option 3:

<TextBox Text="{Binding ItemText}" />

Gives me the list but with no text at all.

I also tried to work with relativeSource but only with similar results.

   #region Person

    Person _selectedPerson;
    public Person SelectedPerson
    {
        get => _selectedPerson;
        set
        {
            if (value != _selectedPerson)
            {
                _selectedPerson = value;
                OnPropertyChanged("SelectedPerson");

            }
        }
    }


    ObservableCollection<Person> _persons;
    public ObservableCollection<Person> Persons
    {
        get => _persons;
        set
        {
            if (value != _persons)
            {
                _persons = value;
                OnPropertyChanged("Persons");

            }
        }
    }

    public void populatePersons()
    {
        Persons = new ObservableCollection<Person>();  
        Persons.Add(new Person("Carl"));
        Persons.Add(new Person("Max"));
        Persons.Add(new Person("May"));
        Persons.Add(new Person("Jen"));
        Persons.Add(new Person("Charly"));
        Persons.Add(new Person("Nora"));
        Persons.Add(new Person("Yvonne"));
    }

    #endregion

I have added the List I am binding to. The Method Populate Persons is called in the constructor of the ViewModel.


Solution

  • The closest solution working so far is to expose the ItemContentTemplate Dependency Property and then to bind it to a static resource (e.g. from App.xaml)

    The XAML of the UserControl looks like this:

         <StackPanel Style="{StaticResource CardStackPanel}" Orientation="{Binding Orientation, ElementName=root}" >
            <Label  x:Name="Label" Content="{Binding TitleText, ElementName=root}"/>
            <ComboBox ItemsSource ="{Binding ItemsSource,           RelativeSource={RelativeSource AncestorType=UserControl}}" 
                      ItemTemplate="{Binding ItemContentTemplate,   RelativeSource={RelativeSource AncestorType=UserControl}}" 
                      SelectedItem="{Binding SelectedItem,          RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}" />
     </StackPanel>
    

    the Code Behind for the ComboBox Part:

            public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CardComboBox));
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }
    
    
        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(CardComboBox));
        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }
    
        public DataTemplate ItemContentTemplate
        {
            get { return (DataTemplate)GetValue(ItemContentTemplateProperty); }
            set { SetValue(ItemContentTemplateProperty, value); }
        }
        public static readonly DependencyProperty ItemContentTemplateProperty =
            DependencyProperty.Register("ItemContentTemplate", typeof(DataTemplate), typeof(CardComboBox));
    

    The Static Ressource in App.xaml (example):

        <DataTemplate x:Key="FirstNamesTemplate">
            <Label Content="{Binding FirstName}"/>
        </DataTemplate>
    

    The Implementation of a ComboBoxCard looks now like this:

    <local:CardComboBox ItemsSource="{Binding PersonModels}" SelectedItem="{Binding SelectedPersonModel, Mode=TwoWay}" ItemContentTemplate="{StaticResource FirstNamesTemplate}" TitleText="With StaticResource" IsEnabled="False"/>
    

    This pattern allows to implement ComboBoxes or ListViews in Custom UserControls .