colosso
colosso

Reputation: 2525

WPF binding object to template

I have two classes defined which look like this:

public class Task {

    public string TaskName { get; set; }
    public string Description { get; set; }
    public string Priority { get; set; }
    public List<Job> listofjobs = new List<Job>();

}

public class Job
{
    public string ID { get; set; }
    public string Name { get; set; }

}

After generating the data I have several Task objects in a List:

public static List<Task> tasklist = new List<Task>();

Each of those tasks has several jobs in it. This list gets databound to a listbox. like this:

        Task newTask = new Task();
        Job newJob = new Job();
        Job newJob2 = new Job();

        newTask.Description = "Task 1";
        newJob.job_name = "Job 1";
        newJob.job_name = "Job 2";


        newTask.listofjobs.Add(newJob);
        newTask.listofjobs.Add(newJob2);
        tasklist.Add(newTask);
        joblist.ItemsSource = tasklist;

The XAML looks like this:

<ListBox Name="joblist" HorizontalContentAlignment="Stretch" >
      <ListBox.ItemTemplate>

                    <DataTemplate>
              <Expander Name="jobgroup" Header="{Binding Path=Description}" HorizontalAlignment="Stretch" Padding="5" FontSize="16" VerticalAlignment="Top">

                  <Expander.ContentTemplate>
                      <DataTemplate>
                          <Border>
                             <WrapPanel>
                                  <Label Content="{Binding Path=ID, RelativeSource={RelativeSource AncestorType=local:Job}}"></Label>
                             </WrapPanel>
                          </Border>
                      </DataTemplate>
                  </Expander.ContentTemplate>
              </Expander>

           </DataTemplate>

      </ListBox.ItemTemplate>
</ListBox>

Generating the expanders by Databinding works flawlessly. Generating the the content of the expanders on the other hand just does now want to work.

I want to achieve that the jobs within the Task objects get generated by databinding to the WrapPanels within the designated expander Content.

The result should be that i have several expanders (the Task objects) with several wrappanels in the content (the Job objects of the Parent task).

Does anyone know what I am doing wrong here / how to achieve this?

EDIT:

With the anwers provided I tried:

  <Expander Name="jobgroup" Header="{Binding Path=Description}" HorizontalAlignment="Stretch" Padding="5" FontSize="16" VerticalAlignment="Top">
                            <ListView ItemsSource="{Binding Path=listofjobs}" >
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <StackPanel Orientation="Vertical">
                                            <Label Content="{Binding Path=Name}"></Label>

                                        </StackPanel>
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
                        </Expander>

and also:

                        <DataTemplate>
                        <Expander Name="jobgroup" Header="{Binding Path=Description}" HorizontalAlignment="Stretch" Padding="5" FontSize="16" VerticalAlignment="Top">
                            <ListView ItemsSource="{Binding Path=listofjobs}" >
                                        <StackPanel Orientation="Vertical">
                                            <Label Content="{Binding Path=Name}"></Label>

                                        </StackPanel>
                            </ListView>
                        </Expander>
                    </DataTemplate>

But both does not seem to work. What am i missing here?

EDIT2:

There are several binding errors showing up:

System.Windows.Data Error: 40 : BindingExpression path error: 'listofjobs' property not found on 'object' ''Task' (HashCode=49998812)'. BindingExpression:Path=listofjobs; DataItem='Task' (HashCode=49998812); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
System.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=listofjobs; DataItem='Task' (HashCode=49998812); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=listofjobs; DataItem='Task' (HashCode=49998812); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=listofjobs; DataItem='Task' (HashCode=49998812); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
System.Windows.Data Error: 40 : BindingExpression path error: 'Name' property not found on 'object' ''Task' (HashCode=49998812)'. BindingExpression:Path=Name; DataItem='Task' (HashCode=49998812); target element is 'Label' (Name=''); target property is 'Content' (type 'Object')
System.Windows.Data Information: 20 : BindingExpression cannot retrieve value due to missing information. BindingExpression:Path=Name; DataItem='Task' (HashCode=49998812); target element is 'Label' (Name=''); target property is 'Content' (type 'Object')
System.Windows.Data Information: 21 : BindingExpression cannot retrieve value from null data item. This could happen when binding is detached or when binding to a Nullable type that has no value. BindingExpression:Path=Name; DataItem='Task' (HashCode=49998812); target element is 'Label' (Name=''); target property is 'Content' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Name; DataItem='Task' (HashCode=49998812); target element is 'Label' (Name=''); target property is 'Content' (type 'Object')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ListViewItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=VerticalContentAlignment; DataItem=null; target element is 'ListViewItem' (Name=''); target property is 'VerticalContentAlignment' (type 'VerticalAlignment')

Upvotes: 1

Views: 1900

Answers (2)

Felix D.
Felix D.

Reputation: 5083

Since you are binding Description successfully your binding must be List<Task> tasklist.

Imagine your visual tree like that:

- ListBox                                               - Binds List<Task>
    - ListBoxItem  (Expander)                           - Binds Task
        - ListBoxItem (ExpanderContent)- ListView       - Binds Task.Jobs (List<Job>)
            - ListView.ItemTemplate                     - Binds Job

So you can only access properies of Task.

Your Expanders ControlTemplate should be something to show the collection of Jobs:

<Expander.ContentTemplate>
    <DataTemplate>
         <ListView ItemsSource="{Binding Jobs}" >
             <ListView.ItemTemplate>
                  <DataTemplate>
                      <TextBlock Text="{Binding Name}" />
                  </DataTemplate>
             </ListView.ItemTemplate>
         </ListView>            
    </DataTemplate>
</Expander.ContentTemplate>

OR

You only bind one job like this:

<Label Content="{Binding Path=Jobs[0].ID" />

You don't need any RelativeSource here.

Make sure every property you bind to has a getter/setter {get; set;} !

public List<Job> ListOfJobs {get; set;} = new List<Job>();

Upvotes: 2

Andr&#233; B
Andr&#233; B

Reputation: 1709

I think it would be easier to Bind your VM entirely in your Listbox, like this:

<ListBox Name="joblist" HorizontalContentAlignment="Stretch" ItemsSource={Binding Path=tasklist}> ... </ListBox>

The expander could stay the same :

 <Expander Name="jobgroup" Header="{Binding Path=Description}" HorizontalAlignment="Stretch" Padding="5" FontSize="16" VerticalAlignment="Top">

And for your content define do it like this:

<ListView ItemsSource={Binding Path=listofjobs} >
   <StackPanel Orientation="Vertical">
      <Label Content="{Binding Path=ID}"></Label>
   </StackPanel>
 </ListView>

Expose your tasklist as a property, and define it as the DataContext of your page/listbox and i think it should be working correctly.

Upvotes: 0

Related Questions