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

WPF, MVVM, Silverlight | создано: 05.10.2010 | опубликовано: 06.10.2010 | обновлено: 13.01.2024 | просмотров: 5065

После выхода Silverlight 4 при разработки некоторого проекта столкнулся с простым вопросом: Как напечатать из Silverlight многостраничный документ? На самом деле, всё просто, достаточно знать некоторые нюансы. Особенно это интересно, если предположить, что печатать должно приложение, которое реализовано по шаблону программирования Model-View-ViewModel (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, в котором указываем имя печатаемого документа.

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

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

// Процедура выполнения команды 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), что существуют еще страницы, которые надо печатать. Что и требовалось… Всё работает так как и планировалось…