1.前言

  最近有部分朋友经常问我,WPF的TreeView控件,如何用MVVM来实现绑定和显示?所以写下了这篇WPF应用基础篇---TreeView.

 2.介绍

  • 案例浏览:

    

                  图 1-1(案例结构图)

  • 目的:本文中做了三个简单的Demo给刚刚入门或者入门不久而且不熟悉TreeView控件在MVVM中具体实现的朋友们。希望以下3个例子能够给他们带来帮助。
  • 背景:Demo是采用现实生活中一个大网络的某一部分网络来作为案例。这里为了演示方便,整个网络由路由器、交换机、集线器等服务器组成。他们的之间的关系是多对多的关系,一个网络中有可能一个路由器包含了多个路由器、交换机、集线器;而且交换机、集线器也是相同的原理。
  • 数据:本文中用到的数据随机产生的测试数据。根据界面中树的深度(下拉框)来选择树最多有多少层,然后创建树结构的数据。这里需要注意的是我们TreeView提供的数据源必须是树结构的;为什么需要树结构的数据呢?大家可能会觉得很奇怪,其实,我们ViewModel要将数据Binding到TreeView控件上就必须指定一个ItemsSource,所以必须把节点的子节点集合绑定到模板中的ItemsSource中。
  • 案例解析:

  整个Demo分为两部分:左边是功能菜单,右边是显示具体内容,可以参考图1-1。

  基础数据:为了实现一下案例功能,我建立了一个SmlAnt.DataLibrary的数据类库,专门提供原始基本类型和基本数据。下面是具体代码:

  

  实体类:

  

  

View Code

  1 namespace DataLibrary
  2 {
  3     /// <summary>
  4     /// 设备状态
  5     /// </summary>
  6     public enum DeviceStatus
  7     {
  8         Connected,Off
  9     }
 10 
 11     /// <summary>
 12     /// 设备基类
 13     /// </summary>
 14     public class Device:INotifyPropertyChanged
 15     {
 16         //是否被选中
 17         private bool? isSelected;
 18         public bool? IsSelected 
 19         {
 20             get { return isSelected; }
 21             set
 22             {
 23                 if (isSelected != value)
 24                 {
 25                     isSelected = value;   
 26                     ChangeChildNodes(this);
 27                     ChangedParentNodes(this);
 28                     NotifyPropertyChanged("IsSelected");
 29                 }
 30             }
 31         }
 32         
 33         private DeviceStatus status;
 34         public DeviceStatus Status
 35         {
 36             get { return status; }
 37             set
 38             {
 39                 if (status != value)
 40                 {
 41                     status = value;
 42                     NotifyPropertyChanged("Status");
 43                 }
 44             }
 45         }
 46 
 47         public string Name { get; set; }
 48         public string ImageUrl{get;set;}
 49 
 50         private List<Device> childNodes;
 51         public List<Device> ChildNodes
 52         {
 53             get { return childNodes; }
 54             set
 55             {
 56                 if (childNodes != value)
 57                 {
 58                     childNodes = value;
 59                     NotifyPropertyChanged("ChildNodes");
 60                 }
 61             }
 62         }
 63 
 64         private Device parentNode;
 65         public Device ParentNode
 66         {
 67             get { return parentNode; }
 68             set
 69             {
 70                 if (parentNode != value)
 71                 {
 72                     parentNode = value;
 73                     NotifyPropertyChanged("ParentNode");
 74                 }
 75             }
 76         }
 77 
 78         /// <summary>
 79         /// 向下遍历,更改孩子节点状态
 80         /// 注意:这里的父节点不是属性而是字段
 81         /// 采用字段的原因是因为不想让父节点触发访问器而触发Setter
 82         /// </summary>
 83         /// <param name="CurrentNode"></param>
 84         public void ChangeChildNodes(Device CurrentNode)
 85         {
 86             if (ChildNodes != null)
 87             {
 88                 foreach (var data in childNodes)
 89                 {
 90                     data.isSelected = CurrentNode.IsSelected;
 91                     CurrentNode.NotifyPropertyChanged("IsSelected");
 92                     if (data.ChildNodes != null)
 93                     {
 94                         data.ChangeChildNodes(data);
 95                     }
 96                 }
 97             }
 98         }
 99 
100         /// <summary>
101         /// 向上遍历,更改父节点状态
102         /// 注意:这里的父节点不是属性而是字段
103         /// 采用字段的原因是因为不想让父节点触发访问器而触发Setter
104         /// </summary>
105         /// <param name="CurrentNode"></param>
106         public void ChangedParentNodes(Device CurrentNode)
107         {
108             if (CurrentNode.ParentNode != null)
109             {
110                 bool? parentNodeState = true;
111                 int selectedCount = 0;  //被选中的个数
112                 int noSelectedCount = 0;    //不被选中的个数
113 
114                 foreach (var data in CurrentNode.ParentNode.ChildNodes)
115                 {
116                     if (data.IsSelected == true)
117                     {
118                         selectedCount++;
119                     }
120                     else if (data.IsSelected == false)
121                     {
122                         noSelectedCount++;
123                     }
124                 }
125 
126                 //如果全部被选中,则修改父节点为选中
127                 if (selectedCount == 
128                     CurrentNode.ParentNode.ChildNodes.Count)
129                 {
130                     parentNodeState = true;
131                 }
132                 //如果全部不被选中,则修改父节点为不被选中
133                 else if (noSelectedCount == 
134                     CurrentNode.ParentNode.ChildNodes.Count)
135                 {
136                     parentNodeState = false;
137                 }
138                 //否则标记父节点(例如用实体矩形填满)
139                 else
140                 {
141                     parentNodeState = null;
142                 }
143 
144                 CurrentNode.parentNode.isSelected = parentNodeState;
145                 CurrentNode.parentNode.NotifyPropertyChanged("IsSelected");
146 
147                 if (CurrentNode.ParentNode.ParentNode != null)
148                 {
149                     ChangedParentNodes(CurrentNode.parentNode);
150                 }
151             }
152         }
153 
154         public void NotifyPropertyChanged(string name)
155         {
156             if(PropertyChanged!=null)
157             PropertyChanged(this,new PropertyChangedEventArgs(name));
158         }
159         public event PropertyChangedEventHandler PropertyChanged;
160     }
161 
162     /// <summary>
163     /// 路由器
164     /// </summary>
165     public class Router : Device
166     {
167 
168     }
169 
170     /// <summary>
171     /// 交换机
172     /// </summary>
173     public class Switcher : Device
174     {
175 
176     }
177 
178     /// <summary>
179     /// 集线器
180     /// </summary>
181     public class Concentrator : Device
182     {
183 
184     }
185 }

  

  

  数据工厂:

  

