Практикум прикладного программирования на C# в среде VS.NET 2008

Текст WPF

Разбить на страницы
Показывать лекцию целиком

Все необходимые для выполнения данной работы программы можно найти в прилагаемом каталоге.

При изучении любого средства программирования используют примеры, которые иллюстрируют применение этого средства. Различают примеры двух видов:

  • Примеры технологий - иллюстрируют отдельные стороны рассматриваемой технологии программирования
  • Примеры приложений - иллюстрируют программирование предметной области с применением рассматриваемой технологии
  • В наших упражнениях рассматриваются преимущественно примеры технологий.

    Введение

    Текст и рисунки - главные информационные составляющие любого приложения. WPF имеет все стандартные элементы, необходимые для работы с текстом. Для простых случаев используются упрощенные элементы представления текста

  • Label - метка
  • TextBlock - текстовый блок
  • TextBox - текстовое поле
  • PasswordBox - поле пароля
  • Для более сложных случаев используются высокоразвитые элементы работы с документами. WPF разделяет текстовые документы на две основные категории на основе их предполагаемого использования:

  • Документы фиксированного формата
  • Документы нефиксированного формата
  • Документы фиксированного формата предназначены для приложений, требующих точного представления в режиме WYSIWYG (режим точного отображения - 'что вижу то и имею'), независимо от используемого дисплея или принтера. Документы фиксированного формата обычно используются при подготовке публикаций с помощью настольных издательских средств, обработке текста и разметке формы, где строгое соблюдение исходного дизайна страницы является обязательным. Содержимое документов фиксированного формата поддерживается классом FixedDocument, а для просмотра используется класс DocumentViewer.

    Документы фиксированного формата, как часть макета, поддерживает точное позиционирование содержимого элементов, независимо от используемых устройств отображения или печати. Например, страница формата фиксированного документа, просматриваемая на экране с разрешением 96 точек на дюйм, будет отображаться точно так же на выводе лазерного принтера с разрешением 600 точек на дюйм или устройстве фотовывода с разрешением 4800 точек на дюйм. Макет страницы остается неизменным во всех случаях, в то время как качество документа повышается в соответствии с возможностями каждого устройства.

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

    В зависимости от среды выполнения, документы нефиксированного формата оптимизируют просмотр и чтение для пользователя. Например, один тот же документ нефиксированного формата будет динамически переформатирован для оптимальной удобочитаемости как на 19-дюймовом экране с высоким разрешением, так и на небольшом 2x3-дюймовом экране карманного ПК. Кроме того, документы нефиксированного формата имеют множество встроенных возможностей, включая поиск, просмотр режимов оптимизации удобочитаемости и возможность изменения размера и вида шрифтов.

    Документы нефиксированного формата представлены классом содержимого FlowDocument. Этот класс обеспечивает размещение и форматирование содержимого потока с такими дополнительными свойствами документов, как разбивка на страницы и столбцы. Для отображения, редактирования и навигации по документу используются классы-контейнеры

  • RichTextBox - текстовое поле с расширенными возможностями
  • FlowDocumentScrollViewer - текстовый блок с расширенным содержимым и управляемой полосой прокрутки
  • FlowDocumentPageViewer - дает возможность просматривать документ постранично и имеет привычные навигационные элементы
  • FlowDocumentReader - элемент управления, в котором пользователь сам вибирает способ просмотра документа: одностраничный, многостраничный или с прокруткой
  • Любой из этих классов может иметь только один элемент содержимого FlowDocument, который адресуется их свойством Document. Если содержимое не загружено в контейнер отображения, то свойство Document имеет значение null.

    В данной лабораторной работе рассматриваются средства WPF для представления текста нефиксированного формата.

    Упражнение 1. Простые текстовые элементы

    Текстовые элементы Label, TextBlock, TextBox, PasswordBox обычно используются для создания небольшого текста в интерфейсе пользователя. Они обладают достаточными для этого возможностями. Но отображать большие потоки текста они непредназначены.

    Создание заготовки решения

  • Создайте решение WpfApp с проектом WpfText1 и назначьте его стартовым
  • Добавьте к проекту новый файл с именем FontPropertyLists.cs
  • Текстовый элемент Label

  • Заполните файл FontPropertyLists.cs следующим кодом
  • using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Media;
        
    namespace WpfText1
    {
        /// <summary>
        /// Код класса из примера MSDN
        /// </summary>
        public class FontPropertyLists
        {
            static Collection<FontFamily> fontFaces;
            static Collection<FontStyle> fontStyles;
            static Collection<FontWeight> fontWeights;
            static Collection<double> fontSizes;
            static int maxFontSizes = 36;
    
            /// <summary>
            /// Заполняет коллекцию доступных системе FontFamily
            /// </summary>
            public static ICollection<FontFamily> FontFaces
            {
                get
                {
                    if (fontFaces == null) fontFaces = new Collection<FontFamily>();
                    foreach (FontFamily fontFamily in Fonts.SystemFontFamilies)
                    {
                        fontFaces.Add(fontFamily);
                    }
                    return fontFaces;
                }
            }
        
            /// <summary>
            /// Заполняет коллекцию доступных FontStyles  
            /// </summary>
            public static ICollection FontStyles
            {
                get
                {
                    if (fontStyles == null)
                    {
                        fontStyles = new Collection<FontStyle>();
                        fontStyles.Add(System.Windows.FontStyles.Normal);
                        fontStyles.Add(System.Windows.FontStyles.Oblique);
                        fontStyles.Add(System.Windows.FontStyles.Italic);
                    }
                    return fontStyles;
                }
            }
        
            /// <summary>
            /// Заполняет коллекцию доступных FontWeight
            /// </summary>
            public static ICollection FontWeights
            {
                get
                {
                    if (fontWeights == null)
                    {
                        fontWeights = new Collection<FontWeight>();
        
                        Unique(System.Windows.FontWeights.Thin);
                        Unique(System.Windows.FontWeights.Light);
                        Unique(System.Windows.FontWeights.Regular);
                        Unique(System.Windows.FontWeights.Normal);
                        Unique(System.Windows.FontWeights.Medium);
                        Unique(System.Windows.FontWeights.Heavy);
                        Unique(System.Windows.FontWeights.SemiBold);
                        Unique(System.Windows.FontWeights.DemiBold);
                        Unique(System.Windows.FontWeights.Bold);
                        Unique(System.Windows.FontWeights.Black);
                        Unique(System.Windows.FontWeights.ExtraLight);
                        Unique(System.Windows.FontWeights.ExtraBold);
                        Unique(System.Windows.FontWeights.ExtraBlack);
                        Unique(System.Windows.FontWeights.UltraLight);
                        Unique(System.Windows.FontWeights.UltraBold);
                        Unique(System.Windows.FontWeights.UltraBlack);
                    }
                    return fontWeights;
                }
            }
        
            static void Unique(FontWeight fontWeight)
            {
                if (fontWeights.IndexOf(fontWeight) == -1)
                    fontWeights.Add(fontWeight);
            }
        
            /// <summary>
            /// Заполняет коллекцию доступных FontSizes 
            /// </summary>
            public static Collection<double> FontSizes
            {
                get
                {
                    if (fontSizes == null)
                    {
                        fontSizes = new Collection<double>();
                        for (double i = 8; i <= maxFontSizes; i++) fontSizes.Add(i);
                    }
                    return fontSizes;
                }
            }
        
            // Методы для преобразования из TypeName в Type
            public static bool CanParseFontStyle(string fontStyleName)
            {
                try
                {
                    FontStyleConverter converter = new FontStyleConverter();
                    converter.ConvertFromString(fontStyleName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            public static FontStyle ParseFontStyle(string fontStyleName)
            {
                FontStyleConverter converter = new FontStyleConverter();
                return (FontStyle)converter.ConvertFromString(fontStyleName);
            }
            public static bool CanParseFontWeight(string fontWeightName)
            {
                try
                {
                    FontWeightConverter converter = new FontWeightConverter();
                    converter.ConvertFromString(fontWeightName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            public static FontWeight ParseFontWeight(string fontWeightName)
            {
                FontWeightConverter converter = new FontWeightConverter();
                return (FontWeight)converter.ConvertFromString(fontWeightName);
            }
        }
    }
  • Заполните файл Window1.xaml следующей разметкой
  • <Window x:Class="WpfText1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" 
        Background="LightGray"
        Height="300"
        Width="300"
        MinHeight="300"
        MinWidth="300"
        ResizeMode="CanResizeWithGrip"
        WindowStartupLocation="CenterScreen"
        FocusManager.FocusedElement="{Binding ElementName='fontSizeListBox'}"        
            >
        <TabControl>
            <!--Вкладка Label-->
            <TabItem Header="Label">
                <!--Сетка 4x3-->
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="60" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <!--Пропорции ширины столбцов-->
                        <ColumnDefinition Width="2.7*" />
                        <ColumnDefinition Width="1.3*" />
                        <ColumnDefinition Width="1*" />
                    </Grid.ColumnDefinitions>
                    <Border 
                        Grid.Row="0" Grid.ColumnSpan="3"
                        BorderThickness="3"
                        BorderBrush="Violet"
                        >
                        <Label
                            VerticalContentAlignment="Center" 
                            HorizontalContentAlignment="Center"
                            Foreground="Red"
                            FontWeight="{Binding ElementName=fontWeightListBox, Path=SelectedItem}"
                            FontSize="{Binding ElementName=fontSizeListBox, Path=SelectedItem}"
                            FontFamily="{Binding ElementName=fontFamilyListBox, Path=SelectedItem}"
                            Content="Элемент Label" 
                            />
                    </Border>
                    <Label
                        Grid.Row="1" Grid.Column="0" 
                        HorizontalContentAlignment="Center"
                        Content="FontFamily:" 
                        />
                    <TextBox 
                        Grid.Row="2" Grid.Column="0"
                        Name="fontFamilyTextBox" 
                        Text="{Binding ElementName=fontFamilyListBox, Path=SelectedItem}" 
                        TextChanged="fontFamilyTextBox_TextChanged" 
                        />
                    <ListBox 
                        Grid.Row="3" Grid.Column="0" 
                        Name="fontFamilyListBox" SelectedIndex="0" 
                        />
                    <Label
                        Grid.Row="1" Grid.Column="1" 
                        HorizontalContentAlignment="Center"
                        Content="FontWeight:" 
                        />
                    <TextBox 
                        Grid.Row="2" Grid.Column="1" 
                        Name="fontWeightTextBox" 
                        Text="{Binding ElementName=fontWeightListBox, Path=SelectedItem}" 
                        TextChanged="fontWeightTextBox_TextChanged" 
                        />
                    <ListBox 
                        Grid.Row="3" Grid.Column="1" 
                        Name="fontWeightListBox" SelectedIndex="0" 
                        />
                    <Label 
                        Grid.Row="1" Grid.Column="2" 
                        HorizontalContentAlignment="Center"
                        Content="FontSize:" 
                        />
                    <TextBox 
                        Grid.Row="2" Grid.Column="2" 
                        Name="fontSizeTextBox" 
                        Text="{Binding ElementName=fontSizeListBox, Path=SelectedItem}" 
                        TextChanged="fontSizeTextBox_TextChanged" 
                        />
                    <ListBox 
                        Grid.Row="3" Grid.Column="2" 
                        Name="fontSizeListBox" SelectedIndex="0" 
                        />
                </Grid>
            </TabItem>
        </TabControl>
    </Window>
  • Зарегистрированные в элементах TextBox обработчики событий TextChanged заполните в файле Window1.xaml.cs
  • 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;
        
    namespace WpfText1
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
        
                // Заполняем коллекции и подключаем их к спискам ListBox
                this.fontFamilyListBox.ItemsSource = FontPropertyLists.FontFaces;
                this.fontWeightListBox.ItemsSource = FontPropertyLists.FontWeights;
                this.fontSizeListBox.ItemsSource = FontPropertyLists.FontSizes;
        
                // Выполняется после XAML
                this.fontSizeListBox.SelectedIndex = this.fontSizeListBox.Items.Count - 1;
                this.fontSizeListBox.ScrollIntoView(this.fontSizeListBox.Items.Count - 1);
            }
        
            // Сокрытие унаследованных от Window одноименных свойств
            new public FontFamily FontFamily
            {
                get { return (FontFamily)this.fontFamilyListBox.SelectedItem; }
                set
                {
                    this.fontFamilyListBox.SelectedItem = value;    // Выделить
                    this.fontFamilyListBox.ScrollIntoView(value);   // Прокрутить до видимого
                }
            }
            new public FontWeight FontWeight
            {
                get { return (FontWeight)this.fontWeightListBox.SelectedItem; }
                set
                {
                    this.fontWeightListBox.SelectedItem = value;    // Выделить
                    this.fontWeightListBox.ScrollIntoView(value);   // Прокрутить до видимого
                }
            }
            new public double FontSize
            {
                get { return (double)this.fontSizeListBox.SelectedItem; }
                set
                {
                    this.fontSizeListBox.SelectedItem = value;      // Выделить
                    this.fontSizeListBox.ScrollIntoView(value);     // Прокрутить до видимого
                }
            }
        
            private void fontFamilyTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // Привести ListBox в соответствие с содержимым TextBox
                this.FontFamily = new FontFamily(this.fontFamilyTextBox.Text);
            }
        
            private void fontWeightTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // Привести ListBox в соответствие с содержимым TextBox
                if (FontPropertyLists.CanParseFontWeight(this.fontWeightTextBox.Text))
                {
                    this.FontWeight = FontPropertyLists.ParseFontWeight(this.fontWeightTextBox.Text);
                }
            }
        
            private void fontSizeTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // Привести ListBox в соответствие с содержимым TextBox
                double fontSize;
                if (double.TryParse(this.fontSizeTextBox.Text, out fontSize))
                {
                    this.FontSize = fontSize;   // В поле действительно число
                }
            }
        }
    }
  • Запустите проект - получим инструмент для просмотра основных атрибутов библиотечного текстового элемента Label
  • Обратите внимание, как компоновка окна сохраняется при изменении его размеров и что окно имеет минимальный размер. Разберитесь с кодом разметки и процедурным кодом C#
  • Элемент TextBlock с анимацией

    Текстовый элемент TextBlock поддерживает способы программирования Windows и стили Web. Он обладает развитыми средствами анимации и динамического программирования стилей. Настройки элемента можно задавать в декларативном ( XAML ) и процедурном ( C# ) режимах. Проиллюстрируем некоторые возможности элемента TextBlock на примере.

  • Добавьте в файл Window1.xaml проекта WpfText1 вкладку TextBlock1 со следующей разметкой
  • <!--Вкладка TextBlock1-->
            <TabItem Header="TextBlock1">
                <Border Background="Black">
                    <Border Margin="25" CornerRadius="50" BorderThickness="2" BorderBrush="Yellow">
                        
                        <!--Радиальный градиент заливки Border-->
                        <Border.Background>
                            <RadialGradientBrush>
                                <GradientStop Color="Blue" Offset="0.1" />
                                <!--Непрозрачный alfa-канал и 000000 (Black)-->
                                <GradientStop Color="#FF000000" Offset="1.2" />
                            </RadialGradientBrush>
                        </Border.Background>
                    
                        <!--Анимация градиентной подсветки внешнего контура Border-->
                        <Border.BitmapEffect>
                            <OuterGlowBitmapEffect x:Name="glow" GlowColor="Yellow" GlowSize="0" />
                        </Border.BitmapEffect>
                        <Border.Triggers>
                            <EventTrigger RoutedEvent="Border.Loaded">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                                            Storyboard.TargetName="glow"
                                            Storyboard.TargetProperty="GlowSize"
                                            From="30" To="0" Duration="0:0:1" 
                                            AutoReverse="True"
                                            RepeatBehavior="Forever" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Border.Triggers>
                        
                        <!--Текстовый блок-->
                        <TextBlock Name="text" 
                                   Text=""
                                   HorizontalAlignment="Center" 
                                   VerticalAlignment="Center" 
                                   Foreground="White" FontSize="16"
                                   Initialized="text_Initialized"
                                   >
                            <TextBlock.Inlines>
                                <Bold>Это текстовый</Bold>
                                <LineBreak />
                                <Italic>элемент TextBlock</Italic>
                                <LineBreak />
                                <Run FontWeight="Bold" Foreground="Red">
                                    Очень
                                </Run>
                                <Run FontStyle="Italic" TextDecorations="Underline" Foreground="Yellow">
                                    красивый
                                </Run>
                            </TextBlock.Inlines>
                            
                            <!--Действия в TextBlock-->
                            <TextBlock.LayoutTransform>
                                <RotateTransform x:Name="rotateTransform" Angle="0" />
                            </TextBlock.LayoutTransform>
                            <TextBlock.Triggers>
                                <!--Анимация поворота-->
                                <EventTrigger RoutedEvent="TextBlock.MouseDown">
                                    <EventTrigger.Actions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <DoubleAnimation
                                                    Storyboard.TargetName="rotateTransform"
                                                    Storyboard.TargetProperty="Angle"
                                                    From="0" To="360" Duration="0:0:3"
                                                    RepeatBehavior="2x"
                                                    />
                                            </Storyboard>
                                        </BeginStoryboard>  
                                    </EventTrigger.Actions>
                                </EventTrigger>    
                                <!--Анимация изменения размера при заходе мыши-->  
                                <EventTrigger RoutedEvent="TextBlock.MouseEnter">
                                    <EventTrigger.Actions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <DoubleAnimation
                                                    Storyboard.TargetName="text" 
                                                    Storyboard.TargetProperty="FontSize"
                                                    To="26" Duration="0:0:0.2"
                                                    />  
                                            </Storyboard>
                                        </BeginStoryboard> 
                                    </EventTrigger.Actions>
                                </EventTrigger>
                                <!--Анимация изменения размера при сходе мыши-->  
                                <EventTrigger RoutedEvent="TextBlock.MouseLeave">
                                    <EventTrigger.Actions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <DoubleAnimation
                                                    Storyboard.TargetName="text" 
                                                    Storyboard.TargetProperty="FontSize"
                                                    Duration="0:0:1"
                                                    />    
                                            </Storyboard>
                                        </BeginStoryboard> 
                                    </EventTrigger.Actions>
                                </EventTrigger>
                            </TextBlock.Triggers>
                        </TextBlock>
                    </Border>
                </Border>
            </TabItem>
  • Добавьте в процедурную часть (файл Window1.xaml.cs ) код обработчика события Initialized элемента TextBlock, который в динамическом режиме дополняет декларативную настройку этого текстового элемента
  • private void text_Initialized(object sender, EventArgs e)
            {
                // Добавляем в TextBlock строку
                Run run = new Run("\nDynamic");
                run.FontFamily = new FontFamily("Curlz MT");
                text.Inlines.Add(run);
                run = new Run("Text");
                run.FontFamily = new FontFamily("Comic Sans MS");
                run.Foreground = Brushes.Aqua;
                text.Inlines.Add(new Bold(run));
            }
  • Запустите проект - получим анимационный текст с персональными настройками стилей
  • Изучите приведенный код
  • Элемент TextBlock с текстурой символов

    Текст в элементе TextBlock можно закрашивать произвольным рисунком.

  • В панели Solution Explorer создайте для корня дерева проекта папку Images и добавьте в нее (командой Add ) из прилагаемого к работе каталога Source файлы Image1.jpg и Image2.png
  • Добавьте в файл Window1.xaml новую вкладку со следующей разметкой
  • <!--Вкладка TextBlock2-->
            <TabItem Header="TextBlock2">
                <Canvas Background="Black">
                    <TextBlock 
                        Canvas.Left = "10" 
                        Canvas.Top="10" 
                        FontFamily="Arial" 
                        FontSize="36" 
                        Foreground="Red"
                        Text="SolidColorBrush" 
                    />
                    <TextBlock 
                        Canvas.Left = "10"
                        Canvas.Top="50" 
                        FontFamily="Arial" 
                        FontSize="75" 
                        Text="Image1">
                        <TextBlock.Foreground>
                            <ImageBrush ImageSource="Images\Image1.jpg" Opacity=".9" />
                        </TextBlock.Foreground>
                    </TextBlock>
                    <TextBlock 
                        Canvas.Left = "10" 
                        Canvas.Top="130" 
                        FontFamily="Arial" 
                        FontSize="75" 
                        Text="Image2">
                        <TextBlock.Foreground>
                            <ImageBrush ImageSource="Images/Image2.png" />
                        </TextBlock.Foreground>
                    </TextBlock>
                </Canvas>
            </TabItem>
  • Запустите приложение - должно получиться следующее
  • Изучите приведенный код
  • Автоматический перенос слов в TextBlock

    Если явно определить ширину элемента TextBlock и задать атрибут TextWraping="Wrap", то блочный текст будет автоматически размещаться внутри заданной ширины.

  • Добавьте в файл Window1.xaml новую вкладку со следующей разметкой
  • <!--Вкладка TextBlock3-->
            <!-- Элемент TextBlock с переносом строк -->        
            <TabItem Header="TextBlock3">
                <Grid Background="White">
                    <TextBlock 
                        TextWrapping="Wrap" 
                        FontSize="18" 
                        TextAlignment="Center" >
                        Если в элементе TextBlock задать атрибут TextWraping="Wrap", 
                        то блочный текст будет автоматически размещаться внутри 
                        заданной ширины
                        <LineBreak />
                        <Run Foreground="Red" FontSize="28" FontFamily="Monotype Corsiva">
                            Панель Grid здесь введена для создания белого фона
                        </Run>
                        <LineBreak />
                        <Run FontSize="12">
                            Общие настройки родителя действуют на потомков до тех пор,
                            пока не будут переопределены внутри самих потомков
                        </Run>
                    </TextBlock>
                </Grid>
            </TabItem>
  • Запустите приложение - должно получиться следующее
  • Изучите приведенный код
  • Использование дополнительных шрифтов

    Иногда нет уверенности в том, что на компьютере пользователя есть нужные шрифты, с использованием которых программист разрабатывает интерфейс. В таком случае шрифты можно поставлять вместе с приложением.

  • Перейдите в раздел "Панель управления", откройте папку " Шрифты " и перетащите мышью шрифт, например, "Monotype Corsiva" в какую-нибудь папку (файл шрифта уже есть в прилагаемом каталоге Source )
  • В панели Solution Explorer создайте в корне проекта папку Fonts, выделите ее и добавьте командой Project/Add Existing Item файл шрифта MTCORSVA.TTF
  • Добавьте в файл Window1.xaml новую вкладку со следующей разметкой
  • <!--Вкладка TextBlock4-->
            <!-- Элемент TextBlock с дополнительным шрифтом -->
            <TabItem Header="TextBlock4">
                <Grid Background="White">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock
                        FontFamily="Fonts/MTCORSVA.TTF#Monotype Corsiva"
                        FontSize="20"
                        TextWrapping="Wrap"
                        TextAlignment="Justify"
                        Margin="10"
                        ToolTip="Элемент TextBlock с дополнительным шрифтом">
                        Шрифт для этого текста подключен явно и поставляется 
                        вместе с приложением на тот случай, если у бедного
                        пользователя компьютер окажется еще беднее.
                    </TextBlock>
                    <TextBlock Grid.Row="1" Margin="10" TextWrapping="Wrap">
                        В этом блоке текста используется шрифт по умолчанию и 
                        нет всплывающей подсказки ToolTip, а в верхнем блоке есть                    
                    </TextBlock>
                </Grid>
            </TabItem>
  • Запустите приложение - должно получиться следующее
  • Изучите приведенный код
  • Элементы TextBox и PasswordBox

  • Добавьте к проекту следующую вкладку со скриптовым кодом
  • <!--TextBox и PasswordBox-->
            <TabItem Header="TextBox" Selector.IsSelected="True">
                <StackPanel VerticalAlignment="Center" Margin="10">
                    <TextBlock HorizontalAlignment="Center">
                        <Run FontSize="14" FontWeight="Bold">
                            Элементы PasswordBox и TextBox
                        </Run>
                    </TextBlock>
                    <Grid Height="20" />
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="Поле PasswordBox: " VerticalAlignment="Center" />
                        <PasswordBox Grid.Column="1" Name="passwordBox" />
                    </Grid>
                    <Grid Height="20" />
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="Поле TextBox: " VerticalAlignment="Center" />
                        <TextBox Grid.Column="1" Name="textBox"></TextBox>
                    </Grid>
                        <Grid Height="20" />
                    <Grid HorizontalAlignment="Center">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition Width="5" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Button Grid.Column="0" Content="Показать" Click="Button_Click" />
                        <ToggleButton Grid.Column="2" Name="toggleButton" Content="EnabledPassword" 
                                      Checked="toggleButton_Checked" Unchecked="toggleButton_Checked"/>
                    </Grid>
                </StackPanel>
            </TabItem>
  • Выделенную в разметке кнопок регистрацию обработчиков наберите вручную
  • Добавьте в кодовую часть следующие обработчики
  • private void Button_Click(object sender, RoutedEventArgs e)
            {
                textBox.Text = passwordBox.Password;
            }
        
            bool flagState = true;
            Brush color;
            private void toggleButton_Checked(object sender, RoutedEventArgs e)
            {
                // Сохраняем первоначальный цвет кнопки 
                if (flagState)
                {
                    color = toggleButton.Background;
                    flagState = false;
                }
        
                if (toggleButton.IsChecked == true)
                {
                    passwordBox.IsEnabled = false;
                    toggleButton.Background = Brushes.Red;
                }
                else
                {
                    passwordBox.IsEnabled = true;
                    toggleButton.Background = color;
                }
            }
  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Упражнение 2. Поддержка документов нефиксированного формата

    Классы-упаковки содержимого нефиксированного формата

  • FlowDocumentReader
  • RichTextBox
  • FlowDocumentScrollViewer
  • FlowDocumentPageViewer
  • имеют дочерний элемент FlowDocument. В свою очередь, FlowDocument задает строгую модель содержимого для своего дочернего содержимого. Дочерние элементы верхнего уровня, содержащиеся в FlowDocument, должны быть производными от Block и называются блочными. Допустимыми дочерними элементами верхнего уровня в FlowDocument являются следующие блоки:

    Блок Описание
    Paragraph Содержит текст, возможно форматированный.
    List Содержит списки разного типа (нумерованный, маркированный и т. д.).
    Table Содержит таблицы, похожие на таблицы Microsoft Word или HTML.
    BlockUIContainer Содержит различные элементы пользовательского интерфейса, становящиеся частью размещения.
    Section Содержит группу прочих блоков. Разделы удобны для применения общих атрибутов к группам блоков, например, одни и те же атрибуты шрифта для нескольких абзацев.

    Внутри блоков дополнительное форматирование текста обеспечивается элементами, которые называются внутристрочными элементами. К ним относятся

  • элемент Bold
  • элемент Italic
  • элемент LineBreak
  • элемент List
  • элемент ListItem
  • элемент Paragraph
  • элемент Run
  • элемент Section
  • элемент Span
  • элемент Underline
  • Внутристрочные элементы могут быть вложенными друг в друга внутри блока или вместе с родительским блоком вложены в другой блок.

    Некоторые элементы могут дополнительно настраиваться, например, такими атрибутами, как

  • BreakPageBefore=True" - начать с новой страницы
  • FontSize="24" - размер шрифта
  • Typography.Variants="Superscript" - надстрочный
  • Typography.Variants="Subscript" - подстрочный
  • TextIndent="25" - отступ первой строки блока
  • TextIndent="-15" - выступ первой строки блока
  • Margin="20,0,0,0" - отступ блока слева
  • TextAlignment="Center" - текст посредине контейнера рендеринга (представления)
  • MarkerStyle="None" - тип маркера в элементе List
  • Использование FlowDocument с контейнером по умолчанию

    Создание экземпляра класса FlowDocument автоматически запускает родительский контейнер FlowDocumentReader, который размещает содержимое и имеет стандартную панель управления документом.

  • Добавьте к решению новый проект WpfText2 типа WPF Application и назначьте его стартовым
  • В декларативный раздел XAML добавьте контейнер <TabControl> с вкладкой <TabItem Header="Text1">
  • Заполните файл Window1.xaml следующим кодом
  • <Window x:Class="WpfText2.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Background="LightGray"
        Title="Window1" MinHeight="300" MinWidth="300" Height="300" Width="300">
        
        <TabControl>
            <TabItem Header="Text1">
                <FlowDocument Background="White">
                    <Paragraph TextAlignment="Center" FontWeight="Bold">
                        Потоковый 
                        <Span Foreground="Red"> документ</Span> 
                        <Italic>с разбивкой на страницы</Italic>
                    </Paragraph>
                    <List MarkerStyle="Decimal" FontFamily="Arial">
                        <ListItem>
                            <Paragraph>Первый элемент ListItem</Paragraph>
                        </ListItem>
                        <ListItem>
                            <Paragraph>Второй элемент ListItem</Paragraph>
                        </ListItem>
                        <ListItem>
                            <Paragraph>Третий элемент ListItem</Paragraph>
                        </ListItem>
                    </List>
                    <Paragraph FontSize="12">Класс FlowDocument поддерживает списки, определения  
                        <Bold>
                            <Span FontFamily="Comic Sans MS" FontSize="12" Foreground="Blue">
                                семейств шрифтов, цвет
                            </Span>
                        </Bold> размеры и стили.
                    </Paragraph>
                </FlowDocument>
            </TabItem>
        </TabControl>
    </Window>
  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Использование контейнера RichTextBox

    Оборачивание элемента FlowDocument в контейнер RichTextBox отключает панель навигации по тексту.

  • В контейнер <TabControl> добавьте вкладку <TabItem Header="Text2" Selector.IsSelected="True"> и заполните ее следующим кодом
  • <TabItem Header="Text2" Selector.IsSelected="True">
                <RichTextBox IsReadOnly="True" FontSize="21" >
                    <FlowDocument Background="White">
                        <Paragraph TextAlignment="Center" FontWeight="Bold">
                            Потоковый
                            <Span Foreground="Red">документ</Span>
                            <Italic>обернут в RichTextBox</Italic>
                        </Paragraph>
                        <List MarkerStyle="LowerLatin"  FontFamily="Arial" FontSize="14">
                            <ListItem>
                                <Paragraph>Маркер LowerLatin</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>Второй элемент ListItem</Paragraph>
                            </ListItem>
                        </List>
                        <List MarkerStyle="Box"  FontFamily="Arial" FontSize="14">
                            <ListItem>
                                <Paragraph>Маркер Box</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>Второй элемент ListItem</Paragraph>
                            </ListItem>
                        </List>
                        <Paragraph FontSize="12">В родительском контейнере RichTextBox 
                            <Bold>
                                <Span FontFamily="Comic Sans MS" FontSize="12" Foreground="Blue">
                                    включен режим IsReadOnly="True" 
                                </Span>
                            </Bold> и не появляется полоса прокрутки при длинном тексте
                        </Paragraph>
                    </FlowDocument>
                </RichTextBox>
            </TabItem>

    Присоединенный к дескриптору вкладки атрибут Selector.IsSelected="True" позиционирует ее при открытии окна.

  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Использование контейнера FlowDocumentScrollViewer

    Оборачивание элемента FlowDocument в контейнер FlowDocumentScrollViewer по умолчанию отключает панель инструментов документа и добавляет вертикальную панель прокрутки, которую можно настраивать. Вертикальная полоса прокрутки включается с помощью присоединенного свойства. Панель инструментов включается свойством IsToolBarVisible="True" (по умолчанию IsToolBarVisible="False" ).

    FlowDocument - единственный допустимый дочерний элемент объекта FlowDocumentScrollViewer. В FlowDocumentScrollViewer может быть размещен не более чем один элемент FlowDocument.

  • В контейнер <TabControl> добавьте вкладку <TabItem Header="Text3"> и заполните ее следующим кодом
  • <TabItem Header="Text3">
                <FlowDocumentScrollViewer 
                    ScrollViewer.VerticalScrollBarVisibility="Auto" 
                    IsToolBarVisible="False"
                    >
                    <FlowDocument Background="White" FontSize="14">
                        <Paragraph TextAlignment="Center" FontWeight="Bold">
                            FlowDocument
                            <Italic>помещен в</Italic>
                            <Span Foreground="Red">FlowDocumentScrollViewer</Span>
                        </Paragraph>
                        <List MarkerStyle="None" FontFamily="Arial">
                            <ListItem>
                                <Paragraph>Буря мглою небо кроет</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>Вихри снежные крутя.</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>То как зверь она завоет</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>То заплачет как дитя.</Paragraph>
                            </ListItem>
                        </List>
                        <Paragraph TextAlignment="Left" FontSize="12">Текст 
                            нефикированного формата FlowDocument нужно заключать в параграфы 
                            (абзацы), внутри которых можно применять стилевые элементы.
                        </Paragraph>
                        <Paragraph>
                            <Bold>
                                <Span FontFamily="Comic Sans MS" FontSize="12" Foreground="Blue">
                                    Буря мглою небо кроет вихри снежные крутя.
                                    <LineBreak />
                                </Span>
                            </Bold>
                            То как зверь она завоет, то заплачет как дитя.
                        </Paragraph>
                    </FlowDocument>
                </FlowDocumentScrollViewer>
            </TabItem>

    Присоединенный к дескриптору <FlowDocumentScrollViewer> атрибут ScrollViewer.VerticalScrollBarVisibility="Auto" обеспечивает автоматическое появление полосы прокрутки, если текст не помещается на экране. Атрибуты, установленные в контейнере <FlowDocument>, действуют по умолчанию на все дочерние элементы, если аналогичные атрибуты в них не переопределены. В списках маркеры элементов можно менять или вообще отключать.

  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Использование контейнера FlowDocumentPageViewer

    Использование контейнера FlowDocumentPageViewer для FlowDocument позволяет представлять документ как одну страницу без средства поиска текста и разбивки его на страницы и колонки, но оставляет панель изменения масштаба и навигации. Однако в самом FlowDocument есть свойства, позволяющие настраивать обычный и многоколоночный текст. Вот некоторые из них

  • TextAlignment="Justify" - выравнивание текста по обеим границам колонок (включен по умолчанию)
  • IsHyphenationEnabled="True" - включение расстановки переносов (при наличии словарей)
  • IsOptimalParagraphEnabled="True" - включение оптимального абзаца, выравнивающего пробелы между словами за счет разрежения символов в самих словах при режиме TextAlignment="Justify"
  • ColumnGap="25" - ширина между колонками в логических пикселах
  • ColumnWidth="150" - предпочтительная (минимальная) ширина колонки
  • IsColumnWidthFlexible="False" - это значение флага явно запрещает дозаполнять окно просмотра и устанавливает жесткую ширину колонки
  • ColumnRuleWidth="5" - ширина вертикальных линеек, разделяющих колонки текста
  • ColumnRuleBrush="Red" - цвет линеек
  • В контейнер <TabControl> добавьте вкладку <TabItem Header="Text4"> и заполните ее следующим кодом
  • <TabItem Header="Text4">
                <FlowDocumentPageViewer>
                    <FlowDocument 
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        Background="White"
                        ColumnWidth="150"
                        ColumnRuleWidth="2"
                        ColumnRuleBrush="Red"
                        ColumnGap="30"
                        IsHyphenationEnabled="True"
                        IsOptimalParagraphEnabled="True"
                        >
                        <Paragraph FontSize="18" FontWeight="Bold" TextAlignment="Center">
                            Примеры форматирования FlowDocument amp; FlowDocumentPageViewer
                        </Paragraph>
                        <Paragraph>
                              Это абзац с атрибутами по умолчанию.
                        </Paragraph>
                        <Paragraph>
                              Это еще один абзац с атрибутами по умолчанию.
                        </Paragraph>
                        <Paragraph FontSize="18">Абзац с FontSize="18"</Paragraph>
                        <Paragraph FontSize="15">Абзац с FontSize="15"</Paragraph>
                        <Paragraph FontSize="15">Список lt;List FontFamily="Arial"gt;</Paragraph>
                        <List FontFamily="Arial">
                            <ListItem>
                                <Paragraph>ListItem 1</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 2</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 3</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 4</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 5</Paragraph>
                            </ListItem>
                        </List>
                        <Paragraph>
                            <Bold>Это элемент lt;Boldgt; внутри абзаца lt;Paragraphgt;</Bold>
                        </Paragraph>
                        <Paragraph>
                            <Underline>Это элемент Underline внутри абзаца</Underline>
                        </Paragraph>
                        <Paragraph>
                            <Bold>
                                <Underline>Подчеркнутый внутри полужирного</Underline>
                            </Bold>
                        </Paragraph>
                        <Paragraph>
                            <Italic>Это элемент lt;Italicgt;</Italic>
                        </Paragraph>
                        <Paragraph>
                            <Span>Элемент Span с настройками по умолчанию</Span>
                        </Paragraph>
                        <Paragraph>
                            <Run>Элемент Run с настройками по умолчанию</Run>
                        </Paragraph>
                        <Paragraph FontSize="15">Subscript, Superscript</Paragraph>
                        <Paragraph>
                            <Run Typography.Variants="Superscript">This text is Superscripted.</Run> 
                            This text isn't.
                        </Paragraph>
                        <Paragraph>
                            <Run Typography.Variants="Subscript">This text is Subscripted.</Run> 
                            This text isn't.
                        </Paragraph>
                        <Paragraph TextIndent="-25" Margin="25,0">
                            If a font does not support a particular form (such as Superscript) 
                            a default font form will be displayed.
                        </Paragraph>
                        <Paragraph FontSize="15">Blocks, breaks, paragraph</Paragraph>
                        <Section>
                            <Paragraph>A block section of text</Paragraph>
                        </Section>
                        <Section>
                            <Paragraph>Another block section of text</Paragraph>
                        </Section>
                        <Paragraph>
                            <LineBreak/>
                        </Paragraph>
                        <Section>
                            <Paragraph>... and another section, preceded by a LineBreak</Paragraph>
                        </Section>
                        <Section BreakPageBefore="True"/>
                        <Section>
                            <Paragraph>... and another section, preceded by a PageBreak</Paragraph>
                        </Section>
                        <Paragraph>Finally, a paragraph. Note the break between this paragraph ...</Paragraph>
                        <Paragraph TextIndent="25">... and this paragraph, and also the left indention.</Paragraph>
                    </FlowDocument>
                </FlowDocumentPageViewer>
            </TabItem>

    Здесь мы применили основной набор элементов форматирования текста 'на лету'. Обратите внимание, что в разметке угловые скобки кодируются как в HTML. Другие управляющие символы разметки кодируются аналогично, например, ' ' кодируется как ' amp; '. Знак подчеркивания ' _ ' считается служебным для выделения горячих клавиш в меню, поэтому в ином употреблении должен экранироваться таким же знаком подчеркивания.

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

  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Обратите внимание, что в длинных словах кириллический текст объект FlowDocument не переносит, а латиницу - переносит.

  • Проверьте действие атрибута IsOptimalParagraphEnabled="True", включающего оптимальный параграф
  • Пример из MSDN

    Следующий пример взят из MSDN (http://msdn.microsoft.com/ru-ru/library/system.windows.controls.flowdocumentscrollviewer.aspx) и иллюстрирует дополнительные возможности разметки неформатированного текста в FlowDocument. Он не содержит кириллицы, поэтому дальше нам пригодится при решении проблем кодировки.

  • Добавьте новую вкладку с приведенным кодом
  • <TabItem Header="Text5" Selector.IsSelected="True">
                <FlowDocumentScrollViewer Margin="10" BorderBrush="Black" BorderThickness="1">
                    <FlowDocument 
                        ColumnWidth="400" 
                        IsOptimalParagraphEnabled="True" 
                        IsHyphenationEnabled="True"
                        >
                        <Section FontSize="12">
                            <Paragraph>
                                <Bold>Neptune</Bold> (planet), major planet in the solar system, eighth planet
                                from the Sun and fourth largest in diameter.  Neptune maintains an almost constant
                                distance, about 4,490 million km (about 2,790 million mi), from the Sun.  Neptune
                                revolves outside the orbit of Uranus and for most of its orbit moves inside the
                                elliptical path of the outermost planet Pluto (see Solar System). Every 248 years,
                                Pluto’s elliptical orbit brings the planet inside Neptune’s nearly circular orbit
                                for about 20 years, temporarily making Neptune the farthest planet from the Sun.
                                The last time Pluto’s orbit brought it inside Neptune’s orbit was in 1979. In
                                1999 Pluto’s orbit carried it back outside Neptune’s orbit.
                                <Figure 
                                      Width="140" Height="50" Background="GhostWhite" 
                                      HorizontalAnchor="PageLeft" 
                                      HorizontalOffset="100" VerticalOffset="20"
                                      >
                                    <Paragraph 
                                        FontStyle="Italic" TextAlignment="Left"
                                        Background="Beige" Foreground="DarkGreen"
                                        >
                                        Neptune has 72 times Earth's volume...
                                    </Paragraph>
                                </Figure>
                                
                                <Floater 
                                    Background="GhostWhite" 
                                    Width="285" HorizontalAlignment="Left"
                                    >
                                    <Table CellSpacing="5">
                                        <Table.Columns>
                                            <TableColumn Width="155"/>
                                            <TableColumn Width="130"/>
                                        </Table.Columns>
                                        
                                        <TableRowGroup>
                                            <TableRow>
                                                <TableCell ColumnSpan="3">
                                                    <Paragraph>Neptune Stats</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            
                                            <TableRow Background="LightGoldenrodYellow" FontSize="12">
                                                <TableCell>
                                                    <Paragraph FontWeight="Bold">Mean Distance from Sun</Paragraph>
                                                </TableCell>
                                                <TableCell>
                                                    <Paragraph>4,504,000,000 km</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            
                                            <TableRow FontSize="12" Background="LightGray">
                                                <TableCell>
                                                    <Paragraph FontWeight="Bold">Mean Diameter</Paragraph>
                                                </TableCell>
                                                <TableCell>
                                                    <Paragraph>49,532 km</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            
                                            <TableRow Background="LightGoldenrodYellow" FontSize="12">
                                                <TableCell>
                                                    <Paragraph FontWeight="Bold">Approximate Mass</Paragraph>
                                                </TableCell>
                                                <TableCell>
                                                    <Paragraph>1.0247e26 kg</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            <TableRow>
                                                <TableCell ColumnSpan="4">
                                                    <Paragraph FontSize="10" FontStyle="Italic">
                                                        Information from the
                                                        <Hyperlink 
                                                            NavigateUri="http://encarta.msn.com/encnet/refpages/artcenter.aspx"
                                                            >
                                                            Encarta
                                                        </Hyperlink>
                                                        web site.
                                                    </Paragraph>
                                                </TableCell>
                                            </TableRow>
                                        </TableRowGroup>
                                    </Table>
                                </Floater>
                            </Paragraph>
                            <Paragraph>
                                Astronomers believe Neptune has an inner rocky core that is surrounded by a vast
                                ocean of water mixed with rocky material. From the inner core, this ocean extends
                                upward until it meets a gaseous atmosphere of hydrogen, helium, and trace amounts
                                of methane. Neptune has four rings and 11 known moons. Even though Neptune's volume
                                is 72 times Earth’s volume, its mass is only 17.15 times Earth’s mass. Because of
                                its size, scientists classify Neptune—along with Jupiter, Saturn, and Uranus—as
                                one of the giant or Jovian planets (so-called because they resemble Jupiter).
                            </Paragraph>
                            <Paragraph>
                                <Figure 
                                    Width="140" Height="50" 
                                    Background="GhostWhite" TextAlignment="Left" 
                                    HorizontalAnchor="PageCenter" WrapDirection="Both"
                                    >
                                    <Paragraph FontStyle="Italic" Background="Beige" Foreground="DarkGreen" >
                                        Neptune has an orbital period of ~20 years...
                                    </Paragraph>
                                </Figure>
                                Mathematical theories of astronomy led to the discovery of Neptune. To account for
                                wobbles in the orbit of the planet Uranus, British astronomer John Couch Adams and
                                French astronomer Urbain Jean Joseph Leverrier independently calculated the existence
                                and position of a new planet in 1845 and 1846, respectively. They theorized that the
                                gravitational attraction of this planet for Uranus was causing the wobbles in Uranus’s
                                orbit. Using information from Leverrier, German astronomer Johann Gottfried Galle first
                                observed the planet in 1846.
                            </Paragraph>
                        </Section>
                    </FlowDocument>
                </FlowDocumentScrollViewer>
            </TabItem>
  • Запустите приложение - результат будет таким
  • Поизменяйте размер окна и разберитесь с кодом
  • Описание использованных в примере элементов можно найти на http://msdn.microsoft.com/ru-ru/library/system.windows.documents.aspx

    Загрузка FlowDocument из XAML-файла

    Документ нефиксированного формата можно создать заранее в XAML -редакторе, сохранить его в файле и загружать во время выполнения в элемент управления просмотром. Продемонстрируем это на предыдущем примере.

  • Добавьте в разметку новую вкладку следующего содержания (атрибут подписки на событие Initialized введите вручную, чтобы правильно создать обработчик)
  • <TabItem Header="Text6" Selector.IsSelected="True">
                <FlowDocumentScrollViewer 
                    Margin="10" 
                    BorderBrush="Black"
                    BorderThickness="1"
                    Initialized="FlowDocumentScrollViewer_Initialized"
                    Name="documentReader" />
            </TabItem>

    Здесь мы декларативно определили и настроили пустой именованный контейнер просмотра документа, а сам документ с содержимым FlowDocument загрузим из файла программно и присоединим к свойству Document этого контейнера просмотра.

  • Уберите из предыдущей вкладки атрибут Selector.IsSelected="True", чтобы при запуске приложения сразу открывалась вкладка Text6
  • Добавьте к проекту WpfText2 новый XML -файл с именем XMLFile1.xml и измените его расширение c xml на xaml
  • Выделите новый файл и измените через панель Properties его свойства на
  • Build Action = None
  • Copy to Output Directory=Copy if newer
  • Скопируйте из предыдущего примера контейнер FlowDocument вместе с его содержимым в файл XMLFile1.xaml и добавьте атрибуты пространств имен WPF в дескриптор FlowDocument
  • Мы создали, так называемый, свободный файл XAML. Свободный файл XAML - это файл со следующими характеристиками:

  • Содержит только XAML (то есть не код C# )
  • Имеет объявление соответствующего пространства имен
  • Имя файла имеет расширение XAML
  • В нашем случае содержимое файла XMLFile1.xaml должно стать таким

    <FlowDocument
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ColumnWidth="400"
        IsOptimalParagraphEnabled="True"
        IsHyphenationEnabled="True"
        >
      <Section FontSize="12">
        <Paragraph>
          <Bold>Neptune</Bold> (planet), major planet in the solar system, eighth planet
          from the Sun and fourth largest in diameter.  Neptune maintains an almost constant
          distance, about 4,490 million km (about 2,790 million mi), from the Sun.  Neptune
          revolves outside the orbit of Uranus and for most of its orbit moves inside the
          elliptical path of the outermost planet Pluto (see Solar System). Every 248 years,
          Pluto’s elliptical orbit brings the planet inside Neptune’s nearly circular orbit
          for about 20 years, temporarily making Neptune the farthest planet from the Sun.
          The last time Pluto’s orbit brought it inside Neptune’s orbit was in 1979. In
          1999 Pluto’s orbit carried it back outside Neptune’s orbit.
          <Figure
                Width="140" Height="50" Background="GhostWhite"
                HorizontalAnchor="PageLeft"
                HorizontalOffset="100" VerticalOffset="20"
                >
            <Paragraph
                FontStyle="Italic" TextAlignment="Left"
                Background="Beige" Foreground="DarkGreen"
                >
              Neptune has 72 times Earth's volume...
            </Paragraph>
          </Figure>
      
          <Floater
              Background="GhostWhite"
              Width="285" HorizontalAlignment="Left"
                                    >
            <Table CellSpacing="5">
              <Table.Columns>
                <TableColumn Width="155"/>
                <TableColumn Width="130"/>
              </Table.Columns>
      
              <TableRowGroup>
                <TableRow>
                  <TableCell ColumnSpan="3">
                    <Paragraph>Neptune Stats</Paragraph>
                  </TableCell>
                </TableRow>
      
                <TableRow Background="LightGoldenrodYellow" FontSize="12">
                  <TableCell>
                    <Paragraph FontWeight="Bold">Mean Distance from Sun</Paragraph>
                  </TableCell>
                  <TableCell>
                    <Paragraph>4,504,000,000 km</Paragraph>
                  </TableCell>
                </TableRow>
      
                <TableRow FontSize="12" Background="LightGray">
                  <TableCell>
                    <Paragraph FontWeight="Bold">Mean Diameter</Paragraph>
                  </TableCell>
                  <TableCell>
                    <Paragraph>49,532 km</Paragraph>
                  </TableCell>
                </TableRow>
      
                <TableRow Background="LightGoldenrodYellow" FontSize="12">
                  <TableCell>
                    <Paragraph FontWeight="Bold">Approximate Mass</Paragraph>
                  </TableCell>
                  <TableCell>
                    <Paragraph>1.0247e26 kg</Paragraph>
                  </TableCell>
                </TableRow>
                <TableRow>
                  <TableCell ColumnSpan="4">
                    <Paragraph FontSize="10" FontStyle="Italic">
                      Information from the
                      <Hyperlink
                          NavigateUri="http://encarta.msn.com/encnet/refpages/artcenter.aspx"
                                                            >
                        Encarta
                      </Hyperlink>
                      web site.
                    </Paragraph>
                  </TableCell>
                </TableRow>
              </TableRowGroup>
            </Table>
          </Floater>
        </Paragraph>
        <Paragraph>
          Astronomers believe Neptune has an inner rocky core that is surrounded by a vast
          ocean of water mixed with rocky material. From the inner core, this ocean extends
          upward until it meets a gaseous atmosphere of hydrogen, helium, and trace amounts
          of methane. Neptune has four rings and 11 known moons. Even though Neptune's volume
          is 72 times Earth’s volume, its mass is only 17.15 times Earth’s mass. Because of
          its size, scientists classify Neptune—along with Jupiter, Saturn, and Uranus—as
          one of the giant or Jovian planets (so-called because they resemble Jupiter).
        </Paragraph>
        <Paragraph>
          <Figure
              Width="140" Height="50"
              Background="GhostWhite" TextAlignment="Left"
              HorizontalAnchor="PageCenter" WrapDirection="Both"
              >
            <Paragraph FontStyle="Italic" Background="Beige" Foreground="DarkGreen" >
              Neptune has an orbital period of ~20 years...
            </Paragraph>
          </Figure>
          Mathematical theories of astronomy led to the discovery of Neptune. To account for
          wobbles in the orbit of the planet Uranus, British astronomer John Couch Adams and
          French astronomer Urbain Jean Joseph Leverrier independently calculated the existence
          and position of a new planet in 1845 and 1846, respectively. They theorized that the
          gravitational attraction of this planet for Uranus was causing the wobbles in Uranus’s
          orbit. Using information from Leverrier, German astronomer Johann Gottfried Galle first
          observed the planet in 1846.
        </Paragraph>
      </Section>
    </FlowDocument>
  • Заполните файл Window1.xaml.cs кодовой части так
  • 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.IO;
        
    namespace WpfText2
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        
            private void FlowDocumentScrollViewer_Initialized(object sender, EventArgs e)
            {
                documentReader.Document = (FlowDocument)XamlReader.
                    Load(File.OpenRead("XMLFile1.xaml"));
        
                // Альтернативный вариант
                //LoadFlowDocumentScrollViewerWithXAMLFile("XMLFile1.xaml");
            }
        
            // Альтернативный вариант
            void LoadFlowDocumentScrollViewerWithXAMLFile(string fileName)
            {
                FileStream xamlFile = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                FlowDocument content = XamlReader.Load(xamlFile) as FlowDocument;
                documentReader.Document = content;
        
                xamlFile.Close();
            }
        }
    }

    Здесь мы свойству Document контейнера просмотра FlowDocumentScrollViewer присваиваем возвращенный классом XamlReader размеченный документ нефиксированного формата.

  • Запустите приложение - получится точно такой же результат, как в предыдущем примере
  • Проверьте работу закомментированного альтернативного варианта процедурного кода
  • Упражнение 3. Работа в WPF с HTML-документами

    Поскольку XAML -формат документов и сама технология WPF являются относительно новыми, может потребоваться применить WPF к уже наработанному большому массиву HTML -документов. Для этого в WPF имеется класс System.Windows.Controls.WebBrowser. Его не нужно путать с одноименным классом System.Windows.Forms.WebBrowser технологии Windows Forms. Описание WebBrowser приведено в

    http://msdn.microsoft.com/ru-ru/library/system.windows.controls.webbrowser.aspx

    В этом упражнении мы познакомимся с применением WebBrowser WPF к просмотру HTML -документов, а также с преобразованием HTML в рассмотренные ранее XAML -документы нефиксированного формата.

  • Добавьте к решению командой File/Add/New Project новый проект WpfText3 типа WPF Application и назначьте его стартовым
  • Просмотр HTML-документов в WebBrowser

  • В декларативный раздел XAML добавьте контейнер <TabControl> с вкладкой <TabItem Header="HTML1">
  • Заполните файл Window1.xaml следующим кодом
  • <Window x:Class="WpfText3.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"
        Background="LightGray"
            >
        
        <TabControl>
            <TabItem Header="HTML1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <WebBrowser Grid.Row="0" Name="webBrowser1" />
                    <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
                        <Button Width="75" Content="Back" Click="backButton_Click" Margin="0,5,0,0" />
                        <Button Width="75" Content="Load" Click="loadButton_Click" Margin="5,5,5,0" />
                        <Button Width="75" Content="Forward" Click="forwardButton_Click" Margin="0,5,0,0" />
                    </StackPanel>
                </Grid>  
            </TabItem>
        </TabControl>
    </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;
        
    namespace WpfText3
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        
            private void loadButton_Click(object sender, RoutedEventArgs e)
            {
                Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
                openFileDialog.Filter = "HTML Documents (*.htm; *.html)|*.htm;*.html";
                openFileDialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
                openFileDialog.Multiselect=false;
                if (openFileDialog.ShowDialog() == false || openFileDialog.FileName == String.Empty)
                    return;
        
                Uri uri = new Uri(openFileDialog.FileName);
                this.webBrowser1.Source = uri;
            }
        
            private void backButton_Click(object sender, RoutedEventArgs e)
            {
                if (this.webBrowser1.CanGoBack)
                {
                    this.webBrowser1.GoBack();
                }
            }
        
            private void forwardButton_Click(object sender, RoutedEventArgs e)
            {
                if (this.webBrowser1.CanGoForward)
                {
                    this.webBrowser1.GoForward();
                }
            }
        }
    }
  • Запустите проект - получится нормальный инструмент просмотра HTML -документов
  • Отображение HTML-текста в WebBrowser из файла

    Обратите внимание, что в предыдущем примере автоматически появляются полосы прокрутки, если HTML -документ не помещается в окне элемента WebBrowser. Фрагмент форматированного HTML -текста включать прямо в WebBrowser нельзя, поскольку XAML -редактор оболочки сразу начнет ругаться. Но во время выполнения загружать файл из кода можно.

    В данном примере мы заранее создадим HTML -файл с простым форматированным текстом.

  • В Solution Explorer вызовите контекстное меню для узла WpfText3 и командой Add/Existing Item добавьте к проекту файл HtmlDoc.htm из прилагаемого каталога Source (или создайте свой HTML -документ и добавьте его к проекту той же командой)
  • Содержимое файла будет примерно таким

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
     <title>Untitled Document</title>
     <!--
      <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
     -->
    </head>
        
    <body style="font-family:Arial, Helvetica, sans-serif">
    <h2 align="center"><font color="#FF0000">Это небольшой фрагмент чистого HTML-текста</font></h2>
    <ol>
      <li>Шрифт Arial</li>
      <li><font color="#0000FF">Цвет font color=quot;#0000FFquot;</font></li>
      <li><font face="Courier New">Шрифт Courier New </font></li>
      <li><b>Стиль полужирный lt;bgt; </b></li>
      <li><i>Стиль Italic  lt;igt; </i></li>
    </ol>
    </body>
    </html>
  • Выделите этот файл и в панели Properties настройте его свойства так
  • Build Action = None
  • Copy to Output Directory=Copy if newer
  • В декларативный раздел XAML в контейнер <TabControl> добавьте новую вкладку <TabItem Header="HTML2">
  • Заполните ее следующим скриптом
  • <TabItem Header="HTML2" Selector.IsSelected="True">
                <WebBrowser x:Name="webBrowser2" Initialized="webBrowser2_Initialized" />
            </TabItem>
  • Обработчик события Initialized заполните так
  • private void webBrowser2_Initialized(object sender, EventArgs e)
            {
                Uri uri = new Uri(System.IO.Directory.GetCurrentDirectory() +
                    "\\HtmlDoc.htm", UriKind.Absolute);
        
                this.webBrowser2.Navigate(uri);
            }

    Здесь мы применили несколько иной способ загрузки файла!

  • Запустите проект - получится примерно следующее
  • Вертикальную полосу прокрутки мне убрать в этом примере не удалось.

    Отображение HTML-текста в WebBrowser из потока

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

  • В декларативный раздел XAML в контейнер <TabControl> добавьте новую вкладку <TabItem Header="HTML3">
  • Заполните ее следующим скриптом
  • <TabItem Header="HTML3" Selector.IsSelected="True">
                <WebBrowser x:Name="webBrowser3" Initialized="webBrowser3_Initialized" />
            </TabItem>
  • Обработчик события Initialized заполните так
  • private void webBrowser3_Initialized(object sender, EventArgs e)
            {
                Uri uri = new Uri("HtmlDoc.htm", UriKind.Relative);
                System.IO.Stream source = Application.GetRemoteStream(uri).Stream;
        
                this.webBrowser3.NavigateToStream(source);
            }
  • Запустите проект - получится то же самое, что в предыдущем примере
  • Еще один способ загрузки HTML-документа из потока

  • В декларативный раздел XAML в контейнер <TabControl> добавьте новую вкладку <TabItem Header="HTML4">
  • Заполните ее следующим скриптом
  • <TabItem Header="HTML4" Selector.IsSelected="True">
                <WebBrowser x:Name="webBrowser4" Initialized="webBrowser4_Initialized" />
            </TabItem>
  • Обработчик события Initialized заполните так
  • private void webBrowser4_Initialized(object sender, EventArgs e)
            {
                string path = System.IO.Directory.GetCurrentDirectory() + "\\HtmlDoc.htm";
                System.IO.StreamReader streamReader = new System.IO.StreamReader(
                    path, Encoding.GetEncoding(1251));
                //MessageBox.Show(streamReader.CurrentEncoding.CodePage.ToString());
                string text = streamReader.ReadToEnd();
        
                webBrowser4.NavigateToString(text);
            }
    Здесь мы задали номер кодовой страницы 1251  !!!
  • Обязательно закомментируйте или удалите HTML-дескриптор <meta> в документе HtmlDoc.htm, иначе будут проблемы с кодировкой "Кириллица (Windows) - 1251" в этом примере HTML4
  • <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>Untitled Document</title>
    <!--
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
    -->
    </head>
        
    <body style="font-family:Arial, Helvetica, sans-serif">
    <h2 align="center"><font color="#FF0000">Это небольшой фрагмент чистого HTML-текста</font></h2>
    <ol>
      <li>Шрифт Arial</li>
      <li><font color="#0000FF">Цвет font color=quot;#0000FFquot;</font></li>
      <li><font face="Courier New">Шрифт Courier New </font></li>
      <li><b>Стиль полужирный lt;bgt; </b></li>
      <li><i>Стиль Italic  lt;igt; </i></li>
    </ol>
    </body>
    </html>
  • Запустите проект - получится то же самое, что в предыдущем примере
  • Упражнение 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 аж целых четыре года назад.

    Упражнение 6. Выборка текста из текстового файла с форматированием на лету

    В данном упражнении приводится несколько примеров обработки текстовых файлов и их форматирования в FlowDocument программным способом. Мне надоело описывать пошаговые инструкции, а вам надоело их разбирать, поэтому приведу итоговый код для исследования.

    Что я сделал?

  • Добавил к решению новый проект типа WPF Application с именем WPFText6
  • Создал в корне проекта каталог Documents и добавил в него готовый текстовый файл TextDocument.txt с таким случайным содержимым (приводится в каталоге Source )
  • Критерием отнесения ТС к маршрутным является наличие установленного маршрута с обозначенными местами остановок. К таковым Правила относят автобусы, троллейбусы и трамваи (п. 1.2). Автомобили-такси к маршрутным ТС не относятся.
  • Знак 4.1.4 "Движение прямо или направо" разрешает вам движение только прямо или поворот на АЗС, расположенную справа от дороги, т.е. в направлениях А или Б.
  • Знак 5.7.1 "Выезд на дорогу с односторонним движением" информирует о направлении движения, установленном на пересекаемой дороге. Кроме поворота направо на этом перекрестке возможны движение прямо и разворот. При движении в любом из этих направлений вы должны уступить дорогу (знак 2.4 "Уступите дорогу") ТС, движущимся по пересекаемой дороге.
  • Знак 5.29 "Зона регулируемой стоянки" относится к так называемым зональным знакам, требования которых действуют на всей территории (участке дороги), обозначенной такими знаками, вплоть до выезда из зоны, обозначенного в данном случае знаком 5.30 "Конец зоны регулируемой стоянки". Действие знаков не прерывается на перекрестках. При этом информацию о видах ТС, способах парковки и т.д. размещают на табличке под знаком или с помощью изображения таблички в нижней части знака. В данном случае табличка 8.6.2 "Способ постановки транспортного средства на стоянку" разрешает ставить легковые автомобили на стоянку с использованием тротуара на территории всей зоны регулируемой стоянки, т.е. на любой стороне дорог, расположенных в зоне регулируемой стоянки (Приложение 1).
  • Разметка 1.19 (в виде стрел) предупреждает вас о необходимости покинуть полосу движения в связи с уменьшением количества полос в данном направлении (сужением проезжей части). Необходимо перестроиться на правую полосу.
  • Для информирования водителей о предстоящей смене зеленого сигнала светофора на желтый Правилами предусмотрена возможность мигания зеленого сигнала. Этот сигнал разрешает движение ТС (п. 6.2). Длительность мигания при этом должна составлять 3 сек.
  • Водитель обязан подавать сигналы световыми указателями поворота соответствующего направления перед началом движения, перестроением, поворотом, остановкой (п. 8.1). Двигаясь по закруглению дороги и не намереваясь перестраиваться или останавливаться, вы не должны включать указатели поворота, что бы не вводить в заблуждение других участников движения (п. 8.2).
  • При взаимном перестроении водитель легкового автомобиля должен уступить дорогу водителю мотоцикла, находящемуся от него справа (п. 8.4).
  • Выполняя разворот на этом перекрестке, вам придется использовать для движения участок проезжей части, имеющий двустороннее движение. Поэтому, въезжая на перекресток, вы можете продолжить движение только по правой стороне этого участка проезжей части (п. 1.4), т.е. по траектории А.
  • Знак 4.6 "Ограничение минимальной скорости" и табличка 8.14 "Полоса движения" предписывают двигаться по левой полосе со скоростью не менее 50 км/ч. Однако при этом на дороге вне населенного пункта, не относящейся к автомагистрали, вы не имеете права развивать скорость на легковом автомобиле более 90 км/ч (п. 10.3).
  • На нерегулируемых перекрестках обгон запрещен при движении по дороге, не являющейся главной (п. 11.5). Поскольку водитель легкового автомобиля приближается к перекрестку неравнозначных дорог, двигаясь по главной дороге (знак 2.3.1 "Пересечение с второстепенной дорогой"), то он может совершить обгон обоих грузовых автомобилей, так как дорога имеет две полосы для движения в данном направлении (п. 11.4). Следовательно, обгон может быть совершен по траекториям А и Б.
  • Остановившись за пешеходным переходом в 5 м от края пересекаемой проезжей части и обеспечив при этом расстояние в 3 м до сплошной линии разметки, водитель не нарушил правила стоянки (пп. 12.4 и 12.5).
  • При движении в направлении стрелки, включенной в дополнительной секции одновременно с красным сигналом светофора, вы обязаны уступать дорогу ТС, движущимся с других направлений (п. 13.5).
  • Несмотря на то что легковой автомобиль первым въехал на перекресток равнозначных дорог, вы имеете перед ним преимущество, и его водитель при левом повороте должен уступить вам дорогу (пп. 13.11 и 13.12).
  • Проезжая данный перекресток неравнозначных дорог по направлению главной дороги (знаки 2.1 "Главная дорога" и 8.13 "Направление главной дороги"), вы никому не должны уступать дорогу, так как пользуетесь преимуществом как перед находящимся на главной дороге автобусом, для которого вы являетесь "помехой справа" (пп. 13.10 и 13.11), так и перед легковым автомобилем, движущимся по второстепенной дороге (п. 13.9).
  • Знак 5.21 "Жилая зона" показывает, что вы въезжаете на территорию, где скорость не должна превышать 20 км/ч (п. 10.2).
  • При движении в темное время суток на неосвещенных участках дорог вы должны использовать, в зависимости от дорожных условий, скорости движения и наличия впереди встречных и попутных ТС, ближний или дальний свет фар (п. 19.1).
  • Из всех перечисленных неисправностей только неработающий стеклоподъемник не является причиной запрещения эксплуатации вашего ТС. Остальные неисправности включены в Перечень (п. 7.4), и при их наличии эксплуатация ТС запрещена.
  • При съезде автомобиля правыми колесами на неукрепленную и влажную обочину возникает опасность заноса из-за разницы сцепления правых и левых колес с дорогой. При этом целесообразно, не меняя скорости, т.е. не прибегая к торможению, плавным поворотом рулевого колеса вернуть автомобиль на проезжую часть. Торможение в данной ситуации может вызвать занос автомобиля.
  • Раствор йода или бриллиантовой зелени вызывает химическое повреждение ткани внутри раны. Наружный слой кожи не повреждается при смазывании этими растворами, но эффективно дезинфицируется, что необходимо перед наложением марлевой повязки.
  • Здесь знак подчеркивания '_' в начале параграфа является меткой для поиска в одном из примеров.

  • Настроил свойства файла TextDocument.txt так
  • Build Action = None
  • Copy to Output Directory=Copy if newe
  • Заполнил (последовательно, думая, соображая!) файл XAML так
    <Window x:Class="WpfText6.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"
        Loaded="Window_Loaded"
            >
        <TabControl>
            
            <TabItem Header="OneParagraph">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="50" />
                    </Grid.ColumnDefinitions>
                    <FlowDocumentScrollViewer 
                        DockPanel.Dock="Left"
                        x:Name="flowDocumentScrollViewer" 
                        ScrollViewer.VerticalScrollBarVisibility="Visible"
                        >
                        <FlowDocument Name="flowDoc" TextAlignment="Left" Background="White">
                            <Paragraph TextAlignment="Center" FontWeight="Bold" FontSize="21">
                                Чтение одного параграфа
                            </Paragraph>
                            <Paragraph Name="text" FontFamily="Arial" />
                        </FlowDocument>
                    </FlowDocumentScrollViewer>
                    <ListBox Grid.Column="1" Name="listBox" 
                        SelectionChanged="listBox_SelectionChanged" />
                </Grid>
            </TabItem>
            
            <TabItem Header="FullDocument">
                <FlowDocument Name="fullDocument" Loaded="fullDocument_Loaded" />
            </TabItem>
            
            <TabItem Header="SplitDocument">
                <FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
                    <FlowDocument Name="splitDocument" 
                        Loaded="splitDocument_Loaded" Background="White" />
                </FlowDocumentScrollViewer>
            </TabItem>
        
            <TabItem Header="ListDocument">
                <FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
                    <FlowDocument Name="listDocument" 
                        Initialized="listDocument_Initialized"  Background="White" />
                </FlowDocumentScrollViewer>
            </TabItem>
        </TabControl>
    </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 System.Windows.Markup;
    using System.Collections;
        
    namespace WpfText6
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        
            // Вынесли как поля для видимости в методах
            FileStream myStream;
            StreamReader myStreamReader;
            String[] arrayText;
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                this.Background = Brushes.LightGray;
                // Можно вместо разделителя '\\' использовать '/' 
                String fileName = "Documents/TextDocument.txt";
        
                try
                {
                    myStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                    myStreamReader = new StreamReader(myStream, Encoding.GetEncoding(1251));
        
                    // Не знаем, какой массив задать, поэтому читаем в коллекцию
                    // Но можно было и сразу в коллекцию StringBuilder.Append()
                    ArrayList arrayList = new ArrayList();
                    while (myStreamReader.EndOfStream != true)
                        arrayList.Add(myStreamReader.ReadLine());// Читаем построчно
        
                    // Копируем ссылки на элементы ArrayList в массив
                    arrayText = new String[arrayList.Count];
                    for (int i = 0; i < arrayList.Count; i++)
                        arrayText[i] = (String)arrayList[i];
        
                    // Заполняем ListBox
                    for (int i = 0; i < arrayText.Length; i++)
                    {
                        int pos = arrayText[i].IndexOf('.');
                        listBox.Items.Add(arrayText[i].Substring(0, pos + 1));
                    }
                    listBox.SelectedIndex = 0; // На первый элемент списка
                }
                catch { } // Один синтаксис!!!
                finally
                {
                    // Системные ресурсы надо закрывать
                    myStream.Close();
                    myStreamReader.Close();
                }
            }
        
            private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                text.Inlines.Clear(); // Очищаем предыдущее содержимое
                text.Inlines.Add(arrayText[listBox.SelectedIndex]);// Добавляем новое
            }
        
            private void fullDocument_Loaded(object sender, RoutedEventArgs e)
            {
                fullDocument.Background = Brushes.White;// Фон документа программно
        
                for (int i = 0; i < arrayText.Length; i++)
                {
                    Paragraph para = new Paragraph();
                    para.FontFamily = new FontFamily("Arial");
                    para.Inlines.Add(arrayText[i]);
                    fullDocument.Blocks.Add(para);
                }
            }
        
            String[] textSplit;
            private void splitDocument_Loaded(object sender, RoutedEventArgs e)
            {
                // Можно вместо разделителя '\\' использовать '/' 
                String fileName = "Documents/TextDocument.txt";
        
                try
                {
                    myStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                    myStreamReader = new StreamReader(myStream, Encoding.GetEncoding(1251));
    
                    String str = myStreamReader.ReadToEnd();// Читаем весь файл
                    textSplit = str.Split('_');// Расщепляем строку по метке '_'
                    // Элемент textSplit[0] получился пустой
                }
                catch (Exception) { } // Другой синтаксис!!!
                finally
                {
                    // Системные ресурсы надо закрывать
                    myStream.Close();
                    myStreamReader.Close();
                }
        
                // Формируем заголовок программно
                Paragraph head = new Paragraph();
                head.TextAlignment = TextAlignment.Center;
                Run run = new Run("Документ получен расщеплением строки");
                run.FontSize = 21;
                Bold bold = new Bold(run);
                head.Inlines.Add(bold);
                splitDocument.Blocks.Add(head);
        
                // Заполняем FlowDocument
                splitDocument.FontFamily = new FontFamily("Arial");
                for (int i = 1; i < textSplit.Length; i++)
                {
                    Paragraph para = new Paragraph();
                    para.Inlines.Add(textSplit[i]);
                    splitDocument.Blocks.Add(para);
                }
            }
        
            private void listDocument_Initialized(object sender, EventArgs e)
            {
                // Формируем заголовок
                Paragraph head = new Paragraph();
                head.TextAlignment = TextAlignment.Center;
                Run run = new Run("Нумерованный список OrderList");
                run.FontSize = 21;
                Bold bold = new Bold(run);
                head.Inlines.Add(bold);
                listDocument.Blocks.Add(head);
        
                // Создаем и настраиваем список
                List list = new List();
                list.MarkerOffset = 25;// Отступ от маркеров
                list.MarkerStyle = TextMarkerStyle.Decimal;
                list.StartIndex = 3; // Нумерация с 3
        
                // Заполняем список дочерними элементами
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 1"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 2"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 3"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 4"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 5"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 6"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 7"))));
        
                // Присоединяем список к документу
                listDocument.FontFamily = new FontFamily("Arial");
                listDocument.Blocks.Add(list);
            }
        }
    }
  • Разберитесь с кодом, или выкиньте мой и сочините свой, но чтобы все работало!
  • Результаты по примерам будут такими:

       

    Проповедь

    Компьютерные технологии изменяются так стремительно, что некоторым студентам самое время подумать - не пересесть ли с компьютера за штурвал комбайна или не стать ли водителем кобылы. А что, Зелинский, это мысль! Будешь давать стране угля, и дров неналомаешь. Семью обеспечишь, да еще и звезду героя соцтруда могут дать. А у нас, программистов, - одни мозоли в голове, в карманах - вши на арканах, а у преподавателей от таких "студентов" - еще и головная боль впридачу!!! Отпусти нас, Зелинский!

    Один из таких комбайнеров написал про меня в Интернете - молодой писака, писательский талант прорезался, на мемуары потянуло, распирать начало. Лучше бы почитывал пока, журналюга... Спасибо, хоть слова не переврал, и то благодаря все тому же Copy-Paste. Почитайте, вам будет поучительно, может станете больше прислушиваться к тому, что я постоянно говорю "Разберитесь с кодом":

    http://blogs.mail.ru/mail/vitaliy2525/31E7226B44550EB4.html#

    Пописывающий сетует, что что-то делал своими собственными ручками и все работало, но лень было в этом разбираться. А через несколько лет созрел и оказалось, что это красиво - вкусно - полезно. Так причем здесь Снетков?

    "Окликнешь, тихо подойдешь, попросишь сторублевку... Причем здесь нож, причем грабеж? - меняй формулировку..." Высоцкий В.С.

    Зреть нужно быстрее. Научить ПРОГРАММИРОВАТЬ можно только тех, кто сам этого хочет. Кода я привожу много, на любой вкус и цвет. Разбирайтесь, экспериментируйте, спрашивайте! Но не говорите, что все так плохо и только 'приколы' хороши - это непорядочно.

    Высшее профессиональное образование - это не школа благородных девиц. И я не приставленный к ней надзиратель, а свободный преподаватель. Классики, и те уже попритихли. Как научить за мизерное количество часов (и зарплату...)? Заставлять покупать учебники по тысячу рублей штука? Так ведь редко же встречаются целиком стоющие - в одних одно, в других - другое, а большей частью - один хлам.

    Взгляните, например, на этот, далеко не полный, ряд 'орудий пристрастья' современного программиста: C#, C++, MFC, VCL, .NET Framework, DirectX, OpenGL, XNA, ADO.NET, SQL, MySQL, SQL Server, Access, Crystal Reports, ASP.NET, HTML, XAML, XML, Windows Forms, WPF, PHP, Ajaxs, сами оболочки C++Builder и MS Visual Studio и т.д. А ведь все это - и есть объектно-ориентированное программирование! И сколько же тысяч нужно заплатить вашим родителям, если все это покупать, чтобы держать себя в форме?

    Некоторые из вас и не заметили, что уже пощупали собственными ручками то, о чем ранее даже и не слышали. Все оплачено и налито, только пей. От ' такой методики ' еще никому не поплохело! Знаю, для 'непьющих' хватило бы и Бэйсика, или программирования на листочке, или сразу зачета? Так зачем тогда пришли и на какие средства в дальнейшем собираетесь жить, позвольте спросить, когда ваши родители состарятся и уйдут?

    Я не программист, я их уже много лет только готовлю (с перцем, с луком, по собственному рецепту, на приспособах домашнего производства). Есть они не только в Красноярске, можно встретить в Новосибирске, Томске, Железногорске и т.д., и даже... - в Силиконовой долине. Моя ли в том вина, что иногда попадаются подпорченные продукты (я их сам не выбираю), или те, кто сел не в свои сани?

    Когда я был маленький, у меня тоже была бабушка. Это было почти вчера, но время летит так быстро: где сейчас маленький, где эта бабушка... Имейте и вы это ввиду - не минет никого чаша сия и нужно все делать вовремя, чтобы успеть состояться в жизни, а не балду гонять! Так что - тужтесь сами, да посильнее, вам 'рожать'. А я на своем месте - стою на вспоможении... (хочешь - напрягайся, буду помогать! не хочешь - дело хозяйское)

    Страницы:

    Все необходимые для выполнения данной работы программы можно найти в прилагаемом каталоге.

    При изучении любого средства программирования используют примеры, которые иллюстрируют применение этого средства. Различают примеры двух видов:

  • Примеры технологий - иллюстрируют отдельные стороны рассматриваемой технологии программирования
  • Примеры приложений - иллюстрируют программирование предметной области с применением рассматриваемой технологии
  • В наших упражнениях рассматриваются преимущественно примеры технологий.

    Введение

    Текст и рисунки - главные информационные составляющие любого приложения. WPF имеет все стандартные элементы, необходимые для работы с текстом. Для простых случаев используются упрощенные элементы представления текста

  • Label - метка
  • TextBlock - текстовый блок
  • TextBox - текстовое поле
  • PasswordBox - поле пароля
  • Для более сложных случаев используются высокоразвитые элементы работы с документами. WPF разделяет текстовые документы на две основные категории на основе их предполагаемого использования:

  • Документы фиксированного формата
  • Документы нефиксированного формата
  • Документы фиксированного формата предназначены для приложений, требующих точного представления в режиме WYSIWYG (режим точного отображения - 'что вижу то и имею'), независимо от используемого дисплея или принтера. Документы фиксированного формата обычно используются при подготовке публикаций с помощью настольных издательских средств, обработке текста и разметке формы, где строгое соблюдение исходного дизайна страницы является обязательным. Содержимое документов фиксированного формата поддерживается классом FixedDocument, а для просмотра используется класс DocumentViewer.

    Документы фиксированного формата, как часть макета, поддерживает точное позиционирование содержимого элементов, независимо от используемых устройств отображения или печати. Например, страница формата фиксированного документа, просматриваемая на экране с разрешением 96 точек на дюйм, будет отображаться точно так же на выводе лазерного принтера с разрешением 600 точек на дюйм или устройстве фотовывода с разрешением 4800 точек на дюйм. Макет страницы остается неизменным во всех случаях, в то время как качество документа повышается в соответствии с возможностями каждого устройства.

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

    В зависимости от среды выполнения, документы нефиксированного формата оптимизируют просмотр и чтение для пользователя. Например, один тот же документ нефиксированного формата будет динамически переформатирован для оптимальной удобочитаемости как на 19-дюймовом экране с высоким разрешением, так и на небольшом 2x3-дюймовом экране карманного ПК. Кроме того, документы нефиксированного формата имеют множество встроенных возможностей, включая поиск, просмотр режимов оптимизации удобочитаемости и возможность изменения размера и вида шрифтов.

    Документы нефиксированного формата представлены классом содержимого FlowDocument. Этот класс обеспечивает размещение и форматирование содержимого потока с такими дополнительными свойствами документов, как разбивка на страницы и столбцы. Для отображения, редактирования и навигации по документу используются классы-контейнеры

  • RichTextBox - текстовое поле с расширенными возможностями
  • FlowDocumentScrollViewer - текстовый блок с расширенным содержимым и управляемой полосой прокрутки
  • FlowDocumentPageViewer - дает возможность просматривать документ постранично и имеет привычные навигационные элементы
  • FlowDocumentReader - элемент управления, в котором пользователь сам вибирает способ просмотра документа: одностраничный, многостраничный или с прокруткой
  • Любой из этих классов может иметь только один элемент содержимого FlowDocument, который адресуется их свойством Document. Если содержимое не загружено в контейнер отображения, то свойство Document имеет значение null.

    В данной лабораторной работе рассматриваются средства WPF для представления текста нефиксированного формата.

    Упражнение 1. Простые текстовые элементы

    Текстовые элементы Label, TextBlock, TextBox, PasswordBox обычно используются для создания небольшого текста в интерфейсе пользователя. Они обладают достаточными для этого возможностями. Но отображать большие потоки текста они непредназначены.

    Создание заготовки решения

  • Создайте решение WpfApp с проектом WpfText1 и назначьте его стартовым
  • Добавьте к проекту новый файл с именем FontPropertyLists.cs
  • Текстовый элемент Label

  • Заполните файл FontPropertyLists.cs следующим кодом
  • using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Windows;
    using System.Windows.Media;
        
    namespace WpfText1
    {
        /// <summary>
        /// Код класса из примера MSDN
        /// </summary>
        public class FontPropertyLists
        {
            static Collection<FontFamily> fontFaces;
            static Collection<FontStyle> fontStyles;
            static Collection<FontWeight> fontWeights;
            static Collection<double> fontSizes;
            static int maxFontSizes = 36;
    
            /// <summary>
            /// Заполняет коллекцию доступных системе FontFamily
            /// </summary>
            public static ICollection<FontFamily> FontFaces
            {
                get
                {
                    if (fontFaces == null) fontFaces = new Collection<FontFamily>();
                    foreach (FontFamily fontFamily in Fonts.SystemFontFamilies)
                    {
                        fontFaces.Add(fontFamily);
                    }
                    return fontFaces;
                }
            }
        
            /// <summary>
            /// Заполняет коллекцию доступных FontStyles  
            /// </summary>
            public static ICollection FontStyles
            {
                get
                {
                    if (fontStyles == null)
                    {
                        fontStyles = new Collection<FontStyle>();
                        fontStyles.Add(System.Windows.FontStyles.Normal);
                        fontStyles.Add(System.Windows.FontStyles.Oblique);
                        fontStyles.Add(System.Windows.FontStyles.Italic);
                    }
                    return fontStyles;
                }
            }
        
            /// <summary>
            /// Заполняет коллекцию доступных FontWeight
            /// </summary>
            public static ICollection FontWeights
            {
                get
                {
                    if (fontWeights == null)
                    {
                        fontWeights = new Collection<FontWeight>();
        
                        Unique(System.Windows.FontWeights.Thin);
                        Unique(System.Windows.FontWeights.Light);
                        Unique(System.Windows.FontWeights.Regular);
                        Unique(System.Windows.FontWeights.Normal);
                        Unique(System.Windows.FontWeights.Medium);
                        Unique(System.Windows.FontWeights.Heavy);
                        Unique(System.Windows.FontWeights.SemiBold);
                        Unique(System.Windows.FontWeights.DemiBold);
                        Unique(System.Windows.FontWeights.Bold);
                        Unique(System.Windows.FontWeights.Black);
                        Unique(System.Windows.FontWeights.ExtraLight);
                        Unique(System.Windows.FontWeights.ExtraBold);
                        Unique(System.Windows.FontWeights.ExtraBlack);
                        Unique(System.Windows.FontWeights.UltraLight);
                        Unique(System.Windows.FontWeights.UltraBold);
                        Unique(System.Windows.FontWeights.UltraBlack);
                    }
                    return fontWeights;
                }
            }
        
            static void Unique(FontWeight fontWeight)
            {
                if (fontWeights.IndexOf(fontWeight) == -1)
                    fontWeights.Add(fontWeight);
            }
        
            /// <summary>
            /// Заполняет коллекцию доступных FontSizes 
            /// </summary>
            public static Collection<double> FontSizes
            {
                get
                {
                    if (fontSizes == null)
                    {
                        fontSizes = new Collection<double>();
                        for (double i = 8; i <= maxFontSizes; i++) fontSizes.Add(i);
                    }
                    return fontSizes;
                }
            }
        
            // Методы для преобразования из TypeName в Type
            public static bool CanParseFontStyle(string fontStyleName)
            {
                try
                {
                    FontStyleConverter converter = new FontStyleConverter();
                    converter.ConvertFromString(fontStyleName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            public static FontStyle ParseFontStyle(string fontStyleName)
            {
                FontStyleConverter converter = new FontStyleConverter();
                return (FontStyle)converter.ConvertFromString(fontStyleName);
            }
            public static bool CanParseFontWeight(string fontWeightName)
            {
                try
                {
                    FontWeightConverter converter = new FontWeightConverter();
                    converter.ConvertFromString(fontWeightName);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            public static FontWeight ParseFontWeight(string fontWeightName)
            {
                FontWeightConverter converter = new FontWeightConverter();
                return (FontWeight)converter.ConvertFromString(fontWeightName);
            }
        }
    }
  • Заполните файл Window1.xaml следующей разметкой
  • <Window x:Class="WpfText1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" 
        Background="LightGray"
        Height="300"
        Width="300"
        MinHeight="300"
        MinWidth="300"
        ResizeMode="CanResizeWithGrip"
        WindowStartupLocation="CenterScreen"
        FocusManager.FocusedElement="{Binding ElementName='fontSizeListBox'}"        
            >
        <TabControl>
            <!--Вкладка Label-->
            <TabItem Header="Label">
                <!--Сетка 4x3-->
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="60" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <!--Пропорции ширины столбцов-->
                        <ColumnDefinition Width="2.7*" />
                        <ColumnDefinition Width="1.3*" />
                        <ColumnDefinition Width="1*" />
                    </Grid.ColumnDefinitions>
                    <Border 
                        Grid.Row="0" Grid.ColumnSpan="3"
                        BorderThickness="3"
                        BorderBrush="Violet"
                        >
                        <Label
                            VerticalContentAlignment="Center" 
                            HorizontalContentAlignment="Center"
                            Foreground="Red"
                            FontWeight="{Binding ElementName=fontWeightListBox, Path=SelectedItem}"
                            FontSize="{Binding ElementName=fontSizeListBox, Path=SelectedItem}"
                            FontFamily="{Binding ElementName=fontFamilyListBox, Path=SelectedItem}"
                            Content="Элемент Label" 
                            />
                    </Border>
                    <Label
                        Grid.Row="1" Grid.Column="0" 
                        HorizontalContentAlignment="Center"
                        Content="FontFamily:" 
                        />
                    <TextBox 
                        Grid.Row="2" Grid.Column="0"
                        Name="fontFamilyTextBox" 
                        Text="{Binding ElementName=fontFamilyListBox, Path=SelectedItem}" 
                        TextChanged="fontFamilyTextBox_TextChanged" 
                        />
                    <ListBox 
                        Grid.Row="3" Grid.Column="0" 
                        Name="fontFamilyListBox" SelectedIndex="0" 
                        />
                    <Label
                        Grid.Row="1" Grid.Column="1" 
                        HorizontalContentAlignment="Center"
                        Content="FontWeight:" 
                        />
                    <TextBox 
                        Grid.Row="2" Grid.Column="1" 
                        Name="fontWeightTextBox" 
                        Text="{Binding ElementName=fontWeightListBox, Path=SelectedItem}" 
                        TextChanged="fontWeightTextBox_TextChanged" 
                        />
                    <ListBox 
                        Grid.Row="3" Grid.Column="1" 
                        Name="fontWeightListBox" SelectedIndex="0" 
                        />
                    <Label 
                        Grid.Row="1" Grid.Column="2" 
                        HorizontalContentAlignment="Center"
                        Content="FontSize:" 
                        />
                    <TextBox 
                        Grid.Row="2" Grid.Column="2" 
                        Name="fontSizeTextBox" 
                        Text="{Binding ElementName=fontSizeListBox, Path=SelectedItem}" 
                        TextChanged="fontSizeTextBox_TextChanged" 
                        />
                    <ListBox 
                        Grid.Row="3" Grid.Column="2" 
                        Name="fontSizeListBox" SelectedIndex="0" 
                        />
                </Grid>
            </TabItem>
        </TabControl>
    </Window>
  • Зарегистрированные в элементах TextBox обработчики событий TextChanged заполните в файле Window1.xaml.cs
  • 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;
        
    namespace WpfText1
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
        
                // Заполняем коллекции и подключаем их к спискам ListBox
                this.fontFamilyListBox.ItemsSource = FontPropertyLists.FontFaces;
                this.fontWeightListBox.ItemsSource = FontPropertyLists.FontWeights;
                this.fontSizeListBox.ItemsSource = FontPropertyLists.FontSizes;
        
                // Выполняется после XAML
                this.fontSizeListBox.SelectedIndex = this.fontSizeListBox.Items.Count - 1;
                this.fontSizeListBox.ScrollIntoView(this.fontSizeListBox.Items.Count - 1);
            }
        
            // Сокрытие унаследованных от Window одноименных свойств
            new public FontFamily FontFamily
            {
                get { return (FontFamily)this.fontFamilyListBox.SelectedItem; }
                set
                {
                    this.fontFamilyListBox.SelectedItem = value;    // Выделить
                    this.fontFamilyListBox.ScrollIntoView(value);   // Прокрутить до видимого
                }
            }
            new public FontWeight FontWeight
            {
                get { return (FontWeight)this.fontWeightListBox.SelectedItem; }
                set
                {
                    this.fontWeightListBox.SelectedItem = value;    // Выделить
                    this.fontWeightListBox.ScrollIntoView(value);   // Прокрутить до видимого
                }
            }
            new public double FontSize
            {
                get { return (double)this.fontSizeListBox.SelectedItem; }
                set
                {
                    this.fontSizeListBox.SelectedItem = value;      // Выделить
                    this.fontSizeListBox.ScrollIntoView(value);     // Прокрутить до видимого
                }
            }
        
            private void fontFamilyTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // Привести ListBox в соответствие с содержимым TextBox
                this.FontFamily = new FontFamily(this.fontFamilyTextBox.Text);
            }
        
            private void fontWeightTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // Привести ListBox в соответствие с содержимым TextBox
                if (FontPropertyLists.CanParseFontWeight(this.fontWeightTextBox.Text))
                {
                    this.FontWeight = FontPropertyLists.ParseFontWeight(this.fontWeightTextBox.Text);
                }
            }
        
            private void fontSizeTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                // Привести ListBox в соответствие с содержимым TextBox
                double fontSize;
                if (double.TryParse(this.fontSizeTextBox.Text, out fontSize))
                {
                    this.FontSize = fontSize;   // В поле действительно число
                }
            }
        }
    }
  • Запустите проект - получим инструмент для просмотра основных атрибутов библиотечного текстового элемента Label
  • Обратите внимание, как компоновка окна сохраняется при изменении его размеров и что окно имеет минимальный размер. Разберитесь с кодом разметки и процедурным кодом C#
  • Элемент TextBlock с анимацией

    Текстовый элемент TextBlock поддерживает способы программирования Windows и стили Web. Он обладает развитыми средствами анимации и динамического программирования стилей. Настройки элемента можно задавать в декларативном ( XAML ) и процедурном ( C# ) режимах. Проиллюстрируем некоторые возможности элемента TextBlock на примере.

  • Добавьте в файл Window1.xaml проекта WpfText1 вкладку TextBlock1 со следующей разметкой
  • <!--Вкладка TextBlock1-->
            <TabItem Header="TextBlock1">
                <Border Background="Black">
                    <Border Margin="25" CornerRadius="50" BorderThickness="2" BorderBrush="Yellow">
                        
                        <!--Радиальный градиент заливки Border-->
                        <Border.Background>
                            <RadialGradientBrush>
                                <GradientStop Color="Blue" Offset="0.1" />
                                <!--Непрозрачный alfa-канал и 000000 (Black)-->
                                <GradientStop Color="#FF000000" Offset="1.2" />
                            </RadialGradientBrush>
                        </Border.Background>
                    
                        <!--Анимация градиентной подсветки внешнего контура Border-->
                        <Border.BitmapEffect>
                            <OuterGlowBitmapEffect x:Name="glow" GlowColor="Yellow" GlowSize="0" />
                        </Border.BitmapEffect>
                        <Border.Triggers>
                            <EventTrigger RoutedEvent="Border.Loaded">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation 
                                            Storyboard.TargetName="glow"
                                            Storyboard.TargetProperty="GlowSize"
                                            From="30" To="0" Duration="0:0:1" 
                                            AutoReverse="True"
                                            RepeatBehavior="Forever" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Border.Triggers>
                        
                        <!--Текстовый блок-->
                        <TextBlock Name="text" 
                                   Text=""
                                   HorizontalAlignment="Center" 
                                   VerticalAlignment="Center" 
                                   Foreground="White" FontSize="16"
                                   Initialized="text_Initialized"
                                   >
                            <TextBlock.Inlines>
                                <Bold>Это текстовый</Bold>
                                <LineBreak />
                                <Italic>элемент TextBlock</Italic>
                                <LineBreak />
                                <Run FontWeight="Bold" Foreground="Red">
                                    Очень
                                </Run>
                                <Run FontStyle="Italic" TextDecorations="Underline" Foreground="Yellow">
                                    красивый
                                </Run>
                            </TextBlock.Inlines>
                            
                            <!--Действия в TextBlock-->
                            <TextBlock.LayoutTransform>
                                <RotateTransform x:Name="rotateTransform" Angle="0" />
                            </TextBlock.LayoutTransform>
                            <TextBlock.Triggers>
                                <!--Анимация поворота-->
                                <EventTrigger RoutedEvent="TextBlock.MouseDown">
                                    <EventTrigger.Actions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <DoubleAnimation
                                                    Storyboard.TargetName="rotateTransform"
                                                    Storyboard.TargetProperty="Angle"
                                                    From="0" To="360" Duration="0:0:3"
                                                    RepeatBehavior="2x"
                                                    />
                                            </Storyboard>
                                        </BeginStoryboard>  
                                    </EventTrigger.Actions>
                                </EventTrigger>    
                                <!--Анимация изменения размера при заходе мыши-->  
                                <EventTrigger RoutedEvent="TextBlock.MouseEnter">
                                    <EventTrigger.Actions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <DoubleAnimation
                                                    Storyboard.TargetName="text" 
                                                    Storyboard.TargetProperty="FontSize"
                                                    To="26" Duration="0:0:0.2"
                                                    />  
                                            </Storyboard>
                                        </BeginStoryboard> 
                                    </EventTrigger.Actions>
                                </EventTrigger>
                                <!--Анимация изменения размера при сходе мыши-->  
                                <EventTrigger RoutedEvent="TextBlock.MouseLeave">
                                    <EventTrigger.Actions>
                                        <BeginStoryboard>
                                            <Storyboard>
                                                <DoubleAnimation
                                                    Storyboard.TargetName="text" 
                                                    Storyboard.TargetProperty="FontSize"
                                                    Duration="0:0:1"
                                                    />    
                                            </Storyboard>
                                        </BeginStoryboard> 
                                    </EventTrigger.Actions>
                                </EventTrigger>
                            </TextBlock.Triggers>
                        </TextBlock>
                    </Border>
                </Border>
            </TabItem>
  • Добавьте в процедурную часть (файл Window1.xaml.cs ) код обработчика события Initialized элемента TextBlock, который в динамическом режиме дополняет декларативную настройку этого текстового элемента
  • private void text_Initialized(object sender, EventArgs e)
            {
                // Добавляем в TextBlock строку
                Run run = new Run("\nDynamic");
                run.FontFamily = new FontFamily("Curlz MT");
                text.Inlines.Add(run);
                run = new Run("Text");
                run.FontFamily = new FontFamily("Comic Sans MS");
                run.Foreground = Brushes.Aqua;
                text.Inlines.Add(new Bold(run));
            }
  • Запустите проект - получим анимационный текст с персональными настройками стилей
  • Изучите приведенный код
  • Элемент TextBlock с текстурой символов

    Текст в элементе TextBlock можно закрашивать произвольным рисунком.

  • В панели Solution Explorer создайте для корня дерева проекта папку Images и добавьте в нее (командой Add ) из прилагаемого к работе каталога Source файлы Image1.jpg и Image2.png
  • Добавьте в файл Window1.xaml новую вкладку со следующей разметкой
  • <!--Вкладка TextBlock2-->
            <TabItem Header="TextBlock2">
                <Canvas Background="Black">
                    <TextBlock 
                        Canvas.Left = "10" 
                        Canvas.Top="10" 
                        FontFamily="Arial" 
                        FontSize="36" 
                        Foreground="Red"
                        Text="SolidColorBrush" 
                    />
                    <TextBlock 
                        Canvas.Left = "10"
                        Canvas.Top="50" 
                        FontFamily="Arial" 
                        FontSize="75" 
                        Text="Image1">
                        <TextBlock.Foreground>
                            <ImageBrush ImageSource="Images\Image1.jpg" Opacity=".9" />
                        </TextBlock.Foreground>
                    </TextBlock>
                    <TextBlock 
                        Canvas.Left = "10" 
                        Canvas.Top="130" 
                        FontFamily="Arial" 
                        FontSize="75" 
                        Text="Image2">
                        <TextBlock.Foreground>
                            <ImageBrush ImageSource="Images/Image2.png" />
                        </TextBlock.Foreground>
                    </TextBlock>
                </Canvas>
            </TabItem>
  • Запустите приложение - должно получиться следующее
  • Изучите приведенный код
  • Автоматический перенос слов в TextBlock

    Если явно определить ширину элемента TextBlock и задать атрибут TextWraping="Wrap", то блочный текст будет автоматически размещаться внутри заданной ширины.

  • Добавьте в файл Window1.xaml новую вкладку со следующей разметкой
  • <!--Вкладка TextBlock3-->
            <!-- Элемент TextBlock с переносом строк -->        
            <TabItem Header="TextBlock3">
                <Grid Background="White">
                    <TextBlock 
                        TextWrapping="Wrap" 
                        FontSize="18" 
                        TextAlignment="Center" >
                        Если в элементе TextBlock задать атрибут TextWraping="Wrap", 
                        то блочный текст будет автоматически размещаться внутри 
                        заданной ширины
                        <LineBreak />
                        <Run Foreground="Red" FontSize="28" FontFamily="Monotype Corsiva">
                            Панель Grid здесь введена для создания белого фона
                        </Run>
                        <LineBreak />
                        <Run FontSize="12">
                            Общие настройки родителя действуют на потомков до тех пор,
                            пока не будут переопределены внутри самих потомков
                        </Run>
                    </TextBlock>
                </Grid>
            </TabItem>
  • Запустите приложение - должно получиться следующее
  • Изучите приведенный код
  • Использование дополнительных шрифтов

    Иногда нет уверенности в том, что на компьютере пользователя есть нужные шрифты, с использованием которых программист разрабатывает интерфейс. В таком случае шрифты можно поставлять вместе с приложением.

  • Перейдите в раздел "Панель управления", откройте папку " Шрифты " и перетащите мышью шрифт, например, "Monotype Corsiva" в какую-нибудь папку (файл шрифта уже есть в прилагаемом каталоге Source )
  • В панели Solution Explorer создайте в корне проекта папку Fonts, выделите ее и добавьте командой Project/Add Existing Item файл шрифта MTCORSVA.TTF
  • Добавьте в файл Window1.xaml новую вкладку со следующей разметкой
  • <!--Вкладка TextBlock4-->
            <!-- Элемент TextBlock с дополнительным шрифтом -->
            <TabItem Header="TextBlock4">
                <Grid Background="White">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock
                        FontFamily="Fonts/MTCORSVA.TTF#Monotype Corsiva"
                        FontSize="20"
                        TextWrapping="Wrap"
                        TextAlignment="Justify"
                        Margin="10"
                        ToolTip="Элемент TextBlock с дополнительным шрифтом">
                        Шрифт для этого текста подключен явно и поставляется 
                        вместе с приложением на тот случай, если у бедного
                        пользователя компьютер окажется еще беднее.
                    </TextBlock>
                    <TextBlock Grid.Row="1" Margin="10" TextWrapping="Wrap">
                        В этом блоке текста используется шрифт по умолчанию и 
                        нет всплывающей подсказки ToolTip, а в верхнем блоке есть                    
                    </TextBlock>
                </Grid>
            </TabItem>
  • Запустите приложение - должно получиться следующее
  • Изучите приведенный код
  • Элементы TextBox и PasswordBox

  • Добавьте к проекту следующую вкладку со скриптовым кодом
  • <!--TextBox и PasswordBox-->
            <TabItem Header="TextBox" Selector.IsSelected="True">
                <StackPanel VerticalAlignment="Center" Margin="10">
                    <TextBlock HorizontalAlignment="Center">
                        <Run FontSize="14" FontWeight="Bold">
                            Элементы PasswordBox и TextBox
                        </Run>
                    </TextBlock>
                    <Grid Height="20" />
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="Поле PasswordBox: " VerticalAlignment="Center" />
                        <PasswordBox Grid.Column="1" Name="passwordBox" />
                    </Grid>
                    <Grid Height="20" />
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="Поле TextBox: " VerticalAlignment="Center" />
                        <TextBox Grid.Column="1" Name="textBox"></TextBox>
                    </Grid>
                        <Grid Height="20" />
                    <Grid HorizontalAlignment="Center">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition Width="5" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Button Grid.Column="0" Content="Показать" Click="Button_Click" />
                        <ToggleButton Grid.Column="2" Name="toggleButton" Content="EnabledPassword" 
                                      Checked="toggleButton_Checked" Unchecked="toggleButton_Checked"/>
                    </Grid>
                </StackPanel>
            </TabItem>
  • Выделенную в разметке кнопок регистрацию обработчиков наберите вручную
  • Добавьте в кодовую часть следующие обработчики
  • private void Button_Click(object sender, RoutedEventArgs e)
            {
                textBox.Text = passwordBox.Password;
            }
        
            bool flagState = true;
            Brush color;
            private void toggleButton_Checked(object sender, RoutedEventArgs e)
            {
                // Сохраняем первоначальный цвет кнопки 
                if (flagState)
                {
                    color = toggleButton.Background;
                    flagState = false;
                }
        
                if (toggleButton.IsChecked == true)
                {
                    passwordBox.IsEnabled = false;
                    toggleButton.Background = Brushes.Red;
                }
                else
                {
                    passwordBox.IsEnabled = true;
                    toggleButton.Background = color;
                }
            }
  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Упражнение 2. Поддержка документов нефиксированного формата

    Классы-упаковки содержимого нефиксированного формата

  • FlowDocumentReader
  • RichTextBox
  • FlowDocumentScrollViewer
  • FlowDocumentPageViewer
  • имеют дочерний элемент FlowDocument. В свою очередь, FlowDocument задает строгую модель содержимого для своего дочернего содержимого. Дочерние элементы верхнего уровня, содержащиеся в FlowDocument, должны быть производными от Block и называются блочными. Допустимыми дочерними элементами верхнего уровня в FlowDocument являются следующие блоки:

    Блок Описание
    Paragraph Содержит текст, возможно форматированный.
    List Содержит списки разного типа (нумерованный, маркированный и т. д.).
    Table Содержит таблицы, похожие на таблицы Microsoft Word или HTML.
    BlockUIContainer Содержит различные элементы пользовательского интерфейса, становящиеся частью размещения.
    Section Содержит группу прочих блоков. Разделы удобны для применения общих атрибутов к группам блоков, например, одни и те же атрибуты шрифта для нескольких абзацев.

    Внутри блоков дополнительное форматирование текста обеспечивается элементами, которые называются внутристрочными элементами. К ним относятся

  • элемент Bold
  • элемент Italic
  • элемент LineBreak
  • элемент List
  • элемент ListItem
  • элемент Paragraph
  • элемент Run
  • элемент Section
  • элемент Span
  • элемент Underline
  • Внутристрочные элементы могут быть вложенными друг в друга внутри блока или вместе с родительским блоком вложены в другой блок.

    Некоторые элементы могут дополнительно настраиваться, например, такими атрибутами, как

  • BreakPageBefore=True" - начать с новой страницы
  • FontSize="24" - размер шрифта
  • Typography.Variants="Superscript" - надстрочный
  • Typography.Variants="Subscript" - подстрочный
  • TextIndent="25" - отступ первой строки блока
  • TextIndent="-15" - выступ первой строки блока
  • Margin="20,0,0,0" - отступ блока слева
  • TextAlignment="Center" - текст посредине контейнера рендеринга (представления)
  • MarkerStyle="None" - тип маркера в элементе List
  • Использование FlowDocument с контейнером по умолчанию

    Создание экземпляра класса FlowDocument автоматически запускает родительский контейнер FlowDocumentReader, который размещает содержимое и имеет стандартную панель управления документом.

  • Добавьте к решению новый проект WpfText2 типа WPF Application и назначьте его стартовым
  • В декларативный раздел XAML добавьте контейнер <TabControl> с вкладкой <TabItem Header="Text1">
  • Заполните файл Window1.xaml следующим кодом
  • <Window x:Class="WpfText2.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Background="LightGray"
        Title="Window1" MinHeight="300" MinWidth="300" Height="300" Width="300">
        
        <TabControl>
            <TabItem Header="Text1">
                <FlowDocument Background="White">
                    <Paragraph TextAlignment="Center" FontWeight="Bold">
                        Потоковый 
                        <Span Foreground="Red"> документ</Span> 
                        <Italic>с разбивкой на страницы</Italic>
                    </Paragraph>
                    <List MarkerStyle="Decimal" FontFamily="Arial">
                        <ListItem>
                            <Paragraph>Первый элемент ListItem</Paragraph>
                        </ListItem>
                        <ListItem>
                            <Paragraph>Второй элемент ListItem</Paragraph>
                        </ListItem>
                        <ListItem>
                            <Paragraph>Третий элемент ListItem</Paragraph>
                        </ListItem>
                    </List>
                    <Paragraph FontSize="12">Класс FlowDocument поддерживает списки, определения  
                        <Bold>
                            <Span FontFamily="Comic Sans MS" FontSize="12" Foreground="Blue">
                                семейств шрифтов, цвет
                            </Span>
                        </Bold> размеры и стили.
                    </Paragraph>
                </FlowDocument>
            </TabItem>
        </TabControl>
    </Window>
  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Использование контейнера RichTextBox

    Оборачивание элемента FlowDocument в контейнер RichTextBox отключает панель навигации по тексту.

  • В контейнер <TabControl> добавьте вкладку <TabItem Header="Text2" Selector.IsSelected="True"> и заполните ее следующим кодом
  • <TabItem Header="Text2" Selector.IsSelected="True">
                <RichTextBox IsReadOnly="True" FontSize="21" >
                    <FlowDocument Background="White">
                        <Paragraph TextAlignment="Center" FontWeight="Bold">
                            Потоковый
                            <Span Foreground="Red">документ</Span>
                            <Italic>обернут в RichTextBox</Italic>
                        </Paragraph>
                        <List MarkerStyle="LowerLatin"  FontFamily="Arial" FontSize="14">
                            <ListItem>
                                <Paragraph>Маркер LowerLatin</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>Второй элемент ListItem</Paragraph>
                            </ListItem>
                        </List>
                        <List MarkerStyle="Box"  FontFamily="Arial" FontSize="14">
                            <ListItem>
                                <Paragraph>Маркер Box</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>Второй элемент ListItem</Paragraph>
                            </ListItem>
                        </List>
                        <Paragraph FontSize="12">В родительском контейнере RichTextBox 
                            <Bold>
                                <Span FontFamily="Comic Sans MS" FontSize="12" Foreground="Blue">
                                    включен режим IsReadOnly="True" 
                                </Span>
                            </Bold> и не появляется полоса прокрутки при длинном тексте
                        </Paragraph>
                    </FlowDocument>
                </RichTextBox>
            </TabItem>

    Присоединенный к дескриптору вкладки атрибут Selector.IsSelected="True" позиционирует ее при открытии окна.

  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Использование контейнера FlowDocumentScrollViewer

    Оборачивание элемента FlowDocument в контейнер FlowDocumentScrollViewer по умолчанию отключает панель инструментов документа и добавляет вертикальную панель прокрутки, которую можно настраивать. Вертикальная полоса прокрутки включается с помощью присоединенного свойства. Панель инструментов включается свойством IsToolBarVisible="True" (по умолчанию IsToolBarVisible="False" ).

    FlowDocument - единственный допустимый дочерний элемент объекта FlowDocumentScrollViewer. В FlowDocumentScrollViewer может быть размещен не более чем один элемент FlowDocument.

  • В контейнер <TabControl> добавьте вкладку <TabItem Header="Text3"> и заполните ее следующим кодом
  • <TabItem Header="Text3">
                <FlowDocumentScrollViewer 
                    ScrollViewer.VerticalScrollBarVisibility="Auto" 
                    IsToolBarVisible="False"
                    >
                    <FlowDocument Background="White" FontSize="14">
                        <Paragraph TextAlignment="Center" FontWeight="Bold">
                            FlowDocument
                            <Italic>помещен в</Italic>
                            <Span Foreground="Red">FlowDocumentScrollViewer</Span>
                        </Paragraph>
                        <List MarkerStyle="None" FontFamily="Arial">
                            <ListItem>
                                <Paragraph>Буря мглою небо кроет</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>Вихри снежные крутя.</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>То как зверь она завоет</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>То заплачет как дитя.</Paragraph>
                            </ListItem>
                        </List>
                        <Paragraph TextAlignment="Left" FontSize="12">Текст 
                            нефикированного формата FlowDocument нужно заключать в параграфы 
                            (абзацы), внутри которых можно применять стилевые элементы.
                        </Paragraph>
                        <Paragraph>
                            <Bold>
                                <Span FontFamily="Comic Sans MS" FontSize="12" Foreground="Blue">
                                    Буря мглою небо кроет вихри снежные крутя.
                                    <LineBreak />
                                </Span>
                            </Bold>
                            То как зверь она завоет, то заплачет как дитя.
                        </Paragraph>
                    </FlowDocument>
                </FlowDocumentScrollViewer>
            </TabItem>

    Присоединенный к дескриптору <FlowDocumentScrollViewer> атрибут ScrollViewer.VerticalScrollBarVisibility="Auto" обеспечивает автоматическое появление полосы прокрутки, если текст не помещается на экране. Атрибуты, установленные в контейнере <FlowDocument>, действуют по умолчанию на все дочерние элементы, если аналогичные атрибуты в них не переопределены. В списках маркеры элементов можно менять или вообще отключать.

  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Использование контейнера FlowDocumentPageViewer

    Использование контейнера FlowDocumentPageViewer для FlowDocument позволяет представлять документ как одну страницу без средства поиска текста и разбивки его на страницы и колонки, но оставляет панель изменения масштаба и навигации. Однако в самом FlowDocument есть свойства, позволяющие настраивать обычный и многоколоночный текст. Вот некоторые из них

  • TextAlignment="Justify" - выравнивание текста по обеим границам колонок (включен по умолчанию)
  • IsHyphenationEnabled="True" - включение расстановки переносов (при наличии словарей)
  • IsOptimalParagraphEnabled="True" - включение оптимального абзаца, выравнивающего пробелы между словами за счет разрежения символов в самих словах при режиме TextAlignment="Justify"
  • ColumnGap="25" - ширина между колонками в логических пикселах
  • ColumnWidth="150" - предпочтительная (минимальная) ширина колонки
  • IsColumnWidthFlexible="False" - это значение флага явно запрещает дозаполнять окно просмотра и устанавливает жесткую ширину колонки
  • ColumnRuleWidth="5" - ширина вертикальных линеек, разделяющих колонки текста
  • ColumnRuleBrush="Red" - цвет линеек
  • В контейнер <TabControl> добавьте вкладку <TabItem Header="Text4"> и заполните ее следующим кодом
  • <TabItem Header="Text4">
                <FlowDocumentPageViewer>
                    <FlowDocument 
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        Background="White"
                        ColumnWidth="150"
                        ColumnRuleWidth="2"
                        ColumnRuleBrush="Red"
                        ColumnGap="30"
                        IsHyphenationEnabled="True"
                        IsOptimalParagraphEnabled="True"
                        >
                        <Paragraph FontSize="18" FontWeight="Bold" TextAlignment="Center">
                            Примеры форматирования FlowDocument amp; FlowDocumentPageViewer
                        </Paragraph>
                        <Paragraph>
                              Это абзац с атрибутами по умолчанию.
                        </Paragraph>
                        <Paragraph>
                              Это еще один абзац с атрибутами по умолчанию.
                        </Paragraph>
                        <Paragraph FontSize="18">Абзац с FontSize="18"</Paragraph>
                        <Paragraph FontSize="15">Абзац с FontSize="15"</Paragraph>
                        <Paragraph FontSize="15">Список lt;List FontFamily="Arial"gt;</Paragraph>
                        <List FontFamily="Arial">
                            <ListItem>
                                <Paragraph>ListItem 1</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 2</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 3</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 4</Paragraph>
                            </ListItem>
                            <ListItem>
                                <Paragraph>ListItem 5</Paragraph>
                            </ListItem>
                        </List>
                        <Paragraph>
                            <Bold>Это элемент lt;Boldgt; внутри абзаца lt;Paragraphgt;</Bold>
                        </Paragraph>
                        <Paragraph>
                            <Underline>Это элемент Underline внутри абзаца</Underline>
                        </Paragraph>
                        <Paragraph>
                            <Bold>
                                <Underline>Подчеркнутый внутри полужирного</Underline>
                            </Bold>
                        </Paragraph>
                        <Paragraph>
                            <Italic>Это элемент lt;Italicgt;</Italic>
                        </Paragraph>
                        <Paragraph>
                            <Span>Элемент Span с настройками по умолчанию</Span>
                        </Paragraph>
                        <Paragraph>
                            <Run>Элемент Run с настройками по умолчанию</Run>
                        </Paragraph>
                        <Paragraph FontSize="15">Subscript, Superscript</Paragraph>
                        <Paragraph>
                            <Run Typography.Variants="Superscript">This text is Superscripted.</Run> 
                            This text isn't.
                        </Paragraph>
                        <Paragraph>
                            <Run Typography.Variants="Subscript">This text is Subscripted.</Run> 
                            This text isn't.
                        </Paragraph>
                        <Paragraph TextIndent="-25" Margin="25,0">
                            If a font does not support a particular form (such as Superscript) 
                            a default font form will be displayed.
                        </Paragraph>
                        <Paragraph FontSize="15">Blocks, breaks, paragraph</Paragraph>
                        <Section>
                            <Paragraph>A block section of text</Paragraph>
                        </Section>
                        <Section>
                            <Paragraph>Another block section of text</Paragraph>
                        </Section>
                        <Paragraph>
                            <LineBreak/>
                        </Paragraph>
                        <Section>
                            <Paragraph>... and another section, preceded by a LineBreak</Paragraph>
                        </Section>
                        <Section BreakPageBefore="True"/>
                        <Section>
                            <Paragraph>... and another section, preceded by a PageBreak</Paragraph>
                        </Section>
                        <Paragraph>Finally, a paragraph. Note the break between this paragraph ...</Paragraph>
                        <Paragraph TextIndent="25">... and this paragraph, and also the left indention.</Paragraph>
                    </FlowDocument>
                </FlowDocumentPageViewer>
            </TabItem>

    Здесь мы применили основной набор элементов форматирования текста 'на лету'. Обратите внимание, что в разметке угловые скобки кодируются как в HTML. Другие управляющие символы разметки кодируются аналогично, например, ' ' кодируется как ' amp; '. Знак подчеркивания ' _ ' считается служебным для выделения горячих клавиш в меню, поэтому в ином употреблении должен экранироваться таким же знаком подчеркивания.

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

  • Запустите приложение, поизменяйте размер окна и разберитесь с кодом
  • Результат будет таким

    Обратите внимание, что в длинных словах кириллический текст объект FlowDocument не переносит, а латиницу - переносит.

  • Проверьте действие атрибута IsOptimalParagraphEnabled="True", включающего оптимальный параграф
  • Пример из MSDN

    Следующий пример взят из MSDN (http://msdn.microsoft.com/ru-ru/library/system.windows.controls.flowdocumentscrollviewer.aspx) и иллюстрирует дополнительные возможности разметки неформатированного текста в FlowDocument. Он не содержит кириллицы, поэтому дальше нам пригодится при решении проблем кодировки.

  • Добавьте новую вкладку с приведенным кодом
  • <TabItem Header="Text5" Selector.IsSelected="True">
                <FlowDocumentScrollViewer Margin="10" BorderBrush="Black" BorderThickness="1">
                    <FlowDocument 
                        ColumnWidth="400" 
                        IsOptimalParagraphEnabled="True" 
                        IsHyphenationEnabled="True"
                        >
                        <Section FontSize="12">
                            <Paragraph>
                                <Bold>Neptune</Bold> (planet), major planet in the solar system, eighth planet
                                from the Sun and fourth largest in diameter.  Neptune maintains an almost constant
                                distance, about 4,490 million km (about 2,790 million mi), from the Sun.  Neptune
                                revolves outside the orbit of Uranus and for most of its orbit moves inside the
                                elliptical path of the outermost planet Pluto (see Solar System). Every 248 years,
                                Pluto’s elliptical orbit brings the planet inside Neptune’s nearly circular orbit
                                for about 20 years, temporarily making Neptune the farthest planet from the Sun.
                                The last time Pluto’s orbit brought it inside Neptune’s orbit was in 1979. In
                                1999 Pluto’s orbit carried it back outside Neptune’s orbit.
                                <Figure 
                                      Width="140" Height="50" Background="GhostWhite" 
                                      HorizontalAnchor="PageLeft" 
                                      HorizontalOffset="100" VerticalOffset="20"
                                      >
                                    <Paragraph 
                                        FontStyle="Italic" TextAlignment="Left"
                                        Background="Beige" Foreground="DarkGreen"
                                        >
                                        Neptune has 72 times Earth's volume...
                                    </Paragraph>
                                </Figure>
                                
                                <Floater 
                                    Background="GhostWhite" 
                                    Width="285" HorizontalAlignment="Left"
                                    >
                                    <Table CellSpacing="5">
                                        <Table.Columns>
                                            <TableColumn Width="155"/>
                                            <TableColumn Width="130"/>
                                        </Table.Columns>
                                        
                                        <TableRowGroup>
                                            <TableRow>
                                                <TableCell ColumnSpan="3">
                                                    <Paragraph>Neptune Stats</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            
                                            <TableRow Background="LightGoldenrodYellow" FontSize="12">
                                                <TableCell>
                                                    <Paragraph FontWeight="Bold">Mean Distance from Sun</Paragraph>
                                                </TableCell>
                                                <TableCell>
                                                    <Paragraph>4,504,000,000 km</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            
                                            <TableRow FontSize="12" Background="LightGray">
                                                <TableCell>
                                                    <Paragraph FontWeight="Bold">Mean Diameter</Paragraph>
                                                </TableCell>
                                                <TableCell>
                                                    <Paragraph>49,532 km</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            
                                            <TableRow Background="LightGoldenrodYellow" FontSize="12">
                                                <TableCell>
                                                    <Paragraph FontWeight="Bold">Approximate Mass</Paragraph>
                                                </TableCell>
                                                <TableCell>
                                                    <Paragraph>1.0247e26 kg</Paragraph>
                                                </TableCell>
                                            </TableRow>
                                            <TableRow>
                                                <TableCell ColumnSpan="4">
                                                    <Paragraph FontSize="10" FontStyle="Italic">
                                                        Information from the
                                                        <Hyperlink 
                                                            NavigateUri="http://encarta.msn.com/encnet/refpages/artcenter.aspx"
                                                            >
                                                            Encarta
                                                        </Hyperlink>
                                                        web site.
                                                    </Paragraph>
                                                </TableCell>
                                            </TableRow>
                                        </TableRowGroup>
                                    </Table>
                                </Floater>
                            </Paragraph>
                            <Paragraph>
                                Astronomers believe Neptune has an inner rocky core that is surrounded by a vast
                                ocean of water mixed with rocky material. From the inner core, this ocean extends
                                upward until it meets a gaseous atmosphere of hydrogen, helium, and trace amounts
                                of methane. Neptune has four rings and 11 known moons. Even though Neptune's volume
                                is 72 times Earth’s volume, its mass is only 17.15 times Earth’s mass. Because of
                                its size, scientists classify Neptune—along with Jupiter, Saturn, and Uranus—as
                                one of the giant or Jovian planets (so-called because they resemble Jupiter).
                            </Paragraph>
                            <Paragraph>
                                <Figure 
                                    Width="140" Height="50" 
                                    Background="GhostWhite" TextAlignment="Left" 
                                    HorizontalAnchor="PageCenter" WrapDirection="Both"
                                    >
                                    <Paragraph FontStyle="Italic" Background="Beige" Foreground="DarkGreen" >
                                        Neptune has an orbital period of ~20 years...
                                    </Paragraph>
                                </Figure>
                                Mathematical theories of astronomy led to the discovery of Neptune. To account for
                                wobbles in the orbit of the planet Uranus, British astronomer John Couch Adams and
                                French astronomer Urbain Jean Joseph Leverrier independently calculated the existence
                                and position of a new planet in 1845 and 1846, respectively. They theorized that the
                                gravitational attraction of this planet for Uranus was causing the wobbles in Uranus’s
                                orbit. Using information from Leverrier, German astronomer Johann Gottfried Galle first
                                observed the planet in 1846.
                            </Paragraph>
                        </Section>
                    </FlowDocument>
                </FlowDocumentScrollViewer>
            </TabItem>
  • Запустите приложение - результат будет таким
  • Поизменяйте размер окна и разберитесь с кодом
  • Описание использованных в примере элементов можно найти на http://msdn.microsoft.com/ru-ru/library/system.windows.documents.aspx

    Загрузка FlowDocument из XAML-файла

    Документ нефиксированного формата можно создать заранее в XAML -редакторе, сохранить его в файле и загружать во время выполнения в элемент управления просмотром. Продемонстрируем это на предыдущем примере.

  • Добавьте в разметку новую вкладку следующего содержания (атрибут подписки на событие Initialized введите вручную, чтобы правильно создать обработчик)
  • <TabItem Header="Text6" Selector.IsSelected="True">
                <FlowDocumentScrollViewer 
                    Margin="10" 
                    BorderBrush="Black"
                    BorderThickness="1"
                    Initialized="FlowDocumentScrollViewer_Initialized"
                    Name="documentReader" />
            </TabItem>

    Здесь мы декларативно определили и настроили пустой именованный контейнер просмотра документа, а сам документ с содержимым FlowDocument загрузим из файла программно и присоединим к свойству Document этого контейнера просмотра.

  • Уберите из предыдущей вкладки атрибут Selector.IsSelected="True", чтобы при запуске приложения сразу открывалась вкладка Text6
  • Добавьте к проекту WpfText2 новый XML -файл с именем XMLFile1.xml и измените его расширение c xml на xaml
  • Выделите новый файл и измените через панель Properties его свойства на
  • Build Action = None
  • Copy to Output Directory=Copy if newer
  • Скопируйте из предыдущего примера контейнер FlowDocument вместе с его содержимым в файл XMLFile1.xaml и добавьте атрибуты пространств имен WPF в дескриптор FlowDocument
  • Мы создали, так называемый, свободный файл XAML. Свободный файл XAML - это файл со следующими характеристиками:

  • Содержит только XAML (то есть не код C# )
  • Имеет объявление соответствующего пространства имен
  • Имя файла имеет расширение XAML
  • В нашем случае содержимое файла XMLFile1.xaml должно стать таким

    <FlowDocument
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ColumnWidth="400"
        IsOptimalParagraphEnabled="True"
        IsHyphenationEnabled="True"
        >
      <Section FontSize="12">
        <Paragraph>
          <Bold>Neptune</Bold> (planet), major planet in the solar system, eighth planet
          from the Sun and fourth largest in diameter.  Neptune maintains an almost constant
          distance, about 4,490 million km (about 2,790 million mi), from the Sun.  Neptune
          revolves outside the orbit of Uranus and for most of its orbit moves inside the
          elliptical path of the outermost planet Pluto (see Solar System). Every 248 years,
          Pluto’s elliptical orbit brings the planet inside Neptune’s nearly circular orbit
          for about 20 years, temporarily making Neptune the farthest planet from the Sun.
          The last time Pluto’s orbit brought it inside Neptune’s orbit was in 1979. In
          1999 Pluto’s orbit carried it back outside Neptune’s orbit.
          <Figure
                Width="140" Height="50" Background="GhostWhite"
                HorizontalAnchor="PageLeft"
                HorizontalOffset="100" VerticalOffset="20"
                >
            <Paragraph
                FontStyle="Italic" TextAlignment="Left"
                Background="Beige" Foreground="DarkGreen"
                >
              Neptune has 72 times Earth's volume...
            </Paragraph>
          </Figure>
      
          <Floater
              Background="GhostWhite"
              Width="285" HorizontalAlignment="Left"
                                    >
            <Table CellSpacing="5">
              <Table.Columns>
                <TableColumn Width="155"/>
                <TableColumn Width="130"/>
              </Table.Columns>
      
              <TableRowGroup>
                <TableRow>
                  <TableCell ColumnSpan="3">
                    <Paragraph>Neptune Stats</Paragraph>
                  </TableCell>
                </TableRow>
      
                <TableRow Background="LightGoldenrodYellow" FontSize="12">
                  <TableCell>
                    <Paragraph FontWeight="Bold">Mean Distance from Sun</Paragraph>
                  </TableCell>
                  <TableCell>
                    <Paragraph>4,504,000,000 km</Paragraph>
                  </TableCell>
                </TableRow>
      
                <TableRow FontSize="12" Background="LightGray">
                  <TableCell>
                    <Paragraph FontWeight="Bold">Mean Diameter</Paragraph>
                  </TableCell>
                  <TableCell>
                    <Paragraph>49,532 km</Paragraph>
                  </TableCell>
                </TableRow>
      
                <TableRow Background="LightGoldenrodYellow" FontSize="12">
                  <TableCell>
                    <Paragraph FontWeight="Bold">Approximate Mass</Paragraph>
                  </TableCell>
                  <TableCell>
                    <Paragraph>1.0247e26 kg</Paragraph>
                  </TableCell>
                </TableRow>
                <TableRow>
                  <TableCell ColumnSpan="4">
                    <Paragraph FontSize="10" FontStyle="Italic">
                      Information from the
                      <Hyperlink
                          NavigateUri="http://encarta.msn.com/encnet/refpages/artcenter.aspx"
                                                            >
                        Encarta
                      </Hyperlink>
                      web site.
                    </Paragraph>
                  </TableCell>
                </TableRow>
              </TableRowGroup>
            </Table>
          </Floater>
        </Paragraph>
        <Paragraph>
          Astronomers believe Neptune has an inner rocky core that is surrounded by a vast
          ocean of water mixed with rocky material. From the inner core, this ocean extends
          upward until it meets a gaseous atmosphere of hydrogen, helium, and trace amounts
          of methane. Neptune has four rings and 11 known moons. Even though Neptune's volume
          is 72 times Earth’s volume, its mass is only 17.15 times Earth’s mass. Because of
          its size, scientists classify Neptune—along with Jupiter, Saturn, and Uranus—as
          one of the giant or Jovian planets (so-called because they resemble Jupiter).
        </Paragraph>
        <Paragraph>
          <Figure
              Width="140" Height="50"
              Background="GhostWhite" TextAlignment="Left"
              HorizontalAnchor="PageCenter" WrapDirection="Both"
              >
            <Paragraph FontStyle="Italic" Background="Beige" Foreground="DarkGreen" >
              Neptune has an orbital period of ~20 years...
            </Paragraph>
          </Figure>
          Mathematical theories of astronomy led to the discovery of Neptune. To account for
          wobbles in the orbit of the planet Uranus, British astronomer John Couch Adams and
          French astronomer Urbain Jean Joseph Leverrier independently calculated the existence
          and position of a new planet in 1845 and 1846, respectively. They theorized that the
          gravitational attraction of this planet for Uranus was causing the wobbles in Uranus’s
          orbit. Using information from Leverrier, German astronomer Johann Gottfried Galle first
          observed the planet in 1846.
        </Paragraph>
      </Section>
    </FlowDocument>
  • Заполните файл Window1.xaml.cs кодовой части так
  • 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.IO;
        
    namespace WpfText2
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        
            private void FlowDocumentScrollViewer_Initialized(object sender, EventArgs e)
            {
                documentReader.Document = (FlowDocument)XamlReader.
                    Load(File.OpenRead("XMLFile1.xaml"));
        
                // Альтернативный вариант
                //LoadFlowDocumentScrollViewerWithXAMLFile("XMLFile1.xaml");
            }
        
            // Альтернативный вариант
            void LoadFlowDocumentScrollViewerWithXAMLFile(string fileName)
            {
                FileStream xamlFile = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                FlowDocument content = XamlReader.Load(xamlFile) as FlowDocument;
                documentReader.Document = content;
        
                xamlFile.Close();
            }
        }
    }

    Здесь мы свойству Document контейнера просмотра FlowDocumentScrollViewer присваиваем возвращенный классом XamlReader размеченный документ нефиксированного формата.

  • Запустите приложение - получится точно такой же результат, как в предыдущем примере
  • Проверьте работу закомментированного альтернативного варианта процедурного кода
  • Упражнение 3. Работа в WPF с HTML-документами

    Поскольку XAML -формат документов и сама технология WPF являются относительно новыми, может потребоваться применить WPF к уже наработанному большому массиву HTML -документов. Для этого в WPF имеется класс System.Windows.Controls.WebBrowser. Его не нужно путать с одноименным классом System.Windows.Forms.WebBrowser технологии Windows Forms. Описание WebBrowser приведено в

    http://msdn.microsoft.com/ru-ru/library/system.windows.controls.webbrowser.aspx

    В этом упражнении мы познакомимся с применением WebBrowser WPF к просмотру HTML -документов, а также с преобразованием HTML в рассмотренные ранее XAML -документы нефиксированного формата.

  • Добавьте к решению командой File/Add/New Project новый проект WpfText3 типа WPF Application и назначьте его стартовым
  • Просмотр HTML-документов в WebBrowser

  • В декларативный раздел XAML добавьте контейнер <TabControl> с вкладкой <TabItem Header="HTML1">
  • Заполните файл Window1.xaml следующим кодом
  • <Window x:Class="WpfText3.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"
        Background="LightGray"
            >
        
        <TabControl>
            <TabItem Header="HTML1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <WebBrowser Grid.Row="0" Name="webBrowser1" />
                    <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
                        <Button Width="75" Content="Back" Click="backButton_Click" Margin="0,5,0,0" />
                        <Button Width="75" Content="Load" Click="loadButton_Click" Margin="5,5,5,0" />
                        <Button Width="75" Content="Forward" Click="forwardButton_Click" Margin="0,5,0,0" />
                    </StackPanel>
                </Grid>  
            </TabItem>
        </TabControl>
    </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;
        
    namespace WpfText3
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        
            private void loadButton_Click(object sender, RoutedEventArgs e)
            {
                Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
                openFileDialog.Filter = "HTML Documents (*.htm; *.html)|*.htm;*.html";
                openFileDialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
                openFileDialog.Multiselect=false;
                if (openFileDialog.ShowDialog() == false || openFileDialog.FileName == String.Empty)
                    return;
        
                Uri uri = new Uri(openFileDialog.FileName);
                this.webBrowser1.Source = uri;
            }
        
            private void backButton_Click(object sender, RoutedEventArgs e)
            {
                if (this.webBrowser1.CanGoBack)
                {
                    this.webBrowser1.GoBack();
                }
            }
        
            private void forwardButton_Click(object sender, RoutedEventArgs e)
            {
                if (this.webBrowser1.CanGoForward)
                {
                    this.webBrowser1.GoForward();
                }
            }
        }
    }
  • Запустите проект - получится нормальный инструмент просмотра HTML -документов
  • Отображение HTML-текста в WebBrowser из файла

    Обратите внимание, что в предыдущем примере автоматически появляются полосы прокрутки, если HTML -документ не помещается в окне элемента WebBrowser. Фрагмент форматированного HTML -текста включать прямо в WebBrowser нельзя, поскольку XAML -редактор оболочки сразу начнет ругаться. Но во время выполнения загружать файл из кода можно.

    В данном примере мы заранее создадим HTML -файл с простым форматированным текстом.

  • В Solution Explorer вызовите контекстное меню для узла WpfText3 и командой Add/Existing Item добавьте к проекту файл HtmlDoc.htm из прилагаемого каталога Source (или создайте свой HTML -документ и добавьте его к проекту той же командой)
  • Содержимое файла будет примерно таким

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
     <title>Untitled Document</title>
     <!--
      <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
     -->
    </head>
        
    <body style="font-family:Arial, Helvetica, sans-serif">
    <h2 align="center"><font color="#FF0000">Это небольшой фрагмент чистого HTML-текста</font></h2>
    <ol>
      <li>Шрифт Arial</li>
      <li><font color="#0000FF">Цвет font color=quot;#0000FFquot;</font></li>
      <li><font face="Courier New">Шрифт Courier New </font></li>
      <li><b>Стиль полужирный lt;bgt; </b></li>
      <li><i>Стиль Italic  lt;igt; </i></li>
    </ol>
    </body>
    </html>
  • Выделите этот файл и в панели Properties настройте его свойства так
  • Build Action = None
  • Copy to Output Directory=Copy if newer
  • В декларативный раздел XAML в контейнер <TabControl> добавьте новую вкладку <TabItem Header="HTML2">
  • Заполните ее следующим скриптом
  • <TabItem Header="HTML2" Selector.IsSelected="True">
                <WebBrowser x:Name="webBrowser2" Initialized="webBrowser2_Initialized" />
            </TabItem>
  • Обработчик события Initialized заполните так
  • private void webBrowser2_Initialized(object sender, EventArgs e)
            {
                Uri uri = new Uri(System.IO.Directory.GetCurrentDirectory() +
                    "\\HtmlDoc.htm", UriKind.Absolute);
        
                this.webBrowser2.Navigate(uri);
            }

    Здесь мы применили несколько иной способ загрузки файла!

  • Запустите проект - получится примерно следующее
  • Вертикальную полосу прокрутки мне убрать в этом примере не удалось.

    Отображение HTML-текста в WebBrowser из потока

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

  • В декларативный раздел XAML в контейнер <TabControl> добавьте новую вкладку <TabItem Header="HTML3">
  • Заполните ее следующим скриптом
  • <TabItem Header="HTML3" Selector.IsSelected="True">
                <WebBrowser x:Name="webBrowser3" Initialized="webBrowser3_Initialized" />
            </TabItem>
  • Обработчик события Initialized заполните так
  • private void webBrowser3_Initialized(object sender, EventArgs e)
            {
                Uri uri = new Uri("HtmlDoc.htm", UriKind.Relative);
                System.IO.Stream source = Application.GetRemoteStream(uri).Stream;
        
                this.webBrowser3.NavigateToStream(source);
            }
  • Запустите проект - получится то же самое, что в предыдущем примере
  • Еще один способ загрузки HTML-документа из потока

  • В декларативный раздел XAML в контейнер <TabControl> добавьте новую вкладку <TabItem Header="HTML4">
  • Заполните ее следующим скриптом
  • <TabItem Header="HTML4" Selector.IsSelected="True">
                <WebBrowser x:Name="webBrowser4" Initialized="webBrowser4_Initialized" />
            </TabItem>
  • Обработчик события Initialized заполните так
  • private void webBrowser4_Initialized(object sender, EventArgs e)
            {
                string path = System.IO.Directory.GetCurrentDirectory() + "\\HtmlDoc.htm";
                System.IO.StreamReader streamReader = new System.IO.StreamReader(
                    path, Encoding.GetEncoding(1251));
                //MessageBox.Show(streamReader.CurrentEncoding.CodePage.ToString());
                string text = streamReader.ReadToEnd();
        
                webBrowser4.NavigateToString(text);
            }
    Здесь мы задали номер кодовой страницы 1251  !!!
  • Обязательно закомментируйте или удалите HTML-дескриптор <meta> в документе HtmlDoc.htm, иначе будут проблемы с кодировкой "Кириллица (Windows) - 1251" в этом примере HTML4
  • <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <title>Untitled Document</title>
    <!--
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
    -->
    </head>
        
    <body style="font-family:Arial, Helvetica, sans-serif">
    <h2 align="center"><font color="#FF0000">Это небольшой фрагмент чистого HTML-текста</font></h2>
    <ol>
      <li>Шрифт Arial</li>
      <li><font color="#0000FF">Цвет font color=quot;#0000FFquot;</font></li>
      <li><font face="Courier New">Шрифт Courier New </font></li>
      <li><b>Стиль полужирный lt;bgt; </b></li>
      <li><i>Стиль Italic  lt;igt; </i></li>
    </ol>
    </body>
    </html>
  • Запустите проект - получится то же самое, что в предыдущем примере
  • Упражнение 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 аж целых четыре года назад.

    Упражнение 6. Выборка текста из текстового файла с форматированием на лету

    В данном упражнении приводится несколько примеров обработки текстовых файлов и их форматирования в FlowDocument программным способом. Мне надоело описывать пошаговые инструкции, а вам надоело их разбирать, поэтому приведу итоговый код для исследования.

    Что я сделал?

  • Добавил к решению новый проект типа WPF Application с именем WPFText6
  • Создал в корне проекта каталог Documents и добавил в него готовый текстовый файл TextDocument.txt с таким случайным содержимым (приводится в каталоге Source )
  • Критерием отнесения ТС к маршрутным является наличие установленного маршрута с обозначенными местами остановок. К таковым Правила относят автобусы, троллейбусы и трамваи (п. 1.2). Автомобили-такси к маршрутным ТС не относятся.
  • Знак 4.1.4 "Движение прямо или направо" разрешает вам движение только прямо или поворот на АЗС, расположенную справа от дороги, т.е. в направлениях А или Б.
  • Знак 5.7.1 "Выезд на дорогу с односторонним движением" информирует о направлении движения, установленном на пересекаемой дороге. Кроме поворота направо на этом перекрестке возможны движение прямо и разворот. При движении в любом из этих направлений вы должны уступить дорогу (знак 2.4 "Уступите дорогу") ТС, движущимся по пересекаемой дороге.
  • Знак 5.29 "Зона регулируемой стоянки" относится к так называемым зональным знакам, требования которых действуют на всей территории (участке дороги), обозначенной такими знаками, вплоть до выезда из зоны, обозначенного в данном случае знаком 5.30 "Конец зоны регулируемой стоянки". Действие знаков не прерывается на перекрестках. При этом информацию о видах ТС, способах парковки и т.д. размещают на табличке под знаком или с помощью изображения таблички в нижней части знака. В данном случае табличка 8.6.2 "Способ постановки транспортного средства на стоянку" разрешает ставить легковые автомобили на стоянку с использованием тротуара на территории всей зоны регулируемой стоянки, т.е. на любой стороне дорог, расположенных в зоне регулируемой стоянки (Приложение 1).
  • Разметка 1.19 (в виде стрел) предупреждает вас о необходимости покинуть полосу движения в связи с уменьшением количества полос в данном направлении (сужением проезжей части). Необходимо перестроиться на правую полосу.
  • Для информирования водителей о предстоящей смене зеленого сигнала светофора на желтый Правилами предусмотрена возможность мигания зеленого сигнала. Этот сигнал разрешает движение ТС (п. 6.2). Длительность мигания при этом должна составлять 3 сек.
  • Водитель обязан подавать сигналы световыми указателями поворота соответствующего направления перед началом движения, перестроением, поворотом, остановкой (п. 8.1). Двигаясь по закруглению дороги и не намереваясь перестраиваться или останавливаться, вы не должны включать указатели поворота, что бы не вводить в заблуждение других участников движения (п. 8.2).
  • При взаимном перестроении водитель легкового автомобиля должен уступить дорогу водителю мотоцикла, находящемуся от него справа (п. 8.4).
  • Выполняя разворот на этом перекрестке, вам придется использовать для движения участок проезжей части, имеющий двустороннее движение. Поэтому, въезжая на перекресток, вы можете продолжить движение только по правой стороне этого участка проезжей части (п. 1.4), т.е. по траектории А.
  • Знак 4.6 "Ограничение минимальной скорости" и табличка 8.14 "Полоса движения" предписывают двигаться по левой полосе со скоростью не менее 50 км/ч. Однако при этом на дороге вне населенного пункта, не относящейся к автомагистрали, вы не имеете права развивать скорость на легковом автомобиле более 90 км/ч (п. 10.3).
  • На нерегулируемых перекрестках обгон запрещен при движении по дороге, не являющейся главной (п. 11.5). Поскольку водитель легкового автомобиля приближается к перекрестку неравнозначных дорог, двигаясь по главной дороге (знак 2.3.1 "Пересечение с второстепенной дорогой"), то он может совершить обгон обоих грузовых автомобилей, так как дорога имеет две полосы для движения в данном направлении (п. 11.4). Следовательно, обгон может быть совершен по траекториям А и Б.
  • Остановившись за пешеходным переходом в 5 м от края пересекаемой проезжей части и обеспечив при этом расстояние в 3 м до сплошной линии разметки, водитель не нарушил правила стоянки (пп. 12.4 и 12.5).
  • При движении в направлении стрелки, включенной в дополнительной секции одновременно с красным сигналом светофора, вы обязаны уступать дорогу ТС, движущимся с других направлений (п. 13.5).
  • Несмотря на то что легковой автомобиль первым въехал на перекресток равнозначных дорог, вы имеете перед ним преимущество, и его водитель при левом повороте должен уступить вам дорогу (пп. 13.11 и 13.12).
  • Проезжая данный перекресток неравнозначных дорог по направлению главной дороги (знаки 2.1 "Главная дорога" и 8.13 "Направление главной дороги"), вы никому не должны уступать дорогу, так как пользуетесь преимуществом как перед находящимся на главной дороге автобусом, для которого вы являетесь "помехой справа" (пп. 13.10 и 13.11), так и перед легковым автомобилем, движущимся по второстепенной дороге (п. 13.9).
  • Знак 5.21 "Жилая зона" показывает, что вы въезжаете на территорию, где скорость не должна превышать 20 км/ч (п. 10.2).
  • При движении в темное время суток на неосвещенных участках дорог вы должны использовать, в зависимости от дорожных условий, скорости движения и наличия впереди встречных и попутных ТС, ближний или дальний свет фар (п. 19.1).
  • Из всех перечисленных неисправностей только неработающий стеклоподъемник не является причиной запрещения эксплуатации вашего ТС. Остальные неисправности включены в Перечень (п. 7.4), и при их наличии эксплуатация ТС запрещена.
  • При съезде автомобиля правыми колесами на неукрепленную и влажную обочину возникает опасность заноса из-за разницы сцепления правых и левых колес с дорогой. При этом целесообразно, не меняя скорости, т.е. не прибегая к торможению, плавным поворотом рулевого колеса вернуть автомобиль на проезжую часть. Торможение в данной ситуации может вызвать занос автомобиля.
  • Раствор йода или бриллиантовой зелени вызывает химическое повреждение ткани внутри раны. Наружный слой кожи не повреждается при смазывании этими растворами, но эффективно дезинфицируется, что необходимо перед наложением марлевой повязки.
  • Здесь знак подчеркивания '_' в начале параграфа является меткой для поиска в одном из примеров.

  • Настроил свойства файла TextDocument.txt так
  • Build Action = None
  • Copy to Output Directory=Copy if newe
  • Заполнил (последовательно, думая, соображая!) файл XAML так
    <Window x:Class="WpfText6.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"
        Loaded="Window_Loaded"
            >
        <TabControl>
            
            <TabItem Header="OneParagraph">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition Width="50" />
                    </Grid.ColumnDefinitions>
                    <FlowDocumentScrollViewer 
                        DockPanel.Dock="Left"
                        x:Name="flowDocumentScrollViewer" 
                        ScrollViewer.VerticalScrollBarVisibility="Visible"
                        >
                        <FlowDocument Name="flowDoc" TextAlignment="Left" Background="White">
                            <Paragraph TextAlignment="Center" FontWeight="Bold" FontSize="21">
                                Чтение одного параграфа
                            </Paragraph>
                            <Paragraph Name="text" FontFamily="Arial" />
                        </FlowDocument>
                    </FlowDocumentScrollViewer>
                    <ListBox Grid.Column="1" Name="listBox" 
                        SelectionChanged="listBox_SelectionChanged" />
                </Grid>
            </TabItem>
            
            <TabItem Header="FullDocument">
                <FlowDocument Name="fullDocument" Loaded="fullDocument_Loaded" />
            </TabItem>
            
            <TabItem Header="SplitDocument">
                <FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
                    <FlowDocument Name="splitDocument" 
                        Loaded="splitDocument_Loaded" Background="White" />
                </FlowDocumentScrollViewer>
            </TabItem>
        
            <TabItem Header="ListDocument">
                <FlowDocumentScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
                    <FlowDocument Name="listDocument" 
                        Initialized="listDocument_Initialized"  Background="White" />
                </FlowDocumentScrollViewer>
            </TabItem>
        </TabControl>
    </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 System.Windows.Markup;
    using System.Collections;
        
    namespace WpfText6
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
        
            // Вынесли как поля для видимости в методах
            FileStream myStream;
            StreamReader myStreamReader;
            String[] arrayText;
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                this.Background = Brushes.LightGray;
                // Можно вместо разделителя '\\' использовать '/' 
                String fileName = "Documents/TextDocument.txt";
        
                try
                {
                    myStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                    myStreamReader = new StreamReader(myStream, Encoding.GetEncoding(1251));
        
                    // Не знаем, какой массив задать, поэтому читаем в коллекцию
                    // Но можно было и сразу в коллекцию StringBuilder.Append()
                    ArrayList arrayList = new ArrayList();
                    while (myStreamReader.EndOfStream != true)
                        arrayList.Add(myStreamReader.ReadLine());// Читаем построчно
        
                    // Копируем ссылки на элементы ArrayList в массив
                    arrayText = new String[arrayList.Count];
                    for (int i = 0; i < arrayList.Count; i++)
                        arrayText[i] = (String)arrayList[i];
        
                    // Заполняем ListBox
                    for (int i = 0; i < arrayText.Length; i++)
                    {
                        int pos = arrayText[i].IndexOf('.');
                        listBox.Items.Add(arrayText[i].Substring(0, pos + 1));
                    }
                    listBox.SelectedIndex = 0; // На первый элемент списка
                }
                catch { } // Один синтаксис!!!
                finally
                {
                    // Системные ресурсы надо закрывать
                    myStream.Close();
                    myStreamReader.Close();
                }
            }
        
            private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                text.Inlines.Clear(); // Очищаем предыдущее содержимое
                text.Inlines.Add(arrayText[listBox.SelectedIndex]);// Добавляем новое
            }
        
            private void fullDocument_Loaded(object sender, RoutedEventArgs e)
            {
                fullDocument.Background = Brushes.White;// Фон документа программно
        
                for (int i = 0; i < arrayText.Length; i++)
                {
                    Paragraph para = new Paragraph();
                    para.FontFamily = new FontFamily("Arial");
                    para.Inlines.Add(arrayText[i]);
                    fullDocument.Blocks.Add(para);
                }
            }
        
            String[] textSplit;
            private void splitDocument_Loaded(object sender, RoutedEventArgs e)
            {
                // Можно вместо разделителя '\\' использовать '/' 
                String fileName = "Documents/TextDocument.txt";
        
                try
                {
                    myStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                    myStreamReader = new StreamReader(myStream, Encoding.GetEncoding(1251));
    
                    String str = myStreamReader.ReadToEnd();// Читаем весь файл
                    textSplit = str.Split('_');// Расщепляем строку по метке '_'
                    // Элемент textSplit[0] получился пустой
                }
                catch (Exception) { } // Другой синтаксис!!!
                finally
                {
                    // Системные ресурсы надо закрывать
                    myStream.Close();
                    myStreamReader.Close();
                }
        
                // Формируем заголовок программно
                Paragraph head = new Paragraph();
                head.TextAlignment = TextAlignment.Center;
                Run run = new Run("Документ получен расщеплением строки");
                run.FontSize = 21;
                Bold bold = new Bold(run);
                head.Inlines.Add(bold);
                splitDocument.Blocks.Add(head);
        
                // Заполняем FlowDocument
                splitDocument.FontFamily = new FontFamily("Arial");
                for (int i = 1; i < textSplit.Length; i++)
                {
                    Paragraph para = new Paragraph();
                    para.Inlines.Add(textSplit[i]);
                    splitDocument.Blocks.Add(para);
                }
            }
        
            private void listDocument_Initialized(object sender, EventArgs e)
            {
                // Формируем заголовок
                Paragraph head = new Paragraph();
                head.TextAlignment = TextAlignment.Center;
                Run run = new Run("Нумерованный список OrderList");
                run.FontSize = 21;
                Bold bold = new Bold(run);
                head.Inlines.Add(bold);
                listDocument.Blocks.Add(head);
        
                // Создаем и настраиваем список
                List list = new List();
                list.MarkerOffset = 25;// Отступ от маркеров
                list.MarkerStyle = TextMarkerStyle.Decimal;
                list.StartIndex = 3; // Нумерация с 3
        
                // Заполняем список дочерними элементами
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 1"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 2"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 3"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 4"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 5"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 6"))));
                list.ListItems.Add(new ListItem(new Paragraph(new Run("ListItem 7"))));
        
                // Присоединяем список к документу
                listDocument.FontFamily = new FontFamily("Arial");
                listDocument.Blocks.Add(list);
            }
        }
    }
  • Разберитесь с кодом, или выкиньте мой и сочините свой, но чтобы все работало!
  • Результаты по примерам будут такими:

       

    Проповедь

    Компьютерные технологии изменяются так стремительно, что некоторым студентам самое время подумать - не пересесть ли с компьютера за штурвал комбайна или не стать ли водителем кобылы. А что, Зелинский, это мысль! Будешь давать стране угля, и дров неналомаешь. Семью обеспечишь, да еще и звезду героя соцтруда могут дать. А у нас, программистов, - одни мозоли в голове, в карманах - вши на арканах, а у преподавателей от таких "студентов" - еще и головная боль впридачу!!! Отпусти нас, Зелинский!

    Один из таких комбайнеров написал про меня в Интернете - молодой писака, писательский талант прорезался, на мемуары потянуло, распирать начало. Лучше бы почитывал пока, журналюга... Спасибо, хоть слова не переврал, и то благодаря все тому же Copy-Paste. Почитайте, вам будет поучительно, может станете больше прислушиваться к тому, что я постоянно говорю "Разберитесь с кодом":

    http://blogs.mail.ru/mail/vitaliy2525/31E7226B44550EB4.html#

    Пописывающий сетует, что что-то делал своими собственными ручками и все работало, но лень было в этом разбираться. А через несколько лет созрел и оказалось, что это красиво - вкусно - полезно. Так причем здесь Снетков?

    "Окликнешь, тихо подойдешь, попросишь сторублевку... Причем здесь нож, причем грабеж? - меняй формулировку..." Высоцкий В.С.

    Зреть нужно быстрее. Научить ПРОГРАММИРОВАТЬ можно только тех, кто сам этого хочет. Кода я привожу много, на любой вкус и цвет. Разбирайтесь, экспериментируйте, спрашивайте! Но не говорите, что все так плохо и только 'приколы' хороши - это непорядочно.

    Высшее профессиональное образование - это не школа благородных девиц. И я не приставленный к ней надзиратель, а свободный преподаватель. Классики, и те уже попритихли. Как научить за мизерное количество часов (и зарплату...)? Заставлять покупать учебники по тысячу рублей штука? Так ведь редко же встречаются целиком стоющие - в одних одно, в других - другое, а большей частью - один хлам.

    Взгляните, например, на этот, далеко не полный, ряд 'орудий пристрастья' современного программиста: C#, C++, MFC, VCL, .NET Framework, DirectX, OpenGL, XNA, ADO.NET, SQL, MySQL, SQL Server, Access, Crystal Reports, ASP.NET, HTML, XAML, XML, Windows Forms, WPF, PHP, Ajaxs, сами оболочки C++Builder и MS Visual Studio и т.д. А ведь все это - и есть объектно-ориентированное программирование! И сколько же тысяч нужно заплатить вашим родителям, если все это покупать, чтобы держать себя в форме?

    Некоторые из вас и не заметили, что уже пощупали собственными ручками то, о чем ранее даже и не слышали. Все оплачено и налито, только пей. От ' такой методики ' еще никому не поплохело! Знаю, для 'непьющих' хватило бы и Бэйсика, или программирования на листочке, или сразу зачета? Так зачем тогда пришли и на какие средства в дальнейшем собираетесь жить, позвольте спросить, когда ваши родители состарятся и уйдут?

    Я не программист, я их уже много лет только готовлю (с перцем, с луком, по собственному рецепту, на приспособах домашнего производства). Есть они не только в Красноярске, можно встретить в Новосибирске, Томске, Железногорске и т.д., и даже... - в Силиконовой долине. Моя ли в том вина, что иногда попадаются подпорченные продукты (я их сам не выбираю), или те, кто сел не в свои сани?

    Когда я был маленький, у меня тоже была бабушка. Это было почти вчера, но время летит так быстро: где сейчас маленький, где эта бабушка... Имейте и вы это ввиду - не минет никого чаша сия и нужно все делать вовремя, чтобы успеть состояться в жизни, а не балду гонять! Так что - тужтесь сами, да посильнее, вам 'рожать'. А я на своем месте - стою на вспоможении... (хочешь - напрягайся, буду помогать! не хочешь - дело хозяйское)

    Вернуться к учебному плану