From 69c56c681b187d0b220a5921625e4b0970caa079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=89=BE=E7=AB=B9?= Date: Sun, 5 Feb 2023 18:54:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E5=85=A5=E4=B8=80=E4=B8=AA=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E5=85=83=E7=B4=A0=E7=9A=84=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Helpers/VisualHelper.cs | 742 ++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 AIStudio.Wpf.DiagramDesigner/Helpers/VisualHelper.cs diff --git a/AIStudio.Wpf.DiagramDesigner/Helpers/VisualHelper.cs b/AIStudio.Wpf.DiagramDesigner/Helpers/VisualHelper.cs new file mode 100644 index 0000000..f8348ba --- /dev/null +++ b/AIStudio.Wpf.DiagramDesigner/Helpers/VisualHelper.cs @@ -0,0 +1,742 @@ +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; + } + + + } +}