View Code

  1 public class DataFactory
  2     {
  3         /// <summary>
  4         /// 随机数据产生器
  5         /// </summary>
  6         static Random random = new Random();        
  7 
  8         /// <summary>
  9         /// 根据参数获取设备状态
 10         /// </summary>
 11         /// <param name="intValue"></param>
 12         /// <returns></returns>
 13         private static DeviceStatus GetStatus(int intValue)
 14         {
 15             return intValue % 2 == 0 ? DeviceStatus.Off : DeviceStatus.Connected;
 16         }
 17         
 18         /// <summary>
 19         /// 
 20         /// </summary>
 21         /// <param name="intValue"></param>
 22         /// <returns></returns>
 23         private static String GetName(int intValue)
 24         {
 25             string refValue = "路由器";
 26             if (intValue % 3 == 0)
 27             {
 28                 refValue = "路由器";
 29             }
 30             else if (intValue % 3 == 1)
 31             {
 32                 refValue = "交换机";
 33             }
 34             else
 35             {
 36                 refValue = "集线器";
 37             }
 38             return refValue;
 39         }
 40 
 41         /// <summary>
 42         /// 根据参数创建设备(简单工厂-参数工厂)
 43         /// </summary>
 44         /// <param name="typeValue"></param>
 45         /// <returns></returns>
 46         public static Device DeviceFactory(int typeValue)
 47         {
 48             Device refEntity = null;
 49             if (typeValue % 3 == 0)
 50             {
 51                 refEntity = new Router();
 52             }
 53             else if (typeValue % 3 == 1)
 54             {
 55                 refEntity = new Switcher();
 56             }
 57             else
 58             {
 59                 refEntity = new Concentrator();
 60             }
 61             return refEntity;
 62         }
 63 
 64         /// <summary>
 65         /// 随即获取基类设备数据
 66         /// </summary>
 67         /// <param name="level">当前节点所在层</param>
 68         /// <param name="MaxLevel">树最大深度</param>
 69         /// <returns>设备树</returns>
 70         public static List<Device> GetBaseTypeDevices(int level, int MaxLevel)
 71         {
 72             level++;
 73             var count = random.Next(6, 10);
 74             List<Device> listTo = new List<Device>();
 75             for (int i = 1; i < count; i++)
 76             {
 77                 Device entity = new Device();
 78                 var typeValue = random.Next(1, 6);
 79                 entity.Name = GetName(typeValue);
 80                 entity.ImageUrl = "..\\..\\Resource\\" + entity.Name + ".png";
 81                 entity.Status = GetStatus(typeValue);
 82                 if (level <= MaxLevel)
 83                     entity.ChildNodes = GetBaseTypeDevices(level, MaxLevel);
 84                 listTo.Add(entity);
 85             }
 86             return listTo;
 87         }
 88 
 89         /// <summary>
 90         /// 随即获取所有子类型设备数据
 91         /// </summary>
 92         /// <param name="level">当前节点所在层</param>
 93         /// <param name="MaxLevel">树最大深度</param>
 94         /// <returns>设备树</returns>
 95         public static List<Device> GetAllTypeDevice(int level,int MaxLevel)
 96         {
 97             level++;
 98             var count = random.Next(6, 10);
 99             List<Device> listTo = new List<Device>();
100             for (int i = 1; i < count; i++)
101             {
102                 var typeValue = random.Next(1, 6);
103                 Device entity = DeviceFactory(typeValue);                
104                 entity.Name = GetName(typeValue);
105                 entity.ImageUrl = "..\\..\\Resource\\" + entity.Name + ".png";
106                 entity.Status = GetStatus(typeValue); 
107                 if (level <= MaxLevel)
108                     entity.ChildNodes = GetAllTypeDevice(level,MaxLevel);
109                 listTo.Add(entity);
110             }
111             return listTo;
112         }
113 
114         /// <summary>
115         /// 随即获取所有子类型设备数据
116         /// </summary>
117         /// <param name="level">当前节点所在层</param>
118         /// <param name="MaxLevel">树最大深度</param>
119         /// <param name="parentNode">父节点</param>
120         /// <returns>设备树</returns>
121         public static List<Device> GetAllTypeDevice(int level, int MaxLevel, Device parentNode)
122         {
123             level++;
124             var count = random.Next(6, 10);
125             List<Device> listTo = new List<Device>();
126             for (int i = 1; i < count; i++)
127             {
128                 var typeValue = random.Next(1, 6);
129                 Device entity = DeviceFactory(typeValue);
130                 entity.IsSelected = false;
131                 entity.Name = GetName(typeValue);
132                 entity.ParentNode = parentNode;
133                 entity.ImageUrl = "..\\..\\Resource\\" + entity.Name + ".png";
134                 entity.Status = GetStatus(typeValue);               
135                 if (level <= MaxLevel)
136                     entity.ChildNodes = GetAllTypeDevice(level, MaxLevel, entity);
137                 listTo.Add(entity);
138             }
139             return listTo;
140         }
141     }

 

  案例一,主要为大家介绍如何创建一个无限级的树,其实说简单点就是采用HierarchicalDataTemplate 作为树模板,然后通过Binding把数据绑定到树上。因为模板是HierarchicalDataTemplate这个模板,这里就不详细讲解,如果了解多点可以到MSDN,所以会无限级别的增加,只要数据结构上能支持,数据有多少级别,View中显示的树也会对应有多少级别。而如果采用的是DataTemplate的话,则只能有一层的数据。

  效果图如下:

  

        图 1-2(无限级别树)

  View(XAML)代码 代码1-3:

  

