WPF Multiselect TreeView - almost fully implemented, but
I wrote my own "Multiselect"-Treeview. It uses an interface ITreeViewItem for all databound items. This treeview will not work without that the bound items will implement this interface.
public interface ITreeViewItem : INotifyPropertyChanged
{
bool IsExpanded { get; set; }
bool IsSelected { get; set; }
bool IsEnabled { get; set; }
Visibility Visibility { get; set; }
ITreeViewItem GetParentTreeViewItem();
}
This is my first question: Has someone an idea how to not force the implementation of this interface? It seems not to be clean. The main reason why i decided to implement this interface is, that it seems impossible to find the ItemsControl matching to the data item without (in worst case) expanding whole tree. Which will in the case of lazy loaded data result in loading all this data.
Another reason is the coloring of the selected items background. If i want to highlight the background I need some property to bind on. A normal trigger on IsSelected seems not to work because only one item at a time can have this value set (or?).
About the last issue I already asked here WPF TreeViewItem Background The problem with the given answer is, that the default Template have not the default colors (on first click on an item the color will be light violett, on second it will be blue; which is another issue). So how can I set the Color of all selected items to light gray or of the actually focused item to blue after the treeview looses focus?
If you need some more information on my implementation feel free to ask.
EDIT: @Snowbear JIM-compiler:
internal static bool ExecuteOnTreeViewItem(this TreeView tree, ITreeViewItem dataItem, Action<TreeViewItem> action)
{
Stack<ITreeViewItem> pathToRoot = GetPathToRoot(dataItem);
TreeViewItem firstVisibleItem = tree.SyncTreeLevels(pathToRoot);
if (firstVisibleItem == null)
return false; // can't find any item in path which is currently selectable
if (pathToRoot.Count != 0) // expand the first item if nextLevelItems will follow
firstVisibleItem.IsExpanded = true;
ExecuteOnTreeViewItem(tree, pathToRoot, action, firstVisibleItem);
return true;
}
private static void ExecuteOnTreeViewItem(TreeView tree, Stack<ITreeViewItem> pathToRoot, Action<TreeViewItem> action, TreeViewItem treeViewItem)
{
if (pathToRoot.Count == 0)
{
action(treeViewItem);
return;
}
var nextLevelItem = pathToRoot.Pop();
if (pathToRoot.Count != 0)
nextLevelItem.IsExpanded = true; // make children visible to create the item containers
if (treeViewItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
EventHandler eventHandler = null;
if (!treeViewItem.IsExpanded)
treeViewItem.IsExpanded = true;
eventHandler = delegate
{
if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.Error
|| treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
treeViewItem.ItemContainerGenerator.StatusChanged -= eventHandler;
if (treeViewItem.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
var nextTreeViewItem = treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem;
ExecuteOnTreeViewItem(tree, pathToRoot, action, nextTreeViewItem);
}
};
treeViewItem.ItemContainerGenerator.StatusChanged += eventHandler;
}
else
{
var nextTreeViewItem = treeViewItem.ItemContainerGenerator.ContainerFromItem(nextLevelItem) as TreeViewItem;
ExecuteOnTreeViewItem(tree, pathToRoot, action, nextTreeViewItem);
}
}
internal static Stack<ITreeViewItem> GetPathToRoot(ITreeViewItem item)
{
Stack<ITreeViewItem> items = new Stack<ITreeViewItem>();
ITreeViewItem parent = item;
while (parent != null)
{
items.Push(parent);
parent = parent.GetParentTreeViewItem();
}
return items;
}
/// <summary>
/// Returns the first item in stack which is visible in tree view. This is used
/// for the case that the first Item of ITreeViewItem is not the first bound item
/// </summary>
/// <param name="tree"></param>
/// <param name="hierachyItems"></param>
/// <returns></returns>
internal static TreeViewItem SyncTreeLevels(this TreeView tree, Stack<ITreeViewItem> hierachyItems)
{
TreeViewItem item = null;
while (item == null && hierachyItems.Count > 0)
item = tree.ItemContainerGenerator.ContainerFromItem(hierachyItems.Pop()) as TreeViewItem;
return item;
}
The bad thing about this code is, that the Action is only executed after leaving the calling event in the inherited TreeViewItem... 开发者_开发技巧but it works.
Probably it won't answer all your questions, but still:
that it seems impossible to find the ItemsControl matching to the data item without (in worst case) expanding whole tree
As far as I got your intention ItemContainerGenerator.ContainerFromItem should help you. It returns TreeViewItem
for given data item.
精彩评论