今天按照这一年来经常用到的那些静态的工具类再来做一次总结,这些小的工具来可以作为自己学习的很好的例子,通过总结这些东西,能够很大程度上梳理自己的知识体系,当然这个是经常用到的,接下来就一个个去分析这些类的作用和蕴含的知识点。

ControlUtil

  这个工具类是非常重要的,它的主要作用就是通过视觉树和逻辑树去寻找父节点或者子节点,这个例子在网上非常多,其核心都是通过迭代的思想去寻找自己最终想要的子控件,当然这里面有很多的内容,下面通过一个个的实例来说明这些代码的具体作用。

/// <summary>
/// 查找制定类别的子控件
/// </summary>
/// <typeparam name="T">控件类别</typeparam>
/// <param name="parent">父控件</param>
/// <returns>子控件集合</returns>
public static List<T> FindChildControls<T>(UIElement parent)
where T : UIElement
{
List<T> list = new List<T>();
if (parent == null) return list; if (parent is ContentControl)
{
if ((parent as ContentControl).Content is T)
{
list.Add((parent as ContentControl).Content as T);
}
list.AddRange(FindChildControls<T>((parent as ContentControl).Content as UIElement));
} if (parent is Decorator)
{
if ((parent as Decorator).Child is T)
list.Add((parent as Decorator).Child as T);
list.AddRange(FindChildControls<T>((parent as Decorator).Child));
} if (parent is ItemsControl)
{
ItemsControl ic = parent as ItemsControl;
foreach (object itm in ic.Items)
{
if (itm is T)
list.Add(itm as T);
if (itm is UIElement)
list.AddRange(FindChildControls<T>(itm as UIElement));
}
} if (parent is Panel)
{
Panel pan = parent as Panel;
foreach (UIElement ui in pan.Children)
{
if (ui is T)
{
list.Add(ui as T);
}
list.AddRange(FindChildControls<T>(ui));
}
} return list;
}

  由于传入的参数是UIElement,非常顶层的元素,所以在代码中我们进行了不同的分类,具体的分类分为下面几种ContentControl、Decorator、ItemsControl、Panel,通过这个分类基本上可以把父元素的所有子控件全部枚举出来,当然这段代码不太理想的地方就是过于庞大,无法准确去获取某一个元素,如果传入顶层的元素,那么我们将获取非常庞大的内容,这个具体的可以写一些测试代码去测试!

 public static T FindVisualParent<T>(DependencyObject reference) where T : class
{
var parent = VisualTreeHelper.GetParent(reference);
if (parent == null)
return null;
else
{
if (parent is T)
{
return parent as T;
}
else
{
return FindVisualParent<T>(parent);
}
}
}

  这段代码非常好理解,就是通过VisualTreeHelper来不断去获取Parent直到获取到第一个类型为T的父类型,下面我会针对这几种类型来写一个测试的demo,现在我来说说这些主要用在哪些地方。

  a 比如重写了Button的模板,但是在点击的时候首先获取点击事件的可能是Button里面能够接受焦点的元素,这个时候我们可以传入首先获取焦点的对象,通过这个方法来获取Button,比如由于WPF支持事件的路由,所以在这种情况下通过传入e.OriginalSource来获取到Button,然后为Button做各种操作。

  b 为当前对象this查找父类容器,比如我们我们经常会用到将Model绑定到前台的控件中(比如ItemsControl)那么我们可以传入当前的Model所应用到的数据模板中的对象(例如Image、TextBox、Label......),然后获取到父容器或者其他的对象,这种也是非常常用的方面(但是这里需要特别注意的地方是传入的参数必须是继承自DependencyObject的对象)。

public static List<T> FindVisualChildren<T>(DependencyObject reference)
where T : UIElement
{
List<T> list = new List<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(reference); i++)
{
var child = VisualTreeHelper.GetChild(reference, i);
if (child != null && child is T)
{
list.Add(child as T);
}
list.AddRange(FindVisualChildren<T>(child));
}
return list;
}

  与寻找父类型不同,这段代码能够获取到所有的类型为T的子元素,并返回一个List<T>的对象,这个方法的好处就是能够沿着当前传入的DependencyObject为起点沿着视觉树一直向下查找,包括模板中找到的元素。

下面通过一个DEMO(一个简单的照片浏览器)来说明一下,首先看一下软件界面。  

  这是一个简单的照片浏览程序,右边是一个图像列表,是一个ListBox对象,里面绑定了一个ObservableCollection的集合,当点击每一个ListBoxItem对象时,左侧会弹出相应的对象,同时下面的按钮也能够同步控制图片的选择功能,对于这个DEMO我们来一步步去分析视觉树上的父对象和子对象!

  首先来看看MainWindow.xaml(这里仅仅贴出核心的代码)

