├── TreeViewDemo
├── App.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── App.xaml
├── App.xaml.cs
├── TreeItemViewModel.cs
├── MainWindow.xaml.cs
├── TreeViewDemo.csproj
└── MainWindow.xaml
├── MultiSelectTreeView
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── MultiSelectTreeView.csproj
└── MultiSelectTreeView.cs
├── README.md
├── LICENSE.txt
└── MultiSelectTreeView.sln
/TreeViewDemo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/TreeViewDemo/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/TreeViewDemo/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TreeViewDemo/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace TreeViewDemo
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MultiSelectTreeView
2 | Inherits from standard WPF TreeView and adds functionality for selecting multiple TreeViewItems.
3 |
4 | This control is based on the following blog post:
5 | http://chrigas.blogspot.de/2014/08/wpf-treeview-with-multiple-selection.html
6 |
7 | Thanks to Christoph Gattnar for providing the basics!
8 |
9 | Differences in this control in regard to the blog post:
10 | * MultiSelectTreeView inherits WPF TreeView instead of attaching behavior
11 | * Support from keyboard selection in addition to mouse selection
12 | * Support for SHIFT + CTRL selection
13 |
14 | For usage example, see the TreeViewDemo project.
15 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Christian Myksvoll
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/TreeViewDemo/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TreeViewDemo.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace MultiSelectTreeView.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TreeViewDemo/TreeItemViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq.Expressions;
5 |
6 | namespace TreeViewDemo
7 | {
8 | public class TreeItemViewModel : INotifyPropertyChanged
9 | {
10 | public TreeItemViewModel(string displayName)
11 | {
12 | DisplayName = displayName;
13 | }
14 |
15 | private bool _isExpanded;
16 | private bool _isSelected;
17 |
18 | public string DisplayName { get; set; }
19 |
20 | public bool IsExpanded
21 | {
22 | get { return _isExpanded; }
23 | set
24 | {
25 | _isExpanded = value;
26 | OnPropertyChanged(() => IsExpanded);
27 | }
28 | }
29 |
30 | public virtual bool IsSelected
31 | {
32 | get { return _isSelected; }
33 | set
34 | {
35 | _isSelected = value;
36 | OnPropertyChanged(() => IsSelected);
37 | }
38 | }
39 |
40 | public List Children { get; set; }
41 |
42 | public event PropertyChangedEventHandler PropertyChanged;
43 | public void OnPropertyChanged(Expression> propertyExpression)
44 | {
45 | var body = propertyExpression.Body as MemberExpression;
46 | PropertyChangedEventHandler handler = PropertyChanged;
47 | if (handler != null) handler(this, new PropertyChangedEventArgs(body.Member.Name));
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/MultiSelectTreeView.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiSelectTreeView", "MultiSelectTreeView\MultiSelectTreeView.csproj", "{97B2220D-4A20-47ED-98B8-AED7C7188F2F}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TreeViewDemo", "TreeViewDemo\TreeViewDemo.csproj", "{FC629040-33D4-49F9-AD11-99566C62C112}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {97B2220D-4A20-47ED-98B8-AED7C7188F2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {97B2220D-4A20-47ED-98B8-AED7C7188F2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {97B2220D-4A20-47ED-98B8-AED7C7188F2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {97B2220D-4A20-47ED-98B8-AED7C7188F2F}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {FC629040-33D4-49F9-AD11-99566C62C112}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {FC629040-33D4-49F9-AD11-99566C62C112}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {FC629040-33D4-49F9-AD11-99566C62C112}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {FC629040-33D4-49F9-AD11-99566C62C112}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/TreeViewDemo/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("TreeViewDemo")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("TreeViewDemo")]
15 | [assembly: AssemblyCopyright("Copyright © 2015")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("MultiSelectTreeView")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("MultiSelectTreeView")]
15 | [assembly: AssemblyCopyright("Copyright © 2015")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly:ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/TreeViewDemo/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Windows;
4 |
5 | namespace TreeViewDemo
6 | {
7 | public partial class MainWindow : Window
8 | {
9 | public MainWindow()
10 | {
11 | SelectedNodes = new ObservableCollection();
12 | SelectedNodes.CollectionChanged += SelectedNodes_CollectionChanged;
13 |
14 | RootNodes = BuildTreeModel();
15 |
16 | InitializeComponent();
17 | }
18 |
19 | private static List BuildTreeModel()
20 | {
21 | return new List
22 | {
23 | new TreeItemViewModel("Node 1")
24 | {
25 | IsExpanded = true,
26 | Children = new List
27 | {
28 | new TreeItemViewModel("Node 1.1"),
29 | new TreeItemViewModel("Node 1.2")
30 | {
31 | Children = new List
32 | {
33 | new TreeItemViewModel("Node 1.2.1"),
34 | new TreeItemViewModel("Node 1.2.2"),
35 | new TreeItemViewModel("Node 1.2.3"),
36 | new TreeItemViewModel("Node 1.2.4"),
37 | new TreeItemViewModel("Node 1.2.5"),
38 | new TreeItemViewModel("Node 1.2.6")
39 | }
40 | }
41 | }
42 | },
43 | new TreeItemViewModel("Node 2")
44 | {
45 | Children = new List
46 | {
47 | new TreeItemViewModel("Node 2.2.1"),
48 | new TreeItemViewModel("Node 2.2.2"),
49 | new TreeItemViewModel("Node 2.2.3"),
50 | new TreeItemViewModel("Node 2.2.4")
51 | }
52 | }
53 | };
54 | }
55 |
56 | public List RootNodes { get; set; }
57 |
58 | public ObservableCollection SelectedNodes { get; set; }
59 |
60 | void SelectedNodes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
61 | {
62 | NumberOfSelectedNodes.Text = SelectedNodes.Count.ToString();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace MultiSelectTreeView.Properties {
12 |
13 |
14 | ///
15 | /// A strongly-typed resource class, for looking up localized strings, etc.
16 | ///
17 | // This class was auto-generated by the StronglyTypedResourceBuilder
18 | // class via a tool like ResGen or Visual Studio.
19 | // To add or remove a member, edit your .ResX file then rerun ResGen
20 | // with the /str option, or rebuild your VS project.
21 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
24 | internal class Resources {
25 |
26 | private static global::System.Resources.ResourceManager resourceMan;
27 |
28 | private static global::System.Globalization.CultureInfo resourceCulture;
29 |
30 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
31 | internal Resources() {
32 | }
33 |
34 | ///
35 | /// Returns the cached ResourceManager instance used by this class.
36 | ///
37 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
38 | internal static global::System.Resources.ResourceManager ResourceManager {
39 | get {
40 | if ((resourceMan == null)) {
41 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MultiSelectTreeView.Properties.Resources", typeof(Resources).Assembly);
42 | resourceMan = temp;
43 | }
44 | return resourceMan;
45 | }
46 | }
47 |
48 | ///
49 | /// Overrides the current thread's CurrentUICulture property for all
50 | /// resource lookups using this strongly typed resource class.
51 | ///
52 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
53 | internal static global::System.Globalization.CultureInfo Culture {
54 | get {
55 | return resourceCulture;
56 | }
57 | set {
58 | resourceCulture = value;
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/TreeViewDemo/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TreeViewDemo.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TreeViewDemo.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/MultiSelectTreeView.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {97B2220D-4A20-47ED-98B8-AED7C7188F2F}
8 | library
9 | Properties
10 | MultiSelectTreeView
11 | MultiSelectTreeView
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 4.0
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | Code
53 |
54 |
55 | True
56 | True
57 | Resources.resx
58 |
59 |
60 | True
61 | Settings.settings
62 | True
63 |
64 |
65 | ResXFileCodeGenerator
66 | Resources.Designer.cs
67 |
68 |
69 | SettingsSingleFileGenerator
70 | Settings.Designer.cs
71 |
72 |
73 |
74 |
75 |
82 |
--------------------------------------------------------------------------------
/TreeViewDemo/TreeViewDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {FC629040-33D4-49F9-AD11-99566C62C112}
8 | WinExe
9 | Properties
10 | TreeViewDemo
11 | TreeViewDemo
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | AnyCPU
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 4.0
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | MSBuild:Compile
54 | Designer
55 |
56 |
57 |
58 | MSBuild:Compile
59 | Designer
60 |
61 |
62 | App.xaml
63 | Code
64 |
65 |
66 | MainWindow.xaml
67 | Code
68 |
69 |
70 |
71 |
72 | Code
73 |
74 |
75 | True
76 | True
77 | Resources.resx
78 |
79 |
80 | True
81 | Settings.settings
82 | True
83 |
84 |
85 | ResXFileCodeGenerator
86 | Resources.Designer.cs
87 |
88 |
89 | SettingsSingleFileGenerator
90 | Settings.Designer.cs
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | {97b2220d-4a20-47ed-98b8-aed7c7188f2f}
100 | MultiSelectTreeView
101 |
102 |
103 |
104 |
111 |
--------------------------------------------------------------------------------
/TreeViewDemo/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/TreeViewDemo/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
70 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | # of selected nodes:
145 |
146 |
147 |
148 |
149 |
150 |
151 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/MultiSelectTreeView/MultiSelectTreeView.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Input;
7 | using System.Windows.Media;
8 | using System.Windows.Media.Media3D;
9 |
10 | namespace MultiSelectTreeView
11 | {
12 | public class MultiSelectTreeView : TreeView
13 | {
14 | public MultiSelectTreeView()
15 | {
16 | GotFocus += OnTreeViewItemGotFocus;
17 | PreviewMouseLeftButtonDown += OnTreeViewItemPreviewMouseDown;
18 | PreviewMouseLeftButtonUp += OnTreeViewItemPreviewMouseUp;
19 | }
20 |
21 | private static TreeViewItem _selectTreeViewItemOnMouseUp;
22 |
23 |
24 | public static readonly DependencyProperty IsItemSelectedProperty = DependencyProperty.RegisterAttached("IsItemSelected", typeof(Boolean), typeof(MultiSelectTreeView), new PropertyMetadata(false, OnIsItemSelectedPropertyChanged));
25 |
26 | public static bool GetIsItemSelected(TreeViewItem element)
27 | {
28 | return (bool)element.GetValue(IsItemSelectedProperty);
29 | }
30 |
31 | public static void SetIsItemSelected(TreeViewItem element, Boolean value)
32 | {
33 | if (element == null) return;
34 |
35 | element.SetValue(IsItemSelectedProperty, value);
36 | }
37 |
38 | private static void OnIsItemSelectedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
39 | {
40 | var treeViewItem = d as TreeViewItem;
41 | var treeView = FindTreeView(treeViewItem);
42 | if (treeViewItem != null && treeView != null)
43 | {
44 | var selectedItems = GetSelectedItems(treeView);
45 | if (selectedItems != null)
46 | {
47 | if (GetIsItemSelected(treeViewItem))
48 | {
49 | selectedItems.Add(treeViewItem.Header);
50 | }
51 | else
52 | {
53 | selectedItems.Remove(treeViewItem.Header);
54 | }
55 | }
56 | }
57 | }
58 |
59 | public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(MultiSelectTreeView));
60 |
61 | public static IList GetSelectedItems(TreeView element)
62 | {
63 | return (IList)element.GetValue(SelectedItemsProperty);
64 | }
65 |
66 | public static void SetSelectedItems(TreeView element, IList value)
67 | {
68 | element.SetValue(SelectedItemsProperty, value);
69 | }
70 |
71 | private static readonly DependencyProperty StartItemProperty = DependencyProperty.RegisterAttached("StartItem", typeof(TreeViewItem), typeof(MultiSelectTreeView));
72 |
73 |
74 | private static TreeViewItem GetStartItem(TreeView element)
75 | {
76 | return (TreeViewItem)element.GetValue(StartItemProperty);
77 | }
78 |
79 | private static void SetStartItem(TreeView element, TreeViewItem value)
80 | {
81 | element.SetValue(StartItemProperty, value);
82 | }
83 |
84 |
85 | private static void OnTreeViewItemGotFocus(object sender, RoutedEventArgs e)
86 | {
87 | _selectTreeViewItemOnMouseUp = null;
88 |
89 | if (e.OriginalSource is TreeView) return;
90 |
91 | var treeViewItem = FindTreeViewItem(e.OriginalSource as DependencyObject);
92 | if (Mouse.LeftButton == MouseButtonState.Pressed && GetIsItemSelected(treeViewItem) && Keyboard.Modifiers != ModifierKeys.Control)
93 | {
94 | _selectTreeViewItemOnMouseUp = treeViewItem;
95 | return;
96 | }
97 |
98 | SelectItems(treeViewItem, sender as TreeView);
99 | }
100 |
101 | private static void SelectItems(TreeViewItem treeViewItem, TreeView treeView)
102 | {
103 | if (treeViewItem != null && treeView != null)
104 | {
105 | if ((Keyboard.Modifiers & (ModifierKeys.Control | ModifierKeys.Shift)) == (ModifierKeys.Control | ModifierKeys.Shift))
106 | {
107 | SelectMultipleItemsContinuously(treeView, treeViewItem, true);
108 | }
109 | else if (Keyboard.Modifiers == ModifierKeys.Control)
110 | {
111 | SelectMultipleItemsRandomly(treeView, treeViewItem);
112 | }
113 | else if (Keyboard.Modifiers == ModifierKeys.Shift)
114 | {
115 | SelectMultipleItemsContinuously(treeView, treeViewItem);
116 | }
117 | else
118 | {
119 | SelectSingleItem(treeView, treeViewItem);
120 | }
121 | }
122 | }
123 |
124 | private static void OnTreeViewItemPreviewMouseDown(object sender, MouseEventArgs e)
125 | {
126 | var treeViewItem = FindTreeViewItem(e.OriginalSource as DependencyObject);
127 |
128 | if (treeViewItem != null && treeViewItem.IsFocused)
129 | OnTreeViewItemGotFocus(sender, e);
130 | }
131 |
132 | private static void OnTreeViewItemPreviewMouseUp(object sender, MouseButtonEventArgs e)
133 | {
134 | var treeViewItem = FindTreeViewItem(e.OriginalSource as DependencyObject);
135 |
136 | if (treeViewItem == _selectTreeViewItemOnMouseUp)
137 | {
138 | SelectItems(treeViewItem, sender as TreeView);
139 | }
140 | }
141 |
142 | private static TreeViewItem FindTreeViewItem(DependencyObject dependencyObject)
143 | {
144 | if (!(dependencyObject is Visual || dependencyObject is Visual3D))
145 | return null;
146 |
147 | var treeViewItem = dependencyObject as TreeViewItem;
148 | if (treeViewItem != null)
149 | {
150 | return treeViewItem;
151 | }
152 |
153 | return FindTreeViewItem(VisualTreeHelper.GetParent(dependencyObject));
154 | }
155 |
156 | private static void SelectSingleItem(TreeView treeView, TreeViewItem treeViewItem)
157 | {
158 | // first deselect all items
159 | DeSelectAllItems(treeView, null);
160 | SetIsItemSelected(treeViewItem, true);
161 | SetStartItem(treeView, treeViewItem);
162 | }
163 |
164 | private static void DeSelectAllItems(TreeView treeView, TreeViewItem treeViewItem)
165 | {
166 | if (treeView != null)
167 | {
168 | for (int i = 0; i < treeView.Items.Count; i++)
169 | {
170 | var item = treeView.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
171 | if (item != null)
172 | {
173 | SetIsItemSelected(item, false);
174 | DeSelectAllItems(null, item);
175 | }
176 | }
177 | }
178 | else
179 | {
180 | for (int i = 0; i < treeViewItem.Items.Count; i++)
181 | {
182 | var item = treeViewItem.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
183 | if (item != null)
184 | {
185 | SetIsItemSelected(item, false);
186 | DeSelectAllItems(null, item);
187 | }
188 | }
189 | }
190 | }
191 |
192 | private static TreeView FindTreeView(DependencyObject dependencyObject)
193 | {
194 | if (dependencyObject == null)
195 | {
196 | return null;
197 | }
198 |
199 | var treeView = dependencyObject as TreeView;
200 |
201 | return treeView ?? FindTreeView(VisualTreeHelper.GetParent(dependencyObject));
202 | }
203 |
204 | private static void SelectMultipleItemsRandomly(TreeView treeView, TreeViewItem treeViewItem)
205 | {
206 | SetIsItemSelected(treeViewItem, !GetIsItemSelected(treeViewItem));
207 | if (GetStartItem(treeView) == null || Keyboard.Modifiers == ModifierKeys.Control)
208 | {
209 | if (GetIsItemSelected(treeViewItem))
210 | {
211 | SetStartItem(treeView, treeViewItem);
212 | }
213 | }
214 | else
215 | {
216 | if (GetSelectedItems(treeView).Count == 0)
217 | {
218 | SetStartItem(treeView, null);
219 | }
220 | }
221 | }
222 |
223 | private static void SelectMultipleItemsContinuously(TreeView treeView, TreeViewItem treeViewItem, bool shiftControl = false)
224 | {
225 | TreeViewItem startItem = GetStartItem(treeView);
226 | if (startItem != null)
227 | {
228 | if (startItem == treeViewItem)
229 | {
230 | SelectSingleItem(treeView, treeViewItem);
231 | return;
232 | }
233 |
234 | ICollection allItems = new List();
235 | GetAllItems(treeView, null, allItems);
236 | //DeSelectAllItems(treeView, null);
237 | bool isBetween = false;
238 | foreach (var item in allItems)
239 | {
240 | if (item == treeViewItem || item == startItem)
241 | {
242 | // toggle to true if first element is found and
243 | // back to false if last element is found
244 | isBetween = !isBetween;
245 |
246 | // set boundary element
247 | SetIsItemSelected(item, true);
248 | continue;
249 | }
250 |
251 | if (isBetween)
252 | {
253 | SetIsItemSelected(item, true);
254 | continue;
255 | }
256 |
257 | if (!shiftControl)
258 | SetIsItemSelected(item, false);
259 | }
260 | }
261 | }
262 |
263 | private static void GetAllItems(TreeView treeView, TreeViewItem treeViewItem, ICollection allItems)
264 | {
265 | if (treeView != null)
266 | {
267 | for (int i = 0; i < treeView.Items.Count; i++)
268 | {
269 | var item = treeView.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
270 | if (item != null)
271 | {
272 | allItems.Add(item);
273 | GetAllItems(null, item, allItems);
274 | }
275 | }
276 | }
277 | else
278 | {
279 | for (int i = 0; i < treeViewItem.Items.Count; i++)
280 | {
281 | var item = treeViewItem.ItemContainerGenerator.ContainerFromIndex(i) as TreeViewItem;
282 | if (item != null)
283 | {
284 | allItems.Add(item);
285 | GetAllItems(null, item, allItems);
286 | }
287 | }
288 | }
289 | }
290 | }
291 | }
292 |
--------------------------------------------------------------------------------