DatePicker and expand-a-node in TreeView: updates to the Bag-O-Tricks for the July CTP


I promised you I’d get these in “this week”. I guess, depending on how you split the weeks, I delivered.

Most of the details are in the post on the Community Site for WPF.

DatePicker/MonthCalendar were implemented by our friends in the Advanced Technology Center in Beijing. Thanks, guys.

I added a helper class to the Library assembly that helps with common operations on a TreeView. Let me know if it works out for ya.

Hopefully I’ll have another late night this week where I can hack on samples. I still have some very cool stuff waiting to be factored and posted.

Comments (4)

  1. John Stewien says:

    Hi Kevin, just reading over your code and I noticed the UIExpand(TreeViewItem treeViewItem) method has a little:

    WaitForPriority(DispatcherPriority.Background);

    I take it this gets around the problem where the ItemContainerGenerator doesn’t get started until the node is visible is it?

    In the past I’ve implemented the same thing you’ve done, except I have a stack holding the nodes to be expanded, and I handle the ItemContainerGenerator.StatusChanged event to listen for when the item can be obtained. So I sort of expand the treeview asyncronously e.g. when the user views the panel that holds the treeview.

    With your method, I’m wondering if the code comes to a halt if the TreeView hasn’t been made visible before a node expansion is to be made such as when the treeview is on a panel whose vibility = collapsed.

  2. MSDN Archive says:

    John, you are a wise man.

    You’re getting to the core of why doing a generic expand is tough (and why I recommend doing it in the data layer).

    You are correct that WaitForPriority cues up an item in the dispatcher that (hopefully) happens after the TreeViewItem template has been expanded. This is not guarenteed, though (as you point out).

  3. John Stewien says:

    Thought I might throw in some sample code to show what I came up with. I hope your comments submission web control handles code ok :-). I’ve found the function below works well where the selection is delayed until the control is visible when used like this:

    if (!SelectItemByPath(ref lastItemToSelectControl, itemToSelectByPath))

    base.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);

    //where the event handler looks like this:

    private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)

    {

    lastItemToSelectControl.ItemContainerGenerator.StatusChanged -= new EventHandler(ItemContainerGenerator_StatusChanged);

    if (!SelectItemByPath(ref lastItemToSelectControl, itemToSelectByPath))

    lastItemToSelectControl.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);

    }

    /// <summary>

    /// Selects the first node in the tree view returns true if the

    /// selection was successful or false if this needs to be called

    /// again when the base.ItemContainerGenerator.StatusChanged event

    /// is fired. Note if false is returned the same items control (which

    /// will be modified as it’a a ref) needs to be passed in next time

    /// to complete the selection.

    /// </summary>

    /// <returns></returns>

    internal static bool SelectItemByPath(ref ItemsControl itemsControl, IList itemToSelectByPath)

    {

    IList source = itemsControl.ItemsSource as IList;

    if (source == null || itemToSelectByPath.Count < 1)

    {

    itemToSelectByPath.Clear();

    return true;

    }

    if (!source.Contains(itemToSelectByPath[0]))

    {

    itemToSelectByPath.Clear();

    return true;

    }

    if (itemsControl.HasItems)

    {

    // Check that the items container generator has been started.

    // If so then just select the item, if not then return false;

    TreeViewItem item = itemsControl.ItemContainerGenerator.ContainerFromItem(itemToSelectByPath[0]) as TreeViewItem;

    if (item != null)

    {

    if (itemToSelectByPath.Count == 1)

    {

    itemToSelectByPath.RemoveAt(0);

    item.IsSelected = true;

    item.BringIntoView();

    return true;

    }

    else

    {

    itemToSelectByPath.RemoveAt(0);

    item.IsExpanded = true;

    itemsControl = item;

    return SelectItemByPath(ref itemsControl, itemToSelectByPath);

    }

    }

    }

    return false;

    }