Shamshiel
Shamshiel

Reputation: 2211

WPF - Binding class with Lists of strings to a ListBox

I have a problem with binding multiple Lists to a ListBox. I want that every List has a different DataTemplate with a different color.

I have following model classes

public class Users
{
        public Members Members{ get; set; }
}

public class Members
{
        public List<string> Moderators { get; set; }
        public List<string> Viewers { get; set; }
}

I have following ViewModel with INotifyPropertyChanged

private Users users;

public Users Users
{
    get { return users; }
    set
    {
        users= value;
        RaisePropertyChanged("Users");
    }
}

And I'm binding to this ListBox

<ListBox ItemsSource="{Binding Users.Members.Viewers}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
 </ListBox>

Now I only have that one List bound to the ListBox. It works great but I want the other list also bound to the same ListBox. Besides that I want that Moderators have a different template.

I tried many different things but nothing seemed to work.

Upvotes: 1

Views: 1624

Answers (2)

ΩmegaMan
ΩmegaMan

Reputation: 31721

Instead of removing the names from the origination object why not keep it and specify different colors based off of the originating class?

Besides that I want that Moderators have a different template.

If you only have strings that is impossible. Remember the listbox ultimately sees only one list; so in one list, how is it possible to tag a string as either moderator or viewer?

a different DataTemplate with a different color.

If there are only strings I suggest you create wrapper classes, one for moderators and one for viewers, then project the strings into those classes to be held. Then you can follow my suggestion/example below.


Via the use of the Composite collection to hold different items (or one could actually use a base class list or a interface list if the instances have that commonality) and then have specialized data templates which look for the originating class, it can be done.

Example

I have two classes one named Ships and one named Passage. Note that both classes both have a Name property, but one could use something other than Name for either or both in the templates.

Below I define the data templates and my listbox.

<Grid>
    <Grid.Resources>
        <DataTemplate DataType="{x:Type c:Ship}">
            <TextBlock Text="{Binding Path=Name}"
                        Foreground="Red" />
        </DataTemplate>
        <DataTemplate DataType="{x:Type c:Passage}">
            <TextBlock Text="{Binding Path=Name}"
                        Foreground="Blue" />
        </DataTemplate>
    </Grid.Resources>
    <ListBox Name="myListBox"
                Height="300"
                Width="200"
                ItemsSource="{Binding MyCompositeCollection}">
    </ListBox>
</Grid>

So what will happen is that my ships will be red and the passages will be blue. enter image description here


Here is my code in the VM:

private CompositeCollection _MyCompositeCollection;

public CompositeCollection MyCompositeCollection
{
    get { return _MyCompositeCollection; }
    set { _MyCompositeCollection = value; OnPropertyChanged("MyCompositeCollection"); }
}

Here I load the composite collection:

var temp  = new CompositeCollection();

Ships.ForEach(sh => temp.Add(sh));
Passages.ForEach(ps => temp.Add(ps));

MyCompositeCollection = temp;

Upvotes: 2

Maximus
Maximus

Reputation: 3448

  1. In order to combine two Lists and set it to ItemsSource use CompositeCollection.
  2. WPF can set distinct template by using ItemTemplateSelector but it entails class to be diffrent in some way. Your type is string so it does not differ in any way. My hint is to create enum as follows

    enum MemberType { Moderator, Viewer }


and following class:

class Person
{
    public string Name{get;set;}
    public MemberType Type{get;set;}
}

then change to this

public class Members
    {
        public List<Person> Moderators { get; set; }
        public List<Person> Viewers { get; set; }
    }

and eventually in ItemTemplateSelector

  public class TemplateSelector : DataTemplateSelector
    {
        public DataTemplate ViewerDataTemplate;
        public DataTemplate ModeratorDataTemplate;

        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var member = item as Person;
            switch (member.Type)
            {
                case MemberType.Moderator:
                    return ModeratorDataTemplate;
                case MemberType.Viewer:
                    return ViewerDataTemplate;
            }

            return null;
        }
    }

Upvotes: 2

Related Questions