├── .gitignore
├── LICENSE
├── README.md
└── source
├── CleanAll.bat
├── Components
├── BusinessLib
│ ├── BusinessLib.csproj
│ ├── Models
│ │ ├── LocationType.cs
│ │ └── MetaLocationModel.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Resources
│ │ ├── lokasyon.sql.gz
│ │ ├── lokasyon.zip
│ │ └── readme.txt
│ └── database.cs
└── FilterTreeViewLib
│ ├── Behaviors
│ ├── HighlightTextBlockBehavior.cs
│ ├── TextChangedCommand.cs
│ └── TreeViewItemExpanded.cs
│ ├── Converters
│ ├── BoolToVisibilityConverter.cs
│ ├── CountToBoolConverter.cs
│ ├── InverseBooleanConverter.cs
│ └── LocationTypeToImageConverter.cs
│ ├── FilterTreeViewLib.csproj
│ ├── Interfaces
│ ├── IHasDummyChild.cs
│ └── ISelectionRange.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Resources
│ ├── Locations
│ │ ├── appbar.base.select.png
│ │ ├── appbar.chess.rook.png
│ │ └── appbar.city.sanfrancisco.png
│ ├── ZoomIn.ico
│ ├── ZoomIn_256x.png
│ ├── ZoomIn_48x.png
│ └── ZoomIn_64x.png
│ ├── ViewModels
│ ├── AppBaseViewModel.cs
│ ├── Base
│ │ ├── BaseViewModel.cs
│ │ └── RelayCommand.cs
│ └── Tree
│ │ ├── MetaLocationRootViewModel.cs
│ │ ├── MetaLocationViewModel.cs
│ │ └── Search
│ │ ├── Enums
│ │ ├── MatchType.cs
│ │ └── SearchMatch.cs
│ │ ├── SearchParams.cs
│ │ ├── SearchResult.cs
│ │ ├── SelectionRange.cs
│ │ └── StringMatchItem.cs
│ ├── Views
│ └── BindingProxy.cs
│ └── packages.config
├── FilterTreeView.sln
└── FilterTreeView
├── App.config
├── App.xaml
├── App.xaml.cs
├── FilterTreeView.csproj
├── FilterTreeView.csproj.user
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── Tasks
├── OneTaskLimitedScheduler.cs
└── OneTaskProcessor.cs
├── ViewModels
└── AppViewModel.cs
└── packages.config
/.gitignore:
--------------------------------------------------------------------------------
1 | source/packages/
2 | packages/
3 | 00_Release/
4 | 01_Nuget/
5 | debug/
6 | release/
7 | build/
8 | bin/
9 | obj/
10 | cache/
11 | log/
12 | tmp/
13 | /source/.vs/
14 | .vs/
15 |
16 | *~
17 | *.lock
18 | *.DS_Store
19 | *.swp
20 | *.out
21 | *.sou
22 | *.suo
23 | *.sqlite
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://ci.appveyor.com/project/Dirkster99/filtertreeview) [](https://github.com/Dirkster99/FilterTreeView/releases/latest)
2 |
3 | # FilterTreeView
4 | This is a WPF/MVVM Search and Filter TreeView Reference Application
5 |
6 | See Codeproject articles:
7 | * Advanced WPF TreeViews Part 3 of n
8 | * A Highlightable WPF/MVVM TextBlock
9 |
10 | for more details or watch a screen captured video on youtube.
11 |
12 | ## TreeLib
13 | See also TreeLib project for Tree Traversal methods used in this project.
14 |
15 | ## City Search (String is contained):
16 |
17 |
18 | * 781 Saint
19 | * 267 los
20 | * 48 Washington
21 | * 32 Berlin
22 | * 22 Holland
23 | * 29 Paris
24 | * 23 London
25 | * 15 Brighton
26 | * 11 Hamburg
27 | * 10 Bremen
28 | * 17 Kiel
29 | * 14 Francisco
30 | * 9 Madrid
31 | * 7 Liverpool
32 | * 7 Amsterdam
33 | * 7 Australia
34 | * 3 Muenster
35 |
--------------------------------------------------------------------------------
/source/CleanAll.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | pushd "%~dp0"
3 | ECHO.
4 | ECHO.
5 | ECHO.
6 | ECHO This script deletes all temporary build files in the .vs folder and the
7 | ECHO BIN and OBJ folders contained in the following projects
8 | ECHO.
9 | ECHO Components\BusinessLib
10 | ECHO Components\FilterTreeViewLib
11 | ECHO FilterTreeView
12 | ECHO.
13 | REM Ask the user if hes really sure to continue beyond this point XXXXXXXX
14 | set /p choice=Are you sure to continue (Y/N)?
15 | if not '%choice%'=='Y' Goto EndOfBatch
16 | REM Script does not continue unless user types 'Y' in upper case letter
17 | ECHO.
18 | ECHO XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
19 | ECHO.
20 | ECHO XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
21 | ECHO.
22 | ECHO Removing vs settings folder with *.sou file
23 | ECHO.
24 | RMDIR /S /Q .vs
25 |
26 | ECHO.
27 | ECHO Deleting BIN and OBJ Folders in BusinessLib
28 | ECHO.
29 | RMDIR /S /Q "Components\BusinessLib\bin"
30 | RMDIR /S /Q "Components\BusinessLib\obj"
31 |
32 | ECHO.
33 | ECHO Deleting BIN and OBJ Folders in FilterTreeViewLib
34 | ECHO.
35 | RMDIR /S /Q "Components\FilterTreeViewLib\bin"
36 | RMDIR /S /Q "Components\FilterTreeViewLib\obj"
37 |
38 | ECHO.
39 | ECHO Deleting BIN and OBJ Folders in FilterTreeView
40 | ECHO.
41 | RMDIR /S /Q ".\FilterTreeView\bin"
42 | RMDIR /S /Q ".\FilterTreeView\obj"
43 |
44 | PAUSE
45 |
46 | :EndOfBatch
47 |
--------------------------------------------------------------------------------
/source/Components/BusinessLib/BusinessLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {70CC82CC-9EFE-461F-A959-F64A06894621}
8 | Library
9 | Properties
10 | BusinessLib
11 | BusinessLib
12 | v4.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | false
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | false
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | Always
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/source/Components/BusinessLib/Models/LocationType.cs:
--------------------------------------------------------------------------------
1 | namespace BusinessLib.Models
2 | {
3 | ///
4 | /// Determines the type of location in a collection entry.
5 | ///
6 | public enum LocationType
7 | {
8 | Unknown = 1000,
9 |
10 | ///
11 | /// This location refers to a country on our little world called Earth.
12 | ///
13 | Country = 100,
14 |
15 | ///
16 | /// This location refers to a region that is part of a country.
17 | ///
18 | Region = 10,
19 |
20 | ///
21 | /// This location refers to a city (e.g. Berlin) located within
22 | /// a region.
23 | ///
24 | City = 0
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/source/Components/BusinessLib/Models/MetaLocationModel.cs:
--------------------------------------------------------------------------------
1 | namespace BusinessLib.Models
2 | {
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Xml.Serialization;
6 |
7 | public class MetaLocationModel
8 | {
9 | #region fields
10 | private readonly ObservableCollection _Children = null;
11 | #endregion fields
12 |
13 | #region constructors
14 | ///
15 | /// Parameterized Class Constructor
16 | ///
17 | public MetaLocationModel(
18 | MetaLocationModel parent
19 | , int id
20 | , string iso
21 | , string localName
22 | , LocationType type
23 | , long in_Location
24 | , double geo_lat
25 | , double geo_lng
26 | , string db_id
27 | )
28 | : this()
29 | {
30 | Parent = parent;
31 |
32 | ID = id;
33 | ISO = iso;
34 | LocalName = localName;
35 | Type = type;
36 | In_Location = in_Location;
37 | Geo_lat = geo_lat;
38 | Geo_lng = geo_lng;
39 | DB_id = db_id;
40 | }
41 |
42 | ///
43 | /// Class Constructor
44 | ///
45 | public MetaLocationModel()
46 | {
47 | _Children = new ObservableCollection();
48 | }
49 | #endregion constructors
50 |
51 | #region properties
52 | [XmlIgnore]
53 | public MetaLocationModel Parent { get; private set; }
54 |
55 | public int ID { get; set; }
56 | public string ISO { get; set; }
57 | public string LocalName { get; set; }
58 | public LocationType Type { get; set; }
59 |
60 | public long In_Location { get; set; }
61 |
62 | public double Geo_lat { get; set; }
63 | public double Geo_lng { get; set; }
64 |
65 | public string DB_id { get; set; }
66 |
67 | [XmlIgnore]
68 | public IEnumerable Children
69 | {
70 | get
71 | {
72 | return _Children;
73 | }
74 | }
75 | #endregion properties
76 |
77 | #region methods
78 | public int ChildrenCount => _Children.Count;
79 |
80 | public void ChildrenAdd(MetaLocationModel child)
81 | {
82 | _Children.Add(child);
83 | }
84 |
85 | public void ChildrenRemove(MetaLocationModel child)
86 | {
87 | _Children.Remove(child);
88 | }
89 |
90 | public void ChildrenClear()
91 | {
92 | _Children.Clear();
93 | }
94 |
95 | public void SetParent(MetaLocationModel parent)
96 | {
97 | Parent = parent;
98 | }
99 | #endregion methods
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/source/Components/BusinessLib/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("BusinessLib")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("BusinessLib")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("70cc82cc-9efe-461f-a959-f64a06894621")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/source/Components/BusinessLib/Resources/lokasyon.sql.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/BusinessLib/Resources/lokasyon.sql.gz
--------------------------------------------------------------------------------
/source/Components/BusinessLib/Resources/lokasyon.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/BusinessLib/Resources/lokasyon.zip
--------------------------------------------------------------------------------
/source/Components/BusinessLib/Resources/readme.txt:
--------------------------------------------------------------------------------
1 |
2 | The sample data in *lokasyon.sql.gz* directory was drawn from here:
3 | https://code.google.com/archive/p/worlddb/downloads
4 |
5 | This data was converted to XML using a custom sqlite converter program.
6 | The result of that conversion was: lokasyon.zip
7 |
--------------------------------------------------------------------------------
/source/Components/BusinessLib/database.cs:
--------------------------------------------------------------------------------
1 | namespace BusinessLib
2 | {
3 | using BusinessLib.Models;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Xml.Serialization;
11 |
12 | ///
13 | /// A data source that provides raw data objects. In a real
14 | /// application this class could also make calls to a database.
15 | ///
16 | public class Database
17 | {
18 | ///
19 | /// Loads country, region, city data from an open source archiv:
20 | /// lokasyon.sql.gz dated back to 2012.
21 | /// Source: https://code.google.com/archive/p/worlddb/downloads
22 | ///
23 | /// The zipped XML format used here was converted from the above
24 | /// SQL data source.
25 | ///
26 | ///
27 | ///
28 | ///
29 | ///
30 | ///
31 | public static async Task> LoadData(
32 | string pathZipFile
33 | , string countriesFileName
34 | , string regionsFileName
35 | , string citiesFileName
36 | )
37 | {
38 | return await Task.Run>(async () =>
39 | {
40 | // Load all regions into a dicationary collection
41 | Dictionary isoDicRegions = await
42 | IsoDicFromZipXml(pathZipFile, regionsFileName);
43 |
44 | // Load cities (that belong to 1 region) into a dictionary collection
45 | List isoCities
46 | = await FromZipXml(pathZipFile, citiesFileName);
47 |
48 | // Insert each city into its region
49 | foreach (var cityItem in isoCities)
50 | {
51 | int isoCountryLength = cityItem.ISO.IndexOf('-');
52 | int isoRegionLength = cityItem.ISO.IndexOf('-', isoCountryLength + 1);
53 | string isoRegion = cityItem.ISO.Substring(0, isoRegionLength);
54 |
55 | MetaLocationModel regionItem;
56 | isoDicRegions.TryGetValue(isoRegion, out regionItem);
57 |
58 | if (regionItem != null)
59 | {
60 | cityItem.SetParent(regionItem);
61 | regionItem.ChildrenAdd(cityItem);
62 | }
63 | }
64 |
65 | // Load all countries (about 220 world-wide) into a collection
66 | List isoCountries = null;
67 | isoCountries = await FromZipXml(pathZipFile, countriesFileName);
68 |
69 | // Insert all regions (and cities below them) into their countries
70 | foreach (var regionItem in isoDicRegions.Values)
71 | {
72 | int isoCountryLength = regionItem.ISO.IndexOf('-');
73 | string isoCountry = regionItem.ISO.Substring(0, isoCountryLength);
74 |
75 | var countryItem = isoCountries.Where(x => x.ISO.Equals(isoCountry, StringComparison.InvariantCulture)).FirstOrDefault();
76 |
77 | if (countryItem != null)
78 | {
79 | regionItem.SetParent(countryItem);
80 | countryItem.ChildrenAdd(regionItem);
81 | }
82 | }
83 |
84 | return isoCountries;
85 | });
86 | }
87 |
88 | #region Xml Reader Methods
89 | ///
90 | /// Reads all Meta Location Models directly from a given Xml file.
91 | ///
92 | /// Path the Xml file eg.: @".\Resources\countries.xml"
93 | ///
94 | public static Task> ReadFromXml(string targetFileName)
95 | {
96 | return Task.Run(() =>
97 | {
98 | List list = null;
99 |
100 | try
101 | {
102 | using (StreamReader sr = new StreamReader(targetFileName, Encoding.UTF8))
103 | {
104 | using (TextReader reader = TextReader.Synchronized(sr))
105 | {
106 | object ds = new XmlSerializer(typeof(List)).Deserialize(reader);
107 |
108 | list = ds as List;
109 | }
110 | }
111 | }
112 | catch (System.Exception)
113 | {
114 | throw;
115 | }
116 |
117 | return list;
118 | });
119 | }
120 |
121 | ///
122 | /// Reads all Meta Location Models directly from a given Xml file.
123 | ///
124 | /// @".\Resources\lokasyon.zip"
125 | /// Path the Xml file eg.: "countries.xml"
126 | ///
127 | public static Task> FromZipXml(
128 | string zipFileFullPath
129 | , string targetFileName)
130 | {
131 | return Task.Run(() =>
132 | {
133 | List list = null;
134 |
135 | try
136 | {
137 | var compressedFile = System.IO.Compression.ZipFile.OpenRead(zipFileFullPath)
138 | .Entries.Where(x => x.Name.Equals(targetFileName, StringComparison.InvariantCulture))
139 | .FirstOrDefault().Open();
140 |
141 | using (StreamReader sr = new StreamReader(compressedFile, Encoding.UTF8))
142 | {
143 | using (TextReader reader = TextReader.Synchronized(sr))
144 | {
145 | object ds = new XmlSerializer(typeof(List)).Deserialize(reader);
146 |
147 | list = ds as List;
148 | }
149 | }
150 | }
151 | catch (System.Exception)
152 | {
153 | throw;
154 | }
155 |
156 | return list;
157 | });
158 | }
159 |
160 | ///
161 | /// Reads all Meta Location Models directly from a Zipped Xml file.
162 | ///
163 | ///
164 | ///
165 | ///
166 | public static async Task> IsoDicFromZipXml(
167 | string zipFileFullPath
168 | , string targetFileName)
169 | {
170 | List isoList = await FromZipXml(zipFileFullPath, targetFileName);
171 |
172 | Dictionary isoDictionary = new Dictionary();
173 |
174 | foreach (var item in isoList)
175 | isoDictionary.Add(item.ISO, item);
176 |
177 | return isoDictionary;
178 | }
179 | #endregion Xml Reader Methods
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Behaviors/HighlightTextBlockBehavior.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Behaviors
2 | {
3 | using FilterTreeViewLib.Interfaces;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Documents;
7 | using System.Windows.Media;
8 | using System;
9 |
10 | ///
11 | /// Implements a behavior to highlight text as specified by
12 | /// a bound property that adheres to the interface.
13 | ///
14 | public static class HighlightTextBlockBehavior
15 | {
16 | #region fields
17 | ///
18 | /// Back Store of Attachable Dependency Property that indicates the range
19 | /// of text that should be highlighting (if any).
20 | ///
21 | private static readonly DependencyProperty RangeProperty =
22 | DependencyProperty.RegisterAttached("Range",
23 | typeof(ISelectionRange),
24 | typeof(HighlightTextBlockBehavior), new PropertyMetadata(null, OnRangeChanged));
25 | #endregion fields
26 |
27 | #region methods
28 | ///
29 | /// Gets the current values of the Range dependency property.
30 | ///
31 | public static ISelectionRange GetRange(DependencyObject obj)
32 | {
33 | return (ISelectionRange)obj.GetValue(RangeProperty);
34 | }
35 |
36 | ///
37 | /// Gets the current values of the Range dependency property.
38 | ///
39 | public static void SetRange(DependencyObject obj, ISelectionRange value)
40 | {
41 | obj.SetValue(RangeProperty, value);
42 | }
43 |
44 | ///
45 | /// Method executes whenever the Range dependency property valua has changed
46 | /// (in the bound viewmodel).
47 | ///
48 | ///
49 | ///
50 | private static void OnRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
51 | {
52 | TextBlock txtblock = d as TextBlock;
53 |
54 | if (txtblock == null)
55 | return;
56 |
57 | var range = GetRange(d); // Get the bound Range value to do highlighting
58 |
59 | // Standard background is transparent
60 | SolidColorBrush normalBackGround = new SolidColorBrush(Color.FromArgb(00, 00, 00, 00));
61 | if (range != null)
62 | {
63 | if (range.NormalBackground != default(Color))
64 | normalBackGround = new SolidColorBrush(range.NormalBackground);
65 | }
66 |
67 | // Reset Highlighting - this must be done anyways since
68 | // multiple selection runs will overlay each other
69 | var txtrange = new TextRange(txtblock.ContentStart, txtblock.ContentEnd);
70 | txtrange.ApplyPropertyValue(TextElement.BackgroundProperty, normalBackGround);
71 |
72 | if (range == null)
73 | return;
74 |
75 | if (range.Start < 0 || range.End < 0) // Nothing to highlight here :-(
76 | return;
77 |
78 | try
79 | {
80 | // Standard selection background color on dark skin: 254, 252, 200
81 | // Standard selection background color on light skin: 208, 247, 255
82 | Color selColor = (range.DarkSkin ? Color.FromArgb(255, 254, 252, 200) :
83 | Color.FromArgb(255, 208, 247, 255));
84 |
85 | Brush selectionBackground = new SolidColorBrush(selColor);
86 | if (range != null)
87 | {
88 | if (range.SelectionBackground != default(Color))
89 | selectionBackground = new SolidColorBrush(range.SelectionBackground);
90 | }
91 |
92 | TextRange txtrangel = new TextRange(
93 | txtblock.ContentStart.GetPositionAtOffset(range.Start + 1)
94 | , txtblock.ContentStart.GetPositionAtOffset(range.End + 1));
95 |
96 | txtrangel.ApplyPropertyValue(TextElement.BackgroundProperty, selectionBackground);
97 | }
98 | catch (Exception exc)
99 | {
100 | Console.WriteLine(exc.Message);
101 | Console.WriteLine(exc.StackTrace);
102 | }
103 | }
104 | #endregion methods
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Behaviors/TextChangedCommand.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Behaviors
2 | {
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Input;
6 |
7 | ///
8 | /// Source:
9 | /// http://stackoverflow.com/questions/1034374/drag-and-drop-in-mvvm-with-scatterview
10 | /// http://social.msdn.microsoft.com/Forums/de-DE/wpf/thread/21bed380-c485-44fb-8741-f9245524d0ae
11 | ///
12 | /// Attached behaviour to implement the drop event via delegate command binding or routed commands.
13 | ///
14 | public static class TextChangedCommand
15 | {
16 | // Field of attached ICommand property
17 | private static readonly DependencyProperty ChangedCommandProperty = DependencyProperty.RegisterAttached(
18 | "ChangedCommand",
19 | typeof(ICommand),
20 | typeof(TextChangedCommand),
21 | new PropertyMetadata(null, OnTextChangedCommandChange));
22 |
23 | ///
24 | /// Setter method of the attached DropCommand property
25 | ///
26 | ///
27 | ///
28 | public static void SetChangedCommand(DependencyObject source, ICommand value)
29 | {
30 | source.SetValue(ChangedCommandProperty, value);
31 | }
32 |
33 | ///
34 | /// Getter method of the attached DropCommand property
35 | ///
36 | ///
37 | ///
38 | public static ICommand GetChangedCommand(DependencyObject source)
39 | {
40 | return (ICommand)source.GetValue(ChangedCommandProperty);
41 | }
42 |
43 | ///
44 | /// This method is hooked in the definition of the .
45 | /// It is called whenever the attached property changes - in our case the event of binding
46 | /// and unbinding the property to a sink is what we are looking for.
47 | ///
48 | ///
49 | ///
50 | private static void OnTextChangedCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
51 | {
52 | TextBox uiElement = d as TextBox; // Remove the handler if it exist to avoid memory leaks
53 |
54 | if (uiElement != null)
55 | {
56 | uiElement.TextChanged -= OnText_Changed;
57 |
58 | var command = e.NewValue as ICommand;
59 | if (command != null)
60 | {
61 | // the property is attached so we attach the Drop event handler
62 | uiElement.TextChanged += OnText_Changed;
63 | }
64 | }
65 | }
66 |
67 | ///
68 | /// This method is called when the TextChanged event occurs. The sender should be the control
69 | /// on which this behaviour is attached - so we convert the sender into a
70 | /// and receive the Command through the GetDropCommand getter listed above.
71 | ///
72 | /// The parameter contains the standard data,
73 | /// which is unpacked and realesed upon the bound command.
74 | ///
75 | /// This implementation supports binding of delegate commands and routed commands.
76 | ///
77 | ///
78 | ///
79 | private static void OnText_Changed(object sender, TextChangedEventArgs e)
80 | {
81 | TextBox uiElement = sender as TextBox;
82 |
83 |
84 | // Sanity check just in case this was somehow send by something else
85 | if (uiElement == null)
86 | return;
87 |
88 | // Bugfix: A change during disabled state is likely to be caused by a bound property
89 | // in a viewmodel (a machine based edit rather than user input)
90 | // -> Lets break the message loop here to avoid unnecessary CPU processings...
91 | if (uiElement.IsEnabled == false)
92 | return;
93 |
94 | ICommand changedCommand = TextChangedCommand.GetChangedCommand(uiElement);
95 |
96 | // There may not be a command bound to this after all
97 | if (changedCommand == null)
98 | return;
99 |
100 | var item = uiElement.Text;
101 |
102 | // Check whether this attached behaviour is bound to a RoutedCommand
103 | if (changedCommand is RoutedCommand)
104 | {
105 | // Execute the routed command
106 | (changedCommand as RoutedCommand).Execute(item, uiElement);
107 | }
108 | else
109 | {
110 | // Execute the Command as bound delegate
111 | changedCommand.Execute(item);
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Behaviors/TreeViewItemExpanded.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Behaviors
2 | {
3 | using FilterTreeViewLib.Interfaces;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Input;
7 |
8 | ///
9 | /// Class implements an attached behaviour to bring a selected TreeViewItem
10 | /// into view when selection is driven by the viewmodel (not the user).
11 | ///
12 | public static class TreeViewItemExpanded
13 | {
14 | public static ICommand GetCommand(DependencyObject obj)
15 | {
16 | return (ICommand)obj.GetValue(CommandProperty);
17 | }
18 |
19 | public static void SetCommand(DependencyObject obj, ICommand value)
20 | {
21 | obj.SetValue(CommandProperty, value);
22 | }
23 |
24 | public static readonly DependencyProperty CommandProperty =
25 | DependencyProperty.RegisterAttached("Command",
26 | typeof(ICommand),
27 | typeof(TreeViewItemExpanded),
28 | new PropertyMetadata(null, OnPropertyChanged));
29 | #region methods
30 | private static void OnPropertyChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
31 | {
32 | TreeViewItem item = depObj as TreeViewItem;
33 | if (item == null)
34 | return;
35 |
36 | if (e.NewValue is ICommand == false)
37 | return;
38 |
39 | if ((ICommand)e.NewValue != null)
40 | {
41 | item.Expanded += Item_Expanded;
42 | }
43 | else
44 | {
45 | item.Expanded -= Item_Expanded;
46 | }
47 | }
48 |
49 | private static void Item_Expanded(object sender, RoutedEventArgs e)
50 | {
51 | var uiElement = sender as TreeViewItem;
52 |
53 | // Sanity check just in case this was somehow send by something else
54 | if (uiElement == null)
55 | return;
56 |
57 | IHasDummyChild f = null;
58 |
59 | if (uiElement.DataContext is IHasDummyChild)
60 | {
61 | f = uiElement.DataContext as IHasDummyChild;
62 |
63 | // Message Expand only for those who have 1 dummy folder below
64 | if (f.HasDummyChild == false)
65 | return;
66 | }
67 |
68 | ICommand changedCommand = TreeViewItemExpanded.GetCommand(uiElement);
69 |
70 | // There may not be a command bound to this after all
71 | if (changedCommand == null || f == null)
72 | return;
73 |
74 | // Check whether this attached behaviour is bound to a RoutedCommand
75 | if (changedCommand is RoutedCommand)
76 | {
77 | // Execute the routed command
78 | (changedCommand as RoutedCommand).Execute(f, uiElement);
79 | }
80 | else
81 | {
82 | // Execute the Command as bound delegate
83 | changedCommand.Execute(f);
84 | }
85 | }
86 | #endregion methods
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Converters/BoolToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Converters
2 | {
3 | using System;
4 | using System.Globalization;
5 | using System.Windows;
6 | using System.Windows.Data;
7 |
8 | public class BoolToVisibilityConverter : IValueConverter
9 | {
10 | public BoolToVisibilityConverter()
11 | {
12 | this.True = Visibility.Visible;
13 | this.False = Visibility.Collapsed;
14 | }
15 |
16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
17 | {
18 | if (value == null)
19 | return Binding.DoNothing;
20 |
21 | if (value is bool == false)
22 | return Binding.DoNothing;
23 |
24 | bool input = (bool)value;
25 |
26 | if (input == true)
27 | return True;
28 |
29 | return False;
30 | }
31 |
32 | public Visibility True { get; set; }
33 |
34 | public Visibility False { get; set; }
35 |
36 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
37 | {
38 | throw new NotImplementedException();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Converters/CountToBoolConverter.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Converters
2 | {
3 | using System;
4 | using System.Globalization;
5 | using System.Windows.Data;
6 |
7 | public class CountToBoolConverter : IValueConverter
8 | {
9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | if (value == null)
12 | return false;
13 |
14 | if (value is int == false)
15 | return false;
16 |
17 | int convertValue = (int)value;
18 |
19 | if (convertValue > 0)
20 | return true;
21 |
22 | return false;
23 | }
24 |
25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
26 | {
27 | throw new NotImplementedException();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Converters/InverseBooleanConverter.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Converters
2 | {
3 | using System;
4 | using System.Windows.Data;
5 |
6 | [ValueConversion(typeof(bool), typeof(bool))]
7 | public class InverseBooleanConverter : IValueConverter
8 | {
9 | #region IValueConverter Members
10 |
11 | public object Convert(object value, Type targetType, object parameter,
12 | System.Globalization.CultureInfo culture)
13 | {
14 | if (targetType != typeof(bool))
15 | throw new InvalidOperationException("The target must be a boolean");
16 |
17 | return !(bool)value;
18 | }
19 |
20 | public object ConvertBack(object value, Type targetType, object parameter,
21 | System.Globalization.CultureInfo culture)
22 | {
23 | throw new NotSupportedException();
24 | }
25 |
26 | #endregion
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Converters/LocationTypeToImageConverter.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Converters
2 | {
3 | using BusinessLib.Models;
4 | using System;
5 | using System.Globalization;
6 | using System.Windows;
7 | using System.Windows.Data;
8 |
9 | ///
10 | /// Converts an enum into its image resource.
11 | /// The corresponding image resource must be present in the applications's
12 | /// resource dictionary.
13 | ///
14 | [ValueConversion(typeof(LocationType), typeof(System.Windows.Media.Imaging.BitmapImage))]
15 | public class LocationTypeToImageConverter : IValueConverter
16 | {
17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
18 | {
19 | if (value == null)
20 | return Binding.DoNothing;
21 |
22 | if (value is LocationType == false)
23 | return Binding.DoNothing;
24 |
25 | LocationType typeOfLocation = (LocationType)value;
26 | string locationIconName = string.Empty;
27 |
28 | try
29 | {
30 | switch (typeOfLocation)
31 | {
32 | case LocationType.Country:
33 | locationIconName = "CountryImage";
34 | break;
35 |
36 | case LocationType.Region:
37 | locationIconName = "RegionImage";
38 | break;
39 |
40 | case LocationType.City:
41 | locationIconName = "CityImage";
42 | break;
43 |
44 | case LocationType.Unknown:
45 | return Binding.DoNothing;
46 |
47 | default:
48 | throw new ArgumentOutOfRangeException(typeOfLocation.ToString());
49 | }
50 |
51 | return Application.Current.Resources[locationIconName];
52 | }
53 | catch
54 | {
55 | }
56 |
57 | return Binding.DoNothing;
58 | }
59 |
60 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
61 | {
62 | throw new NotImplementedException();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/FilterTreeViewLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {C5030463-0469-4AFA-92F4-F6B55A6B4C89}
8 | Library
9 | Properties
10 | FilterTreeViewLib
11 | FilterTreeViewLib
12 | v4.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | TRACE;DEBUG;TREELIB
22 | prompt
23 | 4
24 | false
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE;TREELIB
31 | prompt
32 | 4
33 | false
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | ..\..\packages\Dirkster.TreeLib.1.2.0\lib\net40\TreeLib.dll
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
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 | {70cc82cc-9efe-461f-a959-f64a06894621}
99 | BusinessLib
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Interfaces/IHasDummyChild.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Interfaces
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | public interface IHasDummyChild
10 | {
11 |
12 | ///
13 | /// Determines whether this item has a dummy child below or not.
14 | ///
15 | bool HasDummyChild { get; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Interfaces/ISelectionRange.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.Interfaces
2 | {
3 | using System;
4 | using System.Windows.Media;
5 |
6 | ///
7 | /// Defines a range that can be used to indicate
8 | /// the start and end of a text selection or any other kind of range.
9 | ///
10 | public interface ISelectionRange : ICloneable
11 | {
12 | ///
13 | /// Gets the start of the indicated range.
14 | ///
15 | int Start { get; }
16 |
17 | ///
18 | /// Gets the end of the indicated range.
19 | ///
20 | int End { get; }
21 |
22 | ///
23 | /// Gets a bool value to determine whether DarkSkin default
24 | /// value for property should
25 | /// be applied or not.
26 | ///
27 | bool DarkSkin { get; }
28 |
29 | ///
30 | /// Gets the background color that is applied to the background brush,
31 | /// which should be applied when no match is indicated
32 | /// (this can be default(Color) in which case standard selection Brush
33 | /// is applied).
34 | ///
35 | /// Note:
36 | /// Standard selection background color on light skin: 208, 247, 255
37 | /// Standard selection background color on dark skin: 254, 252, 200
38 | ///
39 | Color SelectionBackground { get; }
40 |
41 | ///
42 | /// Gets the background color that is applied to the background brush.
43 | /// which should be applied when no match is indicated
44 | /// (this can be default(Color) in which case Transparent is applied).
45 | ///
46 | Color NormalBackground { get; }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("FilterTreeViewLib")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("FilterTreeViewLib")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("c5030463-0469-4afa-92f4-f6b55a6b4c89")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/Locations/appbar.base.select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/Locations/appbar.base.select.png
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/Locations/appbar.chess.rook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/Locations/appbar.chess.rook.png
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/Locations/appbar.city.sanfrancisco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/Locations/appbar.city.sanfrancisco.png
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/ZoomIn.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/ZoomIn.ico
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/ZoomIn_256x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/ZoomIn_256x.png
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/ZoomIn_48x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/ZoomIn_48x.png
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/Resources/ZoomIn_64x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dirkster99/FilterTreeView/71c690e00af040e64b4d6f0cb9270565e59789de/source/Components/FilterTreeViewLib/Resources/ZoomIn_64x.png
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/ViewModels/AppBaseViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.ViewModels
2 | {
3 | using System.Threading.Tasks;
4 |
5 | public abstract class AppBaseViewModel : Base.BaseViewModel
6 | {
7 | #region fields
8 | private bool _IsStringContainedSearchOption;
9 | private int _CountSearchMatches;
10 | private bool _IsProcessing;
11 | private bool _IsLoading;
12 |
13 | private string _StatusStringResult;
14 | private string _SearchString;
15 | #endregion fields
16 |
17 | #region constructors
18 | ///
19 | /// Class constructor
20 | ///
21 | public AppBaseViewModel()
22 | {
23 | _SearchString = "Washington";
24 | _IsProcessing = _IsLoading = false;
25 | _CountSearchMatches = 0;
26 | _IsStringContainedSearchOption = true;
27 | }
28 | #endregion constructors
29 |
30 | #region properties
31 | ///
32 | /// Gets a property to determine if application is currently processing
33 | /// data (loading or searching for matches in the tree view) or not.
34 | ///
35 | public bool IsProcessing
36 | {
37 | get { return _IsProcessing; }
38 | protected set
39 | {
40 | if (_IsProcessing != value)
41 | {
42 | _IsProcessing = value;
43 | NotifyPropertyChanged(() => IsProcessing);
44 | }
45 | }
46 | }
47 |
48 | ///
49 | /// Gets a property to determine if application is currently processing
50 | /// data (loading or searching for matches in the tree view) or not.
51 | ///
52 | public bool IsLoading
53 | {
54 | get { return _IsLoading; }
55 | protected set
56 | {
57 | if (_IsLoading != value)
58 | {
59 | _IsLoading = value;
60 | NotifyPropertyChanged(() => IsLoading);
61 | }
62 | }
63 | }
64 |
65 | ///
66 | /// Gets the input string from search textbox control.
67 | ///
68 | public string SearchString
69 | {
70 | get { return _SearchString; }
71 | set
72 | {
73 | if (_SearchString != value)
74 | {
75 | _SearchString = value;
76 | NotifyPropertyChanged(() => SearchString);
77 | }
78 | }
79 | }
80 |
81 | ///
82 | /// Gets the search string that is synchronized with the results from the search algorithm.
83 | ///
84 | public string StatusStringResult
85 | {
86 | get { return _StatusStringResult; }
87 | protected set
88 | {
89 | if (_StatusStringResult != value)
90 | {
91 | _StatusStringResult = value;
92 | NotifyPropertyChanged(() => StatusStringResult);
93 | }
94 | }
95 | }
96 |
97 | ///
98 | /// Determines whether the search is looking for:
99 | /// - strings that are contained in a given string or
100 | /// - strings that match the searched string with all letters.
101 | ///
102 | public bool IsStringContainedSearchOption
103 | {
104 | get { return _IsStringContainedSearchOption; }
105 | set
106 | {
107 | if (_IsStringContainedSearchOption != value)
108 | {
109 | _IsStringContainedSearchOption = value;
110 | NotifyPropertyChanged(() => IsStringContainedSearchOption);
111 | }
112 | }
113 | }
114 |
115 | ///
116 | /// Gets the number of matches that are found durring
117 | /// a search in the tree view nodes for a given string.
118 | ///
119 | public int CountSearchMatches
120 | {
121 | get { return _CountSearchMatches; }
122 | protected set
123 | {
124 | if (_CountSearchMatches != value)
125 | {
126 | _CountSearchMatches = value;
127 | NotifyPropertyChanged(() => CountSearchMatches);
128 | }
129 | }
130 | }
131 | #endregion properties
132 |
133 | #region methods
134 | ///
135 | /// Loads the initial sample data from XML file into memory
136 | ///
137 | ///
138 | public abstract Task LoadSampleDataAsync();
139 | #endregion methods
140 | }
141 | }
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/ViewModels/Base/BaseViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.ViewModels.Base
2 | {
3 | using System;
4 | using System.ComponentModel;
5 | using System.Linq.Expressions;
6 |
7 | ///
8 | /// Every ViewModel class is required to implement the INotifyPropertyChanged
9 | /// interface in order to tell WPF when a property changed (for instance, when
10 | /// a method or setter is executed).
11 | ///
12 | /// Therefore, the PropertyChanged methode has to be called when data changes,
13 | /// because the relevant properties may or may not be bound to GUI elements,
14 | /// which in turn have to refresh their display.
15 | ///
16 | /// The PropertyChanged method is to be called by the members and properties of
17 | /// the class that derives from this class. Each call contains the name of the
18 | /// property that has to be refreshed.
19 | ///
20 | /// The BaseViewModel is derived from from System.Windows.DependencyObject to allow
21 | /// resulting ViewModels the implemantion of dependency properties. Dependency properties
22 | /// in turn are useful when working with IValueConverter and ConverterParameters.
23 | ///
24 | public class BaseViewModel : INotifyPropertyChanged
25 | {
26 | ///
27 | /// Standard event handler of the interface
28 | ///
29 | public event PropertyChangedEventHandler PropertyChanged;
30 |
31 | ///
32 | /// Tell bound controls (via WPF binding) to refresh their display.
33 | ///
34 | /// Sample call: this.NotifyPropertyChanged(() => this.IsSelected);
35 | /// where 'this' is derived from
36 | /// and IsSelected is a property.
37 | ///
38 | ///
39 | ///
40 | public void NotifyPropertyChanged(Expression> property)
41 | {
42 | var lambda = (LambdaExpression)property;
43 | MemberExpression memberExpression;
44 |
45 | if (lambda.Body is UnaryExpression)
46 | {
47 | var unaryExpression = (UnaryExpression)lambda.Body;
48 | memberExpression = (MemberExpression)unaryExpression.Operand;
49 | }
50 | else
51 | memberExpression = (MemberExpression)lambda.Body;
52 |
53 | this.OnPropertyChanged(memberExpression.Member.Name);
54 | }
55 |
56 | ///
57 | /// Tell bound controls (via WPF binding) to refresh their display.
58 | ///
59 | /// Sample call: this.OnPropertyChanged("IsSelected");
60 | /// where 'this' is derived from
61 | /// and IsSelected is a property.
62 | ///
63 | /// Name of property to refresh
64 | public void OnPropertyChanged(string propertyName)
65 | {
66 | try
67 | {
68 | if (this.PropertyChanged != null)
69 | this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
70 | }
71 | catch
72 | {
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/ViewModels/Base/RelayCommand.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.ViewModels.Base
2 | {
3 | using System;
4 | using System.Diagnostics;
5 | using System.Windows.Input;
6 |
7 | ///
8 | /// A class whose sole purpose is to relay its functionality to other
9 | /// objects by invoking delegates.
10 | ///
11 | /// The default return value for the CanExecute method is 'true'.
12 | ///
13 | /// Source: http://www.codeproject.com/Articles/31837/Creating-an-Internationalized-Wizard-in-WPF
14 | ///
15 | public class RelayCommand : ICommand
16 | {
17 | #region Fields
18 | private readonly Action mExecute = null;
19 | private readonly Predicate mCanExecute = null;
20 | #endregion // Fields
21 |
22 | #region Constructors
23 | ///
24 | /// Class constructor
25 | ///
26 | ///
27 | public RelayCommand(Action execute)
28 | : this(execute, null)
29 | {
30 | }
31 |
32 | ///
33 | /// Creates a new command.
34 | ///
35 | /// The execution logic.
36 | /// The execution status logic.
37 | public RelayCommand(Action execute, Predicate canExecute)
38 | {
39 | if (execute == null)
40 | throw new ArgumentNullException("execute");
41 |
42 | this.mExecute = execute;
43 | this.mCanExecute = canExecute;
44 | }
45 |
46 | #endregion // Constructors
47 |
48 | #region events
49 | ///
50 | /// Eventhandler to re-evaluate whether this command can execute or not
51 | ///
52 | public event EventHandler CanExecuteChanged
53 | {
54 | add
55 | {
56 | if (this.mCanExecute != null)
57 | CommandManager.RequerySuggested += value;
58 | }
59 |
60 | remove
61 | {
62 | if (this.mCanExecute != null)
63 | CommandManager.RequerySuggested -= value;
64 | }
65 | }
66 | #endregion
67 |
68 | #region methods
69 | ///
70 | /// Determine whether this pre-requisites to execute this command are given or not.
71 | ///
72 | ///
73 | ///
74 | [DebuggerStepThrough]
75 | public bool CanExecute(object parameter)
76 | {
77 | return this.mCanExecute == null ? true : this.mCanExecute((T)parameter);
78 | }
79 |
80 | ///
81 | /// Execute the command method managed in this class.
82 | ///
83 | ///
84 | public void Execute(object parameter)
85 | {
86 | this.mExecute((T)parameter);
87 | }
88 | #endregion methods
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/source/Components/FilterTreeViewLib/ViewModels/Tree/MetaLocationRootViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace FilterTreeViewLib.ViewModels
2 | {
3 | using FilterTreeViewLib.ViewModels.Base;
4 | using FilterTreeViewLib.ViewModelsSearch.SearchModels;
5 | using FilterTreeViewLib.ViewModelsSearch.SearchModels.Enums;
6 | using System.Collections.Generic;
7 | using System.Collections.ObjectModel;
8 | using System.Linq;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using System.Windows.Data;
12 | using System.Windows.Input;
13 |
14 | ///
15 | /// Implements the root viewmodel of the tree structure to be presented in a treeview.
16 | ///
17 | public class MetaLocationRootViewModel : Base.BaseViewModel
18 | {
19 | #region fields
20 | protected readonly ObservableCollection _CountryRootItems = null;
21 | protected readonly IList _BackUpCountryRoots = null;
22 |
23 | private ICommand _ExpandCommand;
24 |
25 | private object _itemsLock = new object();
26 | #endregion fields
27 |
28 | #region constructors
29 | ///
30 | /// Class constructor
31 | ///
32 | public MetaLocationRootViewModel()
33 | {
34 | _CountryRootItems = new ObservableCollection();
35 | BindingOperations.EnableCollectionSynchronization(_CountryRootItems, _itemsLock);
36 |
37 | _BackUpCountryRoots = new List(400);
38 | }
39 | #endregion constructors
40 |
41 | #region properties
42 | ///
43 | /// Gets all bindable rootitems of the displayed treeview
44 | ///
45 | public ObservableCollection CountryRootItems
46 | {
47 | get
48 | {
49 | return _CountryRootItems;
50 | }
51 | }
52 |
53 | ///
54 | /// Gets the total count of (backup) rootitems in the tree collectiton.
55 | ///
56 | /// This number can be larger than the nummber of items in the
57 | /// collection if a filter is currently applied.
58 | ///
59 | public int BackUpCountryRootsCount
60 | {
61 | get
62 | {
63 | return _BackUpCountryRoots.Count();
64 | }
65 | }
66 |
67 | ///
68 | /// Gets a command that expands the item given in the command parameter.
69 | ///
70 | public ICommand ExpandCommand
71 | {
72 | get
73 | {
74 | if (_ExpandCommand == null)
75 | {
76 | _ExpandCommand = new RelayCommand