Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / TreeView.cs / 3 / TreeView.cs
//----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using MS.Internal;
using MS.Internal.Data;
using MS.Internal.KnownBoxes;
namespace System.Windows.Controls
{
///
/// A control that presents items in a tree structure.
///
[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(TreeViewItem))]
public class TreeView : ItemsControl
{
#region Constructors
static TreeView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeView), new FrameworkPropertyMetadata(typeof(TreeView)));
_dType = DependencyObjectType.FromSystemTypeInternal(typeof(TreeView));
KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(TreeView), new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(TreeView), new FrameworkPropertyMetadata(KeyboardNavigationMode.None));
}
///
/// Creates an instance of this control.
///
public TreeView()
{
_focusEnterMainFocusScopeEventHandler = new EventHandler(OnFocusEnterMainFocusScope);
KeyboardNavigation.Current.FocusEnterMainFocusScope += _focusEnterMainFocusScopeEventHandler;
}
#endregion
#region Public Properties
private static readonly DependencyPropertyKey SelectedItemPropertyKey =
DependencyProperty.RegisterReadOnly("SelectedItem", typeof(object), typeof(TreeView), new FrameworkPropertyMetadata((object)null));
///
/// The DependencyProperty for the property.
/// Default Value: null
///
public static readonly DependencyProperty SelectedItemProperty = SelectedItemPropertyKey.DependencyProperty;
///
/// Specifies the selected item.
///
[Bindable(true), Category("Appearance"), ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object SelectedItem
{
get
{
return GetValue(SelectedItemProperty);
}
}
private void SetSelectedItem(object data)
{
if (SelectedItem != data)
{
SetValue(SelectedItemPropertyKey, data);
}
}
private static readonly DependencyPropertyKey SelectedValuePropertyKey =
DependencyProperty.RegisterReadOnly("SelectedValue", typeof(object), typeof(TreeView), new FrameworkPropertyMetadata((object)null));
///
/// The DependencyProperty for the property.
/// Default Value: null
///
public static readonly DependencyProperty SelectedValueProperty = SelectedValuePropertyKey.DependencyProperty;
///
/// Specifies the a value on the selected item as defined by .
///
[Bindable(true), Category("Appearance"), ReadOnly(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object SelectedValue
{
get
{
return GetValue(SelectedValueProperty);
}
}
private void SetSelectedValue(object data)
{
if (SelectedValue != data)
{
SetValue(SelectedValuePropertyKey, data);
}
}
///
/// The DependencyProperty for the property.
/// Default Value: String.Empty
///
public static readonly DependencyProperty SelectedValuePathProperty =
DependencyProperty.Register(
"SelectedValuePath",
typeof(string),
typeof(TreeView),
new FrameworkPropertyMetadata(
String.Empty,
new PropertyChangedCallback(OnSelectedValuePathChanged)));
///
/// Specifies the path to query on to calculate .
///
[Bindable(true), Category("Appearance")]
public string SelectedValuePath
{
get { return (string) GetValue(SelectedValuePathProperty); }
set { SetValue(SelectedValuePathProperty, value); }
}
private static void OnSelectedValuePathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TreeView tree = (TreeView)d;
SelectedValuePathBindingExpression.ClearValue(tree);
tree.UpdateSelectedValue(tree.SelectedItem);
}
#endregion
#region Public Events
///
/// Event fired when changes.
///
public static readonly RoutedEvent SelectedItemChangedEvent = EventManager.RegisterRoutedEvent("SelectedItemChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler), typeof(TreeView));
///
/// Event fired when changes.
///
[Category("Behavior")]
public event RoutedPropertyChangedEventHandler SelectedItemChanged
{
add
{
AddHandler(SelectedItemChangedEvent, value);
}
remove
{
RemoveHandler(SelectedItemChangedEvent, value);
}
}
///
/// Called when changes.
/// Default implementation fires the event.
///
/// Event arguments.
protected virtual void OnSelectedItemChanged(RoutedPropertyChangedEventArgs e)
{
//
RaiseEvent(e);
}
#endregion
#region Implementation
#region Selection
internal void ChangeSelection(object data, TreeViewItem container, bool selected)
{
if (IsSelectionChangeActive)
{
return;
}
object oldValue = null;
object newValue = null;
bool changed = false;
TreeViewItem oldContainer = _selectedContainer; // Saved for the automation event
IsSelectionChangeActive = true;
try
{
if (selected)
{
if (container != _selectedContainer)
{
oldValue = SelectedItem;
newValue = data;
if (_selectedContainer != null)
{
_selectedContainer.IsSelected = false;
_selectedContainer.UpdateContainsSelection(false);
}
_selectedContainer = container;
_selectedContainer.UpdateContainsSelection(true);
SetSelectedItem(data);
UpdateSelectedValue(data);
changed = true;
}
}
else
{
if (container == _selectedContainer)
{
_selectedContainer.UpdateContainsSelection(false);
_selectedContainer = null;
SetSelectedItem(null);
oldValue = data;
changed = true;
}
}
if (container.IsSelected != selected)
{
container.IsSelected = selected;
}
}
finally
{
IsSelectionChangeActive = false;
}
if (changed)
{
if ( _selectedContainer != null
&& AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) )
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(_selectedContainer);
if (peer != null)
peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementSelected);
}
if ( oldContainer != null
&& AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection) )
{
AutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(oldContainer);
if (peer != null)
peer.RaiseAutomationEvent(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection);
}
RoutedPropertyChangedEventArgs e = new RoutedPropertyChangedEventArgs(oldValue, newValue, SelectedItemChangedEvent);
OnSelectedItemChanged(e);
}
}
internal bool IsSelectionChangeActive
{
get { return _bits[(int)Bits.IsSelectionChangeActive]; }
set { _bits[(int)Bits.IsSelectionChangeActive] = value; }
}
private void UpdateSelectedValue(object selectedItem)
{
BindingExpression expression = PrepareSelectedValuePathBindingExpression(selectedItem);
if (expression != null)
{
expression.Activate(selectedItem);
object selectedValue = expression.Value;
expression.Deactivate();
SetValue(SelectedValuePropertyKey, selectedValue);
}
else
{
ClearValue(SelectedValuePropertyKey);
}
}
private BindingExpression PrepareSelectedValuePathBindingExpression(object item)
{
if (item == null)
{
return null;
}
Binding binding;
bool useXml = XmlHelper.IsXmlNode(item);
BindingExpression bindingExpr = SelectedValuePathBindingExpression.GetValue(this);
// replace existing binding if it's the wrong kind
if (bindingExpr != null)
{
binding = bindingExpr.ParentBinding;
bool usesXml = (binding.XPath != null);
if (usesXml != useXml)
{
bindingExpr = null;
}
}
if (bindingExpr == null)
{
// create the binding
binding = new Binding();
binding.Source = item;
if (useXml)
{
binding.XPath = SelectedValuePath;
binding.Path = new PropertyPath("/InnerText");
}
else
{
binding.Path = new PropertyPath(SelectedValuePath);
}
bindingExpr = (BindingExpression)BindingExpression.CreateUntargetedBindingExpression(this, binding);
SelectedValuePathBindingExpression.SetValue(this, bindingExpr);
}
return bindingExpr;
}
internal void HandleSelectionAndCollapsed(TreeViewItem collapsed)
{
if ((_selectedContainer != null) && (_selectedContainer != collapsed))
{
// Check if current selection is under the collapsed element
TreeViewItem current = _selectedContainer;
do
{
current = current.ParentTreeViewItem;
if (current == collapsed)
{
ChangeSelection(collapsed.ParentItemsControl.ItemContainerGenerator.ItemFromContainer(collapsed), collapsed, true);
break;
}
}
while (current != null);
}
}
// This method is called when MouseButonDown on TreeViewItem and also listen for handled events too
// The purpose is to restore focus on TreeView when mouse is clicked and focus was outside the TreeView
// Focus goes either to selected item (if any) or treeview itself
internal void HandleMouseButtonDown()
{
if (!this.IsKeyboardFocusWithin)
{
if (_selectedContainer != null)
{
if (!_selectedContainer.IsKeyboardFocused)
_selectedContainer.Focus();
}
else
{
// If we don't have a selection - just focus the treeview
this.Focus();
}
}
}
#endregion
#region Containers
///
/// Returns true if the item is or should be its own container.
///
/// The item to test.
/// true if its type matches the container type.
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is TreeViewItem;
}
///
/// Create or identify the element used to display the given item.
///
/// The container.
protected override DependencyObject GetContainerForItemOverride()
{
return new TreeViewItem();
}
///
/// This method is invoked when the Items property changes.
///
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Remove:
case NotifyCollectionChangedAction.Reset:
if ((SelectedItem != null) && !IsSelectedContainerHookedUp)
{
SelectFirstItem();
}
break;
case NotifyCollectionChangedAction.Replace:
{
// If old item is selected - remove the selection
// Revisit the condition when we support duplicate items in Items collection: if e.OldItems[0] is the same as selected items we will unselect the selected item
object selectedItem = SelectedItem;
if ((selectedItem != null) && selectedItem.Equals(e.OldItems[0]))
{
ChangeSelection(selectedItem, _selectedContainer, false);
}
}
break;
case NotifyCollectionChangedAction.Add:
case NotifyCollectionChangedAction.Move:
break;
default:
throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, e.Action));
}
}
private void SelectFirstItem()
{
object item;
TreeViewItem container;
bool selected = GetFirstItem(out item, out container);
if (!selected)
{
item = SelectedItem;
container = _selectedContainer;
}
ChangeSelection(item, container, selected);
}
private bool GetFirstItem(out object item, out TreeViewItem container)
{
if (HasItems)
{
item = Items[0];
container = ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
return ((item != null) && (container != null));
}
else
{
item = null;
container = null;
return false;
}
}
internal bool IsSelectedContainerHookedUp
{
get
{
return (_selectedContainer != null) && (_selectedContainer.ParentTreeView == this);
}
}
internal TreeViewItem SelectedContainer
{
get
{
return _selectedContainer;
}
}
#endregion
#region Input
///
/// If control has a scrollviewer in its style and has a custom keyboard scrolling behavior when HandlesScrolling should return true.
/// Then ScrollViewer will not handle keyboard input and leave it up to the control.
///
protected internal override bool HandlesScrolling
{
get { return true; }
}
///
/// Called when a keyboard key is pressed down.
///
/// Event Arguments
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled)
{
if (IsControlKeyDown)
{
switch (e.Key)
{
case Key.Up:
case Key.Down:
case Key.Left:
case Key.Right:
case Key.Home:
case Key.End:
case Key.PageUp:
case Key.PageDown:
if (HandleScrollKeys(e.Key))
{
e.Handled = true;
}
break;
}
}
else
{
switch (e.Key)
{
case Key.Up:
case Key.Down:
if ((_selectedContainer == null) && FocusFirstItem())
{
e.Handled = true;
}
break;
case Key.Home:
if (FocusFirstItem())
{
e.Handled = true;
}
break;
case Key.End:
if (FocusLastItem())
{
e.Handled = true;
}
break;
case Key.PageUp:
case Key.PageDown:
if (_selectedContainer == null)
{
if (FocusFirstItem())
{
e.Handled = true;
}
}
else if (HandleScrollByPage(e.Key == Key.PageUp))
{
e.Handled = true;
}
break;
case Key.Tab:
if (IsShiftKeyDown && IsKeyboardFocusWithin)
{
// SHIFT-TAB behavior for KeyboardNavigation needs to happen at the TreeView level
if (MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous)))
{
e.Handled = true;
}
}
break;
}
}
}
}
private static bool IsControlKeyDown
{
get
{
return ((Keyboard.Modifiers & ModifierKeys.Control) == (ModifierKeys.Control));
}
}
private static bool IsShiftKeyDown
{
get
{
return ((Keyboard.Modifiers & ModifierKeys.Shift) == (ModifierKeys.Shift));
}
}
private bool FocusFirstItem()
{
TreeViewItem item = ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
if (item != null)
{
if (item.IsEnabled && item.Focus())
{
return true;
}
else
{
return item.FocusDown();
}
}
return false;
}
private bool FocusLastItem()
{
int index = Items.Count - 1;
while (index >= 0)
{
TreeViewItem item = ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
if ((item != null) && item.IsEnabled)
{
return TreeViewItem.FocusIntoItem(item);
}
index--;
}
return false;
}
private bool HandleScrollKeys(Key key)
{
ScrollViewer scroller = ScrollHost;
if (scroller != null)
{
bool invert = (FlowDirection == FlowDirection.RightToLeft);
switch (key)
{
case Key.Up:
scroller.LineUp();
return true;
case Key.Down:
scroller.LineDown();
return true;
case Key.Left:
if (invert)
{
scroller.LineRight();
}
else
{
scroller.LineLeft();
}
return true;
case Key.Right:
if (invert)
{
scroller.LineLeft();
}
else
{
scroller.LineRight();
}
return true;
case Key.Home:
scroller.ScrollToTop();
return true;
case Key.End:
scroller.ScrollToBottom();
return true;
case Key.PageUp:
//if vertically scrollable - go vertical, otherwise horizontal
if(DoubleUtil.GreaterThan(scroller.ExtentHeight, scroller.ViewportHeight))
{
scroller.PageUp();
}
else
{
scroller.PageLeft();
}
return true;
case Key.PageDown:
//if vertically scrollable - go vertical, otherwise horizontal
if(DoubleUtil.GreaterThan(scroller.ExtentHeight, scroller.ViewportHeight))
{
scroller.PageDown();
}
else
{
scroller.PageRight();
}
return true;
}
}
return false;
}
private bool HandleScrollByPage(bool up)
{
ScrollViewer scroller = ScrollHost;
if (scroller != null)
{
double viewportHeight = scroller.ViewportHeight;
double startTop, startBottom;
_selectedContainer.GetTopAndBottom(scroller, out startTop, out startBottom);
TreeViewItem select = null;
TreeViewItem next = _selectedContainer;
ItemsControl parent = _selectedContainer.ParentItemsControl;
if (parent != null)
{
if (up)
{
// When going up, we need to start at the first level of TreeViewItems.
// When going down, we need to start at the selected container.
while (parent != this)
{
ItemsControl nextParent = ItemsControl.ItemsControlFromItemContainer(parent);
if (nextParent == null)
{
break;
}
else
{
next = (TreeViewItem)parent;
parent = nextParent;
}
}
}
int index = parent.ItemContainerGenerator.IndexFromContainer(next);
int count = parent.Items.Count;
while ((parent != null) && (next != null))
{
if (next.IsEnabled)
{
double delta;
if (next.HandleScrollByPage(up, scroller, viewportHeight, startTop, startBottom, out delta))
{
// This item or one of its children was focused
return true;
}
else if (DoubleUtil.GreaterThan(delta, viewportHeight))
{
// This item does not fit
// If select target is already the same element as _selectedContainer - there is no point to select it again
// In this case we select the next item although it cannot completely fit into view
if (select == _selectedContainer || select == null)
return up ? _selectedContainer.HandleUpKey() : _selectedContainer.HandleDownKey();
break;
}
else
{
// This item does fit, but we should continue searching
select = next;
}
}
index = index + (up ? -1 : 1);
if ((0 <= index) && (index < count))
{
next = parent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
}
else if (parent == this)
{
// That was the last item in the TreeView
next = null;
}
else
{
// Go up the parent chain to a parent with another item
while (parent != null)
{
ItemsControl oldParent = parent;
parent = ItemsControl.ItemsControlFromItemContainer(parent);
if (parent != null)
{
count = parent.Items.Count;
index = parent.ItemContainerGenerator.IndexFromContainer(oldParent) + (up ? -1 : 1);
if ((0 <= index) && (index < count))
{
next = parent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
break;
}
else if (parent == this)
{
// That was the last item in the TreeView
parent = next = null;
}
}
}
}
}
if (select != null)
{
// Earlier we found an item that fit but didn't focus it at that time
if (up)
{
if (select != _selectedContainer)
{
return select.Focus();
}
}
else
{
return TreeViewItem.FocusIntoItem(select);
}
}
}
}
return false;
}
#endregion
#region IsSelectionActive
///
/// An event reporting that the IsKeyboardFocusWithin property changed.
///
protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
{
base.OnIsKeyboardFocusWithinChanged(e);
// When focus within changes we need to update the value of IsSelectionActive.
bool isSelectionActive = false;
bool isKeyboardFocusWithin = IsKeyboardFocusWithin;
if (isKeyboardFocusWithin)
{
// Keyboard focus is within the control, selection should appear active.
isSelectionActive = true;
}
else
{
DependencyObject currentFocus = Keyboard.FocusedElement as DependencyObject;
if (currentFocus != null)
{
UIElement root = KeyboardNavigation.GetVisualRoot(this) as UIElement;
if (root != null && root.IsKeyboardFocusWithin)
{
if (FocusManager.GetFocusScope(currentFocus) != root)
{
isSelectionActive = true;
}
}
}
}
if ((bool)GetValue(Selector.IsSelectionActiveProperty) != isSelectionActive)
{
// The value changed, set the new value.
SetValue(Selector.IsSelectionActivePropertyKey, BooleanBoxes.Box(isSelectionActive));
}
if (isKeyboardFocusWithin && IsKeyboardFocused && (_selectedContainer != null) && !_selectedContainer.IsKeyboardFocusWithin)
{
_selectedContainer.Focus();
}
}
private void OnFocusEnterMainFocusScope(object sender, EventArgs e)
{
// When KeyboardFocus comes back to the main focus scope and the TreeView does not have focus within- clear IsSelectionActivePrivateProperty
if (!IsKeyboardFocusWithin)
{
ClearValue(Selector.IsSelectionActivePropertyKey);
}
}
private static DependencyObject FindParent(DependencyObject o)
{
Visual v = o as Visual;
ContentElement ce = (v == null) ? o as ContentElement : null;
if (ce != null)
{
o = ContentOperations.GetParent(ce);
if (o != null)
{
return o;
}
else
{
FrameworkContentElement fce = ce as FrameworkContentElement;
if (fce != null)
{
return fce.Parent;
}
}
}
else if (v != null)
{
return VisualTreeHelper.GetParent(v);
}
return null;
}
#endregion
#region Automation
///
/// Creates AutomationPeer ( )
///
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TreeViewAutomationPeer(this);
}
#endregion
#region DTypeThemeStyleKey
// Returns the DependencyObjectType for the registered ThemeStyleKey's default
// value. Controls will override this method to return approriate types.
internal override DependencyObjectType DTypeThemeStyleKey
{
get { return _dType; }
}
private static DependencyObjectType _dType;
#endregion DTypeThemeStyleKey
#endregion
#region Data
private enum Bits
{
IsSelectionChangeActive = 0x1,
}
// Packed boolean information
private BitVector32 _bits = new BitVector32(0);
private TreeViewItem _selectedContainer;
// Used to retrieve the value of an item, according to the SelectedValuePath
private static readonly BindingExpressionUncommonField SelectedValuePathBindingExpression = new BindingExpressionUncommonField();
private EventHandler _focusEnterMainFocusScopeEventHandler;
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.