Мы долго изучали инструменты WPF для работы с текстом нефиксированного формата и даже рассмотрели загрузку XAML -документа из файла. Но реально такие документы тяжело получить: либо их нужно 'загонять' вручную в WPF -проект, либо заранее готовить в отдельном файле. Это большая работа и для ее выполнения требуются определенные знания по пройденному нами материалу, в частности, о внутристрочных элементах.
Для заблаговременной подготовки текста существует наипростейший редактор XamlPad, который поставляется вместе с оболочкой и его можно найти примерно в следующем месте компьютера (у меня так, как показано, но я его положил еще и в папку Source ):
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\XamlPad.exe
Фактически этот редактор проверяет синтаксис XAML -документа и чуток визуализирует его, но никак не автоматизирует работу по созданию.
К счастью, в настоящее время появились более развитые редакторы, которые интегрируются в оболочку или используются отдельно. Это Silverlight и Expression Blend - оба от компании Microsoft и оба платные.
Очень часто обычных возможностей по форматированию текста, используемых в HTML -редакторах, вполне достаточно, особенно для больших документов. Поэтому было бы желательно уметь преобразовывать текст HTML в XAML -документ. Встроенных библиотечных классов для решения этой задачи пока нет, зато есть сторонняя библиотека, автор которой - David Waddleton (http://blogs.msdn.com/DavidWaddleton). Проект этой библиотеки можно свободно загрузить по адресу:
http://waddleton.net/Documents/HtmlConversion.zip
Архив HtmlConversion.zip с проектом и готовый файл HtmlConversion.dll библиотеки приведены в прилагаемом каталоге Source. В данном упражнении мы воспользуемся этой библиотекой 'вслепую', хотя никто не мешает познакомиться с исходным кодом, распаковав архив с проектом.
<Window x:Class="WpfText4.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" MinHeight="300" MinWidth="300" > <FlowDocumentReader Name="flowDocumentReader" ViewingMode="Scroll" HorizontalContentAlignment="Left" IsPageViewEnabled="False" IsPrintEnabled="False" IsFindEnabled="False" IsScrollViewEnabled="True" IsTwoPageViewEnabled="False" Zoom="75" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Initialized="flowDocumentReader_Initialized" /> </Window>
using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Markup; using System.Xml; using System.IO; using HTMLConverter; namespace WpfText4 { public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void flowDocumentReader_Initialized(object sender, EventArgs e) { string path = Directory.GetCurrentDirectory() + "\\HtmlDoc.htm"; StreamReader streamReader = new StreamReader( path, Encoding.GetEncoding("windows-1251")); //MessageBox.Show(streamReader.CurrentEncoding.EncodingName); string text = streamReader.ReadToEnd(); flowDocumentReader.Document = ConvertTextToFlowDocument(text); } public static FlowDocument ConvertTextToFlowDocument(string text) { try { string xaml = HTMLConverter.HtmlToXamlConverter.ConvertHtmlToXaml(text, true); return XamlReader.Load(new XmlTextReader(new StringReader(xaml))) as FlowDocument; } catch (Exception) { return null; } } } }
Хоть мы и не видели самого XAML -документа, но трудно отрицать, что в основном он сформирован верно - вся разметка текста практически сохранилась. Для данного примера мы произвольно применили элемент FlowDocumentReader, хотя могли выбрать любой другой из ранее рассмотренных контейнеров.
Обратите внимание, что для FlowDocumentReader установлено много декларативных настроек. Стоит с ними поэкспериментировать, чтобы узнать получше, поскольку эти настройки имеются у всех контейнеров документов нефиксированного формата FlowDocument.
Для больших документов преобразование на лету будет занимать время, да и все прелести WPF пользователь не увидит. Разумнее заранее подготовить документы в формате XAML, отредактировать их и уже готовыми подкачивать пользователю. В этом упражнении мы этим и займемся, для чего опять воспользуемся библиотекой HtmlConversion.dll.
<Window x:Class="WpfText5.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" MinHeight="300" MinWidth="300" ResizeMode="CanResizeWithGrip" > <DockPanel Margin="3"> <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center"> <Button Name="convertToXaml" Width="85" Content="ConvertToXAML" Click="convertToXaml_Click" /> <Button Name="loadXaml" Width="85" Content="LoadXAML" Margin="5,0,5,0" Click="loadXaml_Click" /> <Button Name="editXaml" Width="85" Content="EditXAML" Click="editXaml_Click" /> </StackPanel> <FlowDocumentScrollViewer x:Name="flowDocumentScrollViewer" ScrollViewer.VerticalScrollBarVisibility="Visible" Margin="0,0,0,3" > <FlowDocument TextAlignment="Left"> <Paragraph TextAlignment="Center" FontWeight="Bold" FontSize="21"> Преобразование формата </Paragraph> <List MarkerStyle="Decimal" FontFamily="Arial" FontSize="14"> <ListItem> <Paragraph>ConvertToXAML - выбрать HTML-файлы и преобразовать в формат XAML </Paragraph> </ListItem> <ListItem> <Paragraph>LoadXAML - показать XAML-файл</Paragraph> </ListItem> <ListItem> <Paragraph>EditXAML - редактировать файл в XAMLPad</Paragraph> </ListItem> </List> </FlowDocument> </FlowDocumentScrollViewer> </DockPanel> </Window>
using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; // Включаем дополнительные пространства имен using System.IO; using Microsoft.Win32; using HTMLConverter; using System.Windows.Markup; namespace WpfText5 { public partial class Window1 : Window { OpenFileDialog openFileDialog = new OpenFileDialog(); public Window1() { InitializeComponent(); } // При конвертации создается подкаталог FilesXAML относительно текущего private void convertToXaml_Click(object sender, RoutedEventArgs e) { openFileDialog.Title = "Select HTML-files"; openFileDialog.Filter = "HTML Documents (*.htm; *.html)|*.htm;*.html"; openFileDialog.Multiselect = true; if (openFileDialog.ShowDialog() == false || openFileDialog.FileName == String.Empty) return; if (!Directory.Exists("FilesXAML")) Directory.CreateDirectory("FilesXAML"); string[] fileNames = openFileDialog.FileNames; foreach (String fileName in fileNames) { FileStream htmlStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); StreamReader myStreamReader = new StreamReader(htmlStream, Encoding.GetEncoding(1251)); // Убираем полный путь и расширение String name = fileName.Substring(0, fileName.LastIndexOf('.')); int start = name.LastIndexOf('\\'); start = start != -1 ? start + 1 : 0; name = name.Substring(start); // Конвертируем try { File.WriteAllText("FilesXAML\\" + name + ".xaml", HtmlToXamlConverter.ConvertHtmlToXaml(myStreamReader.ReadToEnd(), true), Encoding.GetEncoding(1251)); } catch (Exception exc) { MessageBox.Show(exc.Message); } finally { // Сохранили информацию и закрыли потоки htmlStream.Close(); myStreamReader.Close(); } } } private void loadXaml_Click(object sender, RoutedEventArgs e) { openFileDialog.Title = "Select XAML-file"; openFileDialog.Filter = "XAML Documents (*.xaml)|*.xaml"; openFileDialog.Multiselect = false; if (openFileDialog.ShowDialog() == false || openFileDialog.FileName == String.Empty) return; String fileName = openFileDialog.FileName; try { FileStream htmlStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); StreamReader myStreamReader = new StreamReader(htmlStream, Encoding.GetEncoding(1251)); FlowDocument content = XamlReader.Parse(myStreamReader.ReadToEnd()) as FlowDocument; flowDocumentScrollViewer.Document = content; } catch { } // Поток закрывать нельзя, разорвется связь с отображением документа } private void editXaml_Click(object sender, RoutedEventArgs e) { openFileDialog.Title = "Choose a folder with a XamlPad.exe"; openFileDialog.Filter = "XamlPad.exe|XamlPad.exe"; openFileDialog.RestoreDirectory = false; if (openFileDialog.ShowDialog() == false || !File.Exists("XamlPad.exe")) return; String pathXamlPad = System.IO.Path.Combine( Directory.GetCurrentDirectory(), "XamlPad.exe"); openFileDialog.Title = "Select XAML-file"; openFileDialog.Filter = "XAML Documents (*.xaml)|*.xaml"; openFileDialog.Multiselect = false; if (openFileDialog.ShowDialog() == false || openFileDialog.FileName == String.Empty) return; System.Diagnostics.Process exe = new System.Diagnostics.Process(); exe.StartInfo.FileName = pathXamlPad; //Имя файла для запуска exe.StartInfo.Arguments = openFileDialog.FileName; //Аргументы try { exe.Start(); } catch { } } } }
Пара HTML -файлов с простой разметкой и готовых XAML -файлов приведена в каталоге Source, но вы можете попробовать конвертировать любой другой файл с не очень сложной HTML -разметкой.
Конечно, это не промышленная программа Microsoft Silverlight или Microsoft Expression Blend, но элементарные преобразования она выполняет. И самое главное, - мы теперь немножко знаем, как и для чего это делается. Посмотрите, как неуклюже выглядит XamlPad, который находится в прилагаемом каталоге Source. Не удивляйтесь, что кириллицу он не отбражает, зато латиницу отображает прекрасно. В нем нет загрузки файлов, их следует указывать как параметр при запуске. И не нужно этому удивляться, ведь сделан-то он был фирмой Microsoft аж целых четыре года назад.