-1

I'm trying to pass an ObservableCollection<object> to a UserControl through XAML bindings. My MainWindow class generates a random list of Student. The list is correctly generated then the components are initialized. In my XAML File I bind the students but the UserControl class does not seem to have any data.

MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        private ObservableCollection<object> _Students;
        public ObservableCollection<object> Students { get => GetStudents(); }

        public MainWindow()
        {
            GenerateStudentList();

            Console.WriteLine("MainWindow: Students = {0}", Students.Count);
            InitializeComponent();
        }

        private void GenerateStudentList()
        {
            for (int i = 0; i < 20; i++)
            {
                var s = Student.GenRandomStudent();
                Console.WriteLine("Student: {0} - {1} {2}, {3}", s.StudentId, s.FirstName, s.LastName, s.Age);
                Students.Add(s);
            }
        }

        private ObservableCollection<object> GetStudents()
        {
            if (_Students == null)
                _Students = new ObservableCollection<object>();

            return _Students;
        }
    }

MainWindow.xaml

<Window x:Class="StudentApp.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:localCtr="clr-namespace:StudentApp.Controls"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <DockPanel>
            <Menu VerticalAlignment="Top" DockPanel.Dock="Top">
                <MenuItem Header="_File">
                    <MenuItem Header="_Abot"/>
                    <Separator/>
                    <MenuItem Header="_Exit"/>
                </MenuItem>
            </Menu>

            <StatusBar DockPanel.Dock="Bottom" Height="auto">
                <StatusBarItem Content="Status Bar"/>
            </StatusBar>
            
            <Grid DockPanel.Dock="Left" MinWidth="250">
                <localCtr:StudentListBox Students="{Binding Students}" ContentStringFormat="{}{0} Students"/>
            </Grid>

            <Grid DockPanel.Dock="Right">
                <Label Content="{Binding Students.Count}"/>
            </Grid>

        </DockPanel>
    </Grid>
</Window>

StudentListBox.xaml.cs

public partial class StudentListBox : UserControl
    {
        public ObservableCollection<Object> Students
        {
            get { return (ObservableCollection<Object>)GetValue(StudentsProperty) ; }
            set { SetValue(StudentsProperty, value); }
        }

        // Generated with 'propdp'
        public static readonly DependencyProperty StudentsProperty =
            DependencyProperty.Register(
                "Students",
                typeof(ObservableCollection<Object>),
                typeof(StudentListBox),
                new PropertyMetadata(new ObservableCollection<Object>())
            );

        /// 

        public StudentListBox()
        {
            Console.WriteLine("StudentListBox: Students = {0}", Students.Count);
            InitializeComponent();
        }
    }

StudentListBox.xaml

<UserControl x:Class="StudentApp.Controls.StudentListBox"
             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:StudentApp.Controls"
             mc:Ignorable="d"
             Name="StudentListBoxControl">
    <Grid>
        <Label Content="{Binding Students.Count, ElementName=StudentListBoxControl}"
               ContentStringFormat="{}{0} Students"/>
    </Grid>
</UserControl>
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
Tristian
  • 65
  • 1
  • 6
  • [`TemplateBinding`](https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/templatebinding-markup-extension) is solution (inside of `ControlTemplate`). [Useful link](https://stackoverflow.com/questions/1131222/wpf-templatebinding-vs-relativesource-templatedparent) – aepot Jul 08 '20 at 21:26
  • I think you need to make it a lookless control to use `ControlTemplate`, but it will make life easier. – Michael Jul 08 '20 at 21:29
  • You basically creating a double binding for the same dependency property. The way I have done this before was to change the source and target for the binding. You will need to create the binding in `StudentListBox.xaml.cs` by code. I can't remember exactly, but when creating a binding by in code you can change which way the binding goes (source and target). Try out which will work. – Michael Jul 08 '20 at 21:33
  • I find UserControls usefull when you want to group things and I typical have one UserControl one ViewModel, but as soon as you want to give it data I refer to a [lookless control](https://www.codeproject.com/Articles/1056014/WPF-Lookless-Controls). Every builtin control in WPF is made as a lookless control. – Michael Jul 08 '20 at 21:37

1 Answers1

0

I Fixed my issue! I forgot to use DataContext:

        public MainWindow()
        {
            GenerateStudentList();
            this.DataContext = this; // <---- Right here!

            InitializeComponent();
        }
Tristian
  • 65
  • 1
  • 6