Question

I want to bind my Datatemplate to 2 Datasources, one datasource that will actually define what is in the ListBox and other that will determine how many ListBoxes are there and what Items in the Listbox are selected\checked.

I have following XAML

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate x:Key="TokenListTemplate">
        <StackPanel Orientation="Horizontal">
            <CheckBox x:Name="chkToken" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
                <TextBlock Text="{Binding Path=Text}" />
            </CheckBox>
        </StackPanel>
    </DataTemplate>

    <DataTemplate x:Key="ItemTemplate">
        <Border BorderThickness="1">
            <StackPanel Margin="3">
                <TextBlock Text="{Binding Path=Header}"/>
                <ListBox ItemTemplate="{StaticResource TokenListTemplate}" 
                         ItemsSource="{Binding Path=Tokens}" >
                </ListBox>
            </StackPanel>
        </Border>
    </DataTemplate>
</Window.Resources>

<Grid>
    <ListBox ItemTemplate="{StaticResource ItemTemplate}" 
             ItemsSource="{Binding}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Grid>

And this is the codebehind

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();

        ObservableCollection<DataEntity> _actualObjects;

        List<Token> tokens1 = new List<Token>() 
                                            { 
                                                new Token("1"),                                                     
                                                new Token("2"), 
                                                new Token("3"), 
                                                new Token("4") 
                                            };

        List<Token> tokens2 = new List<Token>() 
                                            { 
                                                new Token("11"),                                                     
                                                new Token("21"), 
                                                new Token("31")
                                            };

        _actualObjects = new ObservableCollection<DataEntity>()
            {
                new DataEntity(tokens1, "A", "1,2,3", 1),  
                new DataEntity(tokens1, "B", "2,3", 1),
                new DataEntity(tokens2, "C", "21,31", 2)
            };


        DataContext = _actualObjects;
    }

    class DataEntity
    {
        public DataEntity(List<Token> tokens, string header, string tokenString, int entityTypeId)
        {
            Tokens = tokens;
            Header = header;
            TokenString = tokenString;
            EntityTypeId = entityTypeId;
        }
        public List<Token> Tokens { get; set; }
        public String Header { get; set; }
        public String TokenString { get; set; }
        public int EntityTypeId { get; set; }

    }

    public class Token
    {
        public bool IsSelected { get; set; }
        public string Text { get; set; }
        public Token(string text)
        {
            this.IsSelected = false;
            this.Text = text;
        }
    }
}

It produces this enter image description here

I don't want to inject token1 or token2 List into DataEntity object so in other words I want DataEntity constructor to be

public DataEntity(string header, string tokenString, int entityTypeId)

Listbox DataTemplate should select

  • tokens1 List as datasource for its LisBoxItems if Dataentity.EntityTypeId = 1
  • tokens2 List as datasource for its LisBoxItemsif DataEntity.EntityTypeId = 2

Also TokenString in DataEntity should be bound to items in the Listbox i.e. if Listbox shows 1 2 3 4 and DataEntity for this listbox has its TokenString value set to "1,2,3" then 1 2 3 should be checked in the listbox enter image description here

Was it helpful?

Solution

I would recommend to create a ViewModel as a layer between your model and the view. In the ViewModel you can arrange the data to fit to the used controls without changing your model.

So the ViewModel could for example split the tokenString of the DataEntity into a list of tokens.

Just Google for MVVM (Model-View-ViewModel) for examples and furter explanations or look here on SO (like MVVM: Tutorial from start to finish?).

OTHER TIPS

You're not thinking about this correctly. You need to create one class (some may call a view model) with the responsibility of providing all of the data that the view (or UI) will need. Therefore, you will need to have one property which holds a collection of type DataEntity (if I understand you correctly) to 'define what is in the outer ListBox' as you say.

Then you need a DataTemplate to describe what should be displayed for each item in the ListBox - your 'ItemTemplate' template. This DataTemplate should have another ListBox inside in which to display your Token objects. Your DataEntity should have something like this property in it:

public List<Token> Tokens 
{
    get 
    {
        if (EntityTypeId == 1) return tokens1;
        else if (EntityTypeId == 2) return tokens2;
    }
}

You will then need another DataTemplate for your Token objects - your 'TokenListTemplate' template, but without the StackPanel... the inner ListBox replaces that, eg. if there are two Token objects in one DataEntity object, then that object would show two Checkboxes... you have correctly bound the IsChecked property to the Token.IsSelected property.

This may be complicated, but it is entirely possible. Just start with the first layer and get your DataEntity objects displayed in the outer ListBox using your 'ItemTemplate' template. Once that bit is ok, move on to the inner ListBox. Good luck.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top