Silverlight 4: Multi page printing + MVVM или многостраничная печать

Постановка задачи

Итак, после выхода Silverlight 4 при разработки некоторого проекта столкнулся с простым вопросом: Как напечатать из Silverlight многостраничный документ? На самом деле, всё просто, достаточно знать некоторые нюансы. Особенно это интересно, если предположить, что печатать должно приложение, которое реализовано по шаблону программирования Model-View-ViewModel (MVVM).

Реализация

Для начала шаблон программирования реализуем. Для этого создадим класс ViewModelBase, который реализует интерфейс INotifyPropertyChanged:

using System.ComponentModel;

namespace MultiPagePrinting
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
              PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Теперь класс RelayCommand, который реализует интерфейс ICommand.

using System;
using System.Windows.Input;

namespace MultiPagePrinting
{
  public class RelayCommand : ICommand
  {
    public event EventHandler CanExecuteChanged;

    Predicate<Object> _canExecute = null;
    Action<Object> _executeAction = null;

    public RelayCommand(Action<object> executeAction, Predicate<Object> canExecute)
    {
      _canExecute = canExecute;
      _executeAction = executeAction;
    }

    public bool CanExecute(object parameter)
    {
      if (_canExecute != null)
        return _canExecute(parameter);

      return true;
    }

    public void UpdateCanExecuteState()
    {
      if (CanExecuteChanged != null)
        CanExecuteChanged(this, EventArgs.Empty);
    }
    public void Execute(object parameter)
    {
      if (_executeAction != null)
        _executeAction(parameter);
      UpdateCanExecuteState();
    }
  }
}

А теперь, в качестве подопытного класса создадим класс MyItem, с ним и будем “ставить опыты”. Выглядит этот класс так:

using System;

namespace MultiPagePrinting
{
  public class MyItem
  {
    public int ID { get; set; }
    public string Title { get; set; }
    public DateTime ActionDate { get; set; }
    public string Description { get; set; }

    public MyItem()
    {

    }

    public MyItem(int id, string title, DateTime date, string description)
    {
      this.ActionDate = date;
      this.Description = description;
      this.ID = id;
      this.Title = title;
    }
  }
}

Как видите, ничего сложного, нам главное — чтобы было с чем экспериментировать. Теперь пришло время сделать модель-представление (ViewModel) главной страницы MainPageViewModel. Наследовать будем его от заготовленного класса ViewModelBase. Приводить весь листинг класса не буду (можно скачать проект ссылка в конце статьи). Вот только пара самых главных методов.

Печать одной записи

Метод, который печатает одну запись из ListBox, я назвал CommandPrintOneExecute:

// Процедура выполнения команды CommandPrintOne
    private void CommandPrintOneExecute()
    {
      // выполнение команды PrintOne
      // процедура выполнения команды
      PrintDocument doc = new PrintDocument();
      doc.PrintPage += (s, arg) =>
      {
        MyItemControl item = new MyItemControl();
        item.DataContext = this.CurrentItem;
        arg.PageVisual = item;
      };
      doc.Print(CurrentItem.Title);
    }

Кажется, ничего сложного. Создаем экземпляр класса PrintDocument, в EnventHandler PrintPage подсовываем выбранный (CurrentItem) предварительно обернутый в наш MyItemControl контрол-обертку. И более ничего… Запускаем метод Print, в котором указываем имя печатаемого документа. И оп-па!!! На печать вылетает:

one

Вывод на печать много страниц

Вот так выглядит метод, который печатает много страниц:

// Процедура выполнения команды CommandPrintAll
private void CommandPrintAllExecute()
{
  // выполнение команды PrintAll
  // процедура выполнения команды
  PrintDocument multidoc = new PrintDocument();
  int index = 0;
  multidoc.PrintPage+=(s,arg)=>{
    StackPanel host = new StackPanel();
    while (index < Collection.Count)
    {
      MyItemControl m = new MyItemControl();
      m.DataContext = Collection[index];
      host.Children.Add(m);

      host.Measure(new Size(arg.PrintableArea.Width, double.PositiveInfinity));

      if (host.DesiredSize.Height > arg.PrintableArea.Height && host.Children.Count>1)
      {
        host.Children.Remove(m);
        arg.HasMorePages = true;
        break;
      }
      index++;
    }
    arg.PageVisual = host;
  };
  multidoc.Print("Список какой-то фигни");
}

На самом деле даже не могу предположить, что тут может быть непонятного. Код прост как семь копеек. Создаем экземпляр PrintDocument, потом размещаем объекты на печатном листе. При этом проверяем, вмещается ли (StackPanel  по имени host) с дочерними контролами (MyItemControl) на странице или нет.  Если нет, то последний убираем, и сообщаем программе (arg.HasMorePages = true), что существуют еще страницы, которые надо печатать.

В результате выполнения этого метода, получаем:

multi

Что и требовалось… Всё работает так как и планировалось…

Бонус

И еще. Вот небольшой бонус. При программировании по шаблону MVVM, приходится писать большее количество строк кода. Вот такой snippet Вам может пригодится:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
            <Title>RelayCommand snippet</Title>
            <Author>Calaboga</Author>
            <Description>
                Быстрокоманда для RelayCommand
            </Description>
            <HelpUrl>
            </HelpUrl>
            <Shortcut>
                clbCommand
            </Shortcut>
        </Header>
        <Snippet>
            <Declarations>
                <Literal Editable="true">
                    <ID>FilterCompleted</ID>
                    <ToolTip>CommandName</ToolTip>
                    <Default>GotoCalabonga</Default>
                    <Function>
                    </Function>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[#region Command$FilterCompleted$ command

    /// <summary>
    /// Команда Command$FilterCompleted$
    /// </summary>
    public RelayCommand Command$FilterCompleted$
    {
      get
      {
        return new RelayCommand((e) => this.Command$FilterCompleted$Execute(), (e) => this.Can$FilterCompleted$);
      }
    }

    // Процедура выполнения команды Command$FilterCompleted$
    private void Command$FilterCompleted$Execute()
    {
      // выполнение команды $FilterCompleted$
      // процедура выполнения команды
      MessageBox.Show("Bingo!");
    }

    // Свойство проверки возможности выполнения команды Command$FilterCompleted$
    private bool Can$FilterCompleted$
    {
      get
      {
        // разрешен ли запуск команды
        return true;
      }
    }

    #endregion // end Command$FilterCompleted$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

 

Ссылки

Скачать проект MultiPagePrinting.zip

Реклама
Silverlight 4: Multi page printing + MVVM или многостраничная печать