├── source ├── Components │ ├── BusinessLib │ │ ├── Resources │ │ │ ├── lokasyon.zip │ │ │ ├── lokasyon.sql.gz │ │ │ └── readme.txt │ │ ├── Models │ │ │ ├── LocationType.cs │ │ │ └── MetaLocationModel.cs │ │ ├── Properties │ │ │ └── AssemblyInfo.cs │ │ ├── BusinessLib.csproj │ │ └── database.cs │ └── FilterTreeViewLib │ │ ├── Resources │ │ ├── ZoomIn.ico │ │ ├── ZoomIn_256x.png │ │ ├── ZoomIn_48x.png │ │ ├── ZoomIn_64x.png │ │ └── Locations │ │ │ ├── appbar.base.select.png │ │ │ ├── appbar.chess.rook.png │ │ │ └── appbar.city.sanfrancisco.png │ │ ├── packages.config │ │ ├── Interfaces │ │ ├── IHasDummyChild.cs │ │ └── ISelectionRange.cs │ │ ├── ViewModels │ │ ├── Tree │ │ │ ├── Search │ │ │ │ ├── Enums │ │ │ │ │ ├── SearchMatch.cs │ │ │ │ │ └── MatchType.cs │ │ │ │ ├── SearchResult.cs │ │ │ │ ├── SelectionRange.cs │ │ │ │ ├── SearchParams.cs │ │ │ │ └── StringMatchItem.cs │ │ │ ├── MetaLocationRootViewModel.cs │ │ │ └── MetaLocationViewModel.cs │ │ ├── Base │ │ │ ├── RelayCommand.cs │ │ │ └── BaseViewModel.cs │ │ └── AppBaseViewModel.cs │ │ ├── Converters │ │ ├── CountToBoolConverter.cs │ │ ├── InverseBooleanConverter.cs │ │ ├── BoolToVisibilityConverter.cs │ │ └── LocationTypeToImageConverter.cs │ │ ├── Properties │ │ └── AssemblyInfo.cs │ │ ├── Views │ │ └── BindingProxy.cs │ │ ├── Behaviors │ │ ├── TreeViewItemExpanded.cs │ │ ├── HighlightTextBlockBehavior.cs │ │ └── TextChangedCommand.cs │ │ └── FilterTreeViewLib.csproj ├── FilterTreeView │ ├── packages.config │ ├── App.config │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── App.xaml.cs │ ├── FilterTreeView.csproj.user │ ├── MainWindow.xaml.cs │ ├── App.xaml │ ├── FilterTreeView.csproj │ ├── ViewModels │ │ └── AppViewModel.cs │ ├── Tasks │ │ ├── OneTaskLimitedScheduler.cs │ │ └── OneTaskProcessor.cs │ └── MainWindow.xaml ├── CleanAll.bat └── FilterTreeView.sln ├── .gitignore ├── LICENSE └── README.md /source/Components/BusinessLib/Resources/lokasyon.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/BusinessLib/Resources/lokasyon.zip -------------------------------------------------------------------------------- /source/Components/BusinessLib/Resources/lokasyon.sql.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/BusinessLib/Resources/lokasyon.sql.gz -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/ZoomIn.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/ZoomIn.ico -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/ZoomIn_256x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/ZoomIn_256x.png -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/ZoomIn_48x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/ZoomIn_48x.png -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/ZoomIn_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/ZoomIn_64x.png -------------------------------------------------------------------------------- /source/FilterTreeView/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/Locations/appbar.base.select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/Locations/appbar.base.select.png -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/Locations/appbar.chess.rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/Locations/appbar.chess.rook.png -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/Resources/Locations/appbar.city.sanfrancisco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dirkster99/FilterTreeView/HEAD/source/Components/FilterTreeViewLib/Resources/Locations/appbar.city.sanfrancisco.png -------------------------------------------------------------------------------- /source/FilterTreeView/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /source/FilterTreeView/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /source/FilterTreeView/App.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeView 2 | { 3 | using System.Windows; 4 | 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /source/FilterTreeView/FilterTreeView.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ProjectFiles 5 | 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/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/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/FilterTreeViewLib/ViewModels/Tree/Search/Enums/SearchMatch.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.ViewModelsSearch.SearchModels.Enums 2 | { 3 | /// 4 | /// Determines the type of match that should be implemented to 5 | /// match a node to a search string during a search. 6 | /// 7 | public enum SearchMatch 8 | { 9 | /// 10 | /// The string searched is contained somewhere in within a nodes string. 11 | /// 12 | StringIsContained = 0, 13 | 14 | /// 15 | /// The string searched is an exact match within a nodes string 16 | /// (length of strings is identical and ALL letters are present 17 | /// in the given order - but case folding may still be applied). 18 | /// 19 | StringIsMatched = 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /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/FilterTreeView/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeView 2 | { 3 | using System; 4 | using System.Windows; 5 | 6 | /// 7 | /// Interaction logic for MainWindow.xaml 8 | /// 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | 15 | Loaded += MainWindow_Loaded; 16 | Unloaded += MainWindow_Unloaded; 17 | } 18 | 19 | private void MainWindow_Unloaded(object sender, RoutedEventArgs e) 20 | { 21 | var appVM = this.DataContext as IDisposable; 22 | 23 | if (appVM != null) 24 | appVM.Dispose(); 25 | } 26 | 27 | private async void MainWindow_Loaded(object sender, RoutedEventArgs e) 28 | { 29 | Loaded -= MainWindow_Loaded; 30 | 31 | var appVM = new FilterTreeView.ViewModels.AppViewModel(); 32 | this.DataContext = appVM; 33 | 34 | await appVM.LoadSampleDataAsync(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /source/FilterTreeView/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/FilterTreeView/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 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 FilterTreeView.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /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/ViewModels/Tree/Search/Enums/MatchType.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.ViewModelsSearch.SearchModels.Enums 2 | { 3 | /// 4 | /// Determines whether a givem node has a match with regard to search parameters 5 | /// - search string and 6 | /// - additional search options (eg.: string contained, exact match) 7 | /// 8 | /// This includes a reference to child items that might contain a 9 | /// match while the node itself may only be the parent of a child 10 | /// with an actual match. 11 | /// 12 | public enum MatchType 13 | { 14 | /// 15 | /// The node (and its children) contains no match. 16 | /// 17 | NoMatch = 0, 18 | 19 | /// 20 | /// The node was macth against the search parameters. 21 | /// 22 | NodeMatch = 1, 23 | 24 | /// 25 | /// A child node or children nodes of this node contain a match 26 | /// BUT this node does not contain a match. 27 | /// 28 | SubNodeMatch = 2, 29 | 30 | /// 31 | /// A child node or children nodes of this node contain a match 32 | /// AND this node does also contain a match. 33 | /// 34 | Node_AND_SubNodeMatch = 4 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/u762r32aupstrsph?svg=true)](https://ci.appveyor.com/project/Dirkster99/filtertreeview) [![Release](https://img.shields.io/github/release/Dirkster99/FilterTreeView.svg)](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/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/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/Views/BindingProxy.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.Views 2 | { 3 | using System.Windows; 4 | 5 | /// 6 | /// Implements an XAML proxy which can be used to bind items (TreeViewItem, ListViewItem etc) 7 | /// with a viewmodel that manages the collecrions. 8 | /// 9 | /// Source: http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ 10 | /// Issue: http://stackoverflow.com/questions/9994241/mvvm-binding-command-to-contextmenu-item 11 | /// 12 | public class BindingProxy : Freezable 13 | { 14 | /// 15 | /// Backing storage of the Data dependency property. 16 | /// 17 | /// Gets/sets the data object this class is forwarding to everyone 18 | /// who has a reference to this object. 19 | /// 20 | public static readonly DependencyProperty DataProperty = 21 | DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); 22 | 23 | /// 24 | /// Gets/sets the data object this class is forwarding to everyone 25 | /// who has a reference to this object. 26 | /// 27 | public object Data 28 | { 29 | get { return (object)GetValue(DataProperty); } 30 | set { SetValue(DataProperty, value); } 31 | } 32 | 33 | /// 34 | /// Overrides of Freezable 35 | /// 36 | /// 37 | protected override Freezable CreateInstanceCore() 38 | { 39 | return new BindingProxy(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /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/ViewModels/Tree/Search/SearchResult.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.ViewModelsSearch.SearchModels 2 | { 3 | /// 4 | /// Models the results of a search in terms of the actual string and options 5 | /// being searched and the results found. 6 | /// 7 | public class SearchResult 8 | { 9 | #region constructors 10 | /// 11 | /// Class constructor 12 | /// 13 | /// 14 | /// 15 | public SearchResult(SearchParams searchParams, int results) 16 | : this() 17 | { 18 | Options = searchParams; 19 | Results = results; 20 | } 21 | 22 | /// 23 | /// Hidden Class constructor 24 | /// 25 | protected SearchResult() 26 | { 27 | } 28 | #endregion constructors 29 | 30 | /// 31 | /// Gets the searchterm that was usedt o find the shown results. 32 | /// 33 | public string SearchTerm 34 | { 35 | get 36 | { 37 | if (Options != null) 38 | return Options.SearchString; 39 | 40 | return string.Empty; 41 | } 42 | } 43 | 44 | /// 45 | /// Gets the ORIGONAL searchterm that was used to find the shown results. 46 | /// 47 | public string OriginalSearchString 48 | { 49 | get 50 | { 51 | if (Options != null) 52 | return Options.OriginalSearchString; 53 | 54 | return string.Empty; 55 | } 56 | } 57 | 58 | /// 59 | /// Gets the search parameters and the search term for this result. 60 | /// 61 | public SearchParams Options { get; protected set; } 62 | 63 | /// 64 | /// Gets the number of matches found. 65 | /// 66 | public int Results { get; protected set; } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /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/FilterTreeView/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 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("FilterTreeView")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("FilterTreeView")] 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 | //In order to begin building localizable applications, set 23 | //CultureYouAreCodingWith in your .csproj file 24 | //inside a . For example, if you are using US english 25 | //in your source files, set the to en-US. Then uncomment 26 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 27 | //the line below to match the UICulture setting in the project file. 28 | 29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 30 | 31 | 32 | [assembly: ThemeInfo( 33 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 34 | //(used if a resource is not found in the page, 35 | // or application resource dictionaries) 36 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 37 | //(used if a resource is not found in the page, 38 | // app, or any theme specific resource dictionaries) 39 | )] 40 | 41 | 42 | // Version information for an assembly consists of the following four values: 43 | // 44 | // Major Version 45 | // Minor Version 46 | // Build Number 47 | // Revision 48 | // 49 | // You can specify all the values or you can default the Build and Revision Numbers 50 | // by using the '*' as shown below: 51 | // [assembly: AssemblyVersion("1.0.*")] 52 | [assembly: AssemblyVersion("1.0.0.0")] 53 | [assembly: AssemblyFileVersion("1.0.0.0")] 54 | -------------------------------------------------------------------------------- /source/FilterTreeView.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.27004.2009 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FilterTreeView", "FilterTreeView\FilterTreeView.csproj", "{9668A53D-608F-46B1-926C-14AC52CD35AE}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLib", "Components\BusinessLib\BusinessLib.csproj", "{70CC82CC-9EFE-461F-A959-F64A06894621}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FilterTreeViewLib", "Components\FilterTreeViewLib\FilterTreeViewLib.csproj", "{C5030463-0469-4AFA-92F4-F6B55A6B4C89}" 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Components", "Components", "{F0EEB4AD-6E8F-47AC-BE12-98A0A15ABD95}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {9668A53D-608F-46B1-926C-14AC52CD35AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {9668A53D-608F-46B1-926C-14AC52CD35AE}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {9668A53D-608F-46B1-926C-14AC52CD35AE}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {9668A53D-608F-46B1-926C-14AC52CD35AE}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {70CC82CC-9EFE-461F-A959-F64A06894621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {70CC82CC-9EFE-461F-A959-F64A06894621}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {70CC82CC-9EFE-461F-A959-F64A06894621}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {70CC82CC-9EFE-461F-A959-F64A06894621}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {C5030463-0469-4AFA-92F4-F6B55A6B4C89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {C5030463-0469-4AFA-92F4-F6B55A6B4C89}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {C5030463-0469-4AFA-92F4-F6B55A6B4C89}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {C5030463-0469-4AFA-92F4-F6B55A6B4C89}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | GlobalSection(NestedProjects) = preSolution 36 | {70CC82CC-9EFE-461F-A959-F64A06894621} = {F0EEB4AD-6E8F-47AC-BE12-98A0A15ABD95} 37 | {C5030463-0469-4AFA-92F4-F6B55A6B4C89} = {F0EEB4AD-6E8F-47AC-BE12-98A0A15ABD95} 38 | EndGlobalSection 39 | GlobalSection(ExtensibilityGlobals) = postSolution 40 | SolutionGuid = {E5C2784B-651F-471A-84F7-7A11969323CE} 41 | EndGlobalSection 42 | EndGlobal 43 | -------------------------------------------------------------------------------- /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/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/FilterTreeView/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 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 FilterTreeView.Properties { 12 | using System; 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", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FilterTreeView.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /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/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/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/Tree/Search/SelectionRange.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.ViewModels.Tree.Search 2 | { 3 | using System.Windows.Media; 4 | using FilterTreeViewLib.Interfaces; 5 | 6 | /// 7 | /// Implements a range object that can be used to indicate 8 | /// the start and end of a text selection or any other kind of range. 9 | /// 10 | public class SelectionRange : Base.BaseViewModel, ISelectionRange 11 | { 12 | #region fields 13 | private int _Start; 14 | private int _End; 15 | #endregion fields 16 | 17 | #region ctors 18 | /// 19 | /// Parameterized class constructor. 20 | /// 21 | /// 22 | /// 23 | public SelectionRange(int start, int end) 24 | : this() 25 | { 26 | Start = start; 27 | End = end; 28 | } 29 | 30 | /// 31 | /// Copy constructor 32 | /// 33 | /// 34 | public SelectionRange(SelectionRange copyThis) 35 | :this() 36 | { 37 | if (copyThis == null) 38 | return; 39 | 40 | Start = copyThis.Start; 41 | End = copyThis.End; 42 | SelectionBackground = copyThis.SelectionBackground; 43 | NormalBackground = copyThis.NormalBackground; 44 | DarkSkin = copyThis.DarkSkin; 45 | } 46 | 47 | /// 48 | /// Class constructor. 49 | /// 50 | public SelectionRange() 51 | { 52 | _Start = _End = 1; 53 | SelectionBackground = default(Color); 54 | NormalBackground = default(Color); 55 | DarkSkin = false; 56 | } 57 | #endregion ctors 58 | 59 | #region properties 60 | /// 61 | /// Gets the start of the indicated range. 62 | /// 63 | public int Start 64 | { 65 | get 66 | { 67 | return _Start; 68 | } 69 | 70 | private set 71 | { 72 | if (_Start != value) 73 | { 74 | _Start = value; 75 | NotifyPropertyChanged(() => Start); 76 | } 77 | } 78 | } 79 | 80 | /// 81 | /// Gets the end of the indicated range. 82 | /// 83 | public int End 84 | { 85 | get 86 | { 87 | return _End; 88 | } 89 | 90 | private set 91 | { 92 | if (_End != value) 93 | { 94 | _End = value; 95 | NotifyPropertyChanged(() => End); 96 | } 97 | } 98 | } 99 | 100 | /// 101 | /// Gets a bool value to determine whether DarkSkin default 102 | /// value for property should 103 | /// be applied or not. 104 | /// 105 | public bool DarkSkin { get; private set; } 106 | 107 | /// 108 | /// Gets the background color that is applied to the background brush, 109 | /// which should be applied when no match is indicated 110 | /// (this can be default(Color) in which case standard selection Brush 111 | /// is applied). 112 | /// 113 | public Color SelectionBackground { get; set; } 114 | 115 | /// 116 | /// Gets the background color that is applied to the background brush. 117 | /// which should be applied when no match is indicated 118 | /// (this can be default(Color) in which case Transparent is applied). 119 | /// 120 | public Color NormalBackground { get; set; } 121 | 122 | /// 123 | /// Gets a copy of this object. 124 | /// 125 | /// 126 | public object Clone() 127 | { 128 | return new SelectionRange(this); 129 | } 130 | #endregion properties 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /source/Components/FilterTreeViewLib/ViewModels/Tree/Search/SearchParams.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.ViewModelsSearch.SearchModels 2 | { 3 | /// 4 | /// Implements a search object that contains the search string, 5 | /// related options, as well as methods to determine whether a 6 | /// given string is a match against the string or not. 7 | /// 8 | public class SearchParams 9 | { 10 | #region constructors 11 | /// 12 | /// Class constructor 13 | /// 14 | public SearchParams( 15 | string searchString 16 | , Enums.SearchMatch match) 17 | : this() 18 | { 19 | OriginalSearchString = SearchString = (searchString == null ? string.Empty : searchString); 20 | Match = match; 21 | } 22 | 23 | /// 24 | /// Class constructor 25 | /// 26 | public SearchParams() 27 | { 28 | SearchString = string.Empty; 29 | Match = Enums.SearchMatch.StringIsContained; 30 | MinimalSearchStringLength = 1; 31 | } 32 | #endregion constructors 33 | 34 | #region properties 35 | /// 36 | /// Gets the plain text string being searched or filtered. 37 | /// 38 | public string SearchString { get; private set; } 39 | 40 | /// 41 | /// Gets the plain text ORIGINAL string being searched or filtered. 42 | /// This string is the string that was set by the constructor and 43 | /// CANNOT be changed later on. 44 | /// 45 | public string OriginalSearchString { get; private set; } 46 | 47 | /// 48 | /// Gets the string being searched or filtered. 49 | /// 50 | public Enums.SearchMatch Match { get; private set; } 51 | 52 | /// 53 | /// Gets whether search string contains actual content or not. 54 | /// 55 | public bool IsSearchStringEmpty 56 | { 57 | get 58 | { 59 | return string.IsNullOrEmpty(SearchString); 60 | } 61 | } 62 | 63 | /// 64 | /// Gets the minimal search string length required. 65 | /// Any string shorter than this will not be searched at all. 66 | /// 67 | public int MinimalSearchStringLength { get; set; } 68 | #endregion properties 69 | 70 | #region methods 71 | /// 72 | /// Determines if a given string is considered a match in comparison 73 | /// to the search string and its options or not. 74 | /// 75 | /// 76 | /// true if is a match, otherwise false 77 | public int MatchSearchString(string stringToFind) 78 | { 79 | stringToFind = (stringToFind == null ? string.Empty : stringToFind); 80 | 81 | stringToFind = stringToFind.ToUpper(); 82 | 83 | switch (Match) 84 | { 85 | case Enums.SearchMatch.StringIsContained: 86 | return stringToFind.IndexOf(SearchString); 87 | 88 | case Enums.SearchMatch.StringIsMatched: 89 | if (SearchString == stringToFind) 90 | return 0; 91 | break; 92 | 93 | default: 94 | throw new System.ArgumentOutOfRangeException( 95 | string.Format("Internal Error: Search option '{0}' not implemented.", Match)); 96 | } 97 | 98 | return -1; 99 | } 100 | 101 | /// 102 | /// Can be called to trim the search string before matching takes place. 103 | /// 104 | public void SearchStringTrim() 105 | { 106 | SearchString = SearchString.Trim(); 107 | } 108 | 109 | /// 110 | /// Can be called to convert the search string 111 | /// to upper case before matching takes place. 112 | /// 113 | public void SearchStringToUpperCase() 114 | { 115 | SearchString = SearchString.ToUpper(); 116 | } 117 | #endregion methods 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /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/ViewModels/Tree/Search/StringMatchItem.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeViewLib.ViewModels.Tree.Search 2 | { 3 | using FilterTreeViewLib.Interfaces; 4 | 5 | /// 6 | /// Implements a viewmodel that provides a string to display and can indicate 7 | /// a macthing range against another string via the property. 8 | /// 9 | public class StringMatchItem : Base.BaseViewModel 10 | { 11 | #region fields 12 | private ISelectionRange _Range; 13 | private string _DisplayString; 14 | #endregion fields 15 | 16 | #region ctors 17 | /// 18 | /// Parameterized class constructor. 19 | /// 20 | /// 21 | public StringMatchItem(string displayString) 22 | { 23 | DisplayString = displayString; 24 | } 25 | 26 | /// 27 | /// Class constructor. 28 | /// 29 | public StringMatchItem() 30 | { 31 | _Range = new SelectionRange(); 32 | _DisplayString = string.Empty; 33 | } 34 | #endregion ctors 35 | 36 | #region properties 37 | /// 38 | /// Gets the range property that indicates the text range 39 | /// to be considered a match against another (search) string. 40 | /// 41 | public ISelectionRange Range 42 | { 43 | get 44 | { 45 | return _Range; 46 | } 47 | 48 | private set 49 | { 50 | if (_Range != null && value != null) 51 | { 52 | // Nothing changed - so we change nothing here :-) 53 | if (_Range.Start == value.Start && 54 | _Range.End == value.End) 55 | return; 56 | 57 | _Range = value; 58 | NotifyPropertyChanged(() => Range); 59 | } 60 | 61 | if (_Range == null && value != null || 62 | _Range != null && value == null) 63 | { 64 | _Range = value; 65 | NotifyPropertyChanged(() => Range); 66 | } 67 | } 68 | } 69 | 70 | /// 71 | /// Gets te string that should be displayed (with or without hightlighting) 72 | /// 73 | public string DisplayString 74 | { 75 | get 76 | { 77 | return _DisplayString; 78 | } 79 | 80 | private set 81 | { 82 | if (_DisplayString != value) 83 | { 84 | _DisplayString = value; 85 | NotifyPropertyChanged(() => DisplayString); 86 | } 87 | } 88 | } 89 | #endregion properties 90 | 91 | #region methods 92 | /// 93 | /// Evaluates the given string against the 94 | /// property and sets the property to indicate the 95 | /// matched text range. 96 | /// 97 | /// 98 | /// 99 | public bool MatchString(string searchString) 100 | { 101 | if (string.IsNullOrEmpty(DisplayString) == true && 102 | string.IsNullOrEmpty(searchString) == true) 103 | { 104 | Range = new SelectionRange(0, 0); 105 | return true; 106 | } 107 | else 108 | { 109 | // There is no point in making this a match 110 | // if only one of the strings is not present 111 | if (string.IsNullOrEmpty(DisplayString) == true || 112 | string.IsNullOrEmpty(searchString) == true) 113 | { 114 | Range = new SelectionRange(-1, -1); 115 | return false; 116 | } 117 | } 118 | 119 | // Do we have a (sub)match or not ??? 120 | int start; 121 | if ((start = DisplayString.IndexOf(searchString)) >= 0) 122 | { 123 | Range = new SelectionRange(start, start + searchString.Length); 124 | return true; 125 | } 126 | else 127 | { 128 | Range = new SelectionRange(start, -1); 129 | return false; 130 | } 131 | } 132 | #endregion methods 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /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/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/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/FilterTreeView/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 | -------------------------------------------------------------------------------- /source/FilterTreeView/FilterTreeView.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9668A53D-608F-46B1-926C-14AC52CD35AE} 8 | WinExe 9 | Properties 10 | FilterTreeView 11 | FilterTreeView 12 | v4.5.2 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | 18 | 19 | 20 | 21 | AnyCPU 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | TRACE;DEBUG;TREELIB 27 | prompt 28 | 4 29 | false 30 | 31 | 32 | AnyCPU 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 4.0 57 | 58 | 59 | ..\packages\Dirkster.TreeLib.1.2.0\lib\net40\TreeLib.dll 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | MSBuild:Compile 68 | Designer 69 | 70 | 71 | 72 | 73 | 74 | MSBuild:Compile 75 | Designer 76 | 77 | 78 | App.xaml 79 | Code 80 | 81 | 82 | MainWindow.xaml 83 | Code 84 | 85 | 86 | 87 | 88 | Code 89 | 90 | 91 | True 92 | True 93 | Resources.resx 94 | 95 | 96 | True 97 | Settings.settings 98 | True 99 | 100 | 101 | ResXFileCodeGenerator 102 | Resources.Designer.cs 103 | 104 | 105 | 106 | SettingsSingleFileGenerator 107 | Settings.Designer.cs 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | {70cc82cc-9efe-461f-a959-f64a06894621} 117 | BusinessLib 118 | 119 | 120 | {c5030463-0469-4afa-92f4-f6b55a6b4c89} 121 | FilterTreeViewLib 122 | 123 | 124 | 125 | 132 | -------------------------------------------------------------------------------- /source/FilterTreeView/ViewModels/AppViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeView.ViewModels 2 | { 3 | using FilterTreeView.Tasks; 4 | using FilterTreeViewLib.ViewModels; 5 | using FilterTreeViewLib.ViewModels.Base; 6 | using FilterTreeViewLib.ViewModelsSearch.SearchModels; 7 | using FilterTreeViewLib.ViewModelsSearch.SearchModels.Enums; 8 | using System; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using System.Windows.Input; 12 | 13 | /// 14 | /// Implements the application viewmodel object that manages all main commands 15 | /// and bindings visible in the main window. 16 | /// 17 | internal class AppViewModel : FilterTreeViewLib.ViewModels.AppBaseViewModel, IDisposable 18 | { 19 | #region fields 20 | private readonly OneTaskProcessor _procesor; 21 | 22 | private readonly MetaLocationRootViewModel _Root; 23 | 24 | private ICommand _SearchCommand; 25 | private bool _Disposed; 26 | #endregion fields 27 | 28 | #region constructors 29 | /// 30 | /// Class constructor 31 | /// 32 | public AppViewModel() 33 | : base() 34 | { 35 | _procesor = new OneTaskProcessor(); 36 | _Root = new MetaLocationRootViewModel(); 37 | _Disposed = false; 38 | } 39 | #endregion constructors 40 | 41 | #region properties 42 | /// 43 | /// Gets the root viewmodel that can be bound to a treeview control. 44 | /// 45 | public MetaLocationRootViewModel Root 46 | { 47 | get 48 | { 49 | return _Root; 50 | } 51 | } 52 | 53 | /// 54 | /// Gets a command that filters the display of nodes in a treeview 55 | /// with a filterstring (node is shown if filterstring is contained). 56 | /// 57 | public ICommand SearchCommand 58 | { 59 | get 60 | { 61 | if (_SearchCommand == null) 62 | { 63 | _SearchCommand = new RelayCommand(async (p) => 64 | { 65 | string findThis = p as string; 66 | 67 | if (findThis == null) 68 | return; 69 | 70 | await SearchCommand_ExecutedAsync(findThis); 71 | }, 72 | (p => 73 | { 74 | if (Root.BackUpCountryRootsCount == 0 && IsProcessing == false) 75 | return false; 76 | 77 | return true; 78 | }) 79 | ); 80 | } 81 | 82 | return _SearchCommand; 83 | } 84 | } 85 | #endregion properties 86 | 87 | #region methods 88 | /// 89 | /// Implements the interface. 90 | /// 91 | public void Dispose() 92 | { 93 | Dispose(true); 94 | } 95 | 96 | /// 97 | /// Loads the initial sample data from XML file into memory 98 | /// 99 | /// 100 | public override async Task LoadSampleDataAsync() 101 | { 102 | IsProcessing = true; 103 | IsLoading = true; 104 | StatusStringResult = "Loading Data... please wait."; 105 | try 106 | { 107 | await Root.LoadData(@".\Resources\lokasyon.zip" 108 | , "countries.xml", "regions.xml", "cities.xml"); 109 | 110 | IsLoading = false; 111 | StatusStringResult = string.Format("Searching... '{0}'", SearchString); 112 | 113 | await SearchCommand_ExecutedAsync(SearchString); 114 | } 115 | finally 116 | { 117 | IsLoading = false; 118 | IsProcessing = false; 119 | } 120 | } 121 | 122 | /// 123 | /// Filters the display of nodes in a treeview 124 | /// with a filterstring (node is shown if filterstring is contained). 125 | /// 126 | protected async Task SearchCommand_ExecutedAsync(string findThis) 127 | { 128 | // Setup search parameters 129 | SearchParams param = new SearchParams(findThis 130 | , (IsStringContainedSearchOption == true ? 131 | SearchMatch.StringIsContained : SearchMatch.StringIsMatched)); 132 | 133 | // Make sure the task always processes the last input but is not started twice 134 | try 135 | { 136 | IsProcessing = true; 137 | 138 | var tokenSource = new CancellationTokenSource(); 139 | Func a = new Func (() => Root.DoSearch(param, tokenSource.Token)); 140 | var t = await _procesor.ExecuteOneTask(a, tokenSource); 141 | 142 | this.StatusStringResult = findThis; 143 | CountSearchMatches = t; 144 | 145 | return CountSearchMatches; 146 | } 147 | catch (Exception exp) 148 | { 149 | Console.WriteLine(exp.Message); 150 | } 151 | finally 152 | { 153 | IsProcessing = false; 154 | } 155 | 156 | return -1; 157 | } 158 | 159 | 160 | /// 161 | /// The bulk of the clean-up code is implemented here. 162 | /// 163 | /// 164 | protected virtual void Dispose(bool disposing) 165 | { 166 | if (_Disposed == false) 167 | { 168 | if (disposing == true) 169 | { 170 | _procesor.Dispose(); 171 | } 172 | 173 | _Disposed = true; 174 | } 175 | } 176 | #endregion methods 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /source/FilterTreeView/Tasks/OneTaskLimitedScheduler.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeView.Tasks 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | using System.Threading.Tasks; 7 | using System.Threading; 8 | 9 | /// 10 | /// Task Scheduler is based on LimitedConcurrencyLevelTaskScheduler from 11 | /// Source: 12 | /// https://code.msdn.microsoft.com/windowsdesktop/Samples-for-Parallel-b4b76364 13 | /// 14 | /// This scheduler ensures that only task is being executed at any time and 15 | /// will clear the previously scheduled tasks (if any) before queing a new task. 16 | /// 17 | /// This ensures that only the most recently queued task is processed at any time. 18 | /// 19 | public class OneTaskLimitedScheduler : TaskScheduler 20 | { 21 | /// Whether the current thread is processing work items. 22 | [ThreadStatic] 23 | private static bool _currentThreadIsProcessingItems; 24 | /// The list of tasks to be executed. 25 | private readonly LinkedList _tasks = new LinkedList(); // protected by lock(_tasks) 26 | /// The maximum concurrency level allowed by this scheduler. 27 | private readonly int _maxDegreeOfParallelism; 28 | /// Whether the scheduler is currently processing work items. 29 | private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks) 30 | 31 | /// 32 | /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the 33 | /// specified degree of parallelism. 34 | /// 35 | public OneTaskLimitedScheduler() 36 | { 37 | _maxDegreeOfParallelism = 1; 38 | } 39 | 40 | /// Queues a task to the scheduler. 41 | /// The task to be queued. 42 | protected sealed override void QueueTask(Task task) 43 | { 44 | // Add the task to the list of tasks to be processed. If there aren't enough 45 | // delegates currently queued or running to process tasks, schedule another. 46 | lock (_tasks) 47 | { 48 | if (_tasks.Count > 0) // Always empty the queue since we want only 1 task 49 | _tasks.Clear(); // and the last task to be processed at any time... 50 | 51 | _tasks.AddLast(task); 52 | if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) 53 | { 54 | ++_delegatesQueuedOrRunning; 55 | NotifyThreadPoolOfPendingWork(); 56 | } 57 | } 58 | } 59 | 60 | /// 61 | /// Informs the ThreadPool that there's work to be executed for this scheduler. 62 | /// 63 | private void NotifyThreadPoolOfPendingWork() 64 | { 65 | ThreadPool.UnsafeQueueUserWorkItem(_ => 66 | { 67 | // Note that the current thread is now processing work items. 68 | // This is necessary to enable inlining of tasks into this thread. 69 | _currentThreadIsProcessingItems = true; 70 | try 71 | { 72 | // Process all available items in the queue. 73 | while (true) 74 | { 75 | Task item; 76 | lock (_tasks) 77 | { 78 | // When there are no more items to be processed, 79 | // note that we're done processing, and get out. 80 | if (_tasks.Count == 0) 81 | { 82 | --_delegatesQueuedOrRunning; 83 | break; 84 | } 85 | 86 | // Get the next item from the queue 87 | item = _tasks.First.Value; 88 | _tasks.RemoveFirst(); 89 | } 90 | 91 | // Execute the task we pulled out of the queue 92 | base.TryExecuteTask(item); 93 | } 94 | } 95 | // We're done processing items on the current thread 96 | finally { _currentThreadIsProcessingItems = false; } 97 | }, null); 98 | } 99 | 100 | /// Attempts to execute the specified task on the current thread. 101 | /// The task to be executed. 102 | /// 103 | /// Whether the task could be executed on the current thread. 104 | protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 105 | { 106 | // If this thread isn't already processing a task, we don't support inlining 107 | if (!_currentThreadIsProcessingItems) return false; 108 | 109 | // If the task was previously queued, remove it from the queue 110 | if (taskWasPreviouslyQueued) TryDequeue(task); 111 | 112 | // Try to run the task. 113 | return base.TryExecuteTask(task); 114 | } 115 | 116 | /// Attempts to remove a previously scheduled task from the scheduler. 117 | /// The task to be removed. 118 | /// Whether the task could be found and removed. 119 | protected sealed override bool TryDequeue(Task task) 120 | { 121 | lock (_tasks) return _tasks.Remove(task); 122 | } 123 | 124 | /// Gets the maximum concurrency level supported by this scheduler. 125 | public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } } 126 | 127 | /// Gets an enumerable of the tasks currently scheduled on this scheduler. 128 | /// An enumerable of the tasks currently scheduled. 129 | protected sealed override IEnumerable GetScheduledTasks() 130 | { 131 | bool lockTaken = false; 132 | try 133 | { 134 | Monitor.TryEnter(_tasks, ref lockTaken); 135 | if (lockTaken) return _tasks.ToArray(); 136 | else throw new NotSupportedException(); 137 | } 138 | finally 139 | { 140 | if (lockTaken) Monitor.Exit(_tasks); 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /source/FilterTreeView/Tasks/OneTaskProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace FilterTreeView.Tasks 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | /// 9 | /// Implements a background thread processor that will process 10 | /// ONLY ONE task at any time. 11 | /// 12 | internal class OneTaskProcessor : IDisposable 13 | { 14 | #region fields 15 | private readonly OneTaskLimitedScheduler _myTaskScheduler; 16 | private readonly List _myTaskList; 17 | private readonly SemaphoreSlim _Semaphore; 18 | 19 | private bool _Disposed; 20 | #endregion fields 21 | 22 | #region constructors 23 | /// 24 | /// Class constructor 25 | /// 26 | public OneTaskProcessor() 27 | { 28 | _myTaskScheduler = new OneTaskLimitedScheduler(); 29 | _myTaskList = new List(); 30 | 31 | _Semaphore = new SemaphoreSlim(1, 1); 32 | _Disposed = false; 33 | } 34 | #endregion constructors 35 | 36 | #region methods 37 | /// 38 | /// Implements the interface. 39 | /// 40 | public void Dispose() 41 | { 42 | Dispose(true); 43 | } 44 | 45 | /// 46 | /// Schedules a function that returns an int value for execution in a 47 | /// one task at a time background thread framework. 48 | /// 49 | /// All previously scheduled tasks are cancelled (if any). 50 | /// Their is only one task being executed at any time (via custom . 51 | /// 52 | /// 53 | /// 54 | /// 55 | internal async Task ExecuteOneTask(Func funcToExecute, 56 | CancellationTokenSource tokenSource) 57 | { 58 | try 59 | { 60 | for (int i = 0; i < _myTaskList.Count; i++) 61 | { 62 | if (_myTaskList[i].Cancellation != null) 63 | { 64 | _myTaskList[i].Cancellation.Cancel(); 65 | _myTaskList[i].Cancellation.Dispose(); 66 | } 67 | } 68 | } 69 | catch (AggregateException e) 70 | { 71 | Console.WriteLine("\nAggregateException thrown with the following inner exceptions:"); 72 | // Display information about each exception. 73 | foreach (var v in e.InnerExceptions) 74 | { 75 | if (v is TaskCanceledException) 76 | Console.WriteLine(" TaskCanceledException: Task {0}", 77 | ((TaskCanceledException)v).Task.Id); 78 | else 79 | Console.WriteLine(" Exception: {0}", v.GetType().Name); 80 | } 81 | Console.WriteLine(); 82 | } 83 | finally 84 | { 85 | _myTaskList.Clear(); 86 | } 87 | 88 | await _Semaphore.WaitAsync(); 89 | try 90 | { 91 | // Do the search and return number of results as int 92 | var t = Task.Factory.StartNew(funcToExecute, 93 | tokenSource.Token, 94 | TaskCreationOptions.LongRunning, 95 | _myTaskScheduler); 96 | 97 | _myTaskList.Add(new TaskItem(t, tokenSource)); 98 | 99 | await t; 100 | 101 | return t.Result; 102 | } 103 | finally 104 | { 105 | _Semaphore.Release(); 106 | } 107 | } 108 | 109 | /// 110 | /// The bulk of the clean-up code is implemented here. 111 | /// 112 | /// 113 | protected virtual void Dispose(bool disposing) 114 | { 115 | if (_Disposed == false) 116 | { 117 | if (disposing == true) 118 | { 119 | try 120 | { 121 | for (int i = 0; i < _myTaskList.Count; i++) 122 | { 123 | if (_myTaskList[i].Cancellation != null) 124 | { 125 | _myTaskList[i].Cancellation.Cancel(); 126 | _myTaskList[i].Cancellation.Dispose(); 127 | } 128 | } 129 | 130 | _Semaphore.Dispose(); 131 | } 132 | catch { } 133 | } 134 | 135 | _Disposed = true; 136 | } 137 | } 138 | #endregion methods 139 | 140 | #region private classes 141 | /// 142 | /// Implements a taskitem wich consists of a task and its . 143 | /// 144 | private class TaskItem 145 | { 146 | /// 147 | /// Class constructor. 148 | /// 149 | /// 150 | /// 151 | public TaskItem(Task taskToProcess, 152 | CancellationTokenSource cancellation) 153 | : this() 154 | { 155 | TaskToProcess = taskToProcess; 156 | Cancellation = cancellation; 157 | } 158 | 159 | /// 160 | /// Class constructor. 161 | /// 162 | protected TaskItem() 163 | { 164 | TaskToProcess = null; 165 | Cancellation = null; 166 | } 167 | 168 | /// 169 | /// Gets the task that shoulf be processed. 170 | /// 171 | public Task TaskToProcess { get; } 172 | 173 | /// 174 | /// Gets the that can 175 | /// be used to cancel this task. 176 | /// 177 | public CancellationTokenSource Cancellation { get; } 178 | } 179 | #endregion private Classes 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /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/FilterTreeView/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | 79 |