View Code

1 <HierarchicalDataTemplate x:Key="TreeViewTemplate" ItemsSource="{Binding ChildNodes}">
2             <StackPanel Orientation="Horizontal">
3                 <Image Source="{Binding ImageUrl}" Margin="2"/>
4                 <TextBlock Text="{Binding Name}" Margin="2"/>
5             </StackPanel>
6         </HierarchicalDataTemplate>

8 <TreeView Grid.Row="1" ItemTemplate="{StaticResource TreeViewTemplate}" ItemsSource="{Binding DataSource}" Margin="5"/>

  

  ViewModel代码:

View Code

 1 private List<Device> dataSource;
 2         public List<Device> DataSource
 3         {
 4             get { return dataSource; }
 5             set
 6             {
 7                 if (dataSource != value)
 8                 {
 9                     dataSource = value;
10                     RaisePropertyChanged("DataSource");
11                 }
12             }
13         }
14 
15 DataSource = DataFactory.GetBaseTypeDevices(1, SelectedLevel);

  

  

  案例二,主要给大家讲解的是,如何采用DataTmeplateSelector通过重写SelectTemplate方法来实现的。来控制显示样式、右键菜单等功能。这里主要讲的是,不同服务器之间显示不一样,而且连快捷菜单也对应不一样。这里有个特别说明的是:因为功能显示的需求,这里把集线器定义为没有子设备的模板。还有另外一个功能就是当我按下重启的时候,断开按钮就不能使用。这里用到的是Command。园里前辈们写了很多这方面的文章,我这里就不对ICommand进行详细讨论。

  效果图:图1-1

  快捷菜单(如下图):

  

  