<Grid Background="Gray" x:Name="root">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="150"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="照片浏览器" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Foreground="White" FontSize="28" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="楷体"/>
<!--<ContentPresenter Grid.Row="1" Content="{Binding Path=ImgCollection}"/>--> <ListBox x:Name="pictureItemsControl" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding ImageCollectionView,Mode=TwoWay}" AlternationCount="9999" ClipToBounds="False" FocusVisualStyle="{x:Null}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<interactive:ExInvokeCommandAction Command="{Binding OnListBoxSelected}">
</interactive:ExInvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.Template>
<ControlTemplate TargetType="ItemsControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="Teal" CornerRadius="2">
<Label Content="照片列表" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
</Border>
<Border Padding="2" Background="Teal" CornerRadius="2" Grid.Row="1">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter></ItemsPresenter>
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:PictureModel}">
<Border Padding="2">
<Image Source="{Binding ImgSource, Mode=TwoWay}" Stretch="Fill" Width="120" Height="120" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Image Grid.Row="1" Grid.Column="0" Source="{Binding CurrentItem.ImgSource, Mode=TwoWay}" Stretch="Uniform" Width="Auto" Height="Auto"/>
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="{Binding FirstPicture, Mode=TwoWay}" Width="60" Height="60" Margin="10,0,0,0" RenderTransformOrigin="0.5,0.5">
<Button.Background>
<ImageBrush ImageSource="/CollectionView;Component/Images/First.png"/>
</Button.Background>
</Button>
<Button Command="{Binding PreviousPicture, Mode=TwoWay}" Width="60" Height="60" Margin="10,0,0,0" RenderTransformOrigin="0.5,0.5">
<Button.Background>
<ImageBrush ImageSource="/CollectionView;Component/Images/Forward.png"/>
</Button.Background>
</Button>
<Button Command="{Binding NextPicture, Mode=TwoWay}" Width="60" Height="60" Margin="10,0,0,0" RenderTransformOrigin="0.5,0.5">
<Button.Background>
<ImageBrush ImageSource="/CollectionView;Component/Images/Next.png"/>
</Button.Background>
</Button>
<Button Command="{Binding LastPicture, Mode=TwoWay}" Width="60" Height="60" Margin="10,0,0,0" RenderTransformOrigin="0.5,0.5">
<Button.Background>
<ImageBrush ImageSource="/CollectionView;Component/Images/Last.png"/>
</Button.Background>
</Button>
</StackPanel>
</Grid>
</Grid>
</Grid>

  在ViewModel层绑定到前台ListBox的总共有7个对象(七个图片),定义了一个CollectionView的集合。

private System.Windows.Data.CollectionView _imageCollectionView
= null; public System.Windows.Data.CollectionView ImageCollectionView
{
get
{
return _imageCollectionView;
}
set
{
if (value != _imageCollectionView)
{
_imageCollectionView = value;
RaisePropertyChanged<System.Windows.Data.CollectionView>(() => this.ImageCollectionView);
}
}
}

  在后台的代码中,我们通过 List<Image> myImageList = X.Utils.ControlUtil.FindVisualChildren<Image>(lb) 其中lb是通过程序中获取到的ListBox对象,通过代码调用我们能够获取到所有的ListBox中的Image对象(注:这个对象是在ListBox.ItemTemplate中定义的),总共获取到了7个对象,没有任何问题。

  另外我们再进行一个测试,在这段代码中List<Image> myImageList = X.Utils.ControlUtil.FindVisualChildren<Image>(this.root),即向代码中传入根节点Grid,通过代码总共能够获取到8个对象,即ListBox中的7个对象另外再加上左侧的一张大的图片,总共8张。<Image Grid.Row="1" Grid.Column="0" Source="{Binding CurrentItem.ImgSource, Mode=TwoWay}" Stretch="Uniform" Width="Auto" Height="Auto"/>(大图片)

同样的道理我们可以去获取当前对象的VisualParent,同样也是通过这种方式来获取的......

这里我需要重点介绍的是我们都知道FrameworkElement下面有一个Parent属性,返回的是DependencyObject属性,MSDN的解释是:获取此元素的逻辑父元素。那么这个属性和通过VisualTreeHelper有什么不同呢?我们也列出了一些常用的代码,并通过DEMO来说明这些用法。

