├── .gitattributes
├── .gitignore
├── README.md
├── dll
├── WpfRichText.dll
├── Xceed.Wpf.AvalonDock.Themes.Aero.dll
├── Xceed.Wpf.AvalonDock.Themes.Metro.dll
├── Xceed.Wpf.AvalonDock.Themes.VS2010.dll
├── Xceed.Wpf.AvalonDock.dll
├── Xceed.Wpf.DataGrid.dll
├── Xceed.Wpf.Toolkit.dll
├── de
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── es
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── fr
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── hu
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── it
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── pt-BR
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── ro
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── ru
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── sv
│ └── Xceed.Wpf.AvalonDock.resources.dll
└── zh-Hans
│ └── Xceed.Wpf.AvalonDock.resources.dll
├── logo.png
├── nuget
├── WpfRichText.1.0.10.nupkg
├── WpfRichText.1.0.11.nupkg
├── WpfRichText.1.0.12.nupkg
├── WpfRichText.1.0.13.nupkg
├── WpfRichText.1.0.14.nupkg
├── WpfRichText.1.0.15.nupkg
├── WpfRichText.1.0.16.2.nupkg
├── WpfRichText.1.0.3.nupkg
├── WpfRichText.1.0.4.nupkg
├── WpfRichText.1.0.5.nupkg
├── WpfRichText.1.0.6.nupkg
├── WpfRichText.1.0.7.nupkg
├── WpfRichText.1.0.8.nupkg
└── WpfRichText.1.0.9.nupkg
└── src
├── WpfRichText.Ex
├── App.xaml
├── App.xaml.cs
├── HTMLPage1.html
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Window1.xaml
├── Window1.xaml.cs
├── WpfRichText.Example.csproj
├── app.config
└── packages.config
├── WpfRichText.sln
└── WpfRichText
├── AttachedProperties
└── RichTextboxAssistant.cs
├── Commands
├── CommandReference.cs
└── DelegateCommand.cs
├── Controls
├── Images
│ ├── charactergrowfont.png
│ ├── charactershrinkfont.png
│ ├── cross.png
│ ├── cut.png
│ ├── editredo.png
│ ├── editundo.png
│ ├── page_copy.png
│ ├── page_paste.png
│ ├── text_align_center.png
│ ├── text_align_justify.png
│ ├── text_align_left.png
│ ├── text_align_right.png
│ ├── text_bold.png
│ ├── text_indent.png
│ ├── text_indent_remove.png
│ ├── text_italic.png
│ ├── text_list_bullets.png
│ ├── text_list_numbers.png
│ ├── text_underline.png
│ ├── tick.png
│ └── world_link.png
├── RichTextEditor.xaml
└── RichTextEditor.xaml.cs
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
├── WpfRichText.csproj
├── XamlToHtmlParser
├── HtmlCssParser.cs
├── HtmlFromXamlConverter.cs
├── HtmlLexicalAnalyzer.cs
├── HtmlParser.cs
├── HtmlSchema.cs
├── HtmlToXamlConverter.cs
└── HtmlTokenType.cs
├── packages.config
└── self.snk
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set default behaviour to automatically normalize line endings.
2 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Bb]in/
3 | [Oo]bj/
4 |
5 | # mstest test results
6 | TestResults
7 |
8 | ## Ignore Visual Studio temporary files, build results, and
9 | ## files generated by popular Visual Studio add-ons.
10 |
11 | # User-specific files
12 | *.suo
13 | *.user
14 | *.sln.docstates
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Rr]elease/
19 | x64/
20 | *_i.c
21 | *_p.c
22 | *.ilk
23 | *.meta
24 | *.obj
25 | *.pch
26 | *.pdb
27 | *.pgc
28 | *.pgd
29 | *.rsp
30 | *.sbr
31 | *.tlb
32 | *.tli
33 | *.tlh
34 | *.tmp
35 | *.log
36 | *.vspscc
37 | *.vssscc
38 | .builds
39 |
40 | # Visual C++ cache files
41 | ipch/
42 | *.aps
43 | *.ncb
44 | *.opensdf
45 | *.sdf
46 |
47 | # Visual Studio profiler
48 | *.psess
49 | *.vsp
50 | *.vspx
51 |
52 | # Guidance Automation Toolkit
53 | *.gpState
54 |
55 | # ReSharper is a .NET coding add-in
56 | _ReSharper*
57 |
58 | # NCrunch
59 | *.ncrunch*
60 | .*crunch*.local.xml
61 |
62 | # Installshield output folder
63 | [Ee]xpress
64 |
65 | # DocProject is a documentation generator add-in
66 | DocProject/buildhelp/
67 | DocProject/Help/*.HxT
68 | DocProject/Help/*.HxC
69 | DocProject/Help/*.hhc
70 | DocProject/Help/*.hhk
71 | DocProject/Help/*.hhp
72 | DocProject/Help/Html2
73 | DocProject/Help/html
74 |
75 | # Click-Once directory
76 | publish
77 |
78 | # Publish Web Output
79 | *.Publish.xml
80 |
81 | # NuGet Packages Directory
82 | packages
83 |
84 | # Windows Azure Build Output
85 | csx
86 | *.build.csdef
87 |
88 | # Windows Store app package directory
89 | AppPackages/
90 |
91 | # Others
92 | [Bb]in
93 | [Oo]bj
94 | sql
95 | TestResults
96 | [Tt]est[Rr]esult*
97 | *.Cache
98 | ClientBin
99 | [Ss]tyle[Cc]op.*
100 | ~$*
101 | *.dbmdl
102 | Generated_Code #added for RIA/Silverlight projects
103 |
104 | # Backup & report files from converting an old project file to a newer
105 | # Visual Studio version. Backup files are not needed, because we have git ;-)
106 | _UpgradeReport_Files/
107 | Backup*/
108 | UpgradeLog*.XML
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | WPF-RichText-Editor
2 | ===================
3 |
4 | WPF RichText Editor Control with conversion from and to HTML (XAML)
5 |
6 | Code pulled from http://michaelsync.net/2009/06/09/bindable-wpf-richtext-editor-with-xamlhtml-convertor.
7 | Thanks to Michael Sync for this.
8 |
9 | Moved the code to a UserControlLibrary project and added a nuget package (https://nuget.org/packages/WpfRichText/)
10 |
--------------------------------------------------------------------------------
/dll/WpfRichText.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/WpfRichText.dll
--------------------------------------------------------------------------------
/dll/Xceed.Wpf.AvalonDock.Themes.Aero.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.Themes.Aero.dll
--------------------------------------------------------------------------------
/dll/Xceed.Wpf.AvalonDock.Themes.Metro.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.Themes.Metro.dll
--------------------------------------------------------------------------------
/dll/Xceed.Wpf.AvalonDock.Themes.VS2010.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.Themes.VS2010.dll
--------------------------------------------------------------------------------
/dll/Xceed.Wpf.AvalonDock.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.AvalonDock.dll
--------------------------------------------------------------------------------
/dll/Xceed.Wpf.DataGrid.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.DataGrid.dll
--------------------------------------------------------------------------------
/dll/Xceed.Wpf.Toolkit.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/Xceed.Wpf.Toolkit.dll
--------------------------------------------------------------------------------
/dll/de/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/de/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/es/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/es/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/fr/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/fr/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/hu/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/hu/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/it/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/it/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/pt-BR/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/pt-BR/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/ro/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/ro/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/ru/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/ru/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/sv/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/sv/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/dll/zh-Hans/Xceed.Wpf.AvalonDock.resources.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/dll/zh-Hans/Xceed.Wpf.AvalonDock.resources.dll
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/logo.png
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.10.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.10.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.11.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.11.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.12.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.12.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.13.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.13.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.14.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.14.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.15.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.15.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.16.2.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.16.2.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.3.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.3.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.4.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.4.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.5.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.5.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.6.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.6.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.7.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.7.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.8.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.8.nupkg
--------------------------------------------------------------------------------
/nuget/WpfRichText.1.0.9.nupkg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/nuget/WpfRichText.1.0.9.nupkg
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Windows;
7 |
8 | namespace WpfRichText.Ex
9 | {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application
14 | {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/HTMLPage1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | New Bug Template
5 |
6 |
7 | header 1
8 |
9 | RED TEXT
10 |
11 | 1. Text after break
12 |
13 | header 2
14 |
15 | GREEN TEXT
16 |
17 | a hyperlink
18 |
19 |
20 | TEXT IN DIV
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("WpfRichText.Ex")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("WpfRichText.Ex")]
15 | [assembly: AssemblyCopyright("Copyright © 2009")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.18034
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WpfRichText.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfRichText.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
65 | ///<html>
66 | /// <head>
67 | /// <title>New Bug Template</title>
68 | /// </head>
69 | /// <body>
70 | /// <p>
71 | /// <span style="color:Red; font-weight:bold;">TITEL:</span>
72 | /// <br />
73 | /// 1. Fehlerbereich angeben (Afterbuy,Shop,MSG)
74 | /// <br />
75 | /// 2. Genaue Beschreibung des Bugs
76 | /// </p>
77 | /// <p>
78 | /// <span style="color:Red; font-weight:bold;">PROBLEMBESCHREIBUNG:</span>
79 | /// <br />
80 | /// 1. Genaue Angabe um das Problem zu sehen
81 | /// <br />
82 | /// 2. Genaue Beschreibung wie es zu dem Bug g [rest of string was truncated]";.
83 | ///
84 | internal static string HTMLPage1 {
85 | get {
86 | return ResourceManager.GetString("HTMLPage1", resourceCulture);
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\htmlpage1.html;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.18034
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WpfRichText.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Window1.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/Window1.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Windows.Data;
8 | using System.Windows.Documents;
9 | using System.Windows.Input;
10 | using System.Windows.Media;
11 | using System.Windows.Media.Imaging;
12 | using System.Windows.Navigation;
13 | using System.Windows.Shapes;
14 | using System.ComponentModel;
15 | using System.Linq.Expressions;
16 |
17 | namespace WpfRichText
18 | {
19 | ///
20 | /// Interaction logic for Window1.xaml
21 | ///
22 | public partial class Window1 : Window
23 | {
24 | public Window1()
25 | {
26 | InitializeComponent();
27 | this.DataContext = new PageViewModel();
28 | }
29 |
30 | private void hideToolbar_Checked(object sender, RoutedEventArgs e)
31 | {
32 | this.sampleEditor.IsToolBarVisible = false;
33 | }
34 |
35 | private void disableContextMenu_Checked(object sender, RoutedEventArgs e)
36 | {
37 | this.sampleEditor.IsContextMenuEnabled = false;
38 | }
39 |
40 | private void setReadOnly_Checked(object sender, RoutedEventArgs e)
41 | {
42 | this.sampleEditor.IsReadOnly = true;
43 | }
44 |
45 | private void hideToolbar_Unchecked(object sender, RoutedEventArgs e)
46 | {
47 | this.sampleEditor.IsToolBarVisible = true;
48 | }
49 |
50 | private void disableContextMenu_Unchecked(object sender, RoutedEventArgs e)
51 | {
52 | this.sampleEditor.IsContextMenuEnabled = true;
53 | }
54 |
55 | private void setReadOnly_Unchecked(object sender, RoutedEventArgs e)
56 | {
57 | this.sampleEditor.IsReadOnly = false;
58 | }
59 | }
60 |
61 | #region PageViewModel
62 | public class PageViewModel : ObservableBase
63 | {
64 | public DelegateCommand GetXamlCommand { get; private set; }
65 | public DelegateCommand LoadHtmlCommand { get; private set; }
66 |
67 | #region Constructor
68 | public PageViewModel()
69 | {
70 |
71 | GetXamlCommand = new DelegateCommand(() =>
72 | {
73 | MessageBox.Show(this.Text);
74 | });
75 |
76 | LoadHtmlCommand = new DelegateCommand(() =>
77 | {
78 | this.Text = Properties.Resources.HTMLPage1;
79 | });
80 | }
81 | #endregion
82 |
83 | #region Name
84 | private string text = string.Empty;
85 | public string Text
86 | {
87 | get
88 | {
89 | return text;
90 | }
91 | set
92 | {
93 | text = value;
94 | this.RaisePropertyChanged(p => p.Text);
95 | }
96 | }
97 | #endregion
98 | }
99 | #endregion
100 |
101 | #region Observable
102 | public abstract class ObservableBase : INotifyPropertyChanged
103 | {
104 | public event PropertyChangedEventHandler PropertyChanged;
105 |
106 | public void RaisePropertyChanged(string propertyName)
107 | {
108 | if (this.PropertyChanged != null)
109 | {
110 | this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
111 | }
112 | }
113 | }
114 |
115 | public static class ObservableBaseEx
116 | {
117 | public static void RaisePropertyChanged(this T observableBase, Expression> expression) where T : ObservableBase
118 | {
119 | observableBase.RaisePropertyChanged(observableBase.GetPropertyName(expression));
120 | }
121 |
122 | public static string GetPropertyName(this T owner, Expression> expression)
123 | {
124 | var memberExpression = expression.Body as MemberExpression;
125 | if (memberExpression == null)
126 | {
127 | var unaryExpression = expression.Body as UnaryExpression;
128 | if (unaryExpression != null)
129 | {
130 | memberExpression = unaryExpression.Operand as MemberExpression;
131 |
132 | if (memberExpression == null)
133 | throw new NotImplementedException();
134 | }
135 | else
136 | throw new NotImplementedException();
137 | }
138 |
139 | var propertyName = memberExpression.Member.Name;
140 | return propertyName;
141 | }
142 | }
143 | #endregion
144 | }
145 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/WpfRichText.Example.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x86
6 | 10.0.20506
7 | 2.0
8 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}
9 | WinExe
10 | Properties
11 | WpfRichText
12 | WpfRichText.Example
13 | v4.5
14 | 512
15 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | 4
17 | x86
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | true
30 | full
31 | false
32 | bin\Debug\
33 | DEBUG;TRACE
34 | prompt
35 | 4
36 | false
37 |
38 |
39 | pdbonly
40 | true
41 | bin\Release\
42 | TRACE
43 | prompt
44 | 4
45 | false
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | 4.0
55 |
56 |
57 | 3.5
58 |
59 |
60 | 3.5
61 |
62 |
63 | 3.5
64 |
65 |
66 | 3.0
67 |
68 |
69 | 3.0
70 |
71 |
72 | 3.0
73 |
74 |
75 | False
76 | ..\packages\Extended.Wpf.Toolkit.2.0.0\lib\net40\Xceed.Wpf.Toolkit.dll
77 |
78 |
79 |
80 |
81 | MSBuild:Compile
82 | Designer
83 |
84 |
85 | MSBuild:Compile
86 | Designer
87 |
88 |
89 | App.xaml
90 | Code
91 |
92 |
93 | Window1.xaml
94 | Code
95 |
96 |
97 |
98 |
99 | Code
100 |
101 |
102 | True
103 | True
104 | Resources.resx
105 |
106 |
107 | True
108 | Settings.settings
109 | True
110 |
111 |
112 | ResXFileCodeGenerator
113 | Resources.Designer.cs
114 |
115 |
116 |
117 |
118 | SettingsSingleFileGenerator
119 | Settings.Designer.cs
120 |
121 |
122 |
123 |
124 |
125 | {78147c43-db7b-4571-bc7a-e7f0075b4b78}
126 | WpfRichText
127 |
128 |
129 |
130 |
131 |
132 |
133 |
140 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/WpfRichText.Ex/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/WpfRichText.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfRichText.Example", "WpfRichText.Ex\WpfRichText.Example.csproj", "{5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfRichText", "WpfRichText\WpfRichText.csproj", "{78147C43-DB7B-4571-BC7A-E7F0075B4B78}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|Mixed Platforms = Debug|Mixed Platforms
12 | Debug|x86 = Debug|x86
13 | Release|Any CPU = Release|Any CPU
14 | Release|Mixed Platforms = Release|Mixed Platforms
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Debug|Any CPU.ActiveCfg = Debug|x86
19 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
20 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Debug|Mixed Platforms.Build.0 = Debug|x86
21 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Debug|x86.ActiveCfg = Debug|x86
22 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Debug|x86.Build.0 = Debug|x86
23 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Release|Any CPU.ActiveCfg = Release|x86
24 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Release|Mixed Platforms.ActiveCfg = Release|x86
25 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Release|Mixed Platforms.Build.0 = Release|x86
26 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Release|x86.ActiveCfg = Release|x86
27 | {5922EBAC-5CC0-4079-97D7-FCEC9C01AA43}.Release|x86.Build.0 = Release|x86
28 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
31 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
32 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Debug|x86.ActiveCfg = Debug|Any CPU
33 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
36 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Release|Mixed Platforms.Build.0 = Release|Any CPU
37 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}.Release|x86.ActiveCfg = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | EndGlobal
43 |
--------------------------------------------------------------------------------
/src/WpfRichText/AttachedProperties/RichTextboxAssistant.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Windows;
5 | using System.Windows.Documents;
6 | using System.Windows.Controls;
7 | using System.IO;
8 | using System.Windows.Input;
9 | using System.Windows.Markup;
10 | using System.Windows.Data;
11 |
12 | namespace WpfRichText
13 | {
14 | ///
15 | public static class RichTextBoxAssistant
16 | {
17 | ///
18 | public static readonly DependencyProperty BoundDocument =
19 | DependencyProperty.RegisterAttached("BoundDocument", typeof(string), typeof(RichTextBoxAssistant),
20 | new FrameworkPropertyMetadata(null,
21 | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
22 | OnBoundDocumentChanged)
23 | );
24 |
25 | private static void OnBoundDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
26 | {
27 | RichTextBox box = d as RichTextBox;
28 |
29 | if (box == null)
30 | return;
31 |
32 | RemoveEventHandler(box);
33 |
34 | string newXAML = GetBoundDocument(d);
35 |
36 | box.Document.Blocks.Clear();
37 |
38 | if (!string.IsNullOrEmpty(newXAML))
39 | {
40 | using (MemoryStream xamlMemoryStream = new MemoryStream(Encoding.UTF8.GetBytes(newXAML)))
41 | {
42 | ParserContext parser = new ParserContext();
43 | parser.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
44 | parser.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
45 | //FlowDocument doc = new FlowDocument();
46 | Section section = XamlReader.Load(xamlMemoryStream, parser) as Section;
47 |
48 | box.Document.Blocks.Add(section);
49 |
50 | }
51 | }
52 |
53 | AttachEventHandler(box);
54 |
55 | }
56 |
57 | private static void RemoveEventHandler(RichTextBox box)
58 | {
59 | Binding binding = BindingOperations.GetBinding(box, BoundDocument);
60 |
61 | if (binding != null)
62 | {
63 | if (binding.UpdateSourceTrigger == UpdateSourceTrigger.Default ||
64 | binding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus)
65 | {
66 |
67 | box.LostFocus -= HandleLostFocus;
68 | }
69 | else
70 | {
71 | box.TextChanged -= HandleTextChanged;
72 | }
73 | }
74 | }
75 |
76 | private static void AttachEventHandler(RichTextBox box)
77 | {
78 | Binding binding = BindingOperations.GetBinding(box, BoundDocument);
79 |
80 | if (binding != null)
81 | {
82 | if (binding.UpdateSourceTrigger == UpdateSourceTrigger.Default ||
83 | binding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus)
84 | {
85 |
86 | box.LostFocus += HandleLostFocus;
87 | }
88 | else
89 | {
90 | box.TextChanged += HandleTextChanged;
91 | }
92 | }
93 | }
94 |
95 | private static void HandleLostFocus(object sender, RoutedEventArgs e)
96 | {
97 | RichTextBox box = sender as RichTextBox;
98 |
99 | TextRange tr = new TextRange(box.Document.ContentStart, box.Document.ContentEnd);
100 |
101 | using (MemoryStream ms = new MemoryStream())
102 | {
103 | tr.Save(ms, DataFormats.Xaml);
104 | string xamlText = Encoding.UTF8.GetString(ms.ToArray());
105 | SetBoundDocument(box, xamlText);
106 | }
107 | }
108 |
109 | private static void HandleTextChanged(object sender, RoutedEventArgs e)
110 | {
111 | // TODO: TextChanged is currently not working!
112 | RichTextBox box = sender as RichTextBox;
113 |
114 | TextRange tr = new TextRange(box.Document.ContentStart,
115 | box.Document.ContentEnd);
116 |
117 | using (MemoryStream ms = new MemoryStream())
118 | {
119 | tr.Save(ms, DataFormats.Xaml);
120 | string xamlText = Encoding.UTF8.GetString(ms.ToArray());
121 | SetBoundDocument(box, xamlText);
122 | }
123 | }
124 |
125 | ///
126 | ///
127 | ///
128 | ///
129 | ///
130 | public static string GetBoundDocument(DependencyObject dependencyObject)
131 | {
132 | if (dependencyObject != null)
133 | {
134 | var html = dependencyObject.GetValue(BoundDocument) as string;
135 | var xaml = string.Empty;
136 |
137 | if (!string.IsNullOrEmpty(html))
138 | xaml = HtmlToXamlConverter.ConvertHtmlToXaml(html, false);
139 |
140 | return xaml;
141 | }
142 | return string.Empty;
143 | }
144 | ///
145 | ///
146 | ///
147 | ///
148 | ///
149 | public static void SetBoundDocument(DependencyObject dependencyObject, string value)
150 | {
151 | if (dependencyObject != null)
152 | {
153 | var xaml = value;
154 | var html = HtmlFromXamlConverter.ConvertXamlToHtml(xaml, false);
155 | dependencyObject.SetValue(BoundDocument, html);
156 | }
157 | }
158 | }
159 |
160 |
161 | }
--------------------------------------------------------------------------------
/src/WpfRichText/Commands/CommandReference.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Input;
4 |
5 | namespace WpfRichText
6 | {
7 | ///
8 | /// This class facilitates associating a key binding in XAML markup to a command
9 | /// defined in a View Model by exposing a Command dependency property.
10 | /// The class derives from Freezable to work around a limitation in WPF when data-binding from XAML.
11 | ///
12 | public class CommandReference : Freezable, ICommand
13 | {
14 | ///
15 | public CommandReference()
16 | {
17 | // Blank
18 | }
19 |
20 | ///
21 | public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
22 |
23 | ///
24 | public ICommand Command
25 | {
26 | get { return (ICommand)GetValue(CommandProperty); }
27 | set { SetValue(CommandProperty, value); }
28 | }
29 |
30 | #region ICommand Members
31 | ///
32 | ///
33 | ///
34 | ///
35 | ///
36 | public bool CanExecute(object parameter)
37 | {
38 | if (Command != null)
39 | return Command.CanExecute(parameter);
40 | return false;
41 | }
42 | ///
43 | ///
44 | ///
45 | ///
46 | public void Execute(object parameter)
47 | {
48 | Command.Execute(parameter);
49 | }
50 | ///
51 | ///
52 | ///
53 | public event EventHandler CanExecuteChanged;
54 |
55 | private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
56 | {
57 | CommandReference commandReference = d as CommandReference;
58 | ICommand oldCommand = e.OldValue as ICommand;
59 | ICommand newCommand = e.NewValue as ICommand;
60 |
61 | if (oldCommand != null)
62 | {
63 | oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
64 | }
65 | if (newCommand != null)
66 | {
67 | newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
68 | }
69 | }
70 |
71 | #endregion
72 |
73 | #region Freezable
74 | ///
75 | ///
76 | ///
77 | ///
78 | protected override Freezable CreateInstanceCore()
79 | {
80 | throw new NotImplementedException();
81 | }
82 |
83 | #endregion
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/WpfRichText/Commands/DelegateCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows;
4 | using System.Windows.Input;
5 |
6 | namespace WpfRichText
7 | {
8 | ///
9 | /// This class allows delegating the commanding logic to methods passed as parameters,
10 | /// and enables a View to bind commands to objects that are not part of the element tree.
11 | ///
12 | public class DelegateCommand : ICommand
13 | {
14 | #region Constructors
15 |
16 | ///
17 | /// Constructor
18 | ///
19 | public DelegateCommand(Action executeMethod)
20 | : this(executeMethod, null, false)
21 | {
22 | }
23 |
24 | ///
25 | /// Constructor
26 | ///
27 | public DelegateCommand(Action executeMethod, Func canExecuteMethod)
28 | : this(executeMethod, canExecuteMethod, false)
29 | {
30 | }
31 |
32 | ///
33 | /// Constructor
34 | ///
35 | public DelegateCommand(Action executeMethod, Func canExecuteMethod, bool isAutomaticRequeryDisabled)
36 | {
37 | if (executeMethod == null)
38 | {
39 | throw new ArgumentNullException("executeMethod");
40 | }
41 |
42 | _executeMethod = executeMethod;
43 | _canExecuteMethod = canExecuteMethod;
44 | _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
45 | }
46 |
47 | #endregion
48 |
49 | #region Public Methods
50 |
51 | ///
52 | /// Method to determine if the command can be executed
53 | ///
54 | public bool CanExecute()
55 | {
56 | if (_canExecuteMethod != null)
57 | {
58 | return _canExecuteMethod();
59 | }
60 | return true;
61 | }
62 |
63 | ///
64 | /// Execution of the command
65 | ///
66 | public void Execute()
67 | {
68 | if (_executeMethod != null)
69 | {
70 | _executeMethod();
71 | }
72 | }
73 |
74 | ///
75 | /// Property to enable or disable CommandManager's automatic requery on this command
76 | ///
77 | public bool IsAutomaticRequeryDisabled
78 | {
79 | get
80 | {
81 | return _isAutomaticRequeryDisabled;
82 | }
83 | set
84 | {
85 | if (_isAutomaticRequeryDisabled != value)
86 | {
87 | if (value)
88 | {
89 | CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
90 | }
91 | else
92 | {
93 | CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
94 | }
95 | _isAutomaticRequeryDisabled = value;
96 | }
97 | }
98 | }
99 |
100 | ///
101 | /// Raises the CanExecuteChaged event
102 | ///
103 | public void RaiseCanExecuteChanged()
104 | {
105 | OnCanExecuteChanged();
106 | }
107 |
108 | ///
109 | /// Protected virtual method to raise CanExecuteChanged event
110 | ///
111 | protected virtual void OnCanExecuteChanged()
112 | {
113 | CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
114 | }
115 |
116 | #endregion
117 |
118 | #region ICommand Members
119 |
120 | ///
121 | /// ICommand.CanExecuteChanged implementation
122 | ///
123 | public event EventHandler CanExecuteChanged
124 | {
125 | add
126 | {
127 | if (!_isAutomaticRequeryDisabled)
128 | {
129 | CommandManager.RequerySuggested += value;
130 | }
131 | CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
132 | }
133 | remove
134 | {
135 | if (!_isAutomaticRequeryDisabled)
136 | {
137 | CommandManager.RequerySuggested -= value;
138 | }
139 | CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
140 | }
141 | }
142 |
143 | bool ICommand.CanExecute(object parameter)
144 | {
145 | return CanExecute();
146 | }
147 |
148 | void ICommand.Execute(object parameter)
149 | {
150 | Execute();
151 | }
152 |
153 | #endregion
154 |
155 | #region Data
156 |
157 | private readonly Action _executeMethod = null;
158 | private readonly Func _canExecuteMethod = null;
159 | private bool _isAutomaticRequeryDisabled = false;
160 | private List _canExecuteChangedHandlers;
161 |
162 | #endregion
163 | }
164 |
165 | ///
166 | /// This class allows delegating the commanding logic to methods passed as parameters,
167 | /// and enables a View to bind commands to objects that are not part of the element tree.
168 | ///
169 | /// Type of the parameter passed to the delegates
170 | public class DelegateCommand : ICommand
171 | {
172 | #region Constructors
173 |
174 | ///
175 | /// Constructor
176 | ///
177 | public DelegateCommand(Action executeMethod)
178 | : this(executeMethod, null, false)
179 | {
180 | }
181 |
182 | ///
183 | /// Constructor
184 | ///
185 | public DelegateCommand(Action executeMethod, Func canExecuteMethod)
186 | : this(executeMethod, canExecuteMethod, false)
187 | {
188 | }
189 |
190 | ///
191 | /// Constructor
192 | ///
193 | public DelegateCommand(Action executeMethod, Func canExecuteMethod, bool isAutomaticRequeryDisabled)
194 | {
195 | if (executeMethod == null)
196 | {
197 | throw new ArgumentNullException("executeMethod");
198 | }
199 |
200 | _executeMethod = executeMethod;
201 | _canExecuteMethod = canExecuteMethod;
202 | _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
203 | }
204 |
205 | #endregion
206 |
207 | #region Public Methods
208 |
209 | ///
210 | /// Method to determine if the command can be executed
211 | ///
212 | public bool CanExecute(T parameter)
213 | {
214 | if (_canExecuteMethod != null)
215 | {
216 | return _canExecuteMethod(parameter);
217 | }
218 | return true;
219 | }
220 |
221 | ///
222 | /// Execution of the command
223 | ///
224 | public void Execute(T parameter)
225 | {
226 | if (_executeMethod != null)
227 | {
228 | _executeMethod(parameter);
229 | }
230 | }
231 |
232 | ///
233 | /// Raises the CanExecuteChaged event
234 | ///
235 | public void RaiseCanExecuteChanged()
236 | {
237 | OnCanExecuteChanged();
238 | }
239 |
240 | ///
241 | /// Protected virtual method to raise CanExecuteChanged event
242 | ///
243 | protected virtual void OnCanExecuteChanged()
244 | {
245 | CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
246 | }
247 |
248 | ///
249 | /// Property to enable or disable CommandManager's automatic requery on this command
250 | ///
251 | public bool IsAutomaticRequeryDisabled
252 | {
253 | get
254 | {
255 | return _isAutomaticRequeryDisabled;
256 | }
257 | set
258 | {
259 | if (_isAutomaticRequeryDisabled != value)
260 | {
261 | if (value)
262 | {
263 | CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
264 | }
265 | else
266 | {
267 | CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
268 | }
269 | _isAutomaticRequeryDisabled = value;
270 | }
271 | }
272 | }
273 |
274 | #endregion
275 |
276 | #region ICommand Members
277 |
278 | ///
279 | /// ICommand.CanExecuteChanged implementation
280 | ///
281 | public event EventHandler CanExecuteChanged
282 | {
283 | add
284 | {
285 | if (!_isAutomaticRequeryDisabled)
286 | {
287 | CommandManager.RequerySuggested += value;
288 | }
289 | CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
290 | }
291 | remove
292 | {
293 | if (!_isAutomaticRequeryDisabled)
294 | {
295 | CommandManager.RequerySuggested -= value;
296 | }
297 | CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
298 | }
299 | }
300 |
301 | bool ICommand.CanExecute(object parameter)
302 | {
303 | // if T is of value type and the parameter is not
304 | // set yet, then return false if CanExecute delegate
305 | // exists, else return true
306 | if (parameter == null &&
307 | typeof(T).IsValueType)
308 | {
309 | return (_canExecuteMethod == null);
310 | }
311 | return CanExecute((T)parameter);
312 | }
313 |
314 | void ICommand.Execute(object parameter)
315 | {
316 | Execute((T)parameter);
317 | }
318 |
319 | #endregion
320 |
321 | #region Data
322 |
323 | private readonly Action _executeMethod = null;
324 | private readonly Func _canExecuteMethod = null;
325 | private bool _isAutomaticRequeryDisabled = false;
326 | private List _canExecuteChangedHandlers;
327 |
328 | #endregion
329 | }
330 |
331 | ///
332 | /// This class contains methods for the CommandManager that help avoid memory leaks by
333 | /// using weak references.
334 | ///
335 | internal static class CommandManagerHelper
336 | {
337 | internal static void CallWeakReferenceHandlers(List handlers)
338 | {
339 | if (handlers != null)
340 | {
341 | // Take a snapshot of the handlers before we call out to them since the handlers
342 | // could cause the array to me modified while we are reading it.
343 |
344 | EventHandler[] callees = new EventHandler[handlers.Count];
345 | int count = 0;
346 |
347 | for (int i = handlers.Count - 1; i >= 0; i--)
348 | {
349 | WeakReference reference = handlers[i];
350 | EventHandler handler = reference.Target as EventHandler;
351 | if (handler == null)
352 | {
353 | // Clean up old handlers that have been collected
354 | handlers.RemoveAt(i);
355 | }
356 | else
357 | {
358 | callees[count] = handler;
359 | count++;
360 | }
361 | }
362 |
363 | // Call the handlers that we snapshotted
364 | for (int i = 0; i < count; i++)
365 | {
366 | EventHandler handler = callees[i];
367 | handler(null, EventArgs.Empty);
368 | }
369 | }
370 | }
371 |
372 | internal static void AddHandlersToRequerySuggested(List handlers)
373 | {
374 | if (handlers != null)
375 | {
376 | foreach (WeakReference handlerRef in handlers)
377 | {
378 | EventHandler handler = handlerRef.Target as EventHandler;
379 | if (handler != null)
380 | {
381 | CommandManager.RequerySuggested += handler;
382 | }
383 | }
384 | }
385 | }
386 |
387 | internal static void RemoveHandlersFromRequerySuggested(List handlers)
388 | {
389 | if (handlers != null)
390 | {
391 | foreach (WeakReference handlerRef in handlers)
392 | {
393 | EventHandler handler = handlerRef.Target as EventHandler;
394 | if (handler != null)
395 | {
396 | CommandManager.RequerySuggested -= handler;
397 | }
398 | }
399 | }
400 | }
401 |
402 | //internal static void AddWeakReferenceHandler(ref List handlers, EventHandler handler)
403 | //{
404 | // AddWeakReferenceHandler(ref handlers, handler, -1);
405 | //}
406 |
407 | internal static void AddWeakReferenceHandler(ref List handlers, EventHandler handler, int defaultListSize)
408 | {
409 | if (handlers == null)
410 | {
411 | handlers = (defaultListSize > 0 ? new List(defaultListSize) : new List());
412 | }
413 |
414 | handlers.Add(new WeakReference(handler));
415 | }
416 |
417 | internal static void RemoveWeakReferenceHandler(List handlers, EventHandler handler)
418 | {
419 | if (handlers != null)
420 | {
421 | for (int i = handlers.Count - 1; i >= 0; i--)
422 | {
423 | WeakReference reference = handlers[i];
424 | EventHandler existingHandler = reference.Target as EventHandler;
425 | if ((existingHandler == null) || (existingHandler == handler))
426 | {
427 | // Clean up old handlers that have been collected
428 | // in addition to the handler that is to be removed.
429 | handlers.RemoveAt(i);
430 | }
431 | }
432 | }
433 | }
434 | }
435 | }
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/charactergrowfont.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/charactergrowfont.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/charactershrinkfont.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/charactershrinkfont.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/cross.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/cut.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/editredo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/editredo.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/editundo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/editundo.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/page_copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/page_copy.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/page_paste.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/page_paste.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_align_center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_align_center.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_align_justify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_align_justify.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_align_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_align_left.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_align_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_align_right.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_bold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_bold.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_indent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_indent.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_indent_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_indent_remove.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_italic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_italic.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_list_bullets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_list_bullets.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_list_numbers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_list_numbers.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/text_underline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/text_underline.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/tick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/tick.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/Images/world_link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/Controls/Images/world_link.png
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/RichTextEditor.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
13 |
14 |
15 |
22 |
23 |
27 |
31 |
32 |
108 |
109 |
110 |
111 |
112 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
212 |
217 |
222 |
227 |
232 |
233 |
238 |
243 |
248 |
253 |
258 |
259 |
264 |
269 |
274 |
279 |
284 |
289 |
294 |
299 |
300 |
310 |
311 |
312 |
313 |
314 |
315 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
497 |
500 |
503 |
506 |
509 |
512 |
513 |
514 |
521 |
522 |
525 |
528 |
529 |
532 |
535 |
538 |
539 |
540 |
541 |
544 |
545 |
546 |
549 |
550 |
551 |
554 |
555 |
556 |
559 |
560 |
561 |
562 |
565 |
568 |
569 |
572 |
575 |
576 |
592 |
593 |
594 |
606 |
607 |
608 |
609 |
--------------------------------------------------------------------------------
/src/WpfRichText/Controls/RichTextEditor.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Documents;
11 | using System.Windows.Input;
12 | using System.Windows.Media;
13 | using System.Windows.Media.Imaging;
14 | using System.Windows.Navigation;
15 | using System.Windows.Shapes;
16 |
17 | namespace WpfRichText
18 | {
19 | ///
20 | /// Interaction logic for BindableRichTextbox.xaml
21 | ///
22 | public partial class RichTextEditor : UserControl
23 | {
24 | ///
25 | public static readonly DependencyProperty TextProperty =
26 | DependencyProperty.Register("Text", typeof(string), typeof(RichTextEditor),
27 | new PropertyMetadata(string.Empty));
28 |
29 | ///
30 | public static readonly DependencyProperty IsToolBarVisibleProperty =
31 | DependencyProperty.Register("IsToolBarVisible", typeof(bool), typeof(RichTextEditor),
32 | new PropertyMetadata(true));
33 |
34 | ///
35 | public static readonly DependencyProperty IsContextMenuEnabledProperty =
36 | DependencyProperty.Register("IsContextMenuEnabled", typeof(bool), typeof(RichTextEditor),
37 | new PropertyMetadata(true));
38 |
39 | ///
40 | public static readonly DependencyProperty IsReadOnlyProperty =
41 | DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(RichTextEditor),
42 | new PropertyMetadata(false));
43 |
44 | ///
45 | public static readonly DependencyProperty AvailableFontsProperty =
46 | DependencyProperty.Register("AvailableFonts", typeof(Collection), typeof(RichTextEditor),
47 | new PropertyMetadata(new Collection(
48 | new List(4)
49 | {
50 | "Arial",
51 | "Courier New",
52 | "Tahoma",
53 | "Times New Roman"
54 | }
55 | )));
56 |
57 |
58 | private TextRange textRange = null;
59 |
60 | ///
61 | public RichTextEditor()
62 | {
63 | InitializeComponent();
64 | }
65 |
66 | ///
67 | public string Text
68 | {
69 | get { return GetValue(TextProperty) as string; }
70 | set
71 | {
72 | SetValue(TextProperty, value);
73 | }
74 | }
75 |
76 | ///
77 | public bool IsToolBarVisible
78 | {
79 | get { return (GetValue(IsToolBarVisibleProperty) as bool? == true); }
80 | set
81 | {
82 | SetValue(IsToolBarVisibleProperty, value);
83 | //this.mainToolBar.Visibility = (value == true) ? Visibility.Visible : Visibility.Collapsed;
84 | }
85 | }
86 |
87 | ///
88 | public bool IsContextMenuEnabled
89 | {
90 | get
91 | {
92 | return (GetValue(IsContextMenuEnabledProperty) as bool? == true);
93 | }
94 | set
95 | {
96 | SetValue(IsContextMenuEnabledProperty, value);
97 | }
98 | }
99 |
100 | ///
101 | public bool IsReadOnly
102 | {
103 | get { return (GetValue(IsReadOnlyProperty) as bool? == true); }
104 | set
105 | {
106 | SetValue(IsReadOnlyProperty, value);
107 | SetValue(IsToolBarVisibleProperty, !value);
108 | SetValue(IsContextMenuEnabledProperty, !value);
109 | }
110 | }
111 |
112 | ///
113 | public Collection AvailableFonts
114 | {
115 | get { return GetValue(AvailableFontsProperty) as Collection; }
116 | set
117 | {
118 | SetValue(AvailableFontsProperty, value);
119 | }
120 | }
121 |
122 | private void FontColorPicker_SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs e)
123 | {
124 | this.mainRTB.Selection.ApplyPropertyValue(ForegroundProperty, e.NewValue.GetValueOrDefault().ToString(CultureInfo.InvariantCulture));
125 | }
126 |
127 | private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
128 | {
129 | if (this.mainRTB != null && this.mainRTB.Selection != null)
130 | this.mainRTB.Selection.ApplyPropertyValue(FontFamilyProperty, e.AddedItems[0]);
131 | }
132 |
133 | private void insertLink_Click(object sender, RoutedEventArgs e)
134 | {
135 | this.textRange = new TextRange(this.mainRTB.Selection.Start, this.mainRTB.Selection.End);
136 | this.uriInputPopup.IsOpen = true;
137 | }
138 |
139 | private void uriCancelClick(object sender, RoutedEventArgs e)
140 | {
141 | e.Handled = true;
142 | this.uriInputPopup.IsOpen = false;
143 | this.uriInput.Text = string.Empty;
144 | }
145 |
146 | private void uriSubmitClick(object sender, RoutedEventArgs e)
147 | {
148 | e.Handled = true;
149 | this.uriInputPopup.IsOpen = false;
150 | this.mainRTB.Selection.Select(this.textRange.Start, this.textRange.End);
151 | if (!string.IsNullOrEmpty(this.uriInput.Text))
152 | {
153 | this.textRange = new TextRange(this.mainRTB.Selection.Start, this.mainRTB.Selection.End);
154 | Hyperlink hlink = new Hyperlink(this.textRange.Start, this.textRange.End);
155 | hlink.NavigateUri = new Uri(this.uriInput.Text, UriKind.RelativeOrAbsolute);
156 | this.uriInput.Text = string.Empty;
157 | }
158 | else
159 | this.mainRTB.Selection.ClearAllProperties();
160 | }
161 |
162 | private void uriInput_KeyPressed(object sender, KeyEventArgs e)
163 | {
164 | switch (e.Key)
165 | {
166 | case Key.Enter:
167 | this.uriSubmitClick(sender, e);
168 | break;
169 | case Key.Escape:
170 | this.uriCancelClick(sender, e);
171 | break;
172 | default:
173 | break;
174 | }
175 | }
176 |
177 | private void ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
178 | {
179 | if (!this.IsContextMenuEnabled == true)
180 | e.Handled = true;
181 | }
182 |
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/WpfRichText/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 | using System.Windows;
5 | using System.Resources;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("WpfRichText")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("WpfRichText")]
15 | [assembly: AssemblyCopyright("Copyright © 2013")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | [assembly: CLSCompliant(true)]
25 |
26 | //In order to begin building localizable applications, set
27 | //CultureYouAreCodingWith in your .csproj file
28 | //inside a . For example, if you are using US english
29 | //in your source files, set the to en-US. Then uncomment
30 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
31 | //the line below to match the UICulture setting in the project file.
32 |
33 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
34 |
35 |
36 | [assembly:ThemeInfo(
37 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
38 | //(used if a resource is not found in the page,
39 | // or application resource dictionaries)
40 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
41 | //(used if a resource is not found in the page,
42 | // app, or any theme specific resource dictionaries)
43 | )]
44 |
45 |
46 | // Version information for an assembly consists of the following four values:
47 | //
48 | // Major Version
49 | // Minor Version
50 | // Build Number
51 | // Revision
52 | //
53 | // You can specify all the values or you can default the Build and Revision Numbers
54 | // by using the '*' as shown below:
55 | // [assembly: AssemblyVersion("1.0.*")]
56 | [assembly: AssemblyVersion("1.0.16.0")]
57 | [assembly: AssemblyFileVersion("1.0.16.0")]
58 | [assembly: NeutralResourcesLanguageAttribute("en")]
59 |
--------------------------------------------------------------------------------
/src/WpfRichText/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.18034
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WpfRichText.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfRichText.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap charactergrowfont {
67 | get {
68 | object obj = ResourceManager.GetObject("charactergrowfont", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Bitmap.
75 | ///
76 | internal static System.Drawing.Bitmap charactershrinkfont {
77 | get {
78 | object obj = ResourceManager.GetObject("charactershrinkfont", resourceCulture);
79 | return ((System.Drawing.Bitmap)(obj));
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized resource of type System.Drawing.Bitmap.
85 | ///
86 | internal static System.Drawing.Bitmap cross {
87 | get {
88 | object obj = ResourceManager.GetObject("cross", resourceCulture);
89 | return ((System.Drawing.Bitmap)(obj));
90 | }
91 | }
92 |
93 | ///
94 | /// Looks up a localized resource of type System.Drawing.Bitmap.
95 | ///
96 | internal static System.Drawing.Bitmap cut {
97 | get {
98 | object obj = ResourceManager.GetObject("cut", resourceCulture);
99 | return ((System.Drawing.Bitmap)(obj));
100 | }
101 | }
102 |
103 | ///
104 | /// Looks up a localized resource of type System.Drawing.Bitmap.
105 | ///
106 | internal static System.Drawing.Bitmap editredo {
107 | get {
108 | object obj = ResourceManager.GetObject("editredo", resourceCulture);
109 | return ((System.Drawing.Bitmap)(obj));
110 | }
111 | }
112 |
113 | ///
114 | /// Looks up a localized resource of type System.Drawing.Bitmap.
115 | ///
116 | internal static System.Drawing.Bitmap editundo {
117 | get {
118 | object obj = ResourceManager.GetObject("editundo", resourceCulture);
119 | return ((System.Drawing.Bitmap)(obj));
120 | }
121 | }
122 |
123 | ///
124 | /// Looks up a localized resource of type System.Drawing.Bitmap.
125 | ///
126 | internal static System.Drawing.Bitmap page_copy {
127 | get {
128 | object obj = ResourceManager.GetObject("page_copy", resourceCulture);
129 | return ((System.Drawing.Bitmap)(obj));
130 | }
131 | }
132 |
133 | ///
134 | /// Looks up a localized resource of type System.Drawing.Bitmap.
135 | ///
136 | internal static System.Drawing.Bitmap page_paste {
137 | get {
138 | object obj = ResourceManager.GetObject("page_paste", resourceCulture);
139 | return ((System.Drawing.Bitmap)(obj));
140 | }
141 | }
142 |
143 | ///
144 | /// Looks up a localized resource of type System.Drawing.Bitmap.
145 | ///
146 | internal static System.Drawing.Bitmap text_align_center {
147 | get {
148 | object obj = ResourceManager.GetObject("text_align_center", resourceCulture);
149 | return ((System.Drawing.Bitmap)(obj));
150 | }
151 | }
152 |
153 | ///
154 | /// Looks up a localized resource of type System.Drawing.Bitmap.
155 | ///
156 | internal static System.Drawing.Bitmap text_align_justify {
157 | get {
158 | object obj = ResourceManager.GetObject("text_align_justify", resourceCulture);
159 | return ((System.Drawing.Bitmap)(obj));
160 | }
161 | }
162 |
163 | ///
164 | /// Looks up a localized resource of type System.Drawing.Bitmap.
165 | ///
166 | internal static System.Drawing.Bitmap text_align_left {
167 | get {
168 | object obj = ResourceManager.GetObject("text_align_left", resourceCulture);
169 | return ((System.Drawing.Bitmap)(obj));
170 | }
171 | }
172 |
173 | ///
174 | /// Looks up a localized resource of type System.Drawing.Bitmap.
175 | ///
176 | internal static System.Drawing.Bitmap text_align_right {
177 | get {
178 | object obj = ResourceManager.GetObject("text_align_right", resourceCulture);
179 | return ((System.Drawing.Bitmap)(obj));
180 | }
181 | }
182 |
183 | ///
184 | /// Looks up a localized resource of type System.Drawing.Bitmap.
185 | ///
186 | internal static System.Drawing.Bitmap text_bold {
187 | get {
188 | object obj = ResourceManager.GetObject("text_bold", resourceCulture);
189 | return ((System.Drawing.Bitmap)(obj));
190 | }
191 | }
192 |
193 | ///
194 | /// Looks up a localized resource of type System.Drawing.Bitmap.
195 | ///
196 | internal static System.Drawing.Bitmap text_indent {
197 | get {
198 | object obj = ResourceManager.GetObject("text_indent", resourceCulture);
199 | return ((System.Drawing.Bitmap)(obj));
200 | }
201 | }
202 |
203 | ///
204 | /// Looks up a localized resource of type System.Drawing.Bitmap.
205 | ///
206 | internal static System.Drawing.Bitmap text_indent_remove {
207 | get {
208 | object obj = ResourceManager.GetObject("text_indent_remove", resourceCulture);
209 | return ((System.Drawing.Bitmap)(obj));
210 | }
211 | }
212 |
213 | ///
214 | /// Looks up a localized resource of type System.Drawing.Bitmap.
215 | ///
216 | internal static System.Drawing.Bitmap text_italic {
217 | get {
218 | object obj = ResourceManager.GetObject("text_italic", resourceCulture);
219 | return ((System.Drawing.Bitmap)(obj));
220 | }
221 | }
222 |
223 | ///
224 | /// Looks up a localized resource of type System.Drawing.Bitmap.
225 | ///
226 | internal static System.Drawing.Bitmap text_list_bullets {
227 | get {
228 | object obj = ResourceManager.GetObject("text_list_bullets", resourceCulture);
229 | return ((System.Drawing.Bitmap)(obj));
230 | }
231 | }
232 |
233 | ///
234 | /// Looks up a localized resource of type System.Drawing.Bitmap.
235 | ///
236 | internal static System.Drawing.Bitmap text_list_numbers {
237 | get {
238 | object obj = ResourceManager.GetObject("text_list_numbers", resourceCulture);
239 | return ((System.Drawing.Bitmap)(obj));
240 | }
241 | }
242 |
243 | ///
244 | /// Looks up a localized resource of type System.Drawing.Bitmap.
245 | ///
246 | internal static System.Drawing.Bitmap text_underline {
247 | get {
248 | object obj = ResourceManager.GetObject("text_underline", resourceCulture);
249 | return ((System.Drawing.Bitmap)(obj));
250 | }
251 | }
252 |
253 | ///
254 | /// Looks up a localized resource of type System.Drawing.Bitmap.
255 | ///
256 | internal static System.Drawing.Bitmap tick {
257 | get {
258 | object obj = ResourceManager.GetObject("tick", resourceCulture);
259 | return ((System.Drawing.Bitmap)(obj));
260 | }
261 | }
262 |
263 | ///
264 | /// Looks up a localized resource of type System.Drawing.Bitmap.
265 | ///
266 | internal static System.Drawing.Bitmap world_link {
267 | get {
268 | object obj = ResourceManager.GetObject("world_link", resourceCulture);
269 | return ((System.Drawing.Bitmap)(obj));
270 | }
271 | }
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/src/WpfRichText/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\controls\images\charactergrowfont.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
125 | ..\controls\images\charactershrinkfont.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
128 | ..\controls\images\cross.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
131 | ..\controls\images\cut.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
132 |
133 |
134 | ..\controls\images\editredo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
135 |
136 |
137 | ..\controls\images\editundo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
138 |
139 |
140 | ..\controls\images\page_copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
141 |
142 |
143 | ..\controls\images\page_paste.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
144 |
145 |
146 | ..\controls\images\text_align_center.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
147 |
148 |
149 | ..\controls\images\text_align_justify.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
150 |
151 |
152 | ..\controls\images\text_align_left.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
153 |
154 |
155 | ..\controls\images\text_align_right.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
156 |
157 |
158 | ..\controls\images\text_bold.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
159 |
160 |
161 | ..\controls\images\text_indent.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
162 |
163 |
164 | ..\controls\images\text_indent_remove.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
165 |
166 |
167 | ..\controls\images\text_italic.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
168 |
169 |
170 | ..\controls\images\text_list_bullets.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
171 |
172 |
173 | ..\controls\images\text_list_numbers.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
174 |
175 |
176 | ..\controls\images\text_underline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
177 |
178 |
179 | ..\controls\images\tick.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
180 |
181 |
182 | ..\controls\images\world_link.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
183 |
184 |
--------------------------------------------------------------------------------
/src/WpfRichText/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.18034
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace WpfRichText.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/WpfRichText/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/WpfRichText/WpfRichText.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {78147C43-DB7B-4571-BC7A-E7F0075B4B78}
8 | library
9 | Properties
10 | WpfRichText
11 | WpfRichText
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | true
27 | full
28 | false
29 | bin\Debug\
30 | DEBUG;TRACE
31 | prompt
32 | 4
33 | AllRules.ruleset
34 | bin\Debug\WpfRichText.XML
35 | true
36 |
37 |
38 |
39 |
40 | pdbonly
41 | true
42 | ..\..\dll\
43 | TRACE
44 | prompt
45 | 4
46 | true
47 |
48 |
49 | true
50 |
51 |
52 | self.snk
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | 4.0
66 |
67 |
68 |
69 |
70 |
71 | False
72 | ..\packages\Extended.Wpf.Toolkit.2.0.0\lib\net40\Xceed.Wpf.Toolkit.dll
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | RichTextEditor.xaml
81 |
82 |
83 | Code
84 |
85 |
86 | True
87 | True
88 | Resources.resx
89 |
90 |
91 | True
92 | Settings.settings
93 | True
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | ResXFileCodeGenerator
104 | Resources.Designer.cs
105 | Designer
106 |
107 |
108 |
109 | SettingsSingleFileGenerator
110 | Settings.Designer.cs
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | MSBuild:Compile
130 | Designer
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
166 |
--------------------------------------------------------------------------------
/src/WpfRichText/XamlToHtmlParser/HtmlFromXamlConverter.cs:
--------------------------------------------------------------------------------
1 | //---------------------------------------------------------------------------
2 | //
3 | // File: HtmlFromXamlConverter.cs
4 | //
5 | // Copyright (C) Microsoft Corporation. All rights reserved.
6 | //
7 | // Description: Prototype for Xaml - Html conversion
8 | //
9 | //---------------------------------------------------------------------------
10 |
11 | namespace WpfRichText
12 | {
13 | using System;
14 | using System.Diagnostics;
15 | using System.Globalization;
16 | using System.IO;
17 | using System.Text;
18 | using System.Xml;
19 |
20 | ///
21 | /// HtmlToXamlConverter is a static class that takes an HTML string
22 | /// and converts it into XAML
23 | ///
24 | internal static class HtmlFromXamlConverter
25 | {
26 | // ---------------------------------------------------------------------
27 | //
28 | // Internal Methods
29 | //
30 | // ---------------------------------------------------------------------
31 |
32 | #region Internal Methods
33 | internal static string ConvertXamlToHtml(string xamlString)
34 | {
35 | return ConvertXamlToHtml(xamlString, true);
36 | }
37 | ///
38 | /// Main entry point for Xaml-to-Html converter.
39 | /// Converts a xaml string into html string.
40 | ///
41 | /// Xaml strinng to convert.
42 | ///
43 | ///
44 | /// Html string produced from a source xaml.
45 | ///
46 | internal static string ConvertXamlToHtml(string xamlString, bool asFlowDocument)
47 | {
48 | StringBuilder htmlStringBuilder;
49 | XmlTextWriter htmlWriter;
50 |
51 | if (!asFlowDocument)
52 | {
53 | xamlString = "" + xamlString + "";
54 | }
55 |
56 | using (XmlTextReader xamlReader = new XmlTextReader(new StringReader(xamlString)))
57 | {
58 | htmlStringBuilder = new StringBuilder(100);
59 | using (StringWriter htmlStringWiter = new StringWriter(htmlStringBuilder, CultureInfo.InvariantCulture))
60 | {
61 | htmlWriter = new XmlTextWriter(htmlStringWiter);
62 |
63 | if (!WriteFlowDocument(xamlReader, htmlWriter))
64 | {
65 | return "";
66 | }
67 | }
68 |
69 | string htmlString = htmlStringBuilder.ToString();
70 | return htmlString;
71 | }
72 | }
73 |
74 | #endregion Internal Methods
75 |
76 | // ---------------------------------------------------------------------
77 | //
78 | // Private Methods
79 | //
80 | // ---------------------------------------------------------------------
81 |
82 | #region Private Methods
83 | ///
84 | /// Processes a root level element of XAML (normally it's FlowDocument element).
85 | ///
86 | ///
87 | /// XmlTextReader for a source xaml.
88 | ///
89 | ///
90 | /// XmlTextWriter producing resulting html
91 | ///
92 | private static bool WriteFlowDocument(XmlTextReader xamlReader, XmlTextWriter htmlWriter)
93 | {
94 | if (!ReadNextToken(xamlReader))
95 | {
96 | // Xaml content is empty - nothing to convert
97 | return false;
98 | }
99 |
100 | if (xamlReader.NodeType != XmlNodeType.Element || xamlReader.Name != "FlowDocument")
101 | {
102 | // Root FlowDocument elemet is missing
103 | return false;
104 | }
105 |
106 | // Create a buffer StringBuilder for collecting css properties for inline STYLE attributes
107 | // on every element level (it will be re-initialized on every level).
108 | StringBuilder inlineStyle = new StringBuilder();
109 |
110 | htmlWriter.WriteStartElement("HTML");
111 | htmlWriter.WriteStartElement("BODY");
112 |
113 | WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle);
114 |
115 | WriteElementContent(xamlReader, htmlWriter, inlineStyle);
116 |
117 | htmlWriter.WriteEndElement();
118 | htmlWriter.WriteEndElement();
119 |
120 | return true;
121 | }
122 |
123 | ///
124 | /// Reads attributes of the current xaml element and converts
125 | /// them into appropriate html attributes or css styles.
126 | ///
127 | ///
128 | /// XmlTextReader which is expected to be at XmlNodeType.Element
129 | /// (opening element tag) position.
130 | /// The reader will remain at the same level after function complete.
131 | ///
132 | ///
133 | /// XmlTextWriter for output html, which is expected to be in
134 | /// after WriteStartElement state.
135 | ///
136 | ///
137 | /// String builder for collecting css properties for inline STYLE attribute.
138 | ///
139 | private static void WriteFormattingProperties(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle)
140 | {
141 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);
142 |
143 | // Clear string builder for the inline style
144 | inlineStyle.Remove(0, inlineStyle.Length);
145 |
146 | if (!xamlReader.HasAttributes)
147 | {
148 | return;
149 | }
150 |
151 | bool borderSet = false;
152 |
153 | while (xamlReader.MoveToNextAttribute())
154 | {
155 | string css = null;
156 |
157 | switch (xamlReader.Name)
158 | {
159 | // Character fomatting properties
160 | // ------------------------------
161 | case "Background":
162 | css = "background-color:" + ParseXamlColor(xamlReader.Value) + ";";
163 | break;
164 | case "FontFamily":
165 | css = "font-family:" + xamlReader.Value + ";";
166 | break;
167 | case "FontStyle":
168 | css = "font-style:" + xamlReader.Value.ToLower(CultureInfo.InvariantCulture) + ";";
169 | break;
170 | case "FontWeight":
171 | css = "font-weight:" + xamlReader.Value.ToLower(CultureInfo.InvariantCulture) + ";";
172 | break;
173 | case "FontStretch":
174 | break;
175 | case "FontSize":
176 | css = "font-size:" + xamlReader.Value + ";";
177 | break;
178 | case "Foreground":
179 | css = "color:" + ParseXamlColor(xamlReader.Value) + ";";
180 | break;
181 | case "TextDecorations":
182 | css = "text-decoration:underline;";
183 | break;
184 | case "TextEffects":
185 | break;
186 | case "Emphasis":
187 | break;
188 | case "StandardLigatures":
189 | break;
190 | case "Variants":
191 | break;
192 | case "Capitals":
193 | break;
194 | case "Fraction":
195 | break;
196 |
197 | // Paragraph formatting properties
198 | // -------------------------------
199 | case "Padding":
200 | css = "padding:" + ParseXamlThickness(xamlReader.Value) + ";";
201 | break;
202 | case "Margin":
203 | css = "margin:" + ParseXamlThickness(xamlReader.Value) + ";";
204 | break;
205 | case "BorderThickness":
206 | css = "border-width:" + ParseXamlThickness(xamlReader.Value) + ";";
207 | borderSet = true;
208 | break;
209 | case "BorderBrush":
210 | css = "border-color:" + ParseXamlColor(xamlReader.Value) + ";";
211 | borderSet = true;
212 | break;
213 | case "LineHeight":
214 | break;
215 | case "TextIndent":
216 | css = "text-indent:" + xamlReader.Value + ";";
217 | break;
218 | case "TextAlignment":
219 | css = "text-align:" + xamlReader.Value + ";";
220 | break;
221 | case "IsKeptTogether":
222 | break;
223 | case "IsKeptWithNext":
224 | break;
225 | case "ColumnBreakBefore":
226 | break;
227 | case "PageBreakBefore":
228 | break;
229 | case "FlowDirection":
230 | break;
231 |
232 | // Table attributes
233 | // ----------------
234 | case "Width":
235 | css = "width:" + xamlReader.Value + ";";
236 | break;
237 | case "ColumnSpan":
238 | htmlWriter.WriteAttributeString("COLSPAN", xamlReader.Value);
239 | break;
240 | case "RowSpan":
241 | htmlWriter.WriteAttributeString("ROWSPAN", xamlReader.Value);
242 | break;
243 |
244 | // Hyperlink Attributes
245 | case "NavigateUri" :
246 | htmlWriter.WriteAttributeString("HREF", xamlReader.Value);
247 | break;
248 | }
249 |
250 | if (css != null)
251 | {
252 | inlineStyle.Append(css);
253 | }
254 | }
255 |
256 | if (borderSet)
257 | {
258 | inlineStyle.Append("border-style:solid;mso-element:para-border-div;");
259 | }
260 |
261 | // Return the xamlReader back to element level
262 | xamlReader.MoveToElement();
263 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);
264 | }
265 |
266 | private static string ParseXamlColor(string color)
267 | {
268 | if (color.StartsWith("#", StringComparison.OrdinalIgnoreCase))
269 | {
270 | // Remove transparancy value
271 | color = "#" + color.Substring(3);
272 | }
273 | return color;
274 | }
275 |
276 | private static string ParseXamlThickness(string thickness)
277 | {
278 | string[] values = thickness.Split(',');
279 |
280 | for (int i = 0; i < values.Length; i++)
281 | {
282 | double value;
283 | if (double.TryParse(values[i], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out value))
284 | {
285 | values[i] = Math.Ceiling(value).ToString(CultureInfo.InvariantCulture);
286 | }
287 | else
288 | {
289 | values[i] = "1";
290 | }
291 | }
292 |
293 | string cssThickness;
294 | switch (values.Length)
295 | {
296 | case 1:
297 | cssThickness = thickness;
298 | break;
299 | case 2:
300 | cssThickness = values[1] + " " + values[0];
301 | break;
302 | case 4:
303 | cssThickness = values[1] + " " + values[2] + " " + values[3] + " " + values[0];
304 | break;
305 | default:
306 | cssThickness = values[0];
307 | break;
308 | }
309 |
310 | return cssThickness;
311 | }
312 |
313 | ///
314 | /// Reads a content of current xaml element, converts it
315 | ///
316 | ///
317 | /// XmlTextReader which is expected to be at XmlNodeType.Element
318 | /// (opening element tag) position.
319 | ///
320 | ///
321 | /// May be null, in which case we are skipping the xaml element;
322 | /// witout producing any output to html.
323 | ///
324 | ///
325 | /// StringBuilder used for collecting css properties for inline STYLE attribute.
326 | ///
327 | private static void WriteElementContent(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle)
328 | {
329 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);
330 |
331 | bool elementContentStarted = false;
332 |
333 | if (xamlReader.IsEmptyElement)
334 | {
335 | if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0)
336 | {
337 | // Output STYLE attribute and clear inlineStyle buffer.
338 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
339 | inlineStyle.Remove(0, inlineStyle.Length);
340 | }
341 | elementContentStarted = true;
342 | }
343 | else
344 | {
345 | while (ReadNextToken(xamlReader) && xamlReader.NodeType != XmlNodeType.EndElement)
346 | {
347 | switch (xamlReader.NodeType)
348 | {
349 | case XmlNodeType.Element:
350 | if (xamlReader.Name.Contains("."))
351 | {
352 | AddComplexProperty(xamlReader, inlineStyle);
353 | }
354 | else
355 | {
356 | if (htmlWriter != null && !elementContentStarted && inlineStyle.Length > 0)
357 | {
358 | // Output STYLE attribute and clear inlineStyle buffer.
359 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
360 | inlineStyle.Remove(0, inlineStyle.Length);
361 | }
362 | elementContentStarted = true;
363 | WriteElement(xamlReader, htmlWriter, inlineStyle);
364 | }
365 | Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement || xamlReader.NodeType == XmlNodeType.Element && xamlReader.IsEmptyElement);
366 | break;
367 | case XmlNodeType.Comment:
368 | if (htmlWriter != null)
369 | {
370 | if (!elementContentStarted && inlineStyle.Length > 0)
371 | {
372 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
373 | }
374 | htmlWriter.WriteComment(xamlReader.Value);
375 | }
376 | elementContentStarted = true;
377 | break;
378 | case XmlNodeType.CDATA:
379 | case XmlNodeType.Text:
380 | case XmlNodeType.SignificantWhitespace:
381 | if (htmlWriter != null)
382 | {
383 | if (!elementContentStarted && inlineStyle.Length > 0)
384 | {
385 | htmlWriter.WriteAttributeString("STYLE", inlineStyle.ToString());
386 | }
387 | htmlWriter.WriteString(xamlReader.Value);
388 | }
389 | elementContentStarted = true;
390 | break;
391 | }
392 | }
393 |
394 | Debug.Assert(xamlReader.NodeType == XmlNodeType.EndElement);
395 | }
396 | }
397 |
398 | ///
399 | /// Conberts an element notation of complex property into
400 | ///
401 | ///
402 | /// On entry this XmlTextReader must be on Element start tag;
403 | /// on exit - on EndElement tag.
404 | ///
405 | ///
406 | /// StringBuilder containing a value for STYLE attribute.
407 | ///
408 | private static void AddComplexProperty(XmlTextReader xamlReader, StringBuilder inlineStyle)
409 | {
410 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);
411 |
412 | if (inlineStyle != null && xamlReader.Name.EndsWith(".TextDecorations", StringComparison.OrdinalIgnoreCase))
413 | {
414 | inlineStyle.Append("text-decoration:underline;");
415 | }
416 |
417 | // Skip the element representing the complex property
418 | WriteElementContent(xamlReader, /*htmlWriter:*/null, /*inlineStyle:*/null);
419 | }
420 |
421 | ///
422 | /// Converts a xaml element into an appropriate html element.
423 | ///
424 | ///
425 | /// On entry this XmlTextReader must be on Element start tag;
426 | /// on exit - on EndElement tag.
427 | ///
428 | ///
429 | /// May be null, in which case we are skipping xaml content
430 | /// without producing any html output
431 | ///
432 | ///
433 | /// StringBuilder used for collecting css properties for inline STYLE attributes on every level.
434 | ///
435 | private static void WriteElement(XmlTextReader xamlReader, XmlTextWriter htmlWriter, StringBuilder inlineStyle)
436 | {
437 | Debug.Assert(xamlReader.NodeType == XmlNodeType.Element);
438 |
439 | if (htmlWriter == null)
440 | {
441 | // Skipping mode; recurse into the xaml element without any output
442 | WriteElementContent(xamlReader, /*htmlWriter:*/null, null);
443 | }
444 | else
445 | {
446 | string htmlElementName = null;
447 |
448 | switch (xamlReader.Name)
449 | {
450 | case "Run" :
451 | case "Span":
452 | htmlElementName = "SPAN";
453 | break;
454 | case "InlineUIContainer":
455 | htmlElementName = "SPAN";
456 | break;
457 | case "Bold":
458 | htmlElementName = "B";
459 | break;
460 | case "Italic" :
461 | htmlElementName = "I";
462 | break;
463 | case "Paragraph" :
464 | htmlElementName = "P";
465 | break;
466 | case "BlockUIContainer":
467 | htmlElementName = "DIV";
468 | break;
469 | case "Section":
470 | htmlElementName = "DIV";
471 | break;
472 | case "Table":
473 | htmlElementName = "TABLE";
474 | break;
475 | case "TableColumn":
476 | htmlElementName = "COL";
477 | break;
478 | case "TableRowGroup" :
479 | htmlElementName = "TBODY";
480 | break;
481 | case "TableRow" :
482 | htmlElementName = "TR";
483 | break;
484 | case "TableCell" :
485 | htmlElementName = "TD";
486 | break;
487 | case "List" :
488 | string marker = xamlReader.GetAttribute("MarkerStyle");
489 | if (marker == null || marker == "None" || marker == "Disc" || marker == "Circle" || marker == "Square" || marker == "Box")
490 | htmlElementName = "UL";
491 | else
492 | htmlElementName = "OL";
493 | break;
494 | case "ListItem" :
495 | htmlElementName = "LI";
496 | break;
497 | case "Hyperlink" :
498 | htmlElementName = "A";
499 | break;
500 | case "LineBreak" :
501 | htmlWriter.WriteRaw("
");
502 | break;
503 | default :
504 | htmlElementName = null; // Ignore the element
505 | break;
506 | }
507 |
508 | if (htmlWriter != null && !String.IsNullOrEmpty(htmlElementName))
509 | {
510 | htmlWriter.WriteStartElement(htmlElementName);
511 |
512 | WriteFormattingProperties(xamlReader, htmlWriter, inlineStyle);
513 |
514 | WriteElementContent(xamlReader, htmlWriter, inlineStyle);
515 |
516 | htmlWriter.WriteEndElement();
517 | }
518 | else
519 | {
520 | // Skip this unrecognized xaml element
521 | WriteElementContent(xamlReader, /*htmlWriter:*/null, null);
522 | }
523 | }
524 | }
525 |
526 | // Reader advance helpers
527 | // ----------------------
528 |
529 | ///
530 | /// Reads several items from xamlReader skipping all non-significant stuff.
531 | ///
532 | ///
533 | /// XmlTextReader from tokens are being read.
534 | ///
535 | ///
536 | /// True if new token is available; false if end of stream reached.
537 | ///
538 | private static bool ReadNextToken(XmlReader xamlReader)
539 | {
540 | while (xamlReader.Read())
541 | {
542 | Debug.Assert(xamlReader.ReadState == ReadState.Interactive, "Reader is expected to be in Interactive state (" + xamlReader.ReadState + ")");
543 | switch (xamlReader.NodeType)
544 | {
545 | case XmlNodeType.Element:
546 | case XmlNodeType.EndElement:
547 | case XmlNodeType.None:
548 | case XmlNodeType.CDATA:
549 | case XmlNodeType.Text:
550 | case XmlNodeType.SignificantWhitespace:
551 | return true;
552 |
553 | case XmlNodeType.Whitespace:
554 | if (xamlReader.XmlSpace == XmlSpace.Preserve)
555 | {
556 | return true;
557 | }
558 | // ignore insignificant whitespace
559 | break;
560 |
561 | case XmlNodeType.EndEntity:
562 | case XmlNodeType.EntityReference:
563 | // Implement entity reading
564 | //xamlReader.ResolveEntity();
565 | //xamlReader.Read();
566 | //ReadChildNodes( parent, parentBaseUri, xamlReader, positionInfo);
567 | break; // for now we ignore entities as insignificant stuff
568 |
569 | case XmlNodeType.Comment:
570 | return true;
571 | case XmlNodeType.ProcessingInstruction:
572 | case XmlNodeType.DocumentType:
573 | case XmlNodeType.XmlDeclaration:
574 | default:
575 | // Ignorable stuff
576 | break;
577 | }
578 | }
579 | return false;
580 | }
581 |
582 | #endregion Private Methods
583 |
584 | // ---------------------------------------------------------------------
585 | //
586 | // Private Fields
587 | //
588 | // ---------------------------------------------------------------------
589 |
590 | #region Private Fields
591 |
592 | #endregion Private Fields
593 | }
594 | }
595 |
--------------------------------------------------------------------------------
/src/WpfRichText/XamlToHtmlParser/HtmlParser.cs:
--------------------------------------------------------------------------------
1 | //---------------------------------------------------------------------------
2 | //
3 | // File: HtmlParser.cs
4 | //
5 | // Copyright (C) Microsoft Corporation. All rights reserved.
6 | //
7 | // Description: Parser for Html-to-Xaml converter
8 | //
9 | //---------------------------------------------------------------------------
10 |
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Globalization; // StringBuilder
14 | using System.Text;
15 | using System.Xml;
16 |
17 | // important TODOS:
18 | // TODO 1. Start tags: The ParseXmlElement function has been modified to be called after both the
19 | // angle bracket < and element name have been read, instead of just the < bracket and some valid name character,
20 | // previously the case. This change was made so that elements with optional closing tags could read a new
21 | // element's start tag and decide whether they were required to close. However, there is a question of whether to
22 | // handle this in the parser or lexical analyzer. It is currently handled in the parser - the lexical analyzer still
23 | // recognizes a start tag opener as a '<' + valid name start char; it is the parser that reads the actual name.
24 | // this is correct behavior assuming that the name is a valid html name, because the lexical analyzer should not know anything
25 | // about optional closing tags, etc. UPDATED: 10/13/2004: I am updating this to read the whole start tag of something
26 | // that is not an HTML, treat it as empty, and add it to the tree. That way the converter will know it's there, but
27 | // it will hvae no content. We could also partially recover by trying to look up and match names if they are similar
28 | // TODO 2. Invalid element names: However, it might make sense to give the lexical analyzer the ability to identify
29 | // a valid html element name and not return something as a start tag otherwise. For example, if we type , should
30 | // the lexical analyzer return that it has found the start of an element when this is not the case in HTML? But this will
31 | // require implementing a lookahead token in the lexical analyzer so that it can treat an invalid element name as text. One
32 | // character of lookahead will not be enough.
33 | // TODO 3. Attributes: The attribute recovery is poor when reading attribute values in quotes - if no closing quotes are found,
34 | // the lexical analyzer just keeps reading and if it eventually reaches the end of file, it would have just skipped everything.
35 | // There are a couple of ways to deal with this: 1) stop reading attributes when we encounter a '>' character - this doesn't allow
36 | // the '>' character to be used in attribute values, but it can still be used as an entity. 2) Maintain a HTML-specific list
37 | // of attributes and their values that each html element can take, and if we find correct attribute namesand values for an
38 | // element we use them regardless of the quotes, this way we could just ignore something invalid. One more option: 3) Read ahead
39 | // in the quoted value and if we find an end of file, we can return to where we were and process as text. However this requires
40 | // a lot of lookahead and a resettable reader.
41 | // TODO 4: elements with optional closing tags: For elements with optional closing tags, we always close the element if we find
42 | // that one of it's ancestors has closed. This condition may be too broad and we should develop a better heuristic. We should also
43 | // improve the heuristics for closing certain elements when the next element starts
44 | // TODO 5. Nesting: Support for unbalanced nesting, e.g. : this is not presently supported. To support it we may need
45 | // to maintain two xml elements, one the element that represents what has already been read and another represents what we are presently reading.
46 | // Then if we encounter an unbalanced nesting tag we could close the element that was supposed to close, save the current element
47 | // and store it in the list of already-read content, and then open a new element to which all tags that are currently open
48 | // can be applied. Is there a better way to do this? Should we do it at all?
49 | // TODO 6. Elements with optional starting tags: there are 4 such elements in the HTML 4 specification - html, tbody, body and head.
50 | // The current recovery doesn;t do anything for any of these elements except the html element, because it's not critical - head
51 | // and body elementscan be contained within html element, and tbody is contained within table. To extend this for XHTML
52 | // extensions, and to recover in case other elements are missing start tags, we would need to insert an extra recursive call
53 | // to ParseXmlElement for the missing start tag. It is suggested to do this by giving ParseXmlElement an argument that specifies
54 | // a name to use. If this argument is null, it assumes its name is the next token from the lexical analyzer and continues
55 | // exactly as it does now. However, if the argument contains a valid html element name then it takes that value as its name
56 | // and continues as before. This way, if the next token is the element that should actually be its child, it will see
57 | // the name in the next step and initiate a recursive call. We would also need to add some logic in the loop for when a start tag
58 | // is found - if the start tag is not compatible with current context and indicates that a start tag has been missed, then we
59 | // can initiate the extra recursive call and give it the name of the missed start tag. The issues are when to insert this logic,
60 | // and if we want to support it over multiple missing start tags. If we insert it at the time a start tag is read in element
61 | // text, then we can support only one missing start tag, since the extra call will read the next start tag and make a recursive
62 | // call without checking the context. This is a conceptual problem, and the check should be made just before a recursive call,
63 | // with the choice being whether we should supply an element name as argument, or leave it as NULL and read from the input
64 | // TODO 7: Context: Is it appropriate to keep track of context here? For example, should we only expect td, tr elements when
65 | // reading a table and ignore them otherwise? This may be too much of a load on the parser, I think it's better if the converter
66 | // deals with it
67 |
68 |
69 | namespace WpfRichText
70 | {
71 | ///
72 | /// HtmlParser class accepts a string of possibly badly formed Html, parses it and returns a string
73 | /// of well-formed Html that is as close to the original string in content as possible
74 | ///
75 |
76 | internal class HtmlParser : IDisposable
77 | {
78 | // ---------------------------------------------------------------------
79 | //
80 | // Constructors
81 | //
82 | // ---------------------------------------------------------------------
83 |
84 | #region Constructors
85 |
86 | ///
87 | /// Constructor. Initializes the _htmlLexicalAnalayzer element with the given input string
88 | ///
89 | ///
90 | /// string to parsed into well-formed Html
91 | ///
92 | private HtmlParser(string inputString)
93 | {
94 | // Create an output xml document
95 | _document = new XmlDocument();
96 |
97 | // initialize open tag stack
98 | _openedElements = new Stack();
99 |
100 | _pendingInlineElements = new Stack();
101 |
102 | // initialize lexical analyzer
103 | _htmlLexicalAnalyzer = new HtmlLexicalAnalyzer(inputString);
104 |
105 | // get first token from input, expecting text
106 | _htmlLexicalAnalyzer.GetNextContentToken();
107 | }
108 |
109 | #endregion Constructors
110 |
111 | // ---------------------------------------------------------------------
112 | //
113 | // Internal Methods
114 | //
115 | // ---------------------------------------------------------------------
116 |
117 | #region Internal Methods
118 |
119 | ///
120 | /// Instantiates an HtmlParser element and calls the parsing function on the given input string
121 | ///
122 | ///
123 | /// Input string of pssibly badly-formed Html to be parsed into well-formed Html
124 | ///
125 | ///
126 | /// XmlElement rep
127 | ///
128 | internal static XmlElement ParseHtml(string htmlString)
129 | {
130 | using (HtmlParser htmlParser = new HtmlParser(htmlString))
131 | {
132 |
133 | XmlElement htmlRootElement = htmlParser.ParseHtmlContent();
134 |
135 | return htmlRootElement;
136 | }
137 | }
138 |
139 | // .....................................................................
140 | //
141 | // Html Header on Clipboard
142 | //
143 | // .....................................................................
144 |
145 | // Html header structure.
146 | // Version:1.0
147 | // StartHTML:000000000
148 | // EndHTML:000000000
149 | // StartFragment:000000000
150 | // EndFragment:000000000
151 | // StartSelection:000000000
152 | // EndSelection:000000000
153 | internal const string HtmlHeader = "Version:1.0\r\nStartHTML:{0:D10}\r\nEndHTML:{1:D10}\r\nStartFragment:{2:D10}\r\nEndFragment:{3:D10}\r\nStartSelection:{4:D10}\r\nEndSelection:{5:D10}\r\n";
154 | internal const string HtmlStartFragmentComment = "";
155 | internal const string HtmlEndFragmentComment = "";
156 |
157 | ///
158 | /// Extracts Html string from clipboard data by parsing header information in htmlDataString
159 | ///
160 | ///
161 | /// String representing Html clipboard data. This includes Html header
162 | ///
163 | ///
164 | /// String containing only the Html data part of htmlDataString, without header
165 | ///
166 | internal static string ExtractHtmlFromClipboardData(string htmlDataString)
167 | {
168 | int startHtmlIndex = htmlDataString.IndexOf("StartHTML:", StringComparison.OrdinalIgnoreCase);
169 | if (startHtmlIndex < 0)
170 | {
171 | return "ERROR: Urecognized html header";
172 | }
173 | // TODO: We assume that indices represented by strictly 10 zeros ("0123456789".Length),
174 | // which could be wrong assumption. We need to implement more flrxible parsing here
175 | startHtmlIndex = Int32.Parse(htmlDataString.Substring(startHtmlIndex + "StartHTML:".Length, "0123456789".Length), CultureInfo.InvariantCulture);
176 | if (startHtmlIndex < 0 || startHtmlIndex > htmlDataString.Length)
177 | {
178 | return "ERROR: Urecognized html header";
179 | }
180 |
181 | int endHtmlIndex = htmlDataString.IndexOf("EndHTML:", StringComparison.OrdinalIgnoreCase);
182 | if (endHtmlIndex < 0)
183 | {
184 | return "ERROR: Urecognized html header";
185 | }
186 | // TODO: We assume that indices represented by strictly 10 zeros ("0123456789".Length),
187 | // which could be wrong assumption. We need to implement more flrxible parsing here
188 | endHtmlIndex = Int32.Parse(htmlDataString.Substring(endHtmlIndex + "EndHTML:".Length, "0123456789".Length), CultureInfo.InvariantCulture);
189 | if (endHtmlIndex > htmlDataString.Length)
190 | {
191 | endHtmlIndex = htmlDataString.Length;
192 | }
193 |
194 | return htmlDataString.Substring(startHtmlIndex, endHtmlIndex - startHtmlIndex);
195 | }
196 |
197 | ///
198 | /// Adds Xhtml header information to Html data string so that it can be placed on clipboard
199 | ///
200 | ///
201 | /// Html string to be placed on clipboard with appropriate header
202 | ///
203 | ///
204 | /// String wrapping htmlString with appropriate Html header
205 | ///
206 | internal static string AddHtmlClipboardHeader(string htmlString)
207 | {
208 | StringBuilder stringBuilder = new StringBuilder();
209 |
210 | // each of 6 numbers is represented by "{0:D10}" in the format string
211 | // must actually occupy 10 digit positions ("0123456789")
212 | int startHTML = HtmlHeader.Length + 6 * ("0123456789".Length - "{0:D10}".Length);
213 | int endHTML = startHTML + htmlString.Length;
214 | int startFragment = htmlString.IndexOf(HtmlStartFragmentComment, 0, StringComparison.OrdinalIgnoreCase);
215 | if (startFragment >= 0)
216 | {
217 | startFragment = startHTML + startFragment + HtmlStartFragmentComment.Length;
218 | }
219 | else
220 | {
221 | startFragment = startHTML;
222 | }
223 | int endFragment = htmlString.IndexOf(HtmlEndFragmentComment, 0, StringComparison.OrdinalIgnoreCase);
224 | if (endFragment >= 0)
225 | {
226 | endFragment = startHTML + endFragment;
227 | }
228 | else
229 | {
230 | endFragment = endHTML;
231 | }
232 |
233 | // Create HTML clipboard header string
234 | stringBuilder.AppendFormat(CultureInfo.InvariantCulture,HtmlHeader, startHTML, endHTML, startFragment, endFragment, startFragment, endFragment);
235 |
236 | // Append HTML body.
237 | stringBuilder.Append(htmlString);
238 |
239 | return stringBuilder.ToString();
240 | }
241 |
242 | #endregion Internal Methods
243 |
244 | // ---------------------------------------------------------------------
245 | //
246 | // Private methods
247 | //
248 | // ---------------------------------------------------------------------
249 |
250 | #region Private Methods
251 |
252 | private static void InvariantAssert(bool condition, string message)
253 | {
254 | if (!condition)
255 | {
256 | throw new ArgumentOutOfRangeException("Assertion error: " + message);
257 | }
258 | }
259 |
260 | ///
261 | /// Parses the stream of html tokens starting
262 | /// from the name of top-level element.
263 | /// Returns XmlElement representing the top-level
264 | /// html element
265 | ///
266 | private XmlElement ParseHtmlContent()
267 | {
268 | // Create artificial root elelemt to be able to group multiple top-level elements
269 | // We create "html" element which may be a duplicate of real HTML element, which is ok, as HtmlConverter will swallow it painlessly..
270 | XmlElement htmlRootElement = _document.CreateElement("html", XhtmlNamespace);
271 | OpenStructuringElement(htmlRootElement);
272 |
273 | while (_htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.EOF)
274 | {
275 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.OpeningTagStart)
276 | {
277 | _htmlLexicalAnalyzer.GetNextTagToken();
278 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Name)
279 | {
280 | string htmlElementName = _htmlLexicalAnalyzer.NextToken.ToLower(CultureInfo.InvariantCulture);
281 | _htmlLexicalAnalyzer.GetNextTagToken();
282 |
283 | // Create an element
284 | XmlElement htmlElement = _document.CreateElement(htmlElementName, XhtmlNamespace);
285 |
286 | // Parse element attributes
287 | ParseAttributes(htmlElement);
288 |
289 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.EmptyTagEnd || HtmlSchema.IsEmptyElement(htmlElementName))
290 | {
291 | // It is an element without content (because of explicit slash or based on implicit knowledge aboout html)
292 | AddEmptyElement(htmlElement);
293 | }
294 | else if (HtmlSchema.IsInlineElement(htmlElementName))
295 | {
296 | // Elements known as formatting are pushed to some special
297 | // pending stack, which allows them to be transferred
298 | // over block tags - by doing this we convert
299 | // overlapping tags into normal heirarchical element structure.
300 | OpenInlineElement(htmlElement);
301 | }
302 | else if (HtmlSchema.IsBlockElement(htmlElementName) || HtmlSchema.IsKnownOpenableElement(htmlElementName))
303 | {
304 | // This includes no-scope elements
305 | OpenStructuringElement(htmlElement);
306 | }
307 | else
308 | {
309 | // Do nothing. Skip the whole opening tag.
310 | // Ignoring all unknown elements on their start tags.
311 | // Thus we will ignore them on closinng tag as well.
312 | // Anyway we don't know what to do withthem on conversion to Xaml.
313 | }
314 | }
315 | else
316 | {
317 | // Note that the token following opening angle bracket must be a name - lexical analyzer must guarantee that.
318 | // Otherwise - we skip the angle bracket and continue parsing the content as if it is just text.
319 | // Add the following asserion here, right? or output "<" as a text run instead?:
320 | // InvariantAssert(false, "Angle bracket without a following name is not expected");
321 | }
322 | }
323 | else if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.ClosingTagStart)
324 | {
325 | _htmlLexicalAnalyzer.GetNextTagToken();
326 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Name)
327 | {
328 | string htmlElementName = _htmlLexicalAnalyzer.NextToken.ToLower(CultureInfo.InvariantCulture);
329 |
330 | // Skip the name token. Assume that the following token is end of tag,
331 | // but do not check this. If it is not true, we simply ignore one token
332 | // - this is our recovery from bad xml in this case.
333 | _htmlLexicalAnalyzer.GetNextTagToken();
334 |
335 | CloseElement(htmlElementName);
336 | }
337 | }
338 | else if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Text)
339 | {
340 | AddTextContent(_htmlLexicalAnalyzer.NextToken);
341 | }
342 | else if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Comment)
343 | {
344 | AddComment(_htmlLexicalAnalyzer.NextToken);
345 | }
346 |
347 | _htmlLexicalAnalyzer.GetNextContentToken();
348 | }
349 |
350 | // Get rid of the artificial root element
351 | if (htmlRootElement.FirstChild is XmlElement &&
352 | htmlRootElement.FirstChild == htmlRootElement.LastChild &&
353 | htmlRootElement.FirstChild.LocalName.ToLower(CultureInfo.InvariantCulture) == "html")
354 | {
355 | htmlRootElement = (XmlElement)htmlRootElement.FirstChild;
356 | }
357 |
358 | return htmlRootElement;
359 | }
360 |
361 | private XmlElement CreateElementCopy(XmlElement htmlElement)
362 | {
363 | XmlElement htmlElementCopy = _document.CreateElement(htmlElement.LocalName, XhtmlNamespace);
364 | for (int i = 0; i < htmlElement.Attributes.Count; i++)
365 | {
366 | XmlAttribute attribute = htmlElement.Attributes[i];
367 | htmlElementCopy.SetAttribute(attribute.Name, attribute.Value);
368 | }
369 | return htmlElementCopy;
370 | }
371 |
372 | private void AddEmptyElement(XmlElement htmlEmptyElement)
373 | {
374 | InvariantAssert(_openedElements.Count > 0, "AddEmptyElement: Stack of opened elements cannot be empty, as we have at least one artificial root element");
375 | XmlElement htmlParent = _openedElements.Peek();
376 | htmlParent.AppendChild(htmlEmptyElement);
377 | }
378 |
379 | private void OpenInlineElement(XmlElement htmlInlineElement)
380 | {
381 | _pendingInlineElements.Push(htmlInlineElement);
382 | }
383 |
384 | // Opens structurig element such as Div or Table etc.
385 | private void OpenStructuringElement(XmlElement htmlElement)
386 | {
387 | // Close all pending inline elements
388 | // All block elements are considered as delimiters for inline elements
389 | // which forces all inline elements to be closed and re-opened in the following
390 | // structural element (if any).
391 | // By doing that we guarantee that all inline elements appear only within most nested blocks
392 | if (HtmlSchema.IsBlockElement(htmlElement.LocalName))
393 | {
394 | while (_openedElements.Count > 0 && HtmlSchema.IsInlineElement(_openedElements.Peek().LocalName))
395 | {
396 | XmlElement htmlInlineElement = _openedElements.Pop();
397 | InvariantAssert(_openedElements.Count > 0, "OpenStructuringElement: stack of opened elements cannot become empty here");
398 |
399 | _pendingInlineElements.Push(CreateElementCopy(htmlInlineElement));
400 | }
401 | }
402 |
403 | // Add this block element to its parent
404 | if (_openedElements.Count > 0)
405 | {
406 | XmlElement htmlParent = _openedElements.Peek();
407 |
408 | // Check some known block elements for auto-closing (LI and P)
409 | if (HtmlSchema.ClosesOnNextElementStart(htmlParent.LocalName, htmlElement.LocalName))
410 | {
411 | _openedElements.Pop();
412 | htmlParent = _openedElements.Count > 0 ? _openedElements.Peek() : null;
413 | }
414 |
415 | if (htmlParent != null)
416 | {
417 | // NOTE:
418 | // Actually we never expect null - it would mean two top-level P or LI (without a parent).
419 | // In such weird case we will loose all paragraphs except the first one...
420 | htmlParent.AppendChild(htmlElement);
421 | }
422 | }
423 |
424 | // Push it onto a stack
425 | _openedElements.Push(htmlElement);
426 | }
427 |
428 | private bool IsElementOpened(string htmlElementName)
429 | {
430 | foreach (XmlElement openedElement in _openedElements)
431 | {
432 | if (openedElement.LocalName == htmlElementName)
433 | {
434 | return true;
435 | }
436 | }
437 | return false;
438 | }
439 |
440 | private void CloseElement(string htmlElementName)
441 | {
442 | // Check if the element is opened and already added to the parent
443 | InvariantAssert(_openedElements.Count > 0, "CloseElement: Stack of opened elements cannot be empty, as we have at least one artificial root element");
444 |
445 | // Check if the element is opened and still waiting to be added to the parent
446 | if (_pendingInlineElements.Count > 0 && _pendingInlineElements.Peek().LocalName == htmlElementName)
447 | {
448 | // Closing an empty inline element.
449 | // Note that HtmlConverter will skip empty inlines, but for completeness we keep them here on parser level.
450 | XmlElement htmlInlineElement = _pendingInlineElements.Pop();
451 | InvariantAssert(_openedElements.Count > 0, "CloseElement: Stack of opened elements cannot be empty, as we have at least one artificial root element");
452 | XmlElement htmlParent = _openedElements.Peek();
453 | htmlParent.AppendChild(htmlInlineElement);
454 | return;
455 | }
456 | else if (IsElementOpened(htmlElementName))
457 | {
458 | while (_openedElements.Count > 1) // we never pop the last element - the artificial root
459 | {
460 | // Close all unbalanced elements.
461 | XmlElement htmlOpenedElement = _openedElements.Pop();
462 |
463 | if (htmlOpenedElement.LocalName == htmlElementName)
464 | {
465 | return;
466 | }
467 |
468 | if (HtmlSchema.IsInlineElement(htmlOpenedElement.LocalName))
469 | {
470 | // Unbalances Inlines will be transfered to the next element content
471 | _pendingInlineElements.Push(CreateElementCopy(htmlOpenedElement));
472 | }
473 | }
474 | }
475 |
476 | // If element was not opened, we simply ignore the unbalanced closing tag
477 | return;
478 | }
479 |
480 | private void AddTextContent(string textContent)
481 | {
482 | OpenPendingInlineElements();
483 |
484 | InvariantAssert(_openedElements.Count > 0, "AddTextContent: Stack of opened elements cannot be empty, as we have at least one artificial root element");
485 |
486 | XmlElement htmlParent = _openedElements.Peek();
487 | XmlText textNode = _document.CreateTextNode(textContent);
488 | htmlParent.AppendChild(textNode);
489 | }
490 |
491 | private void AddComment(string comment)
492 | {
493 | OpenPendingInlineElements();
494 |
495 | InvariantAssert(_openedElements.Count > 0, "AddComment: Stack of opened elements cannot be empty, as we have at least one artificial root element");
496 |
497 | XmlElement htmlParent = _openedElements.Peek();
498 | XmlComment xmlComment = _document.CreateComment(comment);
499 | htmlParent.AppendChild(xmlComment);
500 | }
501 |
502 | // Moves all inline elements pending for opening to actual document
503 | // and adds them to current open stack.
504 | private void OpenPendingInlineElements()
505 | {
506 | if (_pendingInlineElements.Count > 0)
507 | {
508 | XmlElement htmlInlineElement = _pendingInlineElements.Pop();
509 |
510 | OpenPendingInlineElements();
511 |
512 | InvariantAssert(_openedElements.Count > 0, "OpenPendingInlineElements: Stack of opened elements cannot be empty, as we have at least one artificial root element");
513 |
514 | XmlElement htmlParent = _openedElements.Peek();
515 | htmlParent.AppendChild(htmlInlineElement);
516 | _openedElements.Push(htmlInlineElement);
517 | }
518 | }
519 |
520 | private void ParseAttributes(XmlElement xmlElement)
521 | {
522 | while (_htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.EOF && //
523 | _htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.TagEnd && //
524 | _htmlLexicalAnalyzer.NextTokenType != HtmlTokenType.EmptyTagEnd)
525 | {
526 | // read next attribute (name=value)
527 | if (_htmlLexicalAnalyzer.NextTokenType == HtmlTokenType.Name)
528 | {
529 | string attributeName = _htmlLexicalAnalyzer.NextToken;
530 | _htmlLexicalAnalyzer.GetNextEqualSignToken();
531 |
532 | _htmlLexicalAnalyzer.GetNextAtomToken();
533 |
534 | string attributeValue = _htmlLexicalAnalyzer.NextToken;
535 | xmlElement.SetAttribute(attributeName, attributeValue);
536 | }
537 | _htmlLexicalAnalyzer.GetNextTagToken();
538 | }
539 | }
540 |
541 | #endregion Private Methods
542 |
543 |
544 | // ---------------------------------------------------------------------
545 | //
546 | // Private Fields
547 | //
548 | // ---------------------------------------------------------------------
549 |
550 | #region Private Fields
551 |
552 | internal const string XhtmlNamespace = "http://www.w3.org/1999/xhtml";
553 |
554 | private HtmlLexicalAnalyzer _htmlLexicalAnalyzer = null;
555 |
556 | // document from which all elements are created
557 | private XmlDocument _document;
558 |
559 | // stack for open elements
560 | Stack _openedElements;
561 | Stack _pendingInlineElements;
562 |
563 | #endregion Private Fields
564 |
565 | private bool isDisposing = false;
566 | public void Dispose()
567 | {
568 | Dispose(isDisposing);
569 | }
570 |
571 | private void Dispose(bool disposing)
572 | {
573 | if (!disposing)
574 | {
575 | isDisposing = true;
576 |
577 | if (this._htmlLexicalAnalyzer != null)
578 | {
579 | this._htmlLexicalAnalyzer.Dispose();
580 | this._htmlLexicalAnalyzer = null;
581 | }
582 | }
583 | }
584 |
585 | }
586 | }
587 |
--------------------------------------------------------------------------------
/src/WpfRichText/XamlToHtmlParser/HtmlTokenType.cs:
--------------------------------------------------------------------------------
1 | //---------------------------------------------------------------------------
2 | //
3 | // File: HtmlTokenType.cs
4 | //
5 | // Copyright (C) Microsoft Corporation. All rights reserved.
6 | //
7 | // Description: Definition of token types supported by HtmlLexicalAnalyzer
8 | //
9 | //---------------------------------------------------------------------------
10 |
11 | namespace WpfRichText
12 | {
13 | ///
14 | /// types of lexical tokens for html-to-xaml converter
15 | ///
16 | internal enum HtmlTokenType
17 | {
18 | OpeningTagStart,
19 | ClosingTagStart,
20 | TagEnd,
21 | EmptyTagEnd,
22 | EqualSign,
23 | Name,
24 | Atom, // any attribute value not in quotes
25 | Text, //text content when accepting text
26 | Comment,
27 | EOF,
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/WpfRichText/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/WpfRichText/self.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ngaillet/WPF-RichText-Editor/71dc6a84f7e5dd92161a415600e63b5cfcc2da21/src/WpfRichText/self.snk
--------------------------------------------------------------------------------