Silverlight 4: Multi page printing (MVVM) или многостраничная печать
WPF, MVVM, Silverlight | создано: 05.10.2010 | опубликовано: 06.10.2010 | обновлено: 13.01.2024 | просмотров: 5049
После выхода 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), что существуют еще страницы, которые надо печатать. Что и требовалось… Всё работает так как и планировалось…