public static Window FindParentWindow(FrameworkElement element)
{
if (element.Parent == null)
{
return null;
} Window window = element.Parent as Window;
if (window != null)
{
return window;
} FrameworkElement parentElement = element.Parent as FrameworkElement;
if (parentElement != null)
{
return FindParentWindow(parentElement);
} return null;
}

  通过代码我们可以发现,这个方法只能够获取到当前节点的逻辑父节点,而且只能够一级一级的去迭代获取,如果其中一个元素的Parent为null的话那么就不能够得到最终的父元素。还是前面的那个例子,我们通过VS 的WPF Tree Visualizer这个工具可以很清楚的看到整个视觉树的所有元素,我们发现视觉树中的很多元素其Parent都是null,特别是定义在模板中的元素,Parent属性都是null,这些该怎么解释呢?FrameWorkElement.Parent 是获取此元素的逻辑父级元素,也就是通过LogicalTreeHelper查找。在MSDN中备注了:

对于模板,模板的 Parent 最终将为 null。 若要忽略这一点并扩展到实际应用模板的逻辑树,请使用 TemplatedParent,也就是通过这种方式查找父元素时只能在逻辑树上查找,否则只能返回为Null。

  按照这个思路我们再一步步分析,请看下面的代码:

public static FrameworkElement FindParentWithDataContextAndName<DataContextT>(FrameworkElement childElement, string name)
where DataContextT : class
{
FrameworkElement parent = (FrameworkElement)childElement.Parent;
if (parent != null)
{
DataContextT data = parent.DataContext as DataContextT;
if (data != null)
{
if (parent.Name == name)
{
return parent;
}
} parent = FindParentWithDataContextAndName<DataContextT>(parent, name);
if (parent != null)
{
return parent;
}
} parent = (FrameworkElement)childElement.TemplatedParent;
if (parent != null)
{
DataContextT data = parent.DataContext as DataContextT;
if (data != null)
{
if (parent.Name == name)
{
return parent;
}
} parent = FindParentWithDataContextAndName<DataContextT>(parent, name);
if (parent != null)
{
return parent;
}
} return null;
}

  这段代码是寻找特定的DataContext和name的Parent对象,在代码中如果通过FrameWorkElement.Parent来获取到的对象为null的话,就会通过parent = (FrameworkElement)childElement.TemplatedParent;通过当前元素的TemplateParent来获取父元素,通过这种方式是可以找到特定的DataContext和name的Parent对象,这点我们也可以通过实际的代码去验证的。

下面的这段代码和上面的类似,这里不再赘述!

public static FrameworkElement FindParentWithDataContext<DataContextT>(FrameworkElement childElement)
where DataContextT : class
{
if (childElement.Parent != null)
{
DataContextT data = ((FrameworkElement)childElement.Parent).DataContext as DataContextT;
if (data != null)
{
return (FrameworkElement)childElement.Parent;
} FrameworkElement parent = FindParentWithDataContext<DataContextT>((FrameworkElement)childElement.Parent);
if (parent != null)
{
return parent;
}
} if (childElement.TemplatedParent != null)
{
DataContextT data = ((FrameworkElement)childElement.TemplatedParent).DataContext as DataContextT;
if (data != null)
{
return (FrameworkElement)childElement.TemplatedParent;
} FrameworkElement parent = FindParentWithDataContext<DataContextT>((FrameworkElement)childElement.TemplatedParent);
if (parent != null)
{
return parent;
}
} return null;
}

  下面来看看将两种方式组合来查找特定类型的父对象以及子对象

public static ParentT FindParentWithType<ParentT>(FrameworkElement childElement)
where ParentT : class
{
if (childElement.Parent != null)
{
ParentT parent = childElement.Parent as ParentT;
if (parent != null)
{
return parent;
} parent = FindParentWithType<ParentT>((FrameworkElement)childElement.Parent);
if (parent != null)
{
return parent;
}
} if (childElement.TemplatedParent != null)
{
ParentT parent = childElement.TemplatedParent as ParentT;
if (parent != null)
{
return parent;
} parent = FindParentWithType<ParentT>((FrameworkElement)childElement.TemplatedParent);
if (parent != null)
{
return parent;
}
} FrameworkElement parentElement = (FrameworkElement)VisualTreeHelper.GetParent(childElement);
if (parentElement != null)
{
ParentT parent = parentElement as ParentT;
if (parent != null)
{
return parent;
} return FindParentWithType<ParentT>(parentElement);
} return null;
}

  查找子对象