图 1-3(路由器快捷菜单)   图 1-4(交换机快捷菜单)       图1-5(集线器快捷菜单)

  快捷菜单代码:

  

View Code

 1 <ContextMenu x:Key="RouterMenu">
 2             <MenuItem Header="启动路由器">
 3                 <MenuItem.Icon>
 4                     <Image Source="..\..\Resource\Connect.png"/>
 5                 </MenuItem.Icon>
 6             </MenuItem>
 7             <MenuItem Header="断开路由器">
 8                 <MenuItem.Icon>
 9                     <Image Source="..\..\Resource\Break.png"/>
10                 </MenuItem.Icon>
11             </MenuItem>
12         </ContextMenu>
13         <ContextMenu x:Key="SwitchMenu">
14             <MenuItem Header="启动交换机">
15                 <MenuItem.Icon>
16                     <Image Source="..\..\Resource\Connect.png"/>
17                 </MenuItem.Icon>
18             </MenuItem>
19             <MenuItem Header="断开交换机">
20                 <MenuItem.Icon>
21                     <Image Source="..\..\Resource\Break.png"/>
22                 </MenuItem.Icon>
23             </MenuItem>
24         </ContextMenu>
25         <ContextMenu x:Key="ConcentratorMenu">
26             <MenuItem Header="启动集线器">
27                 <MenuItem.Icon>
28                     <Image Source="..\..\Resource\Connect.png"/>
29                 </MenuItem.Icon>
30             </MenuItem>
31             <MenuItem Header="断开集线器">
32                 <MenuItem.Icon>
33                     <Image Source="..\..\Resource\Break.png"/>
34                 </MenuItem.Icon>
35             </MenuItem>
36         </ContextMenu>

  TreeView模板代码:

View Code

 1 xmlns:LocalTmeplate="clr-namespace:Smlant.DataTemplates"      
 2 
 3 <LocalTmeplate:ContextMenuDataTemplateSelector x:Key="ContextMenuDataTemplateSelector"/>
 4 
 5 <!--交换机模板-->
 6         <HierarchicalDataTemplate x:Key="SwitchTemplate" ItemsSource="{Binding ChildNodes}" DataType="{x:Type DataLib:Switcher}">
 7             <StackPanel Orientation="Horizontal" ContextMenu="{StaticResource SwitchMenu}">
 8                 <Image Source="{Binding ImageUrl}" Margin="2"/>
 9                 <TextBlock Text="{Binding Name}" Margin="2" VerticalAlignment="Center"/>
