tags:

views:

107

answers:

2

Is there a way in WPF to define the background of alternating visible rows?

I've tried setting the AlternationCount property, but that restarts for every child node which gives a weird look.

Ideally what I would like is to know what the visual index of a given node is. Only expanded nodes are counted.

A: 

Something I've done with javascript is create an OnLoaded event for a table which loops through the table rows and if the row is visible, it sets the background color to a nextColor variable, and changes the nextColor variable to the opposite color. That might work here.

Rachel
I'm not quite sure how you would use JavaScript to loop through a TreeView in WPF. I'm pretty sure that's not going to work.
jjrdk
I meant you could use similar logic.... in the OnLoaded event of the TreeView, loop through the treeview items and set the background color based on if its visible or not and the last used color.
Rachel
A: 

There was no easy way to do this as WPF creates nested containers for the tree nodes. So as Rachel mentioned, looping through the items seemed to be the way to go. But I didn't want to depart too much from the built in ItemsControl.AlternationIndex attached property as that is the one people would expect. Because it is readonly I had to access it via reflection, but after that things fell into place.

First off, make sure you handle the Loaded, Expanded and Collapsed events of your TreeViewItem. In the event handler find the owning TreeView and do a recursive alternation count set of all visible nodes. I created an extension method to handle it:

public static class AlternationExtensions { private static readonly MethodInfo SetAlternationIndexMethod;

    static AlternationExtensions()
    {
        SetAlternationIndexMethod = typeof(ItemsControl).GetMethod(
        "SetAlternationIndex", BindingFlags.Static | BindingFlags.NonPublic);
    }

    public static int SetAlternationIndexRecursively(this ItemsControl control, int firstAlternationIndex)
    {
        var alternationCount = control.AlternationCount;
        if (alternationCount == 0)
        {
            return 0;
        }

        foreach (var item in control.Items)
        {
            var container = control.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
            if (container != null)
            {
                var nextAlternation = firstAlternationIndex++ % alternationCount;
                SetAlternationIndexMethod.Invoke(null, new object[] { container, nextAlternation });
                if (container.IsExpanded)
                {
                    firstAlternationIndex = SetAlternationIndexRecursively(container, firstAlternationIndex);
                }
            }
        }

        return firstAlternationIndex;
    }
}

As you can see it runs through each node and sets the custom alternation index. It checks if the node is expanded and if so continues the count on the child nodes.

Above I mentioned that you have to handle the Loaded event for the TreeViewItem. If you only handle the expanded and collapsed events you won't get the new containers that are created when a node is first opened. So you have to do a new pass when the child node has been created and added to the visual tree.

jjrdk