/// <summary>
/// Find the framework element for the specified connector.
/// </summary>
public static ElementT FindElementWithType<ElementT>(Visual rootElement)
where ElementT : FrameworkElement
{
if (rootElement == null)
{
throw new ArgumentNullException("rootElement");
} FrameworkElement rootFrameworkElement = rootElement as FrameworkElement;
if (rootFrameworkElement != null)
{
rootFrameworkElement.UpdateLayout();
} //
// Check each child.
//
int numChildren = VisualTreeHelper.GetChildrenCount(rootElement);
for (int i = 0; i < numChildren; ++i)
{
Visual childElement = (Visual)VisualTreeHelper.GetChild(rootElement, i); ElementT typedChildElement = childElement as ElementT;
if (typedChildElement != null)
{
return typedChildElement;
}
} //
// Check sub-trees.
//
for (int i = 0; i < numChildren; ++i)
{
Visual childElement = (Visual)VisualTreeHelper.GetChild(rootElement, i); ElementT foundElement = FindElementWithType<ElementT>(childElement);
if (foundElement != null)
{
return foundElement;
}
} return null;
}  

再介绍一个重载的方法用于查找特定类型,特定DataContext的父元素以及子元素,这个方法在WPF这种MVVM模式下是非常重要的。

 public static ParentT FindParentWithTypeAndDataContext<ParentT>(FrameworkElement childElement, object dataContext)
where ParentT : FrameworkElement
{
if (childElement.Parent != null)
{
ParentT parent = childElement.Parent as ParentT;
if (parent != null)
{
if (parent.DataContext == dataContext)
{
return parent;
}
} parent = FindParentWithTypeAndDataContext<ParentT>((FrameworkElement)childElement.Parent, dataContext);
if (parent != null)
{
return parent;
}
} if (childElement.TemplatedParent != null)
{
ParentT parent = childElement.TemplatedParent as ParentT;
if (parent != null)
{
if (parent.DataContext == dataContext)
{
return parent;
}
} parent = FindParentWithTypeAndDataContext<ParentT>((FrameworkElement)childElement.TemplatedParent, dataContext);
if (parent != null)
{
return parent;
}
} FrameworkElement parentElement = (FrameworkElement)VisualTreeHelper.GetParent(childElement);
if (parentElement != null)
{
ParentT parent = parentElement as ParentT;
if (parent != null)
{
return parent;
} return FindParentWithType<ParentT>(parentElement);
} return null;
}

  查找子元素

public static ElementT FindElementWithDataContext<DataContextT, ElementT>(Visual rootElement, DataContextT data)
where DataContextT : class
where ElementT : FrameworkElement
{
if (rootElement == null)
{
throw new ArgumentNullException("rootElement");
} FrameworkElement rootFrameworkElement = rootElement as FrameworkElement;
if (rootFrameworkElement != null)
{
rootFrameworkElement.UpdateLayout();
} int numChildren = VisualTreeHelper.GetChildrenCount(rootElement);
for (int i = 0; i < numChildren; ++i)
{
Visual childElement = (Visual)VisualTreeHelper.GetChild(rootElement, i); ElementT typedChildElement = childElement as ElementT;
if (typedChildElement != null &&
typedChildElement.DataContext == data)
{
return typedChildElement;
} ElementT foundElement = FindElementWithDataContext<DataContextT, ElementT>(childElement, data);
if (foundElement != null)
{
return foundElement;
}
} return null;
}

  最后再以一个非常常见的查找DataTemplate的代码来结束这一节的内容。

在实际的项目开发中查找某一个元素使用了何种DataTemplate也是有需要的,通过为某一个对象定义DataTemplate,同时定义DataTemplate的Key我们就能够获取到相应的元素,通过下面的代码我们也能够获取到相应的内容,仅供参考。

public static DataTemplateT FindTemplateForType<DataTemplateT>(Type type, FrameworkElement element)
where DataTemplateT : class
{
object resource = element.TryFindResource(new DataTemplateKey(type));
DataTemplateT dataTemplate = resource as DataTemplateT;
if (dataTemplate != null)
{
return dataTemplate;
} if (type.BaseType != null &&
type.BaseType != typeof(object))
{
dataTemplate = FindTemplateForType<DataTemplateT>(type.BaseType, element);
if (dataTemplate != null)
{
return dataTemplate;
}
} foreach (Type interfaceType in type.GetInterfaces())
{
dataTemplate = FindTemplateForType<DataTemplateT>(interfaceType, element);
if (dataTemplate != null)
{
return dataTemplate;
}
} return null;
}

  