10                 <Button Margin="2" Command="{Binding DataContext.OffCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"
11                         CommandParameter="{Binding}">
12                     <StackPanel>
13                         <Image Source="..\..\Resource\Connect.png" ToolTip="重新连接"/>
14                     </StackPanel>
15                 </Button>
16                 <Button Margin="2" Command="{Binding DataContext.ConnectionCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"
17                         CommandParameter="{Binding}">
18                     <StackPanel>
19                         <Image Source="..\..\Resource\Break.png" ToolTip="断开连接"/>
20                     </StackPanel>
21                 </Button>
22             </StackPanel>
23         </HierarchicalDataTemplate>
24         <!--路由器模板-->
25         <HierarchicalDataTemplate x:Key="RouterTemplate" ItemsSource="{Binding ChildNodes}" DataType="{x:Type DataLib:Router}">
26             <StackPanel Orientation="Horizontal" ContextMenu="{StaticResource RouterMenu}">
27                 <Image Source="{Binding ImageUrl}" Margin="2"/>
28                 <TextBlock Text="{Binding Name}" Margin="2" VerticalAlignment="Center"/>
29                 <Button Margin="2" Content="重启路由" Command="{Binding DataContext.OffCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"
30                         CommandParameter="{Binding}">
31                 </Button>
32                 <Button Margin="2" Content="断开连接"  Command="{Binding DataContext.ConnectionCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"
33                         CommandParameter="{Binding}">
34                 </Button>
35             </StackPanel>
36         </HierarchicalDataTemplate>
37         <!--集线器模板-->
38         <DataTemplate x:Key="ConcentratorTemplate" DataType="{x:Type DataLib:Concentrator}">
39             <StackPanel Orientation="Horizontal" ContextMenu="{StaticResource ConcentratorMenu}">
40                 <Image Source="{Binding ImageUrl}" Margin="2"/>
41                 <TextBlock Text="{Binding Name}" Margin="2" VerticalAlignment="Center"/>
42                 <Button Margin="2" Content="重新连接" Command="{Binding DataContext.OffCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"
43                         CommandParameter="{Binding}"/>
44                 <Button Margin="2" Content="断开连接"  Command="{Binding DataContext.ConnectionCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"
45                         CommandParameter="{Binding}"/>
46             </StackPanel>
47         </DataTemplate>

  DataTemplateSelector代码:

  

View Code

 1 public class ContextMenuDataTemplateSelector:DataTemplateSelector
 2     {
 3         public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
 4         {
 5             FrameworkElement element = container as FrameworkElement;
 6             DataTemplate template = null;
 7             if (item is Router)
 8             {
 9                 template = element.FindResource("RouterTemplate") as HierarchicalDataTemplate;
10             }
11             else if (item is Switcher)
12             {
13                 template = element.FindResource("SwitchTemplate") as HierarchicalDataTemplate;
14             }
15             else if (item is Concentrator)
16             {
17                 template = element.FindResource("ConcentratorTemplate") as DataTemplate;
18             }
19             return template;
20         }
21     }

  ViewModel代码: 

  

View Code

 1 private List<Device> dataSource;
 2         public List<Device> DataSource
 3         {
 4             get { return dataSource; }
 5             set
 6             {
 7                 if (dataSource != value)
 8                 {
 9                     dataSource = value;
10                     RaisePropertyChanged("DataSource");
11                 }
12             }
13         }
14 
15  DataSource = DataFactory.GetAllTypeDevice(1, SelectedLevel);

 

  

 案例三,主要跟大家分享的是,如何在TreeView上实现三态树的功能。具体什么是三态树的话我在这里就不多说了。以下是案例三的具体结构图和代码:

  结构图:

  

       图 1-6(三态树)

  代码:具体代码实现在上面的实体类代码的 IDevice中实现。请参考上面代码。

  

  

  3.个人观点

    很多朋友都抱怨说WPF的TreeView是一个很麻烦的东西,而且不好用。这点我持反对的意见,每一种新东西,在我们还不熟悉的时候,是挺麻烦的。但是WPF--TreeView较WinForm--Tree来说,WPF提供一个强大的模板功能,能让我们根据自己的需要,灵活地更换模板。如果在做WinForm开发的时候,我想实现一棵树上保存N种数据类型的数据,而且根据不同的类型,在节点上显示不一样的状态和样式,也许你会花很多的时间来重写Tree的控件,而WPF提供了一个模板功能,而且具体的模板是我们自己来实现的。

  

  4.附加代码:

    百度网盘  :http://pan.baidu.com/s/1kVqRyrt

    密码:cm4k

转载于:https://www.cnblogs.com/smlAnt/archive/2011/07/31/2121689.html

