views:

54

answers:

4

Sorry for the vague description, I can't think of a better way to put it.

Let's say that my ViewModel has a property as follows:

public List<MyClass> SubSystems { get; set; }

and the SubSystems class:

public class SubSystem
{
    public string Name { get; set; }

    public bool IsSelected { get; set; }
}

In the view, I'd like to bind the SubSystems property to, what I think would be, a list of checkboxes where the IsChecked and Name properties of the CheckBox is bound to the their respective properties, IsChecked for IsSelected and Content for Name.

I know I can make a ListBox in the XAML, but I'm not sure how I'd go about doing this using binding and a collection..

Thanks for the help!

Edit -

Here's the XAML:

<GroupBox Header="Sub-Systems" Grid.Column="0" Grid.Row="0" Margin="5">
    <Grid>
        <Grid.Resources>
            <DataTemplate x:Key="checkBox">
                <StackPanel Orientation="Horizontal">
                    <CheckBox IsChecked="{Binding IsSelected}" />
                    <TextBlock Text="{Binding Name}" />
                </StackPanel>
            </DataTemplate>
        </Grid.Resources>
        <ListBox ItemTemplate="{StaticResource checkBox}" ItemsSource="{Binding SubSystems}" />
    </Grid>
</GroupBox>

Edit #2 -

Just to clarify, all of the examples populate the box, but none of the examples are breaking on the breakpoints in the setters.

+2  A: 

How about this:

<DataTemplate>
    <StackPanel Orientation="Horizontal">
        <CheckBox IsChecked={Binding IsSelected, Mode=TwoWay} /><TextBlock Text={Binding Name} />
    </StackPanel>
</DataTemplate>
Paul Betts
For some unknown reason, in my ViewModel, the setter is never getting called when I use this. I added x:Key="checkBox" to <DataTemplate> and ItemTemplate="{StaticResource checkBox}" to the ListBox. It appears to be rendering correctly, just not binding.
Ian P
See my edit of the original post for the XAML.
Ian P
Ah yes, fixed. Mode=TwoWay is what you're missing
Paul Betts
@Ian P: I think what you're missing is the ElementName property in your binding, the DataTemplate doesn't know where the SubSystems property is coming from, you need this: ItemsSource="{Binding ElementName=window1, Path=SubSystems}", see the bottom of my answer
Carlo
+1  A: 

Modify the ListBox.ItemTemplate to use a checkbox, and bind the CheckBox.IsChecked to SubSystem.IsSelected and CheckBox.Content to SubSystem.Name:

XAML:

<ListBox ItemsSource="{Binding}">
  <ListBox.ItemTemplate>
   <DataTemplate>
    <CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Name}" Margin="5" Focusable="False" />
   </DataTemplate>
  </ListBox.ItemTemplate>
 </ListBox>

C#:

private void window1_Loaded(object sender, RoutedEventArgs e)
{
 this.SubSystems = new List<SubSystem>();

 this.SubSystems.Add(new SubSystem() { Name = "SubSystem 1", IsSelected = false });
 this.SubSystems.Add(new SubSystem() { Name = "SubSystem 2", IsSelected = false });
 this.SubSystems.Add(new SubSystem() { Name = "SubSystem 3", IsSelected = true });
 this.SubSystems.Add(new SubSystem() { Name = "SubSystem 4", IsSelected = false });
 this.SubSystems.Add(new SubSystem() { Name = "SubSystem 5", IsSelected = true });

 this.DataContext = this.SubSystems;
}

And make sure you set Focusable="False" to the CheckBoxes or else your users will be able to tab into them.

EDIT:

Also from what you added you might be missing the ElementName property (if SubSystems is NOT the DataContext of your window, you need to specify where the SubSystems property is coming from with the ElementName binding property):

 <ListBox ItemTemplate="{StaticResource checkBox}" ItemsSource="{Binding ElementName=window1, Path=SubSystems}" />
Carlo
+1  A: 

I think that instead of a ListBox, you probably want an ItemsControl. ListBoxes assume that you want to select one of the SubSystem but really, you just want to arrange the items with data templates:

<ItemsControl ItemsSource="{Binding SubSystems}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <Checkbox IsChecked="{Binding IsSelected, Mode=TwoWay}" Content="{Binding Name}" />
    </DataTemplate>
  <ItemsControl.ItemTemplate>
</ItemsControl>
Brian Genisio
+1  A: 

Do you mean something like this?

SubSystem class

public class SubSystem : INotifyPropertyChanged
{
    private string mName;
    private Boolean mIsSelected = false;

    public SubSystem()
    {
    }

    public SubSystem(string name, Boolean isSelected)
    {
        this.Name = name;
        this.IsSelected = isSelected;
    }

    public string Name
    {
        get { return mName; }
        set
        {
            if (mName != value)
            {
                mName = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Name"));
            }
        }
    }

    public Boolean IsSelected
    {
        get { return mIsSelected; }
        set
        {
            if (mIsSelected != value)
            {
                mIsSelected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
            }
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

ViewModel

ObservableCollection<SubSystem> mSubSystems = new ObservableCollection<SubSystem>();
public ObservableCollection<SubSystem> SubSystems
{
    get { return mSubSystems; }
    set { mSubSystems = value; }
}

View

<ListBox x:Name="lstSubsystems" ItemsSource="{Binding SubSystems}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsSelected}">
                <ContentPresenter Content="{Binding Name}"  />
            </CheckBox>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Hope that helps, Wts

Wonko the Sane
This is exactly what I'm looking for -- thank you!
Ian P
@Ian P: Remember to se the Focusable="False" on the CheckBox so your users are not able to tab into it, otherwise it looks and feels really weird.
Carlo