Опубликован: 05.08.2010 | Доступ: свободный | Студентов: 2008 / 45 | Оценка: 4.50 / 4.40 | Длительность: 60:26:00
Самостоятельная работа 10:

Текст WPF

Упражнение 4. Преобразование HTML- в XAML-документ на лету

Мы долго изучали инструменты 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. В данном упражнении мы воспользуемся этой библиотекой 'вслепую', хотя никто не мешает познакомиться с исходным кодом, распаковав архив с проектом.

  • Добавьте к решению новый проект типа WPF Application с именем WpfText4 и назначьте его стартовым
  • Для узла References проекта вызовите контекстное меню, вызовите окно Add Reference одноименной командой и добавьте ссылку на библиотеку HtmlConversion.dll


  • В панели Solution Explorer раскройте узел проекта WpfText3 и перетаскиванием мышью скопируйте файл HtmlDoc.htm в корневой узел текущего проекта WpfText4
  • В панели Properties проверьте, сохранились ли свойства HtmlDoc.htm
    • Build Action = None
    • Copy to Output Directory=Copy if newer
  • Декларативный раздел XAML заполните следующим скриптом
<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;
            }
        }
    }
}
Здесь мы задали имя кодовой страницы "windows-1251"  !!!
  • Запустите проект - получится почти то же самое, что в предыдущем упражнении


Хоть мы и не видели самого XAML -документа, но трудно отрицать, что в основном он сформирован верно - вся разметка текста практически сохранилась. Для данного примера мы произвольно применили элемент FlowDocumentReader, хотя могли выбрать любой другой из ранее рассмотренных контейнеров.

Обратите внимание, что для FlowDocumentReader установлено много декларативных настроек. Стоит с ними поэкспериментировать, чтобы узнать получше, поскольку эти настройки имеются у всех контейнеров документов нефиксированного формата FlowDocument.

Упражнение 5. Преобразование HTML- в XAML-документ с записью в файл

Для больших документов преобразование на лету будет занимать время, да и все прелести WPF пользователь не увидит. Разумнее заранее подготовить документы в формате XAML, отредактировать их и уже готовыми подкачивать пользователю. В этом упражнении мы этим и займемся, для чего опять воспользуемся библиотекой HtmlConversion.dll.

  • Добавьте к решению новый проект WPF Application с именем WpfText5 и назначьте его стартовым
  • Добавьте к проекту ссылку на библиотеку 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>
  • На события Click кнопок подпишитесь вручную, чтобы оболочка правильно создала обработчики
  • Кодовая часть проекта будет такой
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


Пара HTML -файлов с простой разметкой и готовых XAML -файлов приведена в каталоге Source, но вы можете попробовать конвертировать любой другой файл с не очень сложной HTML -разметкой.

Конечно, это не промышленная программа Microsoft Silverlight или Microsoft Expression Blend, но элементарные преобразования она выполняет. И самое главное, - мы теперь немножко знаем, как и для чего это делается. Посмотрите, как неуклюже выглядит XamlPad, который находится в прилагаемом каталоге Source. Не удивляйтесь, что кириллицу он не отбражает, зато латиницу отображает прекрасно. В нем нет загрузки файлов, их следует указывать как параметр при запуске. И не нужно этому удивляться, ведь сделан-то он был фирмой Microsoft аж целых четыре года назад.

Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?