C# 中那些常用的工具类(Utility Class)(二)的更多相关文章

  1. C#中那些常用的工具类(Utility Class)(一)

    代码越写越多,但是我们也需要经常去反思那些写过的代码,Utility Class就是这一类需要特别去反思总结的类,这些类像工具一样,我们经常通过一些静态方法,通过传入一些参数,然后得到我们需要的结果, ...

  2. C# 中那些常用的工具类(Utility Class)(三)

    今天来接着写这个系列的文章,这一篇主要是用来介绍关于C#中的XML序列化的问题,这个相信大家一定会经常使用它,特别是在WPF中,有时候我们需要将我们后台的数据保存在数据库中,从而在软件下一次启动的时候 ...

  3. commons-collections包中的常用的工具类

    commons-collections包中的常用的工具类 <dependency> <groupId>commons-collections</groupId> & ...

  4. Hutool中那些常用的工具类和方法

    Hutool中那些常用的工具类和方法 Hutool是一个Java工具包,它帮助我们简化每一行代码,避免重复造轮子.如果你有需要用到某些工具方法的时候,不妨在Hutool里面找找,可能就有.本文将对Hu ...

  5. Android常用的工具类

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Prefe ...

  6. Android常用的工具类(转)

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.Prefer ...

  7. 2013最新Android常用的工具类整理

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Pref ...

  8. Java语言Lang包下常用的工具类介绍_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 无论你在开发哪中 Java 应用程序,都免不了要写很多工具类/工具函数.你可知道,有很多现成的工具类可用,并且代码质量都 ...

  9. Java,面试题,简历,Linux,大数据,常用开发工具类,API文档,电子书,各种思维导图资源,百度网盘资源,BBS论坛系统 ERP管理系统 OA办公自动化管理系统 车辆管理系统 各种后台管理系统

    Java,面试题,简历,Linux,大数据,常用开发工具类,API文档,电子书,各种思维导图资源,百度网盘资源BBS论坛系统 ERP管理系统 OA办公自动化管理系统 车辆管理系统 家庭理财系统 各种后 ...

随机推荐

  1. centos下安装 glances 的问题

    开始想安装htop 然后 yum installhtop 没有 yum searchhtop 也没有  然后上github 发现一个比htop还华丽的东西. Glances 大概这个样子的. 可以一览 ...

  2. 想要快速上手 Spring Boot?看这些教程就足够了!

    1.项目名称:分布式敏捷开发系统架构 项目简介:基于 Spring + SpringMVC + Mybatis 分布式敏捷开发系统架构,提供整套公共微服务服务模块:集中权限管理(单点登录).内容管理. ...

  3. 5-servlet简介

    一.servlet1.是什么:java程序来处理页面请求和响应2.实现方式: a.实现Servlet接口 b.继承HttpServlet类 3.步骤: a.创建一个java程序实现Servlet或者继 ...

  4. (折扣计算)需求说明:普通顾客购物满100元打9折;会员购物打8折;会员购物满200元打7.5折(判断语句if-else和switch语句的嵌套结

    package com.summer.cn; import java.util.Scanner; /** * @author Summer *折扣计算 需求说明:普通顾客购物满100元打9折:会员购物 ...

  5. 环境部署(六):Git关联github

    我们使用Git进行版本管理,前面的博客也介绍了Linux下安装Git以及Git基础教程,这篇博客,简单介绍下如何使用Git关联github... 更多关于Git的内容,可参考下列内容: Git官方文档 ...

  6. windows使用.NET CORE下创建MVC,发布到linux运行

    1.在有dotnet core 的环境下,打开控制台.创建文件夹demo1 2.创建MVC程序 3.创建完成 4.使用记事本修改一下HomeController 修改端口 5.发布 6.压缩发布的文件 ...

  7. JasperReport子报表参数传递

    子报表参数传递 下图的参数名称可以自定义 再子报表新增一个同名称的参数即可

  8. 关于NETCORE中的捆绑与最小化 以及与CDN连用

    参考文档:MSDN   Bundling and minification in ASP.NET Core 细说ASP.NET Core静态文件的缓存方式

  9. Jenkins- job之间传参

    前言: 本文介绍插件: Parameterized Trigger plugin的具体使用方法. 一.插件介绍 Parameterized Trigger plugin插件可以让你在构建完成时触发新的 ...

  10. python第六章:三大利器(装饰器,迭代器,生成器)--小白博客

    python装饰器 什么是装饰器?在不修改源代码和调用方式的基础上给其增加新的功能,多个装饰器可以装饰在同一个函数上 # 原理(个人理解):将原函数(bar)的内存地址重新赋值,进行覆盖.新值为装饰器 ...