WPF应用基础篇---TreeView相关推荐

  1. [转]WF4.0 基础篇 (一)开始使用WF

    本文转自:http://www.cnblogs.com/foundation/archive/2009/10/26/1589993.html 来博客园写WF主题博客已经3年了, 在园子里认识了很多朋友 ...

  2. [转载]潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一)

    潜移默化学会WPF(技巧篇)--具有Items元素的控件子项获取(一) 1. treeview的Item获取 var g = this.tree.ItemContainerGenerator;Tree ...

  3. WPF入门0:WPF的基础知识

    WPF入门0:WPF的基础知识 WPF 可创建动态的数据驱动的呈现系统. 系统的每一部分均可通过驱动行为的属性集来创建对象. 数据绑定是系统的基础部分,在每一层中均进行了集成. 传统的应用程序创建一个 ...

  4. WPF教程(一)---创建一个WPF程序基础知识

    1.前言: 这篇主要讲WPF的开发基础,介绍了如何使用Visual Studio 2019创建一个WPF应用程序. 首先说一下学习WPF的基础知识: 1) 要会一门.NET所支持的编程语言--例如C# ...

  5. WPF编程基础入门 ——— 第二章 XAML

    XAML 简述 XAML(eXtensible Application Markup Language,可扩展应用程序标记语言)是微软公司创建的一种新的描述性语言,用于搭建应用程序用户界面.XAML实 ...

  6. WPF编程基础入门 ——— 第三章 布局(五)布局面板WrapPanel

    WPF布局--布局面板WrapPanel WPF--WrapPanel布局控件 WrapPanel实例--十个按钮 WPF--WrapPanel布局控件 WrapPanel(自动折行面板),允许任意多 ...

  7. wf4.0支持mysql吗_WF4.0 基础篇 (一)开始使用WF

    从WinFX到NET3.x再到NET4.0 ,WPF,WCF,WF 始终是放在一起的,WPF(silverlight)用于程序UI的展现,WCF用于程序通信,WF用于程序的逻辑控制,这种思想在微软提出 ...

  8. Python Qt GUI设计:信号与槽的使用方法(基础篇—7)

    目录 1.信号与槽的概念 2.信号与槽的基础函数 2.1.创建信号函数 2.2.连接信号函数 2.3.断开信号函数 2.4.发射信号函数 3.信号和槽的使用方法 3.1.内置信号与槽的使用 3.2.自 ...

  9. Python Qt GUI设计:窗口布局管理方法【强化】(基础篇—6)

    目录 1. 水平布局类(QHBoxLayout) 2.垂直布局类(QVBoxLayout) 3.网格布局类(QGridLayout) 3.1.单一的网络布局 3.2.跨越行.列的网络布局 4.表单布局 ...

最新文章

  1. java多线程实现归并排序_利用多线程对数组进行归并排序
  2. python基础5(来自廖雪峰的官方网站)
  3. USACO shuttle
  4. poxtfix+dovecot+saslauthd+courier-authlib +mysql + extmail 完整虚拟邮箱系统部署
  5. python访问网页变量_Python 如何访问外围作用域中的变量
  6. [詹兴致矩阵论习题参考解答]习题1.10
  7. myblog test
  8. 【水果识别】基于matlab GUI柑橘质量检测及分级系统【含Matlab源码 738期】
  9. 贪吃蛇程序设计报告python_贪吃蛇程序设计报告
  10. 商业银行的设立与组织形式-单一银行制、分支银行制、持股公司制、连锁银行制、代理银行制...
  11. MT7658芯片组资料,MT7658处理器参数介绍
  12. Perplexity困惑度解释
  13. main()的使用说明 (一叶知秋)
  14. 计算机word表格计算教程F9,Word表格数据计算与域操作
  15. intellijnbsp;idenbsp;激活
  16. matlab景深合成算法,科普向 篇五:Helicon Focus让景深合成变简单!
  17. oracle distinct 用法
  18. topsis(优劣解距离法)在matlab上的实现
  19. 净空法师法语:你想真正消业障,要用真心待人接物!南无阿弥陀佛!
  20. Redis常见面试题你都掌握了没

热门文章

  1. c语言求浮点数矩阵的逆程序,逆矩阵求程序!!!谢谢
  2. 活动、节假日、促销等营销方式的因果效应评估——特征工程篇(一)
  3. 机器学习中应用到的各种距离介绍(附上Matlab代码)
  4. 知识图谱特征学习算法
  5. 表格识别----基于模板的文字识别结果结构化处理技术
  6. 【web前端面试题整理03】来看一点CSS相关的吧
  7. docker运行storm及wordcount实例
  8. java学习笔记之线程(一)
  9. boost1.55.0在vs2013上编译序列化库失败的解决方法
  10. 这些年,我们无法忘却的jQuery日历插件