views:

344

answers:

1

In a WPF treeview I am trying to automatically check the children if the parent node is being checked. I am using a view model for that and a bindable object for the nodes, however all my attempts failed. Here is the code (C# + XAML). Any ideas would be greatly appreciated

<Window x:Class="TestCheckBoxBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestCheckBoxBinding"
Title="Window1" Height="300" Width="300">
<Window.Resources>
    <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="IsExpanded" Value="True"/> 
    </Style>
    <HierarchicalDataTemplate DataType="{x:Type local:TestCategory}" ItemsSource="{Binding Tests, Mode=OneTime}">
        <Label Content="{Binding Name}"></Label>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type local:Test}" ItemsSource="{Binding Children, Mode=OneTime}">
        <StackPanel Orientation="Horizontal">
            <CheckBox VerticalAlignment="Center"  IsChecked="{Binding IsChecked, Mode=TwoWay}"></CheckBox>
            <Label Content="{Binding Name}"></Label>
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>
<Grid>
    <TreeView ItemsSource="{Binding Categories, Mode=OneTime}"></TreeView>
</Grid>

public class TestsViewModel
{
    public static void PopulateList(TestsViewModel vm)
    {
        TestCategory cat1 = new TestCategory() { Id = 1, Name = "First category" };
        Test t1 = new Test() { Name = "Test1" };
        Test t2 = new Test() { Name = "Test2" };
        Test t3 = new Test() { Name = "Test3" };
        t1.AddChild(t2);
        t1.AddChild(t3);
        Test t4 = new Test() { Name = "Test4" };
        cat1.AddTest(t1);
        cat1.AddTest(t4);
        vm.AddTestCategory(cat1);
        TestCategory cat2 = new TestCategory() { Id = 2, Name = "Second category" };
        Test t5 = new Test() { Name = "Test1" };
        Test t6 = new Test() { Name = "Test2" };
        Test t7 = new Test() { Name = "Test3" };
        t6.AddChild(t7);
        Test t8 = new Test() { Name = "Test4" };
        cat2.AddTest(t5);
        cat2.AddTest(t6);
        cat2.AddTest(t8);
        vm.AddTestCategory(cat2);
    }
    private readonly IEnumerable<TestCategory> categories = new List<TestCategory>();
    public IEnumerable<TestCategory> Categories { get { return categories; } }
    public void AddTestCategory(TestCategory testCategory)
    {
        ((IList<TestCategory>)categories).Add(testCategory);
    }
}

public class TestCategory
{
    public int Id { get; set; }
    public string Name { get; set; }
    private readonly IEnumerable<Test> tests = new List<Test>();
    public IEnumerable<Test> Tests { get { return tests; } }
    public void AddTest(Test t)
    {
        ((IList<Test>)tests).Add(t);
    }
}

public class Test : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        set
        {
            if (name != value)
            {
                name = value;
                this.OnPropertyChanged("Name");
            }
        }
        get { return name; }
    }

    public bool? isChecked = false;
    public bool? IsChecked
    {
        get { return isChecked; }
        set
        {
            if (isChecked != value)
            {
                isChecked = value;
                if (children.Count() > 0)
                {
                    foreach (var test in children)
                    {
                        test.isChecked = value;
                        test.Name += ".";
                    }
                }
                this.OnPropertyChanged("IsChecked");
            }
        }
    }

    public void AddChild(Test test)
    {
        ((IList<Test>)children).Add(test);
    }

    private readonly IEnumerable<Test> children = new List<Test>();
    public IEnumerable<Test> Children
    {
        get { return children; }
    }

    #region INotifyPropertyChanged Members

    // Create the OnPropertyChanged method to raise the event
    protected void OnPropertyChanged(string propName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}
A: 

Oups, child checkboxes are not being checked because I wasn't setting their IsChecked property. I was setting the isChecked field, which bypasses the property setter and prevents PropertyChanged from being raised.

Dan