0

I'm trying to populate an ItemsControl with some UserControls, but I was not capable of finding which ancestor should match to the items in the data template.

I've already tried to bind with x:Name property and the results were the same. I also tried with and without RelativeSource on binding and I didn't have any success with that either.

User Control XAML

<UserControl x:Class="FireAntTempConfigurer.UserControls.CommandButton_UC"
             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:FireAntTempConfigurer.UserControls"
             xmlns:materialDesign="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
             mc:Ignorable="d" 
             TextElement.Foreground="{DynamicResource MaterialDesignPaper}"
             d:DesignHeight="50 " d:DesignWidth="50"
             >
    <Grid>
        <Button Height="50" Padding="0,0,0,0" ToolTip="{Binding Path=Placeholder, RelativeSource={RelativeSource AncestorType=UserControl}}">
            <Button.Content>
                <materialDesign:PackIcon Kind="{Binding Path=Icon, RelativeSource={RelativeSource AncestorType=UserControl}}" Height="50" Width="50" />
            </Button.Content>
        </Button>
    </Grid>
</UserControl>

User Control Code Behind

    /// <summary>
    /// Interaction logic for CommandButton_UC.xaml
    /// </summary>
    public partial class CommandButton_UC : UserControl
    {

        public CommandButton_UC()
        {
            this.DataContext = this;
            InitializeComponent();
        }

        public string Icon
        {
            get { return (string)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Icon.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IconProperty =
            DependencyProperty.Register("Icon", typeof(string), typeof(CommandButton_UC), new PropertyMetadata("EmoticonDead"));

        public string Placeholder
        {
            get { return (string)GetValue(PlaceholderProperty); }
            set { SetValue(PlaceholderProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Placeholder.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PlaceholderProperty =
            DependencyProperty.Register("Placeholder", typeof(string), typeof(CommandButton_UC), new PropertyMetadata("fail to load"));

    }

Main window XAML

<Window x:Class="FireAntTempConfigurer.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:FireAntTempConfigurer"
        xmlns:materialDesign="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf"
        xmlns:uc="clr-namespace:FireAntTempConfigurer.UserControls"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        TextElement.Foreground="{DynamicResource SecondaryAccentBrush}"
        Background="{DynamicResource MaterialDesignPaper}"
        TextElement.FontWeight="Medium"
        TextElement.FontSize="20"
        FontFamily="pack://application:,,,/MaterialDesignThemes.Wpf;component/Resources/Roboto/#Roboto"
        x:Name="mw">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="95*"/>
            <ColumnDefinition Width="169*"/>
            <ColumnDefinition Width="95*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="150"/>
            <RowDefinition Height="50"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <materialDesign:Card Grid.Column="1" Grid.Row="1">
            <StackPanel Margin="0">
                <TextBlock>ToolBox:</TextBlock>
                <Separator Margin="0"></Separator>
                <ItemsControl ItemsSource="{Binding Path=MyPropertyList, RelativeSource={RelativeSource AncestorType=Window}}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel  Orientation="Horizontal" Height="50" HorizontalAlignment="Left" Margin="5,0,0,0" IsItemsHost="True"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate >
                        <DataTemplate>
                            <uc:CommandButton_UC Padding="0,0,0,0" Margin="0,0,5,0" 
                                                 Icon="{Binding Path=IconProp, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                                 Placeholder="{Binding Path=PlaceHolderProp , RelativeSource={RelativeSource AncestorType=ItemsControl}}">
                            </uc:CommandButton_UC>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
.
.
.

Main Window Code Behind

public partial class MainWindow : Window
{
    public CommandButtonViewModel MyProperty { get; set; }
    public BindingList<CommandButtonViewModel> MyPropertyList { get; set; }
    public MainWindow()
    {
        MyPropertyList = new BindingList<CommandButtonViewModel>();
        MyProperty = new CommandButtonViewModel { IconProp = "PlusCircleOutline", PlaceHolderProp = "Add" };
        BindingList<CommandButtonViewModel> tempList = new BindingList<CommandButtonViewModel>();
        tempList.Add(MyProperty);
        tempList.Add(MyProperty);
        MyPropertyList = tempList;
        DataContext = this;
        InitializeComponent();
    }

Now I'm receiving the message at the output:

System.Windows.Data Error: 40 : BindingExpression path error: 'IconProp' property not found on 'object' ''CommandButton_UC' (Name='')'. BindingExpression:Path=IconProp; DataItem='CommandButton_UC' (Name=''); target element is 'CommandButton_UC' (Name=''); target property is 'Icon' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'IconProp' property not found on 'object' ''CommandButton_UC' (Name='')'. BindingExpression:Path=IconProp; DataItem='CommandButton_UC' (Name=''); target element is 'CommandButton_UC' (Name=''); target property is 'Icon' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'PlaceHolderProp' property not found on 'object' ''ItemsControl' (Name='')'. BindingExpression:Path=PlaceHolderProp; DataItem='ItemsControl' (Name=''); target element is 'CommandButton_UC' (Name=''); target property is 'Placeholder' (type 'String')
System.Windows.Data Error: 40 : BindingExpression path error: 'PlaceHolderProp' property not found on 'object' ''ItemsControl' (Name='')'. BindingExpression:Path=PlaceHolderProp; DataItem='ItemsControl' (Name=''); target element is 'CommandButton_UC' (Name=''); target property is 'Placeholder' (type 'String')

Would appreciate some help

Stewbob
  • 16,759
  • 9
  • 63
  • 107
  • 1
    Please read all the comments on your previous question again. In short, your must not set `DataContext = this;` in a UserControl. If you do that, you are getting problems with bindings the control's properties to those of a view model in the current (inherited) DataContext, In the DataTemplate of an ItemsControl, the current DataContext is the current item, i.e. the current element from the ItemsSource collection. So, do not set the DataContext and bind the UserControl's properties like `Icon="{Binding Path=IconProp}"`, without RelativeSource. – Clemens Apr 12 '19 at 05:34
  • Remove *all* RelativeSources from the Bindings in the MainWindow's XAML, since you have correctly set the window's DataContext. Besides that, the binding error messages you are showing don't seem to match your actual code. They look like you already had e.g. `Icon="{Binding Path=IconProp}"` without RelativeSource, but also had the UserControl's DataContext set to `this`. – Clemens Apr 12 '19 at 05:40

1 Answers1

1

I'm unable to replicate completely your code because I've not materialdesignlibrary however I think I can reproduce your situation replacing those reference with normal wpf controls. In my opinion you have to perform these steps:

  1. Check that CommandButtonViewModel is really a viewmodel so it has to implement INotifyPropertyChanged something like this:

    public class CommandButtonViewModel : INotifyPropertyChanged  
    {   
        private string _iconprop;
        public string IconProp
        {
            get
            {
                return _iconprop;
            }
            set
            {
                _iconprop = value;
                NotifyPropertyChanged("IconProp");
            }
        }
    
    private string _placeholderprop;
    public string PlaceHolderProp
    {
        get
        {
            return _placeholderprop;
        }
        set
        {
            _placeholderprop = value;
            NotifyPropertyChanged("PlaceHolderProp");
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    }

    1. your DataContext is only the Main Window so remove the set of DataContext from the creator of CommandButton_UC

      public partial class CommandButton_UC : UserControl
      {
          public CommandButton_UC()
          {
              InitializeComponent();
          }
      }
      
    2. in your binding you can bind directly to the CommandButtonViewModel properties

    so you have to modify CommandButton_UC in this way:

    <ItemsControl 
      ItemsSource="{Binding Path=MyPropertyList, 
                    RelativeSource {RelativeSource AncestorType=Window}}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel  
                                 Orientation="Horizontal" 
                                 Height="50" 
                                 HorizontalAlignment="Left" 
                                 Margin="5,0,0,0" IsItemsHost="True"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate >
                            <DataTemplate>
                                <uc:CommandButton_UC 
                                    Padding="0,0,0,0" Margin="0,0,5,0" 
                                    Icon="{Binding Path=IconProp}"
                                    Placeholder="{Binding Path=PlaceHolderProp}">
                                </uc:CommandButton_UC>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
    
Alessandro Ciurlo
  • 102
  • 1
  • 2
  • 11