using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace AIStudio.Wpf.DiagramDesigner.Helpers { public static class VisualHelper { #region BindCommand /// /// 绑定命令和命令事件到宿主UI /// public static void BindCommand(this UIElement @ui, ICommand com, Action call) { var bind = new CommandBinding(com); bind.Executed += new ExecutedRoutedEventHandler(call); @ui.CommandBindings.Add(bind); } /// /// 绑定RelayCommand命令到宿主UI /// public static void BindCommand(this UIElement @ui, ICommand com, object cancelExcute) { var bind = new CommandBinding(com); @ui.CommandBindings.Add(bind); } #endregion #region TreeView操作扩展方法 //code:http://www.codeproject.com/Articles/36193/WPF-TreeView-tools /// /// Returns the TreeViewItem of a data bound object. /// /// TreeView /// Data bound object /// The TreeViewItem of the data bound object or null. public static TreeViewItem GetItemFromObject(this TreeView treeView, object obj) { try { DependencyObject dObject = GetContainerFormObject(treeView, obj); TreeViewItem tvi = dObject as TreeViewItem; while (tvi == null) { dObject = VisualTreeHelper.GetParent(dObject); tvi = dObject as TreeViewItem; } return tvi; } catch { } return null; } private static DependencyObject GetContainerFormObject(ItemsControl item, object obj) { if (item == null) return null; DependencyObject dObject = null; dObject = item.ItemContainerGenerator.ContainerFromItem(obj); if (dObject != null) return dObject; var query = from childItem in item.Items.Cast() let childControl = item.ItemContainerGenerator.ContainerFromItem(childItem) as ItemsControl select GetContainerFormObject(childControl, obj); return query.FirstOrDefault(i => i != null); } /// /// Selects a data bound object of a TreeView. /// /// TreeView /// Data bound object public static void SelectObject(this TreeView treeView, object obj) { treeView.SelectObject(obj, true); } /// /// Selects or deselects a data bound object of a TreeView. /// /// TreeView /// Data bound object /// select or deselect public static void SelectObject(this TreeView treeView, object obj, bool selected) { var tvi = treeView.GetItemFromObject(obj); if (tvi != null) { tvi.IsSelected = selected; } } /// /// Returns if a data bound object of a TreeView is selected. /// /// TreeView /// Data bound object /// Returns true if the object is selected, and false if it is not selected or obj is not in the tree. public static bool IsObjectSelected(this TreeView treeView, object obj) { var tvi = treeView.GetItemFromObject(obj); if (tvi != null) { return tvi.IsSelected; } return false; } /// /// Returns if a data bound object of a TreeView is focused. /// /// TreeView /// Data bound object /// Returns true if the object is focused, and false if it is not focused or obj is not in the tree. public static bool IsObjectFocused(this TreeView treeView, object obj) { var tvi = treeView.GetItemFromObject(obj); if (tvi != null) { return tvi.IsFocused; } return false; } /// /// Expands a data bound object of a TreeView. /// /// TreeView /// Data bound object public static void ExpandObject(this TreeView treeView, object obj) { treeView.ExpandObject(obj, true); } /// /// Expands or collapses a data bound object of a TreeView. /// /// TreeView /// Data bound object /// expand or collapse public static void ExpandObject(this TreeView treeView, object obj, bool expanded) { var tvi = treeView.GetItemFromObject(obj); if (tvi != null) { tvi.IsExpanded = expanded; if (expanded) { // update layout, so that following calls to f.e. SelectObject on child nodes will // find theire TreeViewNodes treeView.UpdateLayout(); } } } /// /// Returns if a douta bound object of a TreeView is expanded. /// /// TreeView /// Data bound object /// Returns true if the object is expanded, and false if it is collapsed or obj is not in the tree. public static bool IsObjectExpanded(this TreeView treeView, object obj) { var tvi = treeView.GetItemFromObject(obj); if (tvi != null) { return tvi.IsExpanded; } return false; } /// /// Retuns the parent TreeViewItem. /// /// TreeViewItem /// Parent TreeViewItem public static TreeViewItem GetParentItem(this TreeViewItem item) { var dObject = VisualTreeHelper.GetParent(item); TreeViewItem tvi = dObject as TreeViewItem; while (tvi == null) { dObject = VisualTreeHelper.GetParent(dObject); tvi = dObject as TreeViewItem; } return tvi; } #endregion public static T GetChild(DependencyObject d) where T : DependencyObject { if (d is T t) return t; for (var i = 0; i < VisualTreeHelper.GetChildrenCount(d); i++) { var child = VisualTreeHelper.GetChild(d, i); var result = GetChild(child); if (result != null) return result; } return default; } #region FindParent/FindChildren /// /// Finds a parent of a given item on the visual tree. /// /// The type of the queried item. /// A direct or indirect child of the /// queried item. /// The first parent item that matches the submitted /// type parameter. If not matching item can be found, a null /// reference is being returned. public static T TryFindParent(this DependencyObject child) where T : DependencyObject { //get parent item DependencyObject parentObject = GetParentObject(child); //we've reached the end of the tree if (parentObject == null) return null; //check if the parent matches the type we're looking for T parent = parentObject as T; return parent ?? TryFindParent(parentObject); } /// /// Finds a Child of a given item in the visual tree. /// /// A direct parent of the queried item. /// The type of the queried item. /// x:Name or Name of child. /// The first parent item that matches the submitted type parameter. /// If not matching item can be found, /// a null parent is being returned. public static T FindChild(this DependencyObject parent, string childName = null) where T : DependencyObject { // Confirm parent and childName are valid. if (parent == null) return null; T foundChild = null; int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); // If the child is not of the request child type child T childType = child as T; if (childType == null) { // recursively drill down the tree foundChild = FindChild(child, childName); // If the child is found, break so we do not overwrite the found child. if (foundChild != null) break; } else if (!string.IsNullOrEmpty(childName)) { var frameworkElement = child as FrameworkElement; // If the child's name is set for search if (frameworkElement != null && frameworkElement.Name == childName) { // if the child's name is of the request name foundChild = (T)child; break; } } else { // child element found. foundChild = (T)child; break; } } return foundChild; } /// /// This will search for a child of the specified type. The search is performed /// hierarchically, breadth first (as opposed to depth first). /// /// The type of the element to find /// The root of the tree to search for. This element itself is not checked. /// Provide a callback to check additional properties /// of the found elements. Can be left Null if no additional criteria are needed. /// Returns the found element. Null if nothing is found. /// Button button = TreeHelper.FindChild<Button>( this, foundChild => foundChild.Focusable ); public static T FindChild(DependencyObject parent, Func additionalCheck) where T : DependencyObject { int childrenCount = VisualTreeHelper.GetChildrenCount(parent); T child; for (int index = 0; index < childrenCount; index++) { child = VisualTreeHelper.GetChild(parent, index) as T; if (child != null) { if (additionalCheck == null) { return child; } else { if (additionalCheck(child)) return child; } } } for (int index = 0; index < childrenCount; index++) { child = FindChild(VisualTreeHelper.GetChild(parent, index), additionalCheck); if (child != null) return child; } return null; } /// /// This method is an alternative to WPF's /// method, which also /// supports content elements. Keep in mind that for content element, /// this method falls back to the logical tree of the element! /// /// The item to be processed. /// The submitted item's parent, if available. Otherwise /// null. public static DependencyObject GetParentObject(this DependencyObject child) { if (child == null) return null; //handle content elements separately var contentElement = child as ContentElement; if (contentElement != null) { DependencyObject parent = ContentOperations.GetParent(contentElement); if (parent != null) return parent; var fce = contentElement as FrameworkContentElement; return fce != null ? fce.Parent : null; } //also try searching for parent in framework elements (such as DockPanel, etc) var frameworkElement = child as FrameworkElement; if (frameworkElement != null) { DependencyObject parent = frameworkElement.Parent; if (parent != null) return parent; } //if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper return VisualTreeHelper.GetParent(child); } /// /// Analyzes both visual and logical tree in order to find all elements of a given /// type that are descendants of the item. /// /// The type of the queried items. /// The root element that marks the source of the search. If the /// source is already of the requested type, it will not be included in the result. /// Sometimes it's better to search in the VisualTree (e.g. in tests) /// All descendants of that match the requested type. public static IEnumerable FindChildren(this DependencyObject source, bool forceUsingTheVisualTreeHelper = false) where T : DependencyObject { if (source != null) { var childs = GetChildObjects(source, forceUsingTheVisualTreeHelper); foreach (DependencyObject child in childs) { //analyze if children match the requested type if (child != null && child is T) { yield return (T)child; } //recurse tree foreach (T descendant in FindChildren(child)) { yield return descendant; } } } } /// /// This method is an alternative to WPF's /// method, which also /// supports content elements. Keep in mind that for content elements, /// this method falls back to the logical tree of the element. /// /// The item to be processed. /// Sometimes it's better to search in the VisualTree (e.g. in tests) /// The submitted item's child elements, if available. public static IEnumerable GetChildObjects(this DependencyObject parent, bool forceUsingTheVisualTreeHelper = false) { if (parent == null) yield break; if (!forceUsingTheVisualTreeHelper && (parent is ContentElement || parent is FrameworkElement)) { //use the logical tree for content / framework elements foreach (object obj in LogicalTreeHelper.GetChildren(parent)) { var depObj = obj as DependencyObject; if (depObj != null) yield return (DependencyObject)obj; } } else { //use the visual tree per default int count = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < count; i++) { yield return VisualTreeHelper.GetChild(parent, i); } } } /// /// Tries to locate a given item within the visual tree, /// starting with the dependency object at a given position. /// /// The type of the element to be found /// on the visual tree of the element at the given location. /// The main element which is used to perform /// hit testing. /// The position to be evaluated on the origin. public static T TryFindFromPoint(UIElement reference, Point point) where T : DependencyObject { var element = reference.InputHitTest(point) as DependencyObject; if (element == null) return null; if (element is T) return (T)element; return TryFindParent(element); } #endregion /// /// 返回指定对象的特定类型的祖先。 /// /// 要获取的祖先的类型。 /// 获取的祖先,如果不存在则为 null。 /// 获取的祖先对象。 public static T GetAncestor(DependencyObject source) where T : DependencyObject { if (source == null) return null; do { source = VisualTreeHelper.GetParent(source); } while (source != null && !(source is T)); return source as T; } private static DependencyObject GetParent(DependencyObject element) { Visual visual = element as Visual; DependencyObject parent = (visual == null) ? null : VisualTreeHelper.GetParent(visual); if (parent == null) { // No Visual parent. Check in the logical tree. FrameworkElement fe = element as FrameworkElement; if (fe != null) { parent = fe.Parent; if (parent == null) { parent = fe.TemplatedParent; } } else { FrameworkContentElement fce = element as FrameworkContentElement; if (fce != null) { parent = fce.Parent; if (parent == null) { parent = fce.TemplatedParent; } } } } return parent; } public static bool IsDescendantOf(DependencyObject element, DependencyObject parent) { while (element != null) { if (element == parent) return true; element = GetParent(element); } return false; } /// /// 查找元素的子元素 /// /// 子元素类型 /// /// public static T FindVisualChild(DependencyObject obj) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is T) return (T)child; else { T childOfChild = FindVisualChild(child); if (childOfChild != null) return childOfChild; } } return null; } /// /// 得到指定元素的集合 /// /// /// /// public static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { yield return (T)child; } foreach (T childOfChild in FindVisualChildren(child)) { yield return childOfChild; } } } } /// /// 利用visualtreehelper寻找对象的子级对象 /// /// /// /// public static List FindVisualChildrenEx(DependencyObject obj) where T : DependencyObject { try { List TList = new List { }; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is T) { TList.Add((T)child); List childOfChildren = FindVisualChildrenEx(child); if (childOfChildren != null) { TList.AddRange(childOfChildren); } } else { List childOfChildren = FindVisualChildrenEx(child); if (childOfChildren != null) { TList.AddRange(childOfChildren); } } } return TList; } catch (Exception e) { return null; } } /// /// 查找元素的父元素 /// /// /// /// public static T FindParent(DependencyObject i_dp) where T : DependencyObject { DependencyObject dobj = (DependencyObject)VisualTreeHelper.GetParent(i_dp); if (dobj != null) { if (dobj is T) { return (T)dobj; } else { dobj = FindParent(dobj); if (dobj != null && dobj is T) { return (T)dobj; } } } return null; } public static T FindParent(DependencyObject i_dp, string elementName) where T : DependencyObject { DependencyObject dobj = (DependencyObject)VisualTreeHelper.GetParent(i_dp); if (dobj != null) { if (dobj is T && ((System.Windows.FrameworkElement)(dobj)).Name.Equals(elementName)) { return (T)dobj; } else { dobj = FindParent(dobj); if (dobj != null && dobj is T) { return (T)dobj; } } } return null; } /// /// 查找指定名称的元素 /// /// 元素类型 /// /// 元素名称,及xaml中的Name /// public static childItem FindVisualElement(DependencyObject obj, string elementName) where childItem : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is childItem && ((System.Windows.FrameworkElement)(child)).Name.Equals(elementName)) return (childItem)child; else { IEnumerator j = FindVisualChildren(child).GetEnumerator(); while (j.MoveNext()) { childItem childOfChild = (childItem)j.Current; if (childOfChild != null && !(childOfChild as FrameworkElement).Name.Equals(elementName)) { FindVisualElement(childOfChild, elementName); } else { return childOfChild; } } } } return null; } /// /// 命中测试。根据当前选中元素,查找视觉树父节点与子节点,看是否存在指定类型的元素 /// /// 想命中的元素类型 /// 当前选中元素 /// true:命中成功 public static bool HitTest(DependencyObject dp) where T : DependencyObject { return FindParent(dp) != null || FindVisualChild(dp) != null; } public static T FindEqualElement(DependencyObject source, DependencyObject element) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(source); i++) { DependencyObject child = VisualTreeHelper.GetChild(source, i); if (child != null && child is T && child == element) { return (T)child; } else { T childOfChild = FindVisualChild(child); if (childOfChild != null) { return childOfChild; } } } return null; } } }