├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
└── SoftFluent.Windows
├── SoftFluent.Windows.Samples
├── AddressListEditorWindow.xaml
├── AddressListEditorWindow.xaml.cs
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Model.cs
├── Properties
│ └── AssemblyInfo.cs
├── SoftFluent.Windows.Samples.csproj
└── Utilities.cs
├── SoftFluent.Windows.sln
├── SoftFluent.Windows
├── ActivatorService.cs
├── AutoObject.cs
├── BaseActivator.cs
├── BaseConverter.cs
├── BaseDecamelizer.cs
├── BaseTypeResolver.cs
├── ByteArrayControl.cs
├── ChangeTypeConverter.cs
├── ConversionService.cs
├── DateTimePicker.cs
├── DecamelizationService.cs
├── DecamelizeOptions.cs
├── DecamelizeTextOptions.cs
├── Diagnostics
│ └── Tracing.cs
├── EnumerableConverter.cs
├── IActivator.cs
├── IConverter.cs
├── IDecamelizer.cs
├── IPropertyGridCommandHandler.cs
├── IPropertyGridEditor.cs
├── IPropertyGridObject.cs
├── ITypeResolver.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── AssemblyVersionInfo.cs
├── PropertyGrid.xaml
├── PropertyGrid.xaml.cs
├── PropertyGridAttribute.cs
├── PropertyGridComboBoxExtension.cs
├── PropertyGridConverter.cs
├── PropertyGridDataProvider.cs
├── PropertyGridDataTemplate.cs
├── PropertyGridDataTemplateSelector.cs
├── PropertyGridEnumProperty.cs
├── PropertyGridEventArgs.cs
├── PropertyGridItem.cs
├── PropertyGridOptionsAttribute.cs
├── PropertyGridProperty.cs
├── PropertyGridWindowManager.cs
├── PropertyGridWindowOptions.cs
├── Resources
│ ├── SR.cs
│ └── Strings.resx
├── ServiceProvider.cs
├── SoftFluent.Windows.csproj
├── SoftFluent.Windows.snk
├── TypeDataTemplateSelector.cs
├── TypeResolutionService.cs
├── UniversalConverter.cs
├── UniversalConverterCase.cs
├── UniversalConverterInput.cs
├── UniversalConverterOperator.cs
├── UniversalConverterOptions.cs
└── Utilities
│ ├── DataBindingEvaluator.cs
│ ├── DynamicObject.cs
│ ├── DynamicObjectProperty.cs
│ └── Extensions.cs
└── nuget
├── NuGet.exe
├── Publish.bat
├── SoftFluent.Windows.csproj.nuspec
└── build.bat
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 | *.csproj.DotSettings
44 |
45 | # Build results
46 |
47 | [Dd]ebug/
48 | [Rr]elease/
49 | x64/
50 | build/
51 | [Bb]in/
52 | [Oo]bj/
53 |
54 | # MSTest test Results
55 | [Tt]est[Rr]esult*/
56 | [Bb]uild[Ll]og.*
57 |
58 | *_i.c
59 | *_p.c
60 | *.ilk
61 | *.meta
62 | *.obj
63 | *.pch
64 | *.pdb
65 | *.pgc
66 | *.pgd
67 | *.rsp
68 | *.sbr
69 | *.tlb
70 | *.tli
71 | *.tlh
72 | *.tmp
73 | *.tmp_proj
74 | *.log
75 | *.vspscc
76 | *.vssscc
77 | .builds
78 | *.pidb
79 | *.log
80 | *.scc
81 |
82 | # Visual C++ cache files
83 | ipch/
84 | *.aps
85 | *.ncb
86 | *.opensdf
87 | *.sdf
88 | *.cachefile
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 |
102 | # TeamCity is a build add-in
103 | _TeamCity*
104 |
105 | # DotCover is a Code Coverage Tool
106 | *.dotCover
107 |
108 | # NCrunch
109 | *.ncrunch*
110 | .*crunch*.local.xml
111 |
112 | # Installshield output folder
113 | [Ee]xpress/
114 |
115 | # DocProject is a documentation generator add-in
116 | DocProject/buildhelp/
117 | DocProject/Help/*.HxT
118 | DocProject/Help/*.HxC
119 | DocProject/Help/*.hhc
120 | DocProject/Help/*.hhk
121 | DocProject/Help/*.hhp
122 | DocProject/Help/Html2
123 | DocProject/Help/html
124 |
125 | # Click-Once directory
126 | publish/
127 |
128 | # Publish Web Output
129 | *.Publish.xml
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages Directory
134 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
135 | #packages/
136 |
137 | # Windows Azure Build Output
138 | csx
139 | *.build.csdef
140 |
141 | # Windows Store app package directory
142 | AppPackages/
143 |
144 | # Others
145 | sql/
146 | *.Cache
147 | ClientBin/
148 | [Ss]tyle[Cc]op.*
149 | ~$*
150 | *~
151 | *.dbmdl
152 | *.[Pp]ublish.xml
153 | *.pfx
154 | *.publishsettings
155 |
156 | # RIA/Silverlight projects
157 | Generated_Code/
158 |
159 | # Backup & report files from converting an old project file to a newer
160 | # Visual Studio version. Backup files are not needed, because we have git ;-)
161 | _UpgradeReport_Files/
162 | Backup*/
163 | UpgradeLog*.XML
164 | UpgradeLog*.htm
165 |
166 | # SQL Server files
167 | App_Data/*.mdf
168 | App_Data/*.ldf
169 |
170 | #############
171 | ## Windows detritus
172 | #############
173 |
174 | # Windows image file caches
175 | Thumbs.db
176 | ehthumbs.db
177 |
178 | # Folder config file
179 | Desktop.ini
180 |
181 | # Recycle Bin used on file shares
182 | $RECYCLE.BIN/
183 |
184 | # Mac crap
185 | .DS_Store
186 |
187 |
188 | #############
189 | ## Python
190 | #############
191 |
192 | *.py[cod]
193 |
194 | # Packages
195 | *.egg
196 | *.egg-info
197 | dist/
198 | build/
199 | eggs/
200 | parts/
201 | var/
202 | sdist/
203 | develop-eggs/
204 | .installed.cfg
205 |
206 | # Installer logs
207 | pip-log.txt
208 |
209 | # Unit test / coverage reports
210 | .coverage
211 | .tox
212 |
213 | #Translations
214 | *.mo
215 |
216 | #Mr Developer
217 | .mr.developer.cfg
218 |
219 | # Nuget
220 | SoftFluent.Windows/nuget/lib/
221 | SoftFluent.Windows/nuget/*.nupkg
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 SoftFluent
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository contains useful controls/converter/classes to work with WPF. The current version of the library is also [available on NuGet](https://www.nuget.org/packages/SoftFluent.Windows/).
2 |
3 | # PropertyGrid
4 |
5 | This repository contains a customizable `PropertyGrid` for WPF.
6 |
7 | 
8 |
9 | - Many data types are supported by default: String, Boolean, Date, DateTime, Number, Enum, Multi-Valued Enum, Byte[], Guid, etc.,
10 | - Property names are decamelized by default (FirstName => First Name) unless you use the `DisplayNameAttribute`,
11 | - Common attributes are supported: `DisplayNameAttribute`, `CategoryAttribute`, `BrowsableAttribute`, `ReadOnlyAttribute`,
12 | - Customizable:
13 | - `CustomEditor` = `DataTemplate` You can create your own editors to provide a better UX to your user (Slider, Url, Password, etc.) by creating a `DataTemplate`
14 |
15 | ````xaml
16 |
17 |
18 |
19 |
20 |
22 |
23 | ````
24 |
25 | Go to the [documentation](https://github.com/SoftFluent/SoftFluent.Windows/wiki)
26 |
27 | # AutoObject
28 |
29 | The `AutoObject` class is a light class which implements `INotifyPropertyChanged` and `IDataErrorInfo` so you can easily and quickly create MVVM like classes with data-binding and validation
30 |
31 | # UniversalConverter
32 |
33 | Stop writing boring converters, use the `UniversalConverter`!
34 |
35 | When you create an application using WPF, you often have to write converters to change one value to the desired type.
36 | The .NET Framework already provides some basics converter such as BooleanToVisibilityConverter.
37 | These converters are very specifics and usually not very configurable.
38 | For example you cannot change the visibility from Collapsed to Hidden.
39 |
40 | Let’s see how to use it for a very basic (and to be honest, quite useless) conversion:
41 |
42 |
43 | ````xaml
44 |
45 |
46 |
47 |
48 |
50 |
52 | ````
53 | In this example, UniversalConverter converts the value to the desired type so string values “true” and “yes” will be converted automatically to the Boolean value “true”.
54 |
55 | With UniversalConverter, you can create a list of cases, like a C# switch. For instance, this is how we would reproduce the boolean to visibility converter behavior using the UniversalConverter:
56 |
57 | ````xaml
58 |
59 |
60 |
61 |
62 |
63 |
64 | ````
65 |
66 | Like C#, you can use a default value:
67 |
68 | ````xaml
69 |
70 |
71 |
72 |
73 |
74 | ````
75 |
76 | There are currently these operators available:
77 |
78 | * Equal,
79 | * NotEqual,
80 | * GreaterThan,
81 | * GreaterThanOrEqual,
82 | * LesserThan,
83 | * LesserThanOrEqual,
84 | * Between: minimum and maximum value included => [min:max[,
85 | * StartsWith,
86 | * EndsWith,
87 | * Contains,
88 | * IsType: type match exactly,
89 | * IsOfType: type or direved types,
90 | * JavaScript: Yes, you can use JavaScript to evaluate a condition!
91 |
92 | With some options:
93 |
94 | * StringComparison
95 | * Trim
96 | * Nullify
97 |
98 | Here’s a list of examples using different operators.
99 |
100 | ### Check if a string contains NewLine using JavaScript:
101 |
102 | ````xaml
103 |
104 |
105 |
106 |
107 |
108 |
109 | ````
110 |
111 | ### Set error message background and foreground color
112 | ````xaml
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | ````
124 | ### Test if a value is over 21
125 | ````xaml
126 |
127 |
128 |
129 |
130 |
131 | ````
132 | ### Is teenager
133 | ````xaml
134 |
135 |
136 |
137 |
138 |
139 | ````
140 | ### Compare types
141 | ````xaml
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | ````
150 | ### Is empty
151 | ````xaml
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | ````
161 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/AddressListEditorWindow.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/AddressListEditorWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Data;
6 | using System.Windows.Input;
7 |
8 | namespace SoftFluent.Windows.Samples
9 | {
10 | ///
11 | /// Interaction logic for AddressListEditorWindow.xaml
12 | ///
13 | public partial class AddressListEditorWindow : Window
14 | {
15 | public AddressListEditorWindow()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | private void NewCommandBinding_OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
21 | {
22 | e.CanExecute = true;
23 | }
24 |
25 | private void NewCommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e)
26 | {
27 | var cvs = CollectionViewSource.GetDefaultView(EditorSelector.ItemsSource);
28 | if (cvs == null)
29 | return;
30 |
31 | var collection = cvs.SourceCollection as ICollection;
32 | if (collection != null)
33 | {
34 | Address address = new Address();
35 | address.Line1 = "Empty";
36 | collection.Add(address);
37 | cvs.MoveCurrentToLast();
38 | }
39 | }
40 |
41 | private void DeleteCommandBinding_OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
42 | {
43 | var cvs = CollectionViewSource.GetDefaultView(EditorSelector.ItemsSource);
44 | if (cvs == null)
45 | return;
46 |
47 | e.CanExecute = cvs.CurrentItem != null;
48 | }
49 |
50 | private void DeleteCommandBinding_OnExecuted(object sender, ExecutedRoutedEventArgs e)
51 | {
52 | var cvs = CollectionViewSource.GetDefaultView(EditorSelector.ItemsSource);
53 | if (cvs == null)
54 | return;
55 |
56 | var currentItem = cvs.CurrentItem as Address;
57 | if (currentItem == null)
58 | return;
59 |
60 | var collection = cvs.SourceCollection as ICollection;
61 | if (collection != null)
62 | {
63 | collection.Remove(currentItem);
64 | }
65 | }
66 |
67 | protected virtual void OnEditorWindowCloseExecuted(object sender, ExecutedRoutedEventArgs e)
68 | {
69 | Window window = (Window)sender;
70 | PropertyGridProperty prop = window.DataContext as PropertyGridProperty;
71 | if (prop != null)
72 | {
73 | prop.Executed(sender, e);
74 | if (e.Handled)
75 | return;
76 | }
77 | window.Close();
78 | }
79 |
80 | protected virtual void OnEditorWindowCloseCanExecute(object sender, CanExecuteRoutedEventArgs e)
81 | {
82 | Window window = (Window)sender;
83 | PropertyGridProperty prop = window.DataContext as PropertyGridProperty;
84 | if (prop != null)
85 | {
86 | prop.CanExecute(sender, e);
87 | if (e.Handled)
88 | return;
89 | }
90 | e.CanExecute = true;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/App.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
45 |
46 |
48 |
49 |
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 |
87 |
97 |
98 |
99 |
100 |
101 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Input;
3 | using SoftFluent.Windows.Diagnostics;
4 |
5 | namespace SoftFluent.Windows.Samples
6 | {
7 | public partial class App : Application
8 | {
9 | protected override void OnStartup(StartupEventArgs e)
10 | {
11 | base.OnStartup(e);
12 | #if DEBUG
13 | Tracing.Enable();
14 | #endif
15 | }
16 |
17 | private void OnEditorWindowCloseCanExecute(object sender, CanExecuteRoutedEventArgs e)
18 | {
19 | e.CanExecute = true;
20 | }
21 |
22 | private void OnEditorWindowCloseExecuted(object sender, ExecutedRoutedEventArgs e)
23 | {
24 | Window window = (Window)sender;
25 | window.DialogResult = false;
26 | window.Close();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace SoftFluent.Windows.Samples
4 | {
5 | public partial class MainWindow : Window
6 | {
7 | public MainWindow()
8 | {
9 | InitializeComponent();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 | using System.Windows;
4 |
5 | [assembly: AssemblyTitle("SoftFluent.Windows.Samples")]
6 | [assembly: AssemblyDescription("SoftFluent's Windows (WPF) tools")]
7 | #if DEBUG
8 | [assembly: AssemblyConfiguration("Debug")]
9 | #else
10 | [assembly: AssemblyConfiguration("Release")]
11 | #endif
12 | [assembly: AssemblyCompany("SoftFluent S.A.S - http://www.softfluent.com")]
13 | [assembly: AssemblyProduct("SoftFluent.Windows")]
14 | [assembly: AssemblyCopyright("Copyright (C) 2014-2017 SoftFluent S.A.S. All rights reserved.")]
15 | [assembly: AssemblyTrademark("SoftFluent Windows (TM) is a trademark of SoftFluent S.A.S.")]
16 | [assembly: AssemblyCulture("")]
17 | [assembly: ComVisible(false)]
18 | [assembly: Guid("42acb969-f6b8-454a-a874-4ab68d85f579")]
19 | [assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
20 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/SoftFluent.Windows.Samples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {815070AF-96B7-460C-AD63-58F50464465F}
8 | WinExe
9 | Properties
10 | SoftFluent.Windows.Samples
11 | SoftFluent.Windows.Samples
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | true
17 |
18 |
19 |
20 | AnyCPU
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 | false
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 | 4.0
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | MSBuild:Compile
57 | Designer
58 |
59 |
60 | Properties\AssemblyVersionInfo.cs
61 |
62 |
63 |
64 | Designer
65 | MSBuild:Compile
66 |
67 |
68 | MSBuild:Compile
69 | Designer
70 |
71 |
72 | AddressListEditorWindow.xaml
73 |
74 |
75 | App.xaml
76 | Code
77 |
78 |
79 |
80 | MainWindow.xaml
81 | Code
82 |
83 |
84 |
85 |
86 | Code
87 |
88 |
89 |
90 |
91 |
92 | {11262ea6-320a-4352-96d3-64e9444f89ce}
93 | SoftFluent.Windows
94 |
95 |
96 |
97 |
104 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.Samples/Utilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Runtime.InteropServices;
5 | using System.Security;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Markup;
9 |
10 | namespace SoftFluent.Windows.Samples
11 | {
12 | public static class Utilities
13 | {
14 | public static readonly DependencyProperty BindableSourceProperty =
15 | DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(Utilities), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
16 |
17 | public static string GetBindableSource(DependencyObject obj)
18 | {
19 | return (string)obj.GetValue(BindableSourceProperty);
20 | }
21 |
22 | public static void SetBindableSource(DependencyObject obj, string value)
23 | {
24 | obj.SetValue(BindableSourceProperty, value);
25 | }
26 |
27 | public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
28 | {
29 | WebBrowser browser = o as WebBrowser;
30 | if (browser != null)
31 | {
32 | string uri = e.NewValue as string;
33 | browser.Source = !string.IsNullOrEmpty(uri) ? new Uri(uri) : null;
34 | }
35 | }
36 |
37 | public static readonly DependencyProperty BindablePasswordProperty = DependencyProperty.RegisterAttached("BindablePassword", typeof(SecureString), typeof(Utilities), new FrameworkPropertyMetadata(null, OnPasswordPropertyChanged));
38 |
39 | public static readonly DependencyProperty BindPasswordProperty = DependencyProperty.RegisterAttached("BindPassword", typeof(bool), typeof(Utilities), new PropertyMetadata(false, BindPassword));
40 |
41 | private static readonly DependencyProperty UpdatingPasswordProperty = DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(Utilities));
42 |
43 | public static void SetBindPassword(DependencyObject dp, bool value)
44 | {
45 | dp.SetValue(BindPasswordProperty, value);
46 | }
47 |
48 | public static bool GetBindPassword(DependencyObject dp)
49 | {
50 | return (bool)dp.GetValue(BindPasswordProperty);
51 | }
52 |
53 | public static string GetBindablePassword(DependencyObject dp)
54 | {
55 | return (string)dp.GetValue(BindablePasswordProperty);
56 | }
57 |
58 | public static void SetBindablePassword(DependencyObject dp, SecureString value)
59 | {
60 | dp.SetValue(BindablePasswordProperty, value);
61 | }
62 |
63 | private static bool GetUpdatingPassword(DependencyObject dp)
64 | {
65 | return (bool)dp.GetValue(UpdatingPasswordProperty);
66 | }
67 |
68 | private static void SetUpdatingPassword(DependencyObject dp, bool value)
69 | {
70 | dp.SetValue(UpdatingPasswordProperty, value);
71 | }
72 |
73 | private static void OnPasswordPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
74 | {
75 | PasswordBox passwordBox = sender as PasswordBox;
76 | if (passwordBox == null)
77 | return;
78 |
79 | passwordBox.PasswordChanged -= PasswordChanged;
80 | if (!GetUpdatingPassword(passwordBox))
81 | {
82 | passwordBox.Password = (string)e.NewValue;
83 | }
84 | passwordBox.PasswordChanged += PasswordChanged;
85 | }
86 |
87 | private static void BindPassword(DependencyObject sender, DependencyPropertyChangedEventArgs e)
88 | {
89 | PasswordBox passwordBox = sender as PasswordBox;
90 | if (passwordBox == null)
91 | return;
92 |
93 | if ((bool)e.OldValue)
94 | {
95 | passwordBox.PasswordChanged -= PasswordChanged;
96 | }
97 |
98 | if ((bool)e.NewValue)
99 | {
100 | passwordBox.PasswordChanged += PasswordChanged;
101 | }
102 | }
103 |
104 | private static void PasswordChanged(object sender, RoutedEventArgs e)
105 | {
106 | PasswordBox passwordBox = sender as PasswordBox;
107 | if (passwordBox == null)
108 | return;
109 |
110 | SetUpdatingPassword(passwordBox, true);
111 | SetBindablePassword(passwordBox, passwordBox.SecurePassword);
112 | SetUpdatingPassword(passwordBox, false);
113 | }
114 |
115 | public static string ConvertToUnsecureString(this SecureString securePassword)
116 | {
117 | if (securePassword == null)
118 | throw new ArgumentNullException("securePassword");
119 |
120 | IntPtr unmanagedString = IntPtr.Zero;
121 | try
122 | {
123 | unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
124 | return Marshal.PtrToStringUni(unmanagedString);
125 | }
126 | finally
127 | {
128 | Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
129 | }
130 | }
131 | }
132 |
133 | public class BooleanValueProvider : MarkupExtension
134 | {
135 | public bool IsNullable { get; set; }
136 |
137 | public override object ProvideValue(IServiceProvider serviceProvider)
138 | {
139 | var items = new ObservableCollection>();
140 | items.Add(new KeyValuePair("Yes", true));
141 | items.Add(new KeyValuePair("No", false));
142 | if (IsNullable)
143 | {
144 | items.Add(new KeyValuePair("", null));
145 | }
146 | return items;
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoftFluent.Windows", "SoftFluent.Windows\SoftFluent.Windows.csproj", "{11262EA6-320A-4352-96D3-64E9444F89CE}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoftFluent.Windows.Samples", "SoftFluent.Windows.Samples\SoftFluent.Windows.Samples.csproj", "{815070AF-96B7-460C-AD63-58F50464465F}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {11262EA6-320A-4352-96D3-64E9444F89CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {11262EA6-320A-4352-96D3-64E9444F89CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {11262EA6-320A-4352-96D3-64E9444F89CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {11262EA6-320A-4352-96D3-64E9444F89CE}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {815070AF-96B7-460C-AD63-58F50464465F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {815070AF-96B7-460C-AD63-58F50464465F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {815070AF-96B7-460C-AD63-58F50464465F}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {815070AF-96B7-460C-AD63-58F50464465F}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/ActivatorService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public static class ActivatorService
6 | {
7 | public static object CreateInstance(Type type, params object[] args)
8 | {
9 | if (type == null)
10 | throw new ArgumentNullException("type");
11 |
12 | object obj = ServiceProvider.Current.GetService().CreateInstance(type, args);
13 | return obj;
14 | }
15 |
16 | public static T CreateInstance(params object[] args)
17 | {
18 | return (T)CreateInstance(typeof(T), args);
19 | }
20 |
21 | public static object CreateInstance(Type type)
22 | {
23 | return CreateInstance(type, null);
24 | }
25 |
26 | public static T CreateInstance()
27 | {
28 | return (T)CreateInstance(typeof(T), null);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/BaseActivator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using SoftFluent.Windows.Utilities;
3 |
4 | namespace SoftFluent.Windows
5 | {
6 | public class BaseActivator : IActivator
7 | {
8 | public virtual object CreateInstance(Type type, params object[] args)
9 | {
10 | if (type == null)
11 | throw new ArgumentNullException("type");
12 |
13 | if (type == typeof(DynamicObject))
14 | return new DynamicObject();
15 |
16 | if (type == typeof(PropertyGridProperty))
17 | return new PropertyGridProperty((PropertyGridDataProvider)args[0]);
18 |
19 | return Activator.CreateInstance(type, args);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/BaseDecamelizer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Text;
4 |
5 | namespace SoftFluent.Windows
6 | {
7 | public class BaseDecamelizer : IDecamelizer
8 | {
9 | // input: a string like loadByWhateverStuff
10 | // output: a string like Load By Whatever Stuff
11 | // BBKing -> BBKing
12 | // BBOKing -> BboKing
13 | // LoadBy25Years -> Load By 25 Years
14 | // SoftFluent.PetShop -> Soft Fluent. Pet Shop
15 | // Data2_FileName -> Data 2 File Name
16 | // _WhatIs -> _What is
17 | // __WhatIs -> __What is
18 | // __What__Is -> __What is
19 | // MyParam1 -> My Param 1
20 | // MyParam1Baby -> My Param1 Baby (if DontDecamelizeNumbers)
21 | public virtual string Decamelize(string text, DecamelizeOptions options)
22 | {
23 | if (string.IsNullOrEmpty(text))
24 | return text;
25 |
26 | if (options == null)
27 | {
28 | options = new DecamelizeOptions();
29 | }
30 |
31 | var sb = new StringBuilder(text.Length);
32 |
33 | // 0=lower, 1=upper, 2=special char
34 | UnicodeCategory lastCategory = CharUnicodeInfo.GetUnicodeCategory(text[0]);
35 | UnicodeCategory prevCategory = lastCategory;
36 | if (lastCategory == UnicodeCategory.UppercaseLetter)
37 | {
38 | lastCategory = UnicodeCategory.LowercaseLetter;
39 | }
40 |
41 | int i = 0;
42 | bool firstIsStillUnderscore = (text[0] == '_');
43 | if (((options.TextOptions & DecamelizeTextOptions.UnescapeUnicode) == DecamelizeTextOptions.UnescapeUnicode) && (CanUnicodeEscape(text, 0)))
44 | {
45 | sb.Append(GetUnicodeEscape(text, ref i));
46 | }
47 | else if (((options.TextOptions & DecamelizeTextOptions.UnescapeHexadecimal) == DecamelizeTextOptions.UnescapeHexadecimal) && (CanHexadecimalEscape(text, 0)))
48 | {
49 | sb.Append(GetHexadecimalEscape(text, ref i));
50 | }
51 | else
52 | {
53 | if ((options.TextOptions & DecamelizeTextOptions.ForceFirstUpper) == DecamelizeTextOptions.ForceFirstUpper)
54 | {
55 | sb.Append(Char.ToUpper(text[0]));
56 | }
57 | else
58 | {
59 | sb.Append(text[0]);
60 | }
61 | }
62 | bool separated = false;
63 | bool keepFormat = (options.TextOptions & DecamelizeTextOptions.KeepFormattingIndices) == DecamelizeTextOptions.KeepFormattingIndices;
64 |
65 | for (i++; i < text.Length; i++)
66 | {
67 | char c = text[i];
68 | if (((options.TextOptions & DecamelizeTextOptions.UnescapeUnicode) == DecamelizeTextOptions.UnescapeUnicode) && (CanUnicodeEscape(text, i)))
69 | {
70 | sb.Append(GetUnicodeEscape(text, ref i));
71 | separated = true;
72 | }
73 | else if (((options.TextOptions & DecamelizeTextOptions.UnescapeHexadecimal) == DecamelizeTextOptions.UnescapeHexadecimal) && (CanHexadecimalEscape(text, i)))
74 | {
75 | sb.Append(GetHexadecimalEscape(text, ref i));
76 | separated = true;
77 | }
78 | else if (c == '_')
79 | {
80 | if ((!firstIsStillUnderscore) || ((options.TextOptions & DecamelizeTextOptions.KeepFirstUnderscores) != DecamelizeTextOptions.KeepFirstUnderscores))
81 | {
82 | sb.Append(' ');
83 | separated = true;
84 | }
85 | else
86 | {
87 | sb.Append(c);
88 | }
89 | }
90 | else
91 | {
92 | UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c);
93 | switch (category)
94 | {
95 | case UnicodeCategory.ClosePunctuation:
96 | case UnicodeCategory.ConnectorPunctuation:
97 | case UnicodeCategory.DashPunctuation:
98 | case UnicodeCategory.EnclosingMark:
99 | case UnicodeCategory.FinalQuotePunctuation:
100 | case UnicodeCategory.Format:
101 | case UnicodeCategory.InitialQuotePunctuation:
102 | case UnicodeCategory.LineSeparator:
103 | case UnicodeCategory.OpenPunctuation:
104 | case UnicodeCategory.OtherPunctuation:
105 | case UnicodeCategory.ParagraphSeparator:
106 | case UnicodeCategory.SpaceSeparator:
107 | case UnicodeCategory.SpacingCombiningMark:
108 | if ((keepFormat) && (c == '{'))
109 | {
110 | while (c != '}')
111 | {
112 | c = text[i++];
113 | sb.Append(c);
114 | }
115 | i--;
116 | separated = true;
117 | break;
118 | }
119 |
120 | if ((options.TextOptions & DecamelizeTextOptions.ForceRestLower) == DecamelizeTextOptions.ForceRestLower)
121 | {
122 | sb.Append(Char.ToLower(c));
123 | }
124 | else
125 | {
126 | sb.Append(c);
127 | }
128 | sb.Append(' ');
129 | separated = true;
130 | break;
131 |
132 | case UnicodeCategory.LetterNumber:
133 | case UnicodeCategory.DecimalDigitNumber:
134 | case UnicodeCategory.OtherNumber:
135 |
136 | case UnicodeCategory.CurrencySymbol:
137 | case UnicodeCategory.LowercaseLetter:
138 | case UnicodeCategory.MathSymbol:
139 | case UnicodeCategory.ModifierLetter:
140 | case UnicodeCategory.ModifierSymbol:
141 | case UnicodeCategory.NonSpacingMark:
142 | case UnicodeCategory.OtherLetter:
143 | case UnicodeCategory.OtherNotAssigned:
144 | case UnicodeCategory.Control:
145 | case UnicodeCategory.OtherSymbol:
146 | case UnicodeCategory.Surrogate:
147 | case UnicodeCategory.PrivateUse:
148 | case UnicodeCategory.TitlecaseLetter:
149 | case UnicodeCategory.UppercaseLetter:
150 | if (((category != lastCategory) && (c != ' ')) && (IsNewCategory(category, options)))
151 | {
152 | if ((!separated) && (prevCategory != UnicodeCategory.UppercaseLetter) &&
153 | ((!firstIsStillUnderscore) || ((options.TextOptions & DecamelizeTextOptions.KeepFirstUnderscores) != DecamelizeTextOptions.KeepFirstUnderscores)))
154 | {
155 | sb.Append(' ');
156 | }
157 |
158 | if ((options.TextOptions & DecamelizeTextOptions.ForceRestLower) != 0)
159 | {
160 | sb.Append(Char.ToLower(c));
161 | }
162 | else
163 | {
164 | sb.Append(Char.ToUpper(c));
165 | }
166 |
167 | char upper = Char.ToUpper(c);
168 | category = CharUnicodeInfo.GetUnicodeCategory(upper);
169 | lastCategory = category == UnicodeCategory.UppercaseLetter ? UnicodeCategory.LowercaseLetter : category;
170 | }
171 | else
172 | {
173 | if ((options.TextOptions & DecamelizeTextOptions.ForceRestLower) == DecamelizeTextOptions.ForceRestLower)
174 | {
175 | sb.Append(Char.ToLower(c));
176 | }
177 | else
178 | {
179 | sb.Append(c);
180 | }
181 | }
182 | separated = false;
183 | break;
184 | }
185 | firstIsStillUnderscore = firstIsStillUnderscore && (c == '_');
186 | prevCategory = category;
187 | }
188 | }
189 |
190 | if ((options.TextOptions & DecamelizeTextOptions.ReplaceSpacesByUnderscore) == DecamelizeTextOptions.ReplaceSpacesByUnderscore)
191 | return sb.Replace(' ', '_').ToString();
192 |
193 | if ((options.TextOptions & DecamelizeTextOptions.ReplaceSpacesByMinus) == DecamelizeTextOptions.ReplaceSpacesByMinus)
194 | return sb.Replace(' ', '-').ToString();
195 |
196 | if ((options.TextOptions & DecamelizeTextOptions.ReplaceSpacesByDot) == DecamelizeTextOptions.ReplaceSpacesByDot)
197 | return sb.Replace(' ', '.').ToString();
198 |
199 | return sb.ToString();
200 | }
201 |
202 | // format is _xXXXX_
203 | private static bool CanHexadecimalEscape(string text, int i)
204 | {
205 | return (i + 6) < text.Length && text[i] == '_' && text[i + 1] == 'x' && text[i + 6] == '_' &&
206 | IsHexNumber(text[i + 2]) &&
207 | IsHexNumber(text[i + 3]) &&
208 | IsHexNumber(text[i + 4]) &&
209 | IsHexNumber(text[i + 5]);
210 | }
211 |
212 | private static bool IsHexNumber(char c)
213 | {
214 | // note: we don't want to use Char.IsDigit nor Char.IsNumber
215 | return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
216 | }
217 |
218 | private static char GetHexadecimalEscape(string text, ref int i)
219 | {
220 | string s = text[i + 2].ToString(CultureInfo.InvariantCulture);
221 | s += text[i + 3].ToString(CultureInfo.InvariantCulture);
222 | s += text[i + 4].ToString(CultureInfo.InvariantCulture);
223 | s += text[i + 5].ToString(CultureInfo.InvariantCulture);
224 | i += 6;
225 | return (char)int.Parse(s, NumberStyles.HexNumber);
226 | }
227 |
228 | // format is \uXXXX
229 | private static bool CanUnicodeEscape(string text, int i)
230 | {
231 | return (i + 5) < text.Length &&
232 | text[i] == '\\' &&
233 | text[i + 1] == 'u' &&
234 | IsPureNumber(text[i + 2]) &&
235 | IsPureNumber(text[i + 3]) &&
236 | IsPureNumber(text[i + 4]) &&
237 | IsPureNumber(text[i + 5]);
238 | }
239 |
240 | private static char GetUnicodeEscape(string text, ref int i)
241 | {
242 | string s = text[i + 2].ToString(CultureInfo.InvariantCulture);
243 | s += text[i + 3].ToString(CultureInfo.InvariantCulture);
244 | s += text[i + 4].ToString(CultureInfo.InvariantCulture);
245 | s += text[i + 5].ToString(CultureInfo.InvariantCulture);
246 | i += 5;
247 | return (char)int.Parse(s);
248 | }
249 |
250 | private static bool IsPureNumber(char c)
251 | {
252 | // note: we don't want to use Char.IsDigit nor Char.IsNumber
253 | return c >= '0' && c <= '9';
254 | }
255 |
256 | private static bool IsNewCategory(UnicodeCategory category, DecamelizeOptions options)
257 | {
258 | if ((options.TextOptions & DecamelizeTextOptions.DontDecamelizeNumbers) == DecamelizeTextOptions.DontDecamelizeNumbers)
259 | {
260 | if (category == UnicodeCategory.LetterNumber ||
261 | category == UnicodeCategory.DecimalDigitNumber ||
262 | category == UnicodeCategory.OtherNumber)
263 | return false;
264 | }
265 | return true;
266 | }
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/BaseTypeResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public class BaseTypeResolver : ITypeResolver
6 | {
7 | public virtual Type ResolveType(string fullName, bool throwOnError)
8 | {
9 | if (fullName == null)
10 | throw new ArgumentNullException("fullName");
11 |
12 | var type = Type.GetType(fullName, throwOnError);
13 | return type;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/ByteArrayControl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Controls.Primitives;
7 | using System.Windows.Media;
8 |
9 | namespace SoftFluent.Windows
10 | {
11 | public class ByteArrayControl : ScrollViewer, IDisposable
12 | {
13 | public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(object), typeof(ByteArrayControl),
14 | new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure, OnSourceChanged));
15 |
16 | public static readonly DependencyProperty RowCountProperty = DependencyProperty.Register("RowCount", typeof(int), typeof(ByteArrayControl),
17 | new FrameworkPropertyMetadata(16, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange, OnRowCountChanged, OnRowCountCoerce));
18 |
19 | public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register("Offset", typeof(long), typeof(ByteArrayControl),
20 | new FrameworkPropertyMetadata(0L, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange, OnOffsetChanged, OnOffsetCoerce));
21 |
22 | public static readonly DependencyProperty AddHeaderProperty = DependencyProperty.Register("AddHeader", typeof(bool), typeof(ByteArrayControl),
23 | new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange, OnAddHeaderChanged));
24 |
25 | private readonly Panel _canvas;
26 | private readonly TextBlock _text;
27 | private bool _dontDisposeStream;
28 | private Stream _stream;
29 | private ScrollBar _verticalScrollBar;
30 | private byte[] _buffer;
31 |
32 | public object Source
33 | {
34 | get { return GetValue(SourceProperty); }
35 | set { SetValue(SourceProperty, value); }
36 | }
37 |
38 | public long Offset
39 | {
40 | get { return (long)GetValue(OffsetProperty); }
41 | set { SetValue(OffsetProperty, value); }
42 | }
43 |
44 | public int RowCount
45 | {
46 | get { return (int)GetValue(RowCountProperty); }
47 | set { SetValue(RowCountProperty, value); }
48 | }
49 |
50 | public bool AddHeader
51 | {
52 | get { return (bool)GetValue(AddHeaderProperty); }
53 | set { SetValue(AddHeaderProperty, value); }
54 | }
55 |
56 | public ByteArrayControl()
57 | {
58 | Margin = new Thickness(5);
59 | VerticalAlignment = VerticalAlignment.Top;
60 | HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
61 | VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
62 | FontFamily = new FontFamily("Lucida Console");
63 |
64 | // NOTE: text is *not* a canvas child because there are rounding issues with big (really big) values for Canvas.SetTop...
65 | _text = new TextBlock();
66 |
67 | _canvas = new Canvas();
68 | Content = _canvas;
69 | }
70 |
71 | protected override Size ArrangeOverride(Size arrangeSize)
72 | {
73 | Size size = base.ArrangeOverride(arrangeSize);
74 | _text.Arrange(new Rect(size));
75 |
76 | // adjust canvas width so the h scrollbar can automatically appear
77 | _canvas.Width = _text.DesiredSize.Width + HorizontalOffset;
78 |
79 | int bufferSize = ComputeNeededBufferSize();
80 | if (_buffer == null || _buffer.Length != bufferSize)
81 | {
82 | _buffer = new byte[bufferSize];
83 | }
84 | return size;
85 | }
86 |
87 | private int ComputeNeededBufferSize()
88 | {
89 | if (_text.FontSize == 0 || DesiredSize.Height == 0)
90 | return 512; // whatever...
91 |
92 | // it's in fact a bit more than what's really needed (if the h scrollbar is displayed, roundings, etc.)
93 | double maxLines = DesiredSize.Height / _text.FontSize;
94 | return RowCount * (int)maxLines;
95 | }
96 |
97 | private static object OnRowCountCoerce(DependencyObject d, object baseValue)
98 | {
99 | int i = (int)baseValue;
100 | if (i < 8)
101 | return 8;
102 |
103 | if (i > 256)
104 | return 256;
105 |
106 | return i;
107 | }
108 |
109 | private static object OnOffsetCoerce(DependencyObject d, object baseValue)
110 | {
111 | var bac = (ByteArrayControl)d;
112 | if (bac._stream == null)
113 | return 0L;
114 |
115 | long l = (long)baseValue;
116 | if (l > bac._stream.Length || l < 0)
117 | return bac._stream.Length;
118 |
119 | return l;
120 | }
121 |
122 | private static void OnOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
123 | {
124 | var bac = (ByteArrayControl)d;
125 | bac.ScrollToVerticalOffset((long)e.NewValue);
126 | }
127 |
128 | private static void OnAddHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
129 | {
130 | var bac = (ByteArrayControl)d;
131 | bac.ResizeContent();
132 | bac.SetupText();
133 | }
134 |
135 | private static void OnRowCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
136 | {
137 | var bac = (ByteArrayControl)d;
138 | bac.ResizeContent();
139 | bac.SetupText();
140 | }
141 |
142 | private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
143 | {
144 | var bac = (ByteArrayControl)d;
145 | bac.OpenStream(e.NewValue);
146 | bac.ResizeContent();
147 | bac.SetupText();
148 | }
149 |
150 | protected virtual void ResizeContent()
151 | {
152 | _text.FontSize = FontSize;
153 | _text.FontFamily = FontFamily;
154 | _text.FontStretch = FontStretch;
155 | _text.FontWeight = FontWeight;
156 | _text.Foreground = Foreground;
157 | _text.Background = Background;
158 | _text.FontStyle = FontStyle;
159 | _text.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
160 | _canvas.Height = _stream == null ? 0 : _stream.Length + _text.FontSize;
161 | _canvas.Width = _text.DesiredSize.Width + HorizontalOffset;
162 | }
163 |
164 | protected virtual void SetupText()
165 | {
166 | if (_stream == null)
167 | {
168 | _text.Text = null;
169 | if (VerticalScrollBar != null)
170 | {
171 | VerticalScrollBar.ToolTip = null;
172 | }
173 | return;
174 | }
175 |
176 | // read bytes and set the text content with it
177 | long pos = _stream.Position;
178 | int read = _buffer != null ? _stream.Read(_buffer, 0, _buffer.Length) : 0;
179 | _text.Text = ToHexaDump(_buffer, 0, read, AddHeader, RowCount, pos);
180 |
181 | // display some tooltip
182 | if (VerticalScrollBar != null)
183 | {
184 | VerticalScrollBar.ToolTip = _stream.Position + " / " + _stream.Length + " (" + (100 * _stream.Position) / _stream.Length + "%)";
185 | }
186 | }
187 |
188 | protected override Visual GetVisualChild(int index)
189 | {
190 | if (base.VisualChildrenCount > index)
191 | return base.GetVisualChild(index);
192 |
193 | return _text;
194 | }
195 |
196 | protected override int VisualChildrenCount
197 | {
198 | get
199 | {
200 | return base.VisualChildrenCount + 1;
201 | }
202 | }
203 |
204 | private ScrollBar VerticalScrollBar
205 | {
206 | get
207 | {
208 | if (_verticalScrollBar == null && Template != null)
209 | {
210 | _verticalScrollBar = Template.FindName("PART_VerticalScrollBar", this) as ScrollBar;
211 | }
212 | return _verticalScrollBar;
213 | }
214 | }
215 |
216 | protected override void OnScrollChanged(ScrollChangedEventArgs e)
217 | {
218 | base.OnScrollChanged(e);
219 | if (_stream == null)
220 | return;
221 |
222 | long pos = _stream.Position;
223 | long newPos = ScrollableHeight == 0 ? (long)e.VerticalOffset : (long)((e.VerticalOffset * (double)(_stream.Length - ComputeNeededBufferSize() / 2)) / ScrollableHeight);
224 | newPos = newPos - (newPos % RowCount);
225 | if (pos != newPos || newPos == 0)
226 | {
227 | _stream.Position = newPos;
228 | SetupText();
229 | }
230 | _text.Margin = new Thickness(-e.HorizontalOffset, 0, 0, 0);
231 | }
232 |
233 | private static bool MustWrapStream(Stream stream)
234 | {
235 | if (!stream.CanSeek)
236 | return true;
237 |
238 | try
239 | {
240 | long pos = stream.Position;
241 | long len = stream.Length;
242 | return false;
243 | }
244 | catch
245 | {
246 | return true;
247 | }
248 | }
249 |
250 | protected virtual void OpenStream(object source)
251 | {
252 | Dispose();
253 | if (source == null)
254 | return;
255 |
256 | var stream = source as Stream;
257 | if (stream != null)
258 | {
259 | if (!stream.CanRead)
260 | throw new ArgumentException("source");
261 |
262 | if (MustWrapStream(stream))
263 | {
264 | _stream = new MemoryStream();
265 | stream.CopyTo(_stream);
266 | _stream.Position = 0;
267 | return;
268 | }
269 |
270 | _dontDisposeStream = true;
271 | _stream = stream;
272 | return;
273 | }
274 |
275 | string filePath = source as string;
276 | if (filePath != null && File.Exists(filePath))
277 | {
278 | _stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
279 | return;
280 | }
281 |
282 | var uri = source as Uri;
283 | if (uri != null && uri.IsFile && File.Exists(uri.LocalPath))
284 | {
285 | _stream = new FileStream(uri.LocalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
286 | return;
287 | }
288 |
289 | byte[] bytes = source as byte[];
290 | if (bytes != null)
291 | {
292 | _stream = new MemoryStream(bytes);
293 | _stream.Position = 0;
294 | return;
295 | }
296 | }
297 |
298 | public virtual void Dispose()
299 | {
300 | if (_stream != null && !_dontDisposeStream)
301 | {
302 | _stream.Dispose();
303 | }
304 | _stream = null;
305 | _dontDisposeStream = false;
306 | }
307 |
308 | public static string ToHexaDump(byte[] bytes)
309 | {
310 | return ToHexaDump(bytes, 0, bytes != null ? bytes.Length : 0, true, 16, 0);
311 | }
312 |
313 | public static string ToHexaDump(byte[] bytes, int offset, int count, bool addHeader, int rowCount, long address)
314 | {
315 | if (bytes == null)
316 | {
317 | bytes = new byte[0];
318 | }
319 |
320 | if (offset < 0)
321 | {
322 | offset = 0;
323 | }
324 |
325 | if (count < 0)
326 | {
327 | count = bytes.Length;
328 | }
329 |
330 | if ((offset + count) > bytes.Length)
331 | {
332 | count = bytes.Length - offset;
333 | }
334 |
335 | bool b16 = address >= int.MaxValue;
336 | string format = b16 ? "{0:X16} " : "{0:X8} ";
337 |
338 | var sb = new StringBuilder();
339 | if (addHeader)
340 | {
341 | // Offset 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF
342 | // -------- ----------------------------------------------- ----------------
343 |
344 | sb.Append("Offset ");
345 | if (b16)
346 | {
347 | sb.Append(" ");
348 | }
349 | sb.Append(" ");
350 | for (int i = 0; i < rowCount; i++)
351 | {
352 | sb.AppendFormat("{0:X2} ", i);
353 | }
354 | sb.Append(" ");
355 | for (int i = 0; i < rowCount; i++)
356 | {
357 | sb.AppendFormat("{0:X1}", (i % 16));
358 | }
359 | sb.AppendLine();
360 |
361 | sb.AppendFormat("--------");
362 | if (b16)
363 | {
364 | sb.AppendFormat("--------");
365 | }
366 | sb.Append(" ");
367 | for (int i = 0; i < (rowCount * 3) - 1; i++)
368 | {
369 | sb.Append('-');
370 | }
371 | sb.Append(" ");
372 | for (int i = 0; i < rowCount; i++)
373 | {
374 | sb.Append('-');
375 | }
376 | sb.AppendLine();
377 | }
378 |
379 | for (int i = 0; i < count; i += rowCount)
380 | {
381 | sb.AppendFormat(format, i + offset + address);
382 |
383 | int j;
384 | for (j = 0; (j < rowCount) && ((i + j) < count); j++)
385 | {
386 | sb.AppendFormat("{0:X2} ", bytes[i + j + offset]);
387 | }
388 | sb.Append(" ");
389 |
390 | if (j < rowCount)
391 | {
392 | sb.Append(new string(' ', 3 * (rowCount - j)));
393 | }
394 |
395 | for (j = 0; j < rowCount && (i + j) < count; j++)
396 | {
397 | byte b = bytes[i + j + offset];
398 | char c = (char)b;
399 | if (b > 31 && b < 128)
400 | {
401 | sb.Append(c);
402 | }
403 | else
404 | {
405 | sb.Append('.');
406 | }
407 | }
408 | sb.AppendLine();
409 | }
410 | return sb.ToString();
411 | }
412 | }
413 | }
414 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/ChangeTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace SoftFluent.Windows
6 | {
7 | public class ChangeTypeConverter : IValueConverter
8 | {
9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10 | {
11 | return ConversionService.ChangeType(value, targetType, null, culture);
12 | }
13 |
14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
15 | {
16 | return value;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/ConversionService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public static class ConversionService
6 | {
7 | public static bool TryChangeType(object input, IFormatProvider provider, out T value)
8 | {
9 | object v;
10 | bool b = ServiceProvider.Current.GetService().TryChangeType(input, typeof(T), provider, out v);
11 | if (!b)
12 | {
13 | if (v == null)
14 | {
15 | if (typeof(T).IsValueType)
16 | {
17 | value = (T)Activator.CreateInstance(typeof(T));
18 | }
19 | else
20 | {
21 | value = default(T);
22 | }
23 | }
24 | else
25 | {
26 | value = (T)v;
27 | }
28 | return false;
29 | }
30 | value = (T)v;
31 | return b;
32 | }
33 |
34 | public static bool TryChangeType(object input, out T value)
35 | {
36 | return TryChangeType(input, null, out value);
37 | }
38 |
39 | public static bool TryChangeType(object input, Type conversionType, out object value)
40 | {
41 | return TryChangeType(input, conversionType, null, out value);
42 | }
43 |
44 | public static bool TryChangeType(object input, Type conversionType, IFormatProvider provider, out object value)
45 | {
46 | return ServiceProvider.Current.GetService().TryChangeType(input, conversionType, provider, out value);
47 | }
48 |
49 | public static object ChangeType(object input, Type conversionType)
50 | {
51 | return ChangeType(input, conversionType, null, null);
52 | }
53 |
54 | public static object ChangeType(object input, Type conversionType, object defaultValue)
55 | {
56 | return ChangeType(input, conversionType, defaultValue, null);
57 | }
58 |
59 | public static object ChangeType(object input, Type conversionType, object defaultValue, IFormatProvider provider)
60 | {
61 | if (conversionType == null)
62 | throw new ArgumentNullException("conversionType");
63 |
64 | if (defaultValue == null && conversionType.IsValueType)
65 | {
66 | defaultValue = Activator.CreateInstance(conversionType);
67 | }
68 |
69 | object value;
70 | if (TryChangeType(input, conversionType, provider, out value))
71 | return value;
72 |
73 | return defaultValue;
74 | }
75 |
76 | public static T ChangeType(object input)
77 | {
78 | return ChangeType(input, default(T));
79 | }
80 |
81 | public static T ChangeType(object input, T defaultValue)
82 | {
83 | return ChangeType(input, defaultValue, null);
84 | }
85 |
86 | public static T ChangeType(object input, T defaultValue, IFormatProvider provider)
87 | {
88 | T value;
89 | if (TryChangeType(input, provider, out value))
90 | return value;
91 |
92 | return defaultValue;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/DateTimePicker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Controls.Primitives;
5 | using System.Windows.Input;
6 |
7 | namespace SoftFluent.Windows
8 | {
9 | public class DateTimePicker : DatePicker
10 | {
11 | public static readonly DependencyProperty SelectedDateTimeProperty =
12 | DependencyProperty.Register("SelectedDateTime", typeof(DateTime?), typeof(DateTimePicker),
13 | new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, SelectedTimesChanged));
14 |
15 | public static readonly DependencyProperty TimeIntervalProperty =
16 | DependencyProperty.Register("TimeInterval", typeof(TimeSpan), typeof(DateTimePicker),
17 | new FrameworkPropertyMetadata(new TimeSpan(0, 15, 0), FrameworkPropertyMetadataOptions.AffectsRender, TimesChanged));
18 |
19 | public static readonly DependencyProperty StartTimeProperty =
20 | DependencyProperty.Register("StartTime", typeof(TimeSpan), typeof(DateTimePicker),
21 | new FrameworkPropertyMetadata(new TimeSpan(0, 0, 0), FrameworkPropertyMetadataOptions.AffectsRender, TimesChanged));
22 |
23 | public static readonly DependencyProperty EndTimeProperty =
24 | DependencyProperty.Register("EndTime", typeof(TimeSpan), typeof(DateTimePicker),
25 | new FrameworkPropertyMetadata(new TimeSpan(1, 0, 0, 0), FrameworkPropertyMetadataOptions.AffectsRender, TimesChanged));
26 |
27 | private static void SelectedTimesChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
28 | {
29 | DateTimePicker dtp = (DateTimePicker)source;
30 | dtp.SelectClosestMatch();
31 | }
32 |
33 | private static void TimesChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
34 | {
35 | var dtp = (DateTimePicker)source;
36 | TimeSpan tsi = dtp.TimeInterval;
37 | if (tsi <= TimeSpan.Zero)
38 | {
39 | tsi = new TimeSpan(0, 15, 0);
40 | }
41 | dtp._timeControl.Items.Clear();
42 | TimeSpan ts = dtp.StartTime;
43 | do
44 | {
45 | dtp._timeControl.Items.Add(ts);
46 | ts += tsi;
47 | }
48 | while (ts < dtp.EndTime);
49 | }
50 |
51 | private Calendar _calendar;
52 | private Popup _popup;
53 | private readonly ListBox _timeControl;
54 | private TextBox _textbox;
55 | private bool _initialSetup;
56 | private bool _handleEvents = true;
57 |
58 | public DateTimePicker()
59 | {
60 | _timeControl = new ListBox();
61 | _timeControl.SelectionChanged += OnTimeControlSelectionChanged;
62 | _timeControl.ItemStringFormat = "{0:hh\\:mm}";
63 | }
64 |
65 | public TimeSpan? SelectedTime
66 | {
67 | get
68 | {
69 | if (!SelectedDateTime.HasValue)
70 | return null;
71 |
72 | return SelectedDateTime.Value.TimeOfDay;
73 | }
74 | set
75 | {
76 | if (SelectedDateTime.HasValue)
77 | {
78 | SelectedDateTime = SelectedDateTime.Value.Date + value;
79 | return;
80 | }
81 | SelectedDateTime = DateTime.Now.Date + value;
82 | }
83 | }
84 |
85 | // it's a DateTime (not a TimeSpan so we can use the exact same bindings as for SelectedDate)
86 | public DateTime? SelectedDateTime
87 | {
88 | get { return (DateTime?)GetValue(SelectedDateTimeProperty); }
89 | set { SetValue(SelectedDateTimeProperty, value); }
90 | }
91 |
92 | public TimeSpan StartTime
93 | {
94 | get { return (TimeSpan)GetValue(StartTimeProperty); }
95 | set { SetValue(StartTimeProperty, value); }
96 | }
97 |
98 | public TimeSpan EndTime
99 | {
100 | get { return (TimeSpan)GetValue(EndTimeProperty); }
101 | set { SetValue(EndTimeProperty, value); }
102 | }
103 |
104 | public TimeSpan TimeInterval
105 | {
106 | get { return (TimeSpan)GetValue(TimeIntervalProperty); }
107 | set { SetValue(TimeIntervalProperty, value); }
108 | }
109 |
110 | protected virtual void SelectClosestMatch()
111 | {
112 | if (_timeControl.Items.Count == 0 || !SelectedTime.HasValue)
113 | {
114 | _timeControl.SelectedIndex = -1;
115 | if (_timeControl.Items.Count > 0)
116 | {
117 | _timeControl.ScrollIntoView(0);
118 | }
119 | return;
120 | }
121 |
122 | TimeSpan? prev = null;
123 | foreach (TimeSpan ts in _timeControl.Items)
124 | {
125 | if (ts > SelectedTime.Value)
126 | {
127 | if (prev.HasValue)
128 | {
129 | _handleEvents = false;
130 | _timeControl.SelectedItem = prev.Value;
131 | _handleEvents = true;
132 | }
133 | else
134 | {
135 | // select first
136 | _timeControl.SelectedIndex = 0;
137 | }
138 |
139 | _timeControl.ScrollIntoView(_timeControl.SelectedItem);
140 | return;
141 | }
142 | prev = ts;
143 | }
144 |
145 | // select last
146 | _timeControl.SelectedIndex = _timeControl.Items.Count - 1;
147 | _timeControl.ScrollIntoView(_timeControl.SelectedItem);
148 | }
149 |
150 | protected virtual void UpdateTextbox()
151 | {
152 | if (SelectedTime.HasValue && SelectedDate.HasValue)
153 | {
154 | string newText;
155 | switch (SelectedDateFormat)
156 | {
157 | case DatePickerFormat.Long:
158 | newText = SelectedDate.Value.ToString(System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern);
159 | break;
160 |
161 | default:
162 | newText = SelectedDate.Value.ToString(System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern);
163 | break;
164 | }
165 | newText += " " + SelectedTime.Value;
166 | _textbox.Text = newText;
167 | }
168 | }
169 |
170 | protected virtual void OnTimeControlSelectionChanged(object sender, SelectionChangedEventArgs e)
171 | {
172 | if (!_handleEvents)
173 | return;
174 |
175 | if (_timeControl.SelectedIndex >= 0)
176 | {
177 | SelectedTime = (TimeSpan)_timeControl.SelectedItem;
178 | }
179 | else
180 | {
181 | SelectedTime = null;
182 | }
183 |
184 | UpdateTextbox();
185 | }
186 |
187 | protected override void OnKeyDown(KeyEventArgs e)
188 | {
189 | if (!e.Handled)
190 | {
191 | switch (e.Key)
192 | {
193 | case Key.Space:
194 | case Key.Enter:
195 | _popup.IsOpen = false;
196 | OnTimeControlSelectionChanged(null, null);
197 | break;
198 | }
199 | }
200 | }
201 |
202 | protected override void OnSelectedDateChanged(SelectionChangedEventArgs e)
203 | {
204 | base.OnSelectedDateChanged(e);
205 | if (SelectedDate.HasValue && SelectedDate.Value.TimeOfDay != TimeSpan.Zero)
206 | {
207 | SelectedTime = SelectedDate.Value.TimeOfDay;
208 | }
209 | }
210 |
211 | protected override void OnInitialized(EventArgs e)
212 | {
213 | base.OnInitialized(e);
214 | TimesChanged(this, new DependencyPropertyChangedEventArgs());
215 | }
216 |
217 | protected override void OnCalendarClosed(RoutedEventArgs e)
218 | {
219 | base.OnCalendarClosed(e);
220 | OnTimeControlSelectionChanged(null, null);
221 | }
222 |
223 | protected override void OnCalendarOpened(RoutedEventArgs e)
224 | {
225 | base.OnCalendarOpened(e);
226 | // small hack when date is 1
227 | if (SelectedDate.HasValue && (SelectedDate.Value.Year == 1 || SelectedDate.Value.Year == 9999))
228 | {
229 | DisplayDate = DateTime.Now;
230 | }
231 | _timeControl.Height = _calendar.RenderSize.Height - 6; // I'd like to improve this
232 | SelectedTimesChanged(this, new DependencyPropertyChangedEventArgs());
233 | OnTimeControlSelectionChanged(null, null);
234 | }
235 |
236 | public override void OnApplyTemplate()
237 | {
238 | _textbox = GetTemplateChild("PART_TextBox") as TextBox;
239 | _textbox.TextChanged += (s, e) =>
240 | {
241 | if (!_initialSetup)
242 | {
243 | UpdateTextbox();
244 | _initialSetup = true;
245 | }
246 | };
247 |
248 | base.OnApplyTemplate();
249 | _popup = GetTemplateChild("PART_Popup") as Popup;
250 | if (_popup != null)
251 | {
252 | _calendar = (Calendar)_popup.Child;
253 | _popup.Child = null;
254 | Grid grid = new Grid();
255 | _popup.Child = grid;
256 |
257 | grid.ColumnDefinitions.Add(new ColumnDefinition());
258 | grid.ColumnDefinitions.Add(new ColumnDefinition());
259 |
260 | _timeControl.VerticalAlignment = VerticalAlignment.Top;
261 | _timeControl.Margin = new Thickness(3); // I'd like to improve this too
262 | Grid.SetColumn(_timeControl, 1);
263 |
264 | grid.Children.Add(_calendar);
265 | grid.Children.Add(_timeControl);
266 | }
267 | }
268 | }
269 | }
270 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/DecamelizationService.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SoftFluent.Windows
3 | {
4 | public static class DecamelizationService
5 | {
6 | public static string Decamelize(string text)
7 | {
8 | return Decamelize(text, null);
9 | }
10 |
11 | public static string Decamelize(string text, DecamelizeOptions options)
12 | {
13 | return ServiceProvider.Current.GetService().Decamelize(text, options);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/DecamelizeOptions.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SoftFluent.Windows
3 | {
4 | public class DecamelizeOptions
5 | {
6 | public DecamelizeOptions()
7 | {
8 | TextOptions = DecamelizeTextOptions.Default;
9 | }
10 |
11 | public virtual DecamelizeTextOptions TextOptions { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/DecamelizeTextOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | [Flags]
6 | public enum DecamelizeTextOptions
7 | {
8 | ///
9 | /// No option is defined.
10 | ///
11 | None = 0,
12 |
13 | ///
14 | /// First character will be upper case.
15 | ///
16 | ForceFirstUpper = 1,
17 |
18 | ///
19 | /// Characters beyond the first will be lower case.
20 | ///
21 | ForceRestLower = 2,
22 |
23 | ///
24 | /// Unescape unicode encoding (format is \u0000).
25 | ///
26 | UnescapeUnicode = 4,
27 |
28 | ///
29 | /// Unescape hexadecimal encoding (format is \x0000).
30 | ///
31 | UnescapeHexadecimal = 8,
32 |
33 | ///
34 | /// Replaces spaces by underscore.
35 | ///
36 | ReplaceSpacesByUnderscore = 0x10,
37 |
38 | ///
39 | /// Replaces spaces by minus.
40 | ///
41 | ReplaceSpacesByMinus = 0x20,
42 |
43 | ///
44 | /// Replaces spaces by dot.
45 | ///
46 | ReplaceSpacesByDot = 0x40,
47 |
48 | ///
49 | /// Keep first underscores sticked as is.
50 | ///
51 | KeepFirstUnderscores = 0x80,
52 |
53 | ///
54 | /// Numbers are not considered as separators.
55 | ///
56 | DontDecamelizeNumbers = 0x100,
57 |
58 | ///
59 | /// Keep indices used by the String.Format method.
60 | ///
61 | KeepFormattingIndices = 0x200,
62 |
63 | ///
64 | /// Defines the default options.
65 | ///
66 | Default = ForceFirstUpper | UnescapeUnicode | UnescapeHexadecimal | KeepFirstUnderscores,
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/Diagnostics/Tracing.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Reflection;
3 |
4 | namespace SoftFluent.Windows.Diagnostics
5 | {
6 | public static class Tracing
7 | {
8 | public static void Enable()
9 | {
10 | Enable(SourceLevels.Warning, null);
11 | }
12 |
13 | public static void Enable(SourceLevels levels, TraceListener listener)
14 | {
15 | if (listener == null)
16 | {
17 | listener = new DefaultTraceListener();
18 | }
19 |
20 | PresentationTraceSources.Refresh();
21 | foreach (PropertyInfo pi in typeof(PresentationTraceSources).GetProperties(BindingFlags.Static | BindingFlags.Public))
22 | {
23 | if (pi.Name == "FreezableSource")
24 | continue;
25 |
26 | if (typeof(TraceSource).IsAssignableFrom(pi.PropertyType))
27 | {
28 | var ts = (TraceSource)pi.GetValue(null, null);
29 | ts.Listeners.Add(listener);
30 | ts.Switch.Level = levels;
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/EnumerableConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Globalization;
4 | using System.Text;
5 | using System.Windows;
6 | using System.Windows.Data;
7 |
8 | namespace SoftFluent.Windows
9 | {
10 | public class EnumerableConverter : DependencyObject, IValueConverter
11 | {
12 | public static readonly DependencyProperty MaxItemsProperty =
13 | DependencyProperty.Register("MaxItems", typeof(int), typeof(EnumerableConverter), new PropertyMetadata(10));
14 |
15 | public static readonly DependencyProperty SeparatorProperty =
16 | DependencyProperty.Register("Separator", typeof(string), typeof(EnumerableConverter), new PropertyMetadata(", "));
17 |
18 | public static readonly DependencyProperty FormatProperty =
19 | DependencyProperty.Register("Format", typeof(string), typeof(EnumerableConverter), new PropertyMetadata("{0}"));
20 |
21 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
22 | {
23 | if (targetType == typeof(string) && !(value is string) && value is IEnumerable)
24 | {
25 | var sb = new StringBuilder();
26 | var enumerable = value as IEnumerable;
27 | if (enumerable != null)
28 | {
29 | foreach (object obj in enumerable)
30 | {
31 | if (sb.Length > 0)
32 | {
33 | sb.Append(Separator);
34 | }
35 | sb.AppendFormat(Format, obj);
36 | }
37 | }
38 | return sb.ToString();
39 | }
40 | return ConversionService.ChangeType(value, targetType);
41 | }
42 |
43 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
44 | {
45 | throw new NotImplementedException();
46 | }
47 |
48 | public int MaxItems
49 | {
50 | get
51 | {
52 | return (int)GetValue(MaxItemsProperty);
53 | }
54 | set
55 | {
56 | SetValue(MaxItemsProperty, value);
57 | }
58 | }
59 |
60 | public string Format
61 | {
62 | get
63 | {
64 | return (string)GetValue(FormatProperty);
65 | }
66 | set
67 | {
68 | SetValue(FormatProperty, value);
69 | }
70 | }
71 |
72 | public string Separator
73 | {
74 | get
75 | {
76 | return (string)GetValue(SeparatorProperty);
77 | }
78 | set
79 | {
80 | SetValue(SeparatorProperty, value);
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/IActivator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public interface IActivator
6 | {
7 | object CreateInstance(Type type, params object[] args);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/IConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public interface IConverter
6 | {
7 | bool TryChangeType(object input, Type conversionType, IFormatProvider provider, out object value);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/IDecamelizer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace SoftFluent.Windows
3 | {
4 | public interface IDecamelizer
5 | {
6 | string Decamelize(string text, DecamelizeOptions options);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/IPropertyGridCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Input;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public interface IPropertyGridCommandHandler
6 | {
7 | void CanExecute(PropertyGridProperty property, object sender, CanExecuteRoutedEventArgs e);
8 | void Executed(PropertyGridProperty property, object sender, ExecutedRoutedEventArgs e);
9 | }
10 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/IPropertyGridEditor.cs:
--------------------------------------------------------------------------------
1 | namespace SoftFluent.Windows
2 | {
3 | public interface IPropertyGridEditor
4 | {
5 | bool SetContext(PropertyGridProperty property, object parameter);
6 | }
7 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/IPropertyGridObject.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public interface IPropertyGridObject
6 | {
7 | void FinalizeProperties(PropertyGridDataProvider dataProvider, IList properties);
8 | bool TryShowEditor(PropertyGridProperty property, object editor, out bool? result);
9 | void EditorClosed(PropertyGridProperty property, object editor);
10 | }
11 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/ITypeResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public interface ITypeResolver
6 | {
7 | Type ResolveType(string fullName, bool throwOnError);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("SoftFluent.Windows")]
5 | [assembly: AssemblyDescription("SoftFluent's Windows (WPF) tools")]
6 | #if DEBUG
7 | [assembly: AssemblyConfiguration("Debug")]
8 | #else
9 | [assembly: AssemblyConfiguration("Release")]
10 | #endif
11 | [assembly: AssemblyCompany("SoftFluent S.A.S - http://www.softfluent.com")]
12 | [assembly: AssemblyProduct("SoftFluent.Windows")]
13 | [assembly: AssemblyCopyright("Copyright (C) 2014-2017 SoftFluent S.A.S. All rights reserved.")]
14 | [assembly: AssemblyTrademark("SoftFluent Windows (TM) is a trademark of SoftFluent S.A.S.")]
15 | [assembly: AssemblyCulture("")]
16 | [assembly: ComVisible(false)]
17 | [assembly: Guid("42acb969-f6b8-454a-a874-4ab68d85f578")]
18 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/Properties/AssemblyVersionInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 |
3 | [assembly: AssemblyVersion("1.0.0.0")]
4 | [assembly: AssemblyFileVersion("1.0.1.6")]
5 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
6 | public class PropertyGridAttribute : Attribute
7 | {
8 | public PropertyGridAttribute()
9 | {
10 | Type = typeof(object);
11 | }
12 |
13 | public object Value { get; set; }
14 | public string Name { get; set; }
15 | public Type Type { get; set; }
16 |
17 | public override object TypeId
18 | {
19 | get
20 | {
21 | return Name;
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows.Data;
4 |
5 | namespace SoftFluent.Windows
6 | {
7 | public class PropertyGridConverter : IValueConverter
8 | {
9 | private static Type GetParameterAsType(object parameter)
10 | {
11 | if (parameter == null)
12 | return null;
13 |
14 | string typeName = string.Format("{0}", parameter);
15 | if (string.IsNullOrWhiteSpace(typeName))
16 | return null;
17 |
18 | return TypeResolutionService.ResolveType(typeName);
19 | }
20 |
21 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
22 | {
23 | Type parameterType = GetParameterAsType(parameter);
24 | if (parameterType != null)
25 | {
26 | value = ConversionService.ChangeType(value, parameterType, null, culture);
27 | }
28 |
29 | object convertedValue = targetType == null ? value : ConversionService.ChangeType(value, targetType, null, culture);
30 | return convertedValue;
31 | }
32 |
33 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
34 | {
35 | object convertedValue = targetType == null ? value : ConversionService.ChangeType(value, targetType, null, culture);
36 | return convertedValue;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridDataProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.ComponentModel;
6 | using System.Linq;
7 | using SoftFluent.Windows.Utilities;
8 |
9 | namespace SoftFluent.Windows
10 | {
11 | public class PropertyGridDataProvider : IListSource
12 | {
13 | public PropertyGridDataProvider(PropertyGrid grid, object data)
14 | {
15 | if (grid == null)
16 | throw new ArgumentNullException("grid");
17 |
18 | if (data == null)
19 | throw new ArgumentNullException("data");
20 |
21 | Grid = grid;
22 | Data = data;
23 | Properties = new ObservableCollection();
24 | ScanProperties();
25 | }
26 |
27 | public PropertyGrid Grid { get; private set; }
28 | public object Data { get; private set; }
29 | public virtual ObservableCollection Properties { get; private set; }
30 |
31 | public static bool HasProperties(Type type)
32 | {
33 | if (type == null)
34 | throw new ArgumentNullException("type");
35 |
36 | foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(type))
37 | {
38 | if (!descriptor.IsBrowsable)
39 | continue;
40 |
41 | return true;
42 | }
43 | return false;
44 | }
45 |
46 | public virtual PropertyGridProperty AddProperty(string propertyName)
47 | {
48 | if (propertyName == null)
49 | throw new ArgumentNullException("propertyName");
50 |
51 | PropertyGridProperty prop = Properties.FirstOrDefault(p => p.Name == propertyName);
52 | if (prop != null)
53 | return prop;
54 |
55 | PropertyDescriptor desc = TypeDescriptor.GetProperties(Data).OfType().FirstOrDefault(p => p.Name == propertyName);
56 | if (desc != null)
57 | {
58 | prop = CreateProperty(desc);
59 | if (prop != null)
60 | {
61 | Properties.Add(prop);
62 | }
63 | }
64 | return prop;
65 | }
66 |
67 | public virtual DynamicObject CreateDynamicObject()
68 | {
69 | return ActivatorService.CreateInstance();
70 | }
71 |
72 | public virtual PropertyGridProperty CreateProperty()
73 | {
74 | return ActivatorService.CreateInstance(this);
75 | }
76 |
77 | protected virtual void Describe(PropertyGridProperty property, PropertyDescriptor descriptor)
78 | {
79 | if (property == null)
80 | throw new ArgumentNullException("property");
81 |
82 | if (descriptor == null)
83 | throw new ArgumentNullException("descriptor");
84 |
85 | property.Descriptor = descriptor;
86 | property.Name = descriptor.Name;
87 | property.PropertyType = descriptor.PropertyType;
88 |
89 | // unset by default. conversion service does the default job
90 | //property.Converter = descriptor.Converter;
91 |
92 | property.Category = string.IsNullOrWhiteSpace(descriptor.Category) || descriptor.Category.EqualsIgnoreCase(CategoryAttribute.Default.Category) ? Grid.DefaultCategoryName : descriptor.Category;
93 | property.IsReadOnly = descriptor.IsReadOnly;
94 | property.Description = descriptor.Description;
95 | property.DisplayName = descriptor.DisplayName;
96 | if (Grid.DecamelizePropertiesDisplayNames && property.DisplayName == descriptor.Name)
97 | {
98 | property.DisplayName = DecamelizationService.Decamelize(property.DisplayName);
99 | }
100 |
101 | property.IsEnum = descriptor.PropertyType.IsEnum;
102 | property.IsFlagsEnum = descriptor.PropertyType.IsEnum && Extensions.IsFlagsEnum(descriptor.PropertyType);
103 |
104 | var options = descriptor.GetAttribute();
105 | if (options != null)
106 | {
107 | if (options.SortOrder != 0)
108 | {
109 | property.SortOrder = options.SortOrder;
110 | }
111 |
112 | property.IsEnum = options.IsEnum;
113 | property.IsFlagsEnum = options.IsFlagsEnum;
114 | }
115 |
116 | var att = descriptor.GetAttribute();
117 | if (att != null)
118 | {
119 | property.HasDefaultValue = true;
120 | property.DefaultValue = att.Value;
121 | }
122 | else if (options != null)
123 | {
124 | if (options.HasDefaultValue)
125 | {
126 | property.HasDefaultValue = true;
127 | property.DefaultValue = options.DefaultValue;
128 | }
129 | else
130 | {
131 | string defaultValue;
132 | if (PropertyGridComboBoxExtension.TryGetDefaultValue(options, out defaultValue))
133 | {
134 | property.DefaultValue = defaultValue;
135 | property.HasDefaultValue = true;
136 | }
137 | }
138 | }
139 |
140 | AddDynamicProperties(descriptor.Attributes.OfType(), property.Attributes);
141 | AddDynamicProperties(descriptor.PropertyType.GetAttributes(), property.TypeAttributes);
142 | }
143 |
144 | public static void AddDynamicProperties(IEnumerable attributes, DynamicObject dynamicObject)
145 | {
146 | if (attributes == null || dynamicObject == null)
147 | return;
148 |
149 | foreach (PropertyGridAttribute pga in attributes)
150 | {
151 | if (string.IsNullOrWhiteSpace(pga.Name))
152 | continue;
153 |
154 | DynamicObjectProperty prop = dynamicObject.AddProperty(pga.Name, pga.Type, null);
155 | prop.SetValue(dynamicObject, pga.Value);
156 | }
157 | }
158 |
159 | public virtual PropertyGridProperty CreateProperty(PropertyDescriptor descriptor)
160 | {
161 | if (descriptor == null)
162 | throw new ArgumentNullException("descriptor");
163 |
164 | bool forceReadWrite = false;
165 | PropertyGridProperty property = null;
166 | var options = descriptor.GetAttribute();
167 | if (options != null)
168 | {
169 | forceReadWrite = options.ForceReadWrite;
170 | if (options.PropertyType != null)
171 | {
172 | property = (PropertyGridProperty)Activator.CreateInstance(options.PropertyType, this);
173 | }
174 | }
175 |
176 | if (property == null)
177 | {
178 | options = descriptor.PropertyType.GetAttribute();
179 | if (options != null)
180 | {
181 | if (!forceReadWrite)
182 | {
183 | forceReadWrite = options.ForceReadWrite;
184 | }
185 | if (options.PropertyType != null)
186 | {
187 | property = (PropertyGridProperty)Activator.CreateInstance(options.PropertyType, this);
188 | }
189 | }
190 | }
191 |
192 | if (property == null)
193 | {
194 | property = CreateProperty();
195 | }
196 |
197 | Describe(property, descriptor);
198 | if (forceReadWrite)
199 | {
200 | property.IsReadOnly = false;
201 | }
202 | property.OnDescribed();
203 | property.RefreshValueFromDescriptor(true, false, true);
204 | return property;
205 | }
206 |
207 | protected virtual void ScanProperties()
208 | {
209 | Properties.Clear();
210 | var props = new List();
211 | foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(Data))
212 | {
213 | if (!descriptor.IsBrowsable)
214 | continue;
215 |
216 | PropertyGridProperty property = CreateProperty(descriptor);
217 | if (property != null)
218 | {
219 | props.Add(property);
220 | }
221 | }
222 |
223 | var pga = Data as IPropertyGridObject;
224 | if (pga != null)
225 | {
226 | pga.FinalizeProperties(this, props);
227 | }
228 |
229 | props.Sort();
230 | foreach (PropertyGridProperty property in props)
231 | {
232 | Properties.Add(property);
233 | }
234 | }
235 |
236 | bool IListSource.ContainsListCollection
237 | {
238 | get { return false; }
239 | }
240 |
241 | IList IListSource.GetList()
242 | {
243 | return Properties;
244 | }
245 | }
246 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridDataTemplate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows;
4 | using System.Windows.Markup;
5 | using SoftFluent.Windows.Utilities;
6 |
7 | namespace SoftFluent.Windows
8 | {
9 | [ContentProperty("DataTemplate")]
10 | public class PropertyGridDataTemplate
11 | {
12 | private class NullableEnum { }
13 | public static readonly Type NullableEnumType = typeof(NullableEnum);
14 |
15 | private List _resolvedPropertyTypes;
16 | private List _resolvedCollectionItemPropertyTypes;
17 |
18 | public string PropertyType { get; set; }
19 | public string CollectionItemPropertyType { get; set; }
20 | public DataTemplate DataTemplate { get; set; } // note: may be null
21 | public bool? IsCollection { get; set; }
22 | public bool? IsReadOnly { get; set; }
23 | public bool? IsError { get; set; }
24 | public bool? IsValid { get; set; }
25 | public bool? IsFlagsEnum { get; set; }
26 | public bool? IsCollectionItemValueType { get; set; }
27 | public bool? IsValueType { get; set; }
28 | public string Category { get; set; }
29 | public string Name { get; set; }
30 |
31 | public virtual IList ResolvedPropertyTypes
32 | {
33 | get
34 | {
35 | if (_resolvedPropertyTypes == null)
36 | {
37 | _resolvedPropertyTypes = new List();
38 | List names = PropertyType.SplitToList('|');
39 | foreach (string name in names)
40 | {
41 | if (string.IsNullOrWhiteSpace(name))
42 | continue;
43 |
44 | Type type;
45 | // a hack to handle nullable enum in a general way
46 | if (name == "System.Nullable`1[System.Enum]")
47 | {
48 | type = NullableEnumType;
49 | }
50 | else
51 | {
52 | type = TypeResolutionService.ResolveType(name);
53 | }
54 | if (type != null)
55 | {
56 | _resolvedPropertyTypes.Add(type);
57 | }
58 | }
59 | }
60 | return _resolvedPropertyTypes;
61 | }
62 | }
63 |
64 | public virtual IList ResolvedCollectionItemPropertyTypes
65 | {
66 | get
67 | {
68 | if (_resolvedCollectionItemPropertyTypes == null)
69 | {
70 | _resolvedCollectionItemPropertyTypes = new List();
71 | List names = CollectionItemPropertyType.SplitToList('|');
72 | foreach (string name in names)
73 | {
74 | if (string.IsNullOrWhiteSpace(name))
75 | continue;
76 |
77 | Type type = TypeResolutionService.ResolveType(name);
78 | if (type != null)
79 | {
80 | _resolvedCollectionItemPropertyTypes.Add(type);
81 | }
82 | }
83 | }
84 | return _resolvedCollectionItemPropertyTypes;
85 | }
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridDataTemplateSelector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.ObjectModel;
3 | using System.ComponentModel;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Markup;
7 | using SoftFluent.Windows.Utilities;
8 |
9 | namespace SoftFluent.Windows
10 | {
11 | [ContentProperty("DataTemplates")]
12 | public class PropertyGridDataTemplateSelector : DataTemplateSelector
13 | {
14 | private PropertyGrid _propertyGrid;
15 |
16 | public PropertyGridDataTemplateSelector()
17 | {
18 | DataTemplates = new ObservableCollection();
19 | }
20 |
21 | public PropertyGrid PropertyGrid
22 | {
23 | get
24 | {
25 | return _propertyGrid;
26 | }
27 | }
28 |
29 | protected virtual bool Filter(PropertyGridDataTemplate template, PropertyGridProperty property)
30 | {
31 | if (template == null)
32 | throw new ArgumentNullException("template");
33 |
34 | if (property == null)
35 | throw new ArgumentNullException("property");
36 |
37 | // check various filters
38 | if (template.IsCollection.HasValue && template.IsCollection.Value != property.IsCollection)
39 | {
40 | return true;
41 | }
42 |
43 | if (template.IsCollectionItemValueType.HasValue && template.IsCollectionItemValueType.Value != property.IsCollectionItemValueType)
44 | {
45 | return true;
46 | }
47 |
48 | if (template.IsValueType.HasValue && template.IsValueType.Value != property.IsValueType)
49 | {
50 | return true;
51 | }
52 |
53 | if (template.IsReadOnly.HasValue && template.IsReadOnly.Value != property.IsReadOnly)
54 | {
55 | return true;
56 | }
57 |
58 | if (template.IsError.HasValue && template.IsError.Value != property.IsError)
59 | {
60 | return true;
61 | }
62 |
63 | if (template.IsValid.HasValue && template.IsValid.Value != property.IsValid)
64 | {
65 | return true;
66 | }
67 |
68 | if (template.IsFlagsEnum.HasValue && template.IsFlagsEnum.Value != property.IsFlagsEnum)
69 | {
70 | return true;
71 | }
72 |
73 | if (template.Category != null && !property.Category.EqualsIgnoreCase(template.Category))
74 | {
75 | return true;
76 | }
77 |
78 | if (template.Name != null && !property.Name.EqualsIgnoreCase(template.Name))
79 | {
80 | return true;
81 | }
82 |
83 | return false;
84 | }
85 |
86 | public virtual bool IsAssignableFrom(Type type, Type propertyType, PropertyGridDataTemplate template, PropertyGridProperty property)
87 | {
88 | if (type == null)
89 | throw new ArgumentNullException("type");
90 |
91 | if (propertyType == null)
92 | throw new ArgumentNullException("propertyType");
93 |
94 | if (template == null)
95 | throw new ArgumentNullException("template");
96 |
97 | if (property == null)
98 | throw new ArgumentNullException("property");
99 |
100 | if (type.IsAssignableFrom(propertyType))
101 | {
102 | // bool? is assignable from bool, but we don't want that match
103 | if (!type.IsNullable() || propertyType.IsNullable())
104 | return true;
105 | }
106 |
107 | // hack for nullable enums...
108 | if (type == PropertyGridDataTemplate.NullableEnumType)
109 | {
110 | Type enumType;
111 | bool nullable;
112 | PropertyGridProperty.IsEnumOrNullableEnum(propertyType, out enumType, out nullable);
113 | if (nullable)
114 | return true;
115 | }
116 |
117 | var options = PropertyGridOptionsAttribute.FromProperty(property);
118 | if (options != null)
119 | {
120 | if ((type.IsEnum || type == typeof(Enum)) && options.IsEnum)
121 | {
122 | if (!options.IsFlagsEnum)
123 | return true;
124 |
125 | if (Extensions.IsFlagsEnum(type))
126 | return true;
127 |
128 | if (template.IsFlagsEnum.HasValue && template.IsFlagsEnum.Value)
129 | return true;
130 | }
131 | }
132 |
133 | return false;
134 | }
135 |
136 | public override DataTemplate SelectTemplate(object item, DependencyObject container)
137 | {
138 | if (container == null)
139 | throw new ArgumentNullException("container");
140 |
141 | var property = item as PropertyGridProperty;
142 | if (property == null)
143 | return base.SelectTemplate(item, container);
144 |
145 | DataTemplate propTemplate = PropertyGridOptionsAttribute.SelectTemplate(property, item, container);
146 | if (propTemplate != null)
147 | return propTemplate;
148 |
149 | if (_propertyGrid == null)
150 | {
151 | _propertyGrid = container.GetVisualSelfOrParent();
152 | }
153 |
154 | if (_propertyGrid.ValueEditorTemplateSelector != null && _propertyGrid.ValueEditorTemplateSelector != this)
155 | {
156 | DataTemplate template = _propertyGrid.ValueEditorTemplateSelector.SelectTemplate(item, container);
157 | if (template != null)
158 | return template;
159 | }
160 |
161 | foreach (PropertyGridDataTemplate template in DataTemplates)
162 | {
163 | if (Filter(template, property))
164 | continue;
165 |
166 | if (template.IsCollection.HasValue && template.IsCollection.Value)
167 | {
168 | if (string.IsNullOrWhiteSpace(template.CollectionItemPropertyType) && template.DataTemplate != null)
169 | return template.DataTemplate;
170 |
171 | if (property.CollectionItemPropertyType != null)
172 | {
173 | foreach (Type type in template.ResolvedCollectionItemPropertyTypes)
174 | {
175 | if (IsAssignableFrom(type, property.CollectionItemPropertyType, template, property))
176 | return template.DataTemplate;
177 | }
178 | }
179 | }
180 | else
181 | {
182 | if (string.IsNullOrWhiteSpace(template.PropertyType) && template.DataTemplate != null)
183 | return template.DataTemplate;
184 |
185 | foreach (Type type in template.ResolvedPropertyTypes)
186 | {
187 | if (IsAssignableFrom(type, property.PropertyType, template, property))
188 | return template.DataTemplate;
189 | }
190 | }
191 | }
192 | return base.SelectTemplate(item, container);
193 | }
194 |
195 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
196 | public ObservableCollection DataTemplates { get; private set; }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridEnumProperty.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using SoftFluent.Windows.Utilities;
3 |
4 | namespace SoftFluent.Windows
5 | {
6 | public class PropertyGridEnumProperty : PropertyGridProperty
7 | {
8 | public PropertyGridEnumProperty(PropertyGridDataProvider provider)
9 | : base(provider)
10 | {
11 | EnumAttributes = provider.CreateDynamicObject();
12 | }
13 |
14 | public override void OnValueChanged()
15 | {
16 | base.OnValueChanged();
17 | EnumAttributes.Properties.Clear();
18 | foreach (FieldInfo fi in PropertyType.GetFields(BindingFlags.Static | BindingFlags.Public))
19 | {
20 | if (fi.Name.Equals(string.Format("{0}", base.Value)))
21 | {
22 | PropertyGridDataProvider.AddDynamicProperties(fi.GetAttributes(), EnumAttributes);
23 | }
24 | }
25 | }
26 |
27 | public virtual DynamicObject EnumAttributes { get; private set; }
28 | }
29 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace SoftFluent.Windows
4 | {
5 | public class PropertyGridEventArgs : CancelEventArgs
6 | {
7 | public PropertyGridEventArgs(PropertyGridProperty property)
8 | : this(property, null)
9 | {
10 | }
11 |
12 | public PropertyGridEventArgs(PropertyGridProperty property, object context)
13 | {
14 | Property = property;
15 | Context = context;
16 | }
17 |
18 | public PropertyGridProperty Property { get; private set; }
19 | public object Context { get; set; }
20 | }
21 | }
--------------------------------------------------------------------------------
/SoftFluent.Windows/SoftFluent.Windows/PropertyGridItem.cs:
--------------------------------------------------------------------------------
1 | namespace SoftFluent.Windows
2 | {
3 | public class PropertyGridItem : AutoObject
4 | {
5 | public PropertyGridItem()
6 | {
7 | IsChecked = false;
8 | }
9 |
10 | public virtual bool IsUnset { get { return GetProperty(); } set { SetProperty(value); } }
11 | public virtual bool IsZero { get { return GetProperty(); } set { SetProperty(value); } }
12 | public virtual string Name { get { return GetProperty(); } set { SetProperty(value); } }
13 | public virtual object Value { get { return GetProperty