├── .gitattributes
├── .gitignore
├── CommonLibrary
├── CommonLibrary.csproj
├── Controls
│ ├── GifRenderer
│ │ ├── GifFileHandler.cs
│ │ ├── GifPropertiesHelper.cs
│ │ ├── GifRenderer.cs
│ │ ├── InactiveGifManager.cs
│ │ ├── ScaleSettings.cs
│ │ ├── Structs
│ │ │ ├── FrameProperties.cs
│ │ │ └── ImageProperties.cs
│ │ └── Themes
│ │ │ └── GifRendererTheme.xaml
│ └── ImageControl
│ │ ├── ImageCrop
│ │ ├── AspectRatio.cs
│ │ ├── BitmapHelper.cs
│ │ ├── Converter.cs
│ │ ├── Crop
│ │ │ ├── CropImageControl.cs
│ │ │ └── CropImageSourceChangedEventHandler.cs
│ │ ├── CropSelection.cs
│ │ ├── CropSelectionSize.cs
│ │ ├── FrameworkElementExtensions.cs
│ │ ├── ImageTool.cs
│ │ └── PlatformIndependent.cs
│ │ └── Themes
│ │ ├── CropImageControl.xaml
│ │ └── ImageTool.xaml
├── DeviceInfoHelper.cs
├── DownloadHelper.cs
├── FileHelper.cs
├── ImageHelper.cs
├── ImageProcessing.cs
├── LocalCacheManager.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── CommonLibrary.rd.xml
├── SecurityHelper.cs
├── StorageHelper.cs
├── StorageManager.cs
├── StorageManagerEx.cs
├── Themes
│ └── Generic.xaml
├── project.json
└── project.lock.json
├── README.md
├── UWPToolkit.sln
├── UWPToolkit
├── App.xaml
├── App.xaml.cs
├── ApplicationInsights.config
├── Assets
│ ├── LockScreenLogo.scale-200.png
│ ├── SplashScreen.scale-200.png
│ ├── Square150x150Logo.scale-200.png
│ ├── Square44x44Logo.scale-200.png
│ ├── Square44x44Logo.targetsize-24_altform-unplated.png
│ ├── StoreLogo.png
│ └── Wide310x150Logo.scale-200.png
├── Controls
│ ├── BasePage.cs
│ ├── PictureCropControl.xaml
│ ├── PictureCropControl.xaml.cs
│ ├── PictureEditor.xaml
│ ├── PictureEditor.xaml.cs
│ ├── PreviewPictureControl.xaml
│ └── PreviewPictureControl.xaml.cs
├── MasterPage.xaml
├── MasterPage.xaml.cs
├── Package.appxmanifest
├── Pages
│ ├── PictureEditorPage.xaml
│ ├── PictureEditorPage.xaml.cs
│ ├── PreviewPicturePage.xaml
│ └── PreviewPicturePage.xaml.cs
├── Properties
│ ├── AssemblyInfo.cs
│ └── Default.rd.xml
├── Styles
│ └── Dictionary.xaml
├── UWPToolkit.csproj
├── UWPToolkit_TemporaryKey.pfx
├── project.json
└── project.lock.json
└── lib
└── InkToolbarControl
├── ARM
├── debug
│ ├── Microsoft.Labs.InkToolbarControl.dll
│ ├── Microsoft.Labs.InkToolbarControl.pri
│ └── Microsoft.Labs.InkToolbarControl.winmd
└── release
│ ├── Microsoft.Labs.InkToolbarControl.dll
│ ├── Microsoft.Labs.InkToolbarControl.pri
│ └── Microsoft.Labs.InkToolbarControl.winmd
└── x86
├── debug
├── Microsoft.Labs.InkToolbarControl.dll
├── Microsoft.Labs.InkToolbarControl.pri
└── Microsoft.Labs.InkToolbarControl.winmd
└── release
├── Microsoft.Labs.InkToolbarControl.dll
├── Microsoft.Labs.InkToolbarControl.pri
└── Microsoft.Labs.InkToolbarControl.winmd
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Microsoft Azure ApplicationInsights config file
170 | ApplicationInsights.config
171 |
172 | # Windows Store app package directory
173 | AppPackages/
174 | BundleArtifacts/
175 |
176 | # Visual Studio cache files
177 | # files ending in .cache can be ignored
178 | *.[Cc]ache
179 | # but keep track of directories ending in .cache
180 | !*.[Cc]ache/
181 |
182 | # Others
183 | ClientBin/
184 | [Ss]tyle[Cc]op.*
185 | ~$*
186 | *~
187 | *.dbmdl
188 | *.dbproj.schemaview
189 | *.pfx
190 | *.publishsettings
191 | node_modules/
192 | orleans.codegen.cs
193 |
194 | # RIA/Silverlight projects
195 | Generated_Code/
196 |
197 | # Backup & report files from converting an old project file
198 | # to a newer Visual Studio version. Backup files are not needed,
199 | # because we have git ;-)
200 | _UpgradeReport_Files/
201 | Backup*/
202 | UpgradeLog*.XML
203 | UpgradeLog*.htm
204 |
205 | # SQL Server files
206 | *.mdf
207 | *.ldf
208 |
209 | # Business Intelligence projects
210 | *.rdl.data
211 | *.bim.layout
212 | *.bim_*.settings
213 |
214 | # Microsoft Fakes
215 | FakesAssemblies/
216 |
217 | # GhostDoc plugin setting file
218 | *.GhostDoc.xml
219 |
220 | # Node.js Tools for Visual Studio
221 | .ntvs_analysis.dat
222 |
223 | # Visual Studio 6 build log
224 | *.plg
225 |
226 | # Visual Studio 6 workspace options file
227 | *.opt
228 |
229 | # Visual Studio LightSwitch build output
230 | **/*.HTMLClient/GeneratedArtifacts
231 | **/*.DesktopClient/GeneratedArtifacts
232 | **/*.DesktopClient/ModelManifest.xml
233 | **/*.Server/GeneratedArtifacts
234 | **/*.Server/ModelManifest.xml
235 | _Pvt_Extensions
236 |
237 | # LightSwitch generated files
238 | GeneratedArtifacts/
239 | ModelManifest.xml
240 |
241 | # Paket dependency manager
242 | .paket/paket.exe
243 |
244 | # FAKE - F# Make
245 | .fake/
--------------------------------------------------------------------------------
/CommonLibrary/CommonLibrary.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1FAA2DB6-1203-4DF4-AC1A-8EC415F6681D}
8 | Library
9 | Properties
10 | CommonLibrary
11 | CommonLibrary
12 | zh-CN
13 | UAP
14 | 10.0.10586.0
15 | 10.0.10240.0
16 | 14
17 | 512
18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
19 |
20 |
21 | AnyCPU
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
27 | prompt
28 | 4
29 |
30 |
31 | AnyCPU
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE;NETFX_CORE;WINDOWS_UWP
36 | prompt
37 | 4
38 |
39 |
40 | x86
41 | true
42 | bin\x86\Debug\
43 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
44 | ;2008
45 | full
46 | x86
47 | false
48 | prompt
49 |
50 |
51 | x86
52 | bin\x86\Release\
53 | TRACE;NETFX_CORE;WINDOWS_UWP
54 | true
55 | ;2008
56 | pdbonly
57 | x86
58 | false
59 | prompt
60 |
61 |
62 | ARM
63 | true
64 | bin\ARM\Debug\
65 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
66 | ;2008
67 | full
68 | ARM
69 | false
70 | prompt
71 |
72 |
73 | ARM
74 | bin\ARM\Release\
75 | TRACE;NETFX_CORE;WINDOWS_UWP
76 | true
77 | ;2008
78 | pdbonly
79 | ARM
80 | false
81 | prompt
82 |
83 |
84 | x64
85 | true
86 | bin\x64\Debug\
87 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
88 | ;2008
89 | full
90 | x64
91 | false
92 | prompt
93 |
94 |
95 | x64
96 | bin\x64\Release\
97 | TRACE;NETFX_CORE;WINDOWS_UWP
98 | true
99 | ;2008
100 | pdbonly
101 | x64
102 | false
103 | prompt
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | MSBuild:Compile
136 | Designer
137 |
138 |
139 | MSBuild:Compile
140 | Designer
141 |
142 |
143 | Designer
144 | MSBuild:Compile
145 | PreserveNewest
146 |
147 |
148 |
149 |
150 | Windows Desktop Extensions for the UWP
151 |
152 |
153 | Windows Mobile Extensions for the UWP
154 |
155 |
156 |
157 | 14.0
158 |
159 |
160 |
167 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/GifFileHandler.cs:
--------------------------------------------------------------------------------
1 | using CommonLibrary.Cache;
2 | using System;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net.Http;
6 | using System.Threading.Tasks;
7 | using Windows.Storage;
8 | using System.Runtime.Serialization.Json;
9 |
10 | namespace CommonLibrary
11 | {
12 | public class GifFileHandler
13 | {
14 | public async Task GetCacheOrDownloadAsStorageFileFromUri(Uri source)
15 | {
16 | StorageFile file = null;
17 |
18 | try
19 | {
20 | string filename = string.Concat(source.Segments).Replace("/", "");
21 | //TODO: Improve and compare by URL rather than just relying on filename
22 |
23 | if (source.AbsoluteUri.Contains("http"))
24 | {
25 | //caches the file, never replaces it. Consider adding expirydate
26 | if (await StorageHelper.FileExistsAsync(filename) == false)
27 | {
28 | file = await DownloadFileToStorageAsync(filename, source, file);
29 | }
30 | else
31 | {
32 | file = await StorageHelper.TryGetFileAsync(filename);//.GetIfFileExistsAsync(filename, ApplicationData.Current.LocalFolder);
33 | }
34 | }
35 | else
36 | {
37 | file = await StorageHelper.TryGetFileFromApplicationUriAsync(source);//.GetFileFromApplicationUriAsync(source);
38 | }
39 |
40 | }
41 | catch (Exception)
42 | {
43 | }
44 |
45 | return file;
46 | }
47 |
48 | public async Task DownloadFileToStorageAsync(string filename, Uri source, StorageFile outputFile)
49 | {
50 | using (HttpClient client = new HttpClient())
51 | {
52 | byte[] buffer = await client.GetByteArrayAsync(source); // Download file
53 | filename = filename != "/" ? filename : Guid.NewGuid().ToString();
54 | outputFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
55 |
56 | // TODO: Autodelete guids after eg. 2 weeks
57 | using (Stream stream = await outputFile.OpenStreamForWriteAsync())
58 | {
59 | stream.Write(buffer, 0, buffer.Length);
60 | }
61 | }
62 |
63 | return outputFile;
64 | }
65 |
66 | internal async Task RemoveFileFromStorage(Uri source)
67 | {
68 | try
69 | {
70 | string filename = string.Empty;
71 | filename = source.Segments.Last().Contains("giphy") ? string.Concat(source.Segments).Replace("/", "") : source.Segments.Last();
72 | var file = await StorageHelper.TryGetFileAsync(filename);
73 | if (file != null)
74 | {
75 | await file.DeleteAsync();
76 | }
77 | return !(await StorageHelper.FileExistsAsync(file.Path));
78 |
79 | }
80 | catch (Exception)
81 | {
82 | return false;
83 | }
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/GifPropertiesHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 | using Windows.Foundation;
5 | using Windows.Graphics.Imaging;
6 |
7 | namespace CommonLibrary
8 | {
9 | public class GifPropertiesHelper
10 | {
11 | ///
12 | /// Retrieve frame specific properties. Each frame has an individual delay before the next, as well as top & left from where the first change appears in the bytes.
13 | ///
14 | public async Task RetrieveFramePropertiesAsync(BitmapFrame frame, int index)
15 | {
16 | const string leftProperty = "/imgdesc/Left";
17 | const string topProperty = "/imgdesc/Top";
18 | const string widthProperty = "/imgdesc/Width";
19 | const string heightProperty = "/imgdesc/Height";
20 | const string delayProperty = "/grctlext/Delay";
21 | const string disposalProperty = "/grctlext/Disposal";
22 |
23 | var propertiesView = frame.BitmapProperties;
24 | var requiredProperties = new[] { leftProperty, topProperty, widthProperty, heightProperty };
25 | var properties = await propertiesView.GetPropertiesAsync(requiredProperties);
26 |
27 | var left = (ushort)properties[leftProperty].Value;
28 | var top = (ushort)properties[topProperty].Value;
29 | var width = (ushort)properties[widthProperty].Value;
30 | var height = (ushort)properties[heightProperty].Value;
31 |
32 | var delayMilliseconds = 30.0;
33 | var shouldDispose = false;
34 |
35 | try
36 | {
37 | var extensionProperties = new[] { delayProperty, disposalProperty };
38 | properties = await propertiesView.GetPropertiesAsync(extensionProperties);
39 |
40 | if (properties.ContainsKey(delayProperty) && properties[delayProperty].Type == PropertyType.UInt16)
41 | {
42 | var delayInHundredths = (ushort)properties[delayProperty].Value;
43 | if (delayInHundredths >= 3u) // Prevent degenerate frames with no delay time
44 | {
45 | delayMilliseconds = 10.0 * delayInHundredths;
46 | }
47 | }
48 |
49 | if (properties.ContainsKey(disposalProperty) && properties[disposalProperty].Type == PropertyType.UInt8)
50 | {
51 | var disposal = (byte)properties[disposalProperty].Value;
52 | if (disposal == 2)
53 | {
54 | shouldDispose = true;
55 | }
56 | }
57 | }
58 | catch
59 | {
60 | }
61 |
62 | return new FrameProperties(
63 | new Rect(left, top, width, height),
64 | delayMilliseconds,
65 | shouldDispose,
66 | index);
67 | }
68 |
69 | ///
70 | /// The entire gif image has properties such as width and height thats required and has to be used in calculating bytes. BitmapDecoder comes with width and height, but those are inaccurate compared to these properties.
71 | ///
72 | public async Task RetrieveImagePropertiesAsync(BitmapDecoder bitmapDecoder)
73 | {
74 | // Properties not currently supported: background color, pixel aspect ratio.
75 | const string widthProperty = "/logscrdesc/Width";
76 | const string heightProperty = "/logscrdesc/Height";
77 | const string applicationProperty = "/appext/application";
78 | const string dataProperty = "/appext/data";
79 |
80 | var propertiesView = bitmapDecoder.BitmapContainerProperties;
81 | var requiredProperties = new[] { widthProperty, heightProperty };
82 | var properties = await propertiesView.GetPropertiesAsync(requiredProperties);
83 |
84 | var pixelWidth = (ushort)properties[widthProperty].Value;
85 | var pixelHeight = (ushort)properties[heightProperty].Value;
86 |
87 | var loopCount = 0;
88 | var isAnimated = true;
89 |
90 | try
91 | {
92 | var extensionProperties = new[] { applicationProperty, dataProperty };
93 | properties = await propertiesView.GetPropertiesAsync(extensionProperties);
94 |
95 | if (properties.ContainsKey(applicationProperty) &&
96 | properties[applicationProperty].Type == PropertyType.UInt8Array)
97 | {
98 | var bytes = (byte[])properties[applicationProperty].Value;
99 | var applicationName = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
100 |
101 | if (applicationName == "NETSCAPE2.0" || applicationName == "ANIMEXTS1.0")
102 | {
103 | if (properties.ContainsKey(dataProperty) && properties[dataProperty].Type == PropertyType.UInt8Array)
104 | {
105 | var data = (byte[])properties[dataProperty].Value;
106 | loopCount = data[2] | data[3] << 8;
107 | isAnimated = data[1] == 1;
108 | }
109 | }
110 | }
111 | }
112 | catch
113 | {
114 | }
115 |
116 | return new ImageProperties(pixelWidth, pixelHeight, isAnimated, loopCount);
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/InactiveGifManager.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading.Tasks;
3 |
4 | namespace CommonLibrary
5 | {
6 | public static class InactiveGifManager
7 | {
8 | private static int _inactiveCheckDelayInMiliseconds = 200;
9 | private static List _inactiveRenderers;
10 | private static List _inactiveRenderersCleanup;
11 | private static bool _isInactiveRunning;
12 |
13 | public static void Add(GifRenderer renderer)
14 | {
15 | if (_inactiveRenderers == null)
16 | {
17 | _inactiveRenderers = new List();
18 | _inactiveRenderersCleanup = new List();
19 | }
20 |
21 | _inactiveRenderers.Add(renderer);
22 |
23 | if (!_isInactiveRunning)
24 | {
25 | Start();
26 | }
27 | }
28 |
29 | public static void Remove(GifRenderer renderer)
30 | {
31 | _inactiveRenderers.Remove(renderer);
32 | }
33 |
34 | private static async void Start()
35 | {
36 | _isInactiveRunning = true;
37 |
38 | while (_isInactiveRunning)
39 | {
40 | CheckForInactivesBackOnScreen();
41 | await Task.Delay(_inactiveCheckDelayInMiliseconds);
42 | }
43 | }
44 |
45 | private static void CheckForInactivesBackOnScreen()
46 | {
47 | var copy = _inactiveRenderers.ToArray();
48 | foreach (var item in copy)
49 | {
50 | if (!item.IsOffScreen())
51 | {
52 | item.Restart();
53 | _inactiveRenderersCleanup.Add(item);
54 | }
55 | }
56 |
57 | foreach (var item in _inactiveRenderersCleanup)
58 | {
59 | Remove(item);
60 | }
61 |
62 | _inactiveRenderersCleanup.Clear();
63 |
64 | if (_inactiveRenderers.Count == 0)
65 | {
66 | Stop();
67 | }
68 | }
69 |
70 | private static void Stop()
71 | {
72 | _isInactiveRunning = false;
73 | }
74 |
75 | internal static void Clear()
76 | {
77 | Stop();
78 | if (_inactiveRenderers != null)
79 | _inactiveRenderers.Clear();
80 |
81 | if (_inactiveRenderersCleanup != null)
82 | _inactiveRenderersCleanup.Clear();
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/ScaleSettings.cs:
--------------------------------------------------------------------------------
1 | namespace CommonLibrary
2 | {
3 | public enum ScaleSettings
4 | {
5 | None, Scale, Fill
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/Structs/FrameProperties.cs:
--------------------------------------------------------------------------------
1 | using Windows.Foundation;
2 |
3 | namespace CommonLibrary
4 | {
5 | public struct FrameProperties
6 | {
7 | public readonly Rect Rect;
8 | public readonly double DelayMilliseconds;
9 | public readonly bool ShouldDispose;
10 | public readonly int Index;
11 |
12 | public FrameProperties(Rect rect, double delayMilliseconds, bool shouldDispose, int index)
13 | {
14 | Index = index;
15 | Rect = rect;
16 | DelayMilliseconds = delayMilliseconds;
17 | ShouldDispose = shouldDispose;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/Structs/ImageProperties.cs:
--------------------------------------------------------------------------------
1 | namespace CommonLibrary
2 | {
3 | public struct ImageProperties
4 | {
5 | public readonly int PixelWidth;
6 | public readonly int PixelHeight;
7 | public readonly bool IsAnimated;
8 | public readonly int LoopCount;
9 |
10 | public ImageProperties(int pixelWidth, int pixelHeight, bool isAnimated, int loopCount)
11 | {
12 | PixelWidth = pixelWidth;
13 | PixelHeight = pixelHeight;
14 | IsAnimated = isAnimated;
15 | LoopCount = loopCount;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/GifRenderer/Themes/GifRendererTheme.xaml:
--------------------------------------------------------------------------------
1 |
5 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/ImageControl/ImageCrop/AspectRatio.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CommonLibrary
8 | {
9 | public enum AspectRatio
10 | {
11 | Custom,
12 | Square
13 | //4X3
14 | //
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/ImageControl/ImageCrop/Converter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Windows.UI.Xaml;
7 | using Windows.UI.Xaml.Data;
8 |
9 | namespace CommonLibrary
10 | {
11 | ///
12 | /// Value converter that translates true to and false to
13 | /// .
14 | ///
15 | public sealed class VisibilityConverter : IValueConverter
16 | {
17 | ///
18 | /// If true - converts from Visibility to Boolean.
19 | ///
20 | public bool IsReversed { get; set; }
21 |
22 | ///
23 | /// If true - converts true to Collapsed and false to Visible.
24 | ///
25 | public bool IsInversed { get; set; }
26 |
27 | ///
28 | /// Modifies the source data before passing it to the target for display in the UI.
29 | ///
30 | /// The source data being passed to the target.
31 | /// The type of the target property, specified by a helper structure that wraps the type name.
32 | /// An optional parameter to be used in the converter logic.
33 | /// The language of the conversion.
34 | /// The value to be passed to the target dependency property.
35 | public object Convert(object value, Type targetType, object parameter, string language)
36 | {
37 | if (IsReversed)
38 | {
39 | return (value is Visibility && (Visibility)value == Visibility.Visible) ^ IsInversed;
40 | }
41 |
42 | return (value is bool && (bool)value) ^ IsInversed ? Visibility.Visible : Visibility.Collapsed;
43 | }
44 |
45 | ///
46 | /// Modifies the target data before passing it to the source object. This method is called only in TwoWay bindings.
47 | ///
48 | /// The target data being passed to the source..
49 | /// The type of the target property, specified by a helper structure that wraps the type name.
50 | /// An optional parameter to be used in the converter logic.
51 | /// The language of the conversion.
52 | /// The value to be passed to the source object.
53 | public object ConvertBack(object value, Type targetType, object parameter, string language)
54 | {
55 | if (IsReversed)
56 | {
57 | return (value is bool && (bool)value) ^ IsInversed ? Visibility.Visible : Visibility.Collapsed;
58 | }
59 |
60 | return (value is Visibility && (Visibility)value == Visibility.Visible) ^ IsInversed;
61 | }
62 | }
63 |
64 |
65 | ///
66 | /// Get the middle of two number
67 | /// 4 and 6 is 5
68 | ///
69 | public sealed class CropImageControlLineCoordinateConverter : DependencyObject, IValueConverter
70 | {
71 |
72 |
73 | public double No1
74 | {
75 | get { return (double)GetValue(No1Property); }
76 | set { SetValue(No1Property, value); }
77 | }
78 |
79 | // Using a DependencyProperty as the backing store for No1. This enables animation, styling, binding, etc...
80 | public static readonly DependencyProperty No1Property =
81 | DependencyProperty.Register("No1", typeof(double), typeof(CropImageControlLineCoordinateConverter), new PropertyMetadata(0.0));
82 |
83 |
84 |
85 | public double No2
86 | {
87 | get { return (double)GetValue(No2Property); }
88 | set { SetValue(No2Property, value); }
89 | }
90 |
91 | // Using a DependencyProperty as the backing store for No2. This enables animation, styling, binding, etc...
92 | public static readonly DependencyProperty No2Property =
93 | DependencyProperty.Register("No2", typeof(double), typeof(CropImageControlLineCoordinateConverter), new PropertyMetadata(0.0));
94 |
95 |
96 |
97 |
98 | public object Convert(object value, Type targetType, object parameter, string language)
99 | {
100 | if (value==null || parameter==null)
101 | {
102 | return 0.0;
103 | }
104 | double no1 = (double)value;
105 | double no2 = double.Parse(parameter.ToString());
106 | return (no2+ no1)/2;
107 |
108 | }
109 |
110 | public object ConvertBack(object value, Type targetType, object parameter, string language)
111 | {
112 | throw new NotImplementedException();
113 | }
114 | }
115 |
116 |
117 | public sealed class DateTimeConverter : IValueConverter
118 | {
119 | public object Convert(object value, Type targetType, object parameter, string language)
120 | {
121 | var datetime = (DateTime)value;
122 | var format = (string)parameter;
123 | if (datetime!=null)
124 | {
125 | return datetime.ToString(format);
126 | }
127 | return "";
128 | }
129 |
130 | public object ConvertBack(object value, Type targetType, object parameter, string language)
131 | {
132 | throw new NotImplementedException();
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/ImageControl/ImageCrop/Crop/CropImageSourceChangedEventHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Windows.UI.Xaml.Media;
3 |
4 | namespace CommonLibrary
5 | {
6 |
7 |
8 | public delegate void CropImageSourceChangedEventHandler(object sender, CropImageSourceChangedEventArgs e);
9 |
10 | public class CropImageSourceChangedEventArgs:EventArgs
11 | {
12 | public ImageSource CropImageSource { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/CommonLibrary/Controls/ImageControl/ImageCrop/CropSelection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Windows.Foundation;
8 | using Windows.UI.Xaml;
9 |
10 | namespace CommonLibrary
11 | {
12 | public class CropSelection : INotifyPropertyChanged
13 | {
14 |
15 | #region Property
16 |
17 | ///
18 | /// The minimun size of the seleced region
19 | ///
20 | public double MinSelectRegionSize { get; set; }
21 |
22 | public AspectRatio CropAspectRatio { get; set; }
23 |
24 | private Rect outerRect;
25 |
26 | ///
27 | /// The outer rect. The non-selected region can be represented by the
28 | /// OuterRect and the SelectedRect.
29 | ///
30 | public Rect OuterRect
31 | {
32 | get { return outerRect; }
33 | set
34 | {
35 | if (outerRect != value)
36 | {
37 | outerRect = value;
38 |
39 | this.OnPropertyChanged("OuterRect");
40 | }
41 | }
42 | }
43 |
44 | private Rect selectedRect;
45 |
46 | ///
47 | /// The selected region, which is represented by the four Thumbs.
48 | ///
49 | public Rect SelectedRect
50 | {
51 | get { return selectedRect; }
52 | set
53 | {
54 | if (selectedRect != value)
55 | {
56 | selectedRect = value;
57 | HorizontalLine1 = new Rect(selectedRect.Left, selectedRect.Top + selectedRect.Height / 3, selectedRect.Width, 0.5);
58 | HorizontalLine2 = new Rect(selectedRect.Left, selectedRect.Top + selectedRect.Height / 3 * 2, selectedRect.Width, 0.5);
59 | VerticalLine1 = new Rect(selectedRect.Left + selectedRect.Width / 3, selectedRect.Top, 0.5, selectedRect.Height);
60 | VerticalLine2 = new Rect(selectedRect.Left + selectedRect.Width / 3 * 2, selectedRect.Top, 0.5, selectedRect.Height);
61 |
62 | //HorizontalLine1StartPoint = new Point(selectedRect.Left, selectedRect.Top + selectedRect.Height / 3);
63 | //HorizontalLine1EndPoint = new Point(selectedRect.Left + selectedRect.Width, selectedRect.Top + selectedRect.Height / 3);
64 |
65 | //HorizontalLine2StartPoint = new Point(selectedRect.Left, selectedRect.Top + selectedRect.Height / 3 * 2);
66 | //HorizontalLine2EndPoint = new Point(selectedRect.Left + selectedRect.Width, selectedRect.Top + selectedRect.Height / 3 * 2);
67 |
68 | //VerticalLine1StartPoint = new Point(selectedRect.Left + selectedRect.Width / 3, selectedRect.Top);
69 | //VerticalLine1EndPoint = new Point(selectedRect.Left + selectedRect.Width / 3, selectedRect.Top + selectedRect.Height);
70 |
71 | //VerticalLine2StartPoint = new Point(selectedRect.Left + selectedRect.Width / 3 * 2, selectedRect.Top);
72 | //VerticalLine2EndPoint = new Point(selectedRect.Left + selectedRect.Width / 3 * 2, selectedRect.Top + selectedRect.Height);
73 |
74 | this.OnPropertyChanged("SelectedRect");
75 | //OnPropertyChanged("HorizontalLineCanvasTop");
76 | //OnPropertyChanged("VerticalLineCanvasLeft");
77 | //OnPropertyChanged("HorizontalLine1CanvasTop");
78 | //OnPropertyChanged("VerticalLine1CanvasLeft");
79 | }
80 | }
81 | }
82 |
83 | private Rect horizontalLine1;
84 | public Rect HorizontalLine1
85 | {
86 | get { return horizontalLine1; }
87 | set
88 | {
89 | if (horizontalLine1 != value)
90 | {
91 | horizontalLine1 = value;
92 |
93 | OnPropertyChanged("HorizontalLine1");
94 | }
95 | }
96 | }
97 |
98 | private Rect horizontalLine2;
99 | public Rect HorizontalLine2
100 | {
101 | get { return horizontalLine2; }
102 | set
103 | {
104 | if (horizontalLine2 != value)
105 | {
106 | horizontalLine2 = value;
107 |
108 | OnPropertyChanged("HorizontalLine2");
109 | }
110 | }
111 | }
112 |
113 | private Rect verticalLine1;
114 | public Rect VerticalLine1
115 | {
116 | get { return verticalLine1; }
117 | set
118 | {
119 | if (verticalLine1 != value)
120 | {
121 | verticalLine1 = value;
122 |
123 | OnPropertyChanged("VerticalLine1");
124 | }
125 | }
126 | }
127 |
128 | private Rect verticalLine2;
129 | public Rect VerticalLine2
130 | {
131 | get { return verticalLine2; }
132 | set
133 | {
134 | if (verticalLine2 != value)
135 | {
136 | verticalLine2 = value;
137 |
138 | OnPropertyChanged("VerticalLine2");
139 | }
140 | }
141 | }
142 |
143 | //private Point horizontalLine1StartPoint;
144 | //public Point HorizontalLine1StartPoint
145 | //{
146 | // get { return horizontalLine1StartPoint; }
147 | // set
148 | // {
149 | // if (horizontalLine1StartPoint != value)
150 | // {
151 | // horizontalLine1StartPoint = value;
152 |
153 | // OnPropertyChanged("HorizontalLine1StartPoint");
154 | // }
155 | // }
156 | //}
157 |
158 | //private Point horizontalLine1EndPoint;
159 | //public Point HorizontalLine1EndPoint
160 | //{
161 | // get { return horizontalLine1EndPoint; }
162 | // set
163 | // {
164 | // if (horizontalLine1EndPoint != value)
165 | // {
166 | // horizontalLine1EndPoint = value;
167 |
168 | // OnPropertyChanged("HorizontalLine1EndPoint");
169 | // }
170 | // }
171 | //}
172 |
173 | //private Point horizontalLine2StartPoint;
174 | //public Point HorizontalLine2StartPoint
175 | //{
176 | // get { return horizontalLine2StartPoint; }
177 | // set
178 | // {
179 | // if (horizontalLine2StartPoint != value)
180 | // {
181 | // horizontalLine2StartPoint = value;
182 |
183 | // OnPropertyChanged("HorizontalLine2StartPoint");
184 | // }
185 | // }
186 | //}
187 |
188 | //private Point horizontalLine2EndPoint;
189 | //public Point HorizontalLine2EndPoint
190 | //{
191 | // get { return horizontalLine2EndPoint; }
192 | // set
193 | // {
194 | // if (horizontalLine2EndPoint != value)
195 | // {
196 | // horizontalLine2EndPoint = value;
197 |
198 | // OnPropertyChanged("HorizontalLine2EndPoint");
199 | // }
200 | // }
201 | //}
202 |
203 | //private Point verticalLine1StartPoint;
204 | //public Point VerticalLine1StartPoint
205 | //{
206 | // get { return verticalLine1StartPoint; }
207 | // set
208 | // {
209 | // if (verticalLine1StartPoint != value)
210 | // {
211 | // verticalLine1StartPoint = value;
212 |
213 | // OnPropertyChanged("VerticalLine1StartPoint");
214 | // }
215 | // }
216 | //}
217 |
218 | //private Point verticalLine1EndPoint;
219 | //public Point VerticalLine1EndPoint
220 | //{
221 | // get { return verticalLine1EndPoint; }
222 | // set
223 | // {
224 | // if (verticalLine1EndPoint != value)
225 | // {
226 | // verticalLine1EndPoint = value;
227 |
228 | // OnPropertyChanged("VerticalLine1EndPoint");
229 | // }
230 | // }
231 | //}
232 |
233 | //private Point verticalLine2StartPoint;
234 | //public Point VerticalLine2StartPoint
235 | //{
236 | // get { return verticalLine2StartPoint; }
237 | // set
238 | // {
239 | // if (verticalLine2StartPoint != value)
240 | // {
241 | // verticalLine2StartPoint = value;
242 |
243 | // OnPropertyChanged("VerticalLine2StartPoint");
244 | // }
245 | // }
246 | //}
247 |
248 | //private Point verticalLine2EndPoint;
249 | //public Point VerticalLine2EndPoint
250 | //{
251 | // get { return verticalLine2EndPoint; }
252 | // set
253 | // {
254 | // if (verticalLine2EndPoint != value)
255 | // {
256 | // verticalLine2EndPoint = value;
257 |
258 | // OnPropertyChanged("VerticalLine2EndPoint");
259 | // }
260 | // }
261 | //}
262 |
263 | public double HorizontalLineCanvasTop
264 | {
265 | get
266 | {
267 | return (SelectedRect.Bottom - SelectedRect.Top) / 3 * 1 + SelectedRect.Top;
268 | }
269 |
270 | }
271 | public double HorizontalLine1CanvasTop
272 | {
273 | get
274 | {
275 | return (SelectedRect.Bottom - SelectedRect.Top) / 3 * 2 + SelectedRect.Top;
276 | }
277 |
278 | }
279 |
280 | public double VerticalLineCanvasLeft
281 | {
282 | get
283 | {
284 | return (SelectedRect.Right - SelectedRect.Left) / 3 * 1 + SelectedRect.Left;
285 | }
286 | }
287 |
288 | public double VerticalLine1CanvasLeft
289 | {
290 | get
291 | {
292 | return (SelectedRect.Right - SelectedRect.Left) / 3 * 2 + SelectedRect.Left;
293 | }
294 | }
295 |
296 |
297 | private Visibility _cropSelectionVisibility=Visibility.Collapsed;
298 |
299 | public Visibility CropSelectionVisibility
300 | {
301 | get { return _cropSelectionVisibility; }
302 | set
303 | {
304 | _cropSelectionVisibility = value;
305 | this.OnPropertyChanged("CropSelectionVisibility");
306 | }
307 | }
308 |
309 |
310 | #endregion
311 |
312 | #region INotifyPropertyChanged
313 | public event PropertyChangedEventHandler PropertyChanged;
314 |
315 | void OnPropertyChanged(string propertyName)
316 | {
317 | if (PropertyChanged != null)
318 | {
319 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
320 | }
321 | }
322 |
323 |
324 | #endregion
325 |
326 | #region Method
327 | internal void UpdateSelectedRect(float scale, double x, double y)
328 | {
329 | var rect = new Rect() { X = SelectedRect.X + x, Y = SelectedRect.Y + y, Width = SelectedRect.Width * scale, Height = SelectedRect.Height * scale };
330 | var leftTop = new Point(rect.Left, rect.Top);
331 | var leftBottom = new Point(rect.Left, rect.Bottom);
332 | var rightTop = new Point(rect.Right, rect.Top);
333 | var rightBottom = new Point(rect.Right, rect.Bottom);
334 |
335 | if (OuterRect.Contains(leftTop)
336 | && OuterRect.Contains(leftBottom)
337 | && OuterRect.Contains(rightTop)
338 | && OuterRect.Contains(rightBottom)
339 | && rect.Width >= 2 * MinSelectRegionSize
340 | && rect.Height >= 2 * MinSelectRegionSize)
341 | {
342 | SelectedRect = rect;
343 | }
344 |
345 | }
346 |
347 | internal void UpdateSelectedRect(string ThumbName, double xUpdate, double yUpdate,Rect? outerRect=null)
348 | {
349 |
350 |
351 | var left = SelectedRect.Left;
352 | var top = SelectedRect.Top;
353 |
354 | var right = SelectedRect.Right;
355 | var bottom = SelectedRect.Bottom;
356 |
357 |
358 | if (ThumbName == "topLeftThumb")
359 | {
360 | left += xUpdate;
361 | top += yUpdate;
362 | }
363 | else if (ThumbName == "topRightThumb")
364 | {
365 | right += xUpdate;
366 | top += yUpdate;
367 | }
368 | else if (ThumbName == "bottomLeftThumb")
369 | {
370 | left += xUpdate;
371 | bottom += yUpdate;
372 | }
373 | else if (ThumbName == "bottomRightThumb")
374 | {
375 | right += xUpdate;
376 | bottom += yUpdate;
377 | }
378 |
379 | var rect = new Rect(new Point(left, top), new Point(right, bottom));
380 | var leftTop = new Point(rect.Left, rect.Top);
381 | var leftBottom = new Point(rect.Left, rect.Bottom);
382 | var rightTop = new Point(rect.Right, rect.Top);
383 | var rightBottom = new Point(rect.Right, rect.Bottom);
384 |
385 | var outerRect1 = outerRect!=null ? outerRect.Value: OuterRect;
386 |
387 | if (outerRect1.Contains(leftTop)
388 | && outerRect1.Contains(leftBottom)
389 | && outerRect1.Contains(rightTop)
390 | && outerRect1.Contains(rightBottom)
391 | && rect.Width >= 2 * MinSelectRegionSize
392 | && rect.Height >= 2 * MinSelectRegionSize)
393 | {
394 | SelectedRect = rect;
395 | }
396 | }
397 |
398 |
399 | internal void ResizeSelectedRect(double scale)
400 | {
401 | var rect = new Rect() { X = SelectedRect.X * scale, Y = SelectedRect.Y * scale, Width = SelectedRect.Width * scale, Height = SelectedRect.Height * scale };
402 | var leftTop = new Point(rect.Left, rect.Top);
403 | var leftBottom = new Point(rect.Left, rect.Bottom);
404 | var rightTop = new Point(rect.Right, rect.Top);
405 | var rightBottom = new Point(rect.Right, rect.Bottom);
406 |
407 | if (OuterRect.Contains(leftTop)
408 | && OuterRect.Contains(leftBottom)
409 | && OuterRect.Contains(rightTop)
410 | && OuterRect.Contains(rightBottom)
411 | && rect.Width >= 2 * MinSelectRegionSize
412 | && rect.Height >= 2 * MinSelectRegionSize)
413 | {
414 | SelectedRect = rect;
415 | }
416 | }
417 | #endregion
418 | }
419 | }
420 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/ImageControl/ImageCrop/CropSelectionSize.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CommonLibrary
8 | {
9 | public enum CropSelectionSize
10 | {
11 | Full=1,
12 | Half=2,
13 | OneFourth=4
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CommonLibrary/Controls/ImageControl/ImageCrop/FrameworkElementExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Windows.Foundation;
7 | using Windows.UI.Xaml;
8 | using Windows.UI.Xaml.Controls;
9 | using Windows.UI.Xaml.Media;
10 |
11 | namespace CommonLibrary.Util
12 | {
13 | public static class FrameworkElementExtensions
14 | {
15 | public static FrameworkElement FindDescendantByName(this FrameworkElement element, string name)
16 | {
17 | if (element == null || string.IsNullOrWhiteSpace(name)) { return null; }
18 |
19 | if (name.Equals(element.Name, StringComparison.OrdinalIgnoreCase))
20 | {
21 | return element;
22 | }
23 | var childCount = VisualTreeHelper.GetChildrenCount(element);
24 | for (int i = 0; i < childCount; i++)
25 | {
26 | var result = (VisualTreeHelper.GetChild(element, i) as FrameworkElement).FindDescendantByName(name);
27 | if (result != null) { return result; }
28 | }
29 | return null;
30 | }
31 |
32 |
33 | public static IEnumerable