RSS

Tag Archives: WPF

WPF Printing Library v2.0

This project was moved to GitHub.

Introduction

Please refer to the first version of this library WPF Printing Library v1.0

What can be done until now using this library?

  1. Printing footer/header/recipient on every single page
    1. Decide whether the footer is print only on the last page
    2. Decide whether the header is print on every page, but not on the last page
  2. Printing the page count on every page
  3. Decide what has to be printed: e.g. header and no footer, no page count
  4. Print a new item on a new page if there is not enough page available
  5. Printing a customizable summary of all items at the end of the report

Resolved drawbacks from v1.0

  1. If an item is longer than the available space for printing items you will get an infinite loop.
  2. According standards of WPF, that a control is created only in the main thread, you cannot print in background(I will check that and try to fix it) Not possible
  3. Impossible to print to OneNote and XPS.
  4. Until now not possible to decide whether the footer is printed only on the last page according to that you also cannot decide whether a header is printed only on the first page.
  5. Only compatible with WPF (I will not change that).

Class Library

PrintAppendixes

The PrintAppendixes is a flag-enum that defines the parts that have to be printed. Here you can set up if you want to print only the page footer, page header, footer and header or all other possible combinations of all values. As you certainly know, flag enums can be combined. That means you can select only one value but also more of them by applying it with the binary operator |. PrintAppendixes pa = PrintAppendixes.Header | PrintAppendixes.Footer; This enum is used for the PrintOnPage attribute that has to be added on the PrintProcessor class.

IPrintPartDefinition

I have decided to remove the definition of the print parts that have to be printed from the PrintDimension constructor. The reason for that was, because there you could set the PrintAppendixes only once and this will hold for the whole document. I thought that it would be useful to define for every page whether the PrintAppendix should be print or not. Therefore I created an Attribute hierarchy that enables this. It is pretty cool and simple. Just set an attribute of your choice on your implementation of the PrintProcessor class. You have four types of attributes available:

  • PrintOnPage
  • PrintOnAllPages
  • ExcludeFromPage
  • ExcludeFromAllPages

I think the names are self-explanatory. Example: You want to print the footer only on the last page, but the header on every page except the last one. You achieve this by defining such a PrintProcessor class:

[PrintOnAllPages(PrintAppendixes.Header)]
[ExcludeFromPage(PrintAppendixes. Header, PrintPartDefinition.LastPage)]
[PrintOnPage(PrintAppendixes.Footer, PrintPartDefinition.LastPage)]
public abstract class TestPrintProcessor : PrintProcessor {}

That is all you need to do. Cool isn’t it? But what is happening if I define following attributes:

PrintOnAllPages(PrintAppendixes.Footer)]
[ExcludeFromPage(PrintAppendixes.Footer,1,4,5)]

If you write such a print part definition, that the footer gets only printed on page 2, 3, 6… If for a single page the ExcludeFrom and PrintOn is defined, than this print part will not be printed on this page. Now you might think that this is pretty cool, but it is very static and needs to be defined during coding. That’s right for the attribute aspect. But I have created an additional property PrintDefinition in the PrintProcessor class on which you can call the SetPrintAttribute(IPrintPartDefinition definition) method. Now you can change when to print which print part at any time.

PrintProcessor

The PrintProcessor class is the class of mayor interest. It is an abstract class based on the interface IPrintProcessor and provides all necessary methods and properties for creating a cool print with custom headers, footers etc. In order to do that you should override the corresponding methods (GetFooter, GetHeader,…). Note: If you define a header on page 2 and you do not provide a value for GetHeader() an exception gets raised. Not providing a header means that you do either return null or that you have not overwritten the method GetHeader().

Alternating the rows

An additional feature is to color the rows as you desire. To do that you can set a list of Brushes to the AlternatingRowColors property. But this is not enough: you have to enable the alternating coloring through the property IsAlternatingRowColor. If you just set IsAlternatingRowColor to true but do not set a value to AlternatingRowColors the default values, transparent and light gray, were used.

Break last item on new page

As you continue in printing it might be sometimes the case that you want to print the footer only on the last page. So it can be that all elements have been printed, but the footer does not fit on the last page. So we need a page break only to fit the footer on the last page. So we have the situation that on the last page the footer (and other PrintAppendixes) is printed. In order to avoid such an awkward situation you can set the property BreakLastItemIfLastPageWouldBeEmpty to.

Print result is not what you wanted

A very cool feature, for debugging reasons only is that you can enable coloring the print parts by setting ColorPrintPartsForDebug to true. This will color all the PrintAppendixes in such a way that you can see where space for a too large element is wasted. Demo ColorPrintPartsForDebug

Bulk print

Beginning from version 2.0 you can bulk print. This method takes a list of PrintProcessors that gets printed all at once.

Print direct to printer

Also a new feature for this version is that you can start printing and choose the printer from the print dialog, but also by direct printing to the printer. This is achieved by calling one overload of the Print method. You have two overloads available:

  • PrintDocument(printQueueName)
printer.PrintDocument("PDF-XChange Printer 2012");
  • PrintDocument(printQueueName, PrintServer)

This overload is used to print to a LocalPrintServer and set some settings to it.

PrintDimension

The next class that is of interest is the PrintDimension class. It provides all necessary information to paginate the document.

Here you can control the height that is reserved for each print part if it gets printed on the page. In addition I suggest to add some properties for the column width for the IPrintContent.Content panel. This is very useful because in many times this content panel is in tabular form. Change from v1.0: You cannot set the PrintAppendix in this class anymore. Instead you set it as attribute on the PrintProcessor class. The default contstructor initializes the Margin to {0,0,0,0}

Relative column position

Facts that your page size may vary from print to print you want stretch or shrink the print. In version 1.0 you set for every column in the IPrintContent.Content panel the width for each column. By setting UseRelativeColumnPosition to true you enable that the columns gets shrink a stretched as they have space. To define the width for a column you simply derive from PrintDimension and create some properties with a ColumnDimensionAttribute on it. The constructor takes a double representing the percentage that a column may fill up. As second parameter you can passeither ColumnDimensionType.Absolute or ColumnDimensionType.Relative relative. These two values are for defining whether the double value is an absolute width value for the column or like in the exaple below 50% of the page width. Example:

ColumnWidthAttribute

If you want to achieve columns that has exactly that width, than you write:

public class CustomPrintDimension: PrintDimension
{
    public CustomPrintDimension()
    {
        UseRelativeColumnPosition = true;
    }

    [ColumnDimension(.50,ColumnDimensionType.Relative)]
    public double WidthColumn1 { get; set; }

    [ColumnDimension(.15,ColumnDimensionType.Relative)]
    public double WidthColumn2 { get; set; }

    [ColumnDimension(.35,ColumnDimensionType.Relative)]
    public double WidthColumn3 { get; set; }
}

//usage

public override UIElement GetTable(out double tableHeaderHeight, out Brush borderBrush)
{
    Grid g = new Grid();
    borderBrush = Brushes.Gray;
   //for _printDimension.WidthColumn1 you will a value that repesents 50%
    //of the width of the page according the page orientation an page size.
    g.ColumnDefinitions.Add(new ColumnDefinition
        {Width = new GridLength(_printDimensions.WidthColumn1)});
    g.ColumnDefinitions.Add(new ColumnDefinition
       { Width = new GridLength(_printDimensions.WidthColumn2) });
    g.ColumnDefinitions.Add(new ColumnDefinition
        { Width = new GridLength(_printDimensions.WidthColumn3) });

Note: If you change the PageOrientation in your PrintProcessor class from Portrait, which is the default value, to Landscape you will get bigger values for WidthColumn1,WidthColumn2 and WidthColumn3 because the width of the printable area changed.

IPrintContent

This interface was refactored from ILineItem. It provides a single property only. With that property you can provide the content of the line. Unlike in the previous version you do not have to provide the height of the line. This gets computed automatically. NOTE: If the content is bigger than the remaining space of a page the element gets positioned automatically on a new page no matter if it has space or not.

public class TestPrintContent : IPrintContent
{
    private readonly int _item;
    public TestPrintContent(int item)
    {
        _item = item;
    }
    public Panel Content
    {
        get
        {
            var sp = new Grid();
            var textBlock = new TextBlock
                {Text = _item.ToString(),};
            sp.Children.Add(textBlock);
            return sp;
        }
    }
}

If you want to specify exactly the height of the content you can do this as follows:

public Panel Content
{
    get
    {
        var sp = new Grid();
        sp.Backgroud = Brushes.Red; //only to make the grid visible.
        sp.Height = 150;
        return sp;
    }
}

PrintProcessorBackground

As you might have seen in the PrintProcessor class there is a GetBackgroud() method that provides the background of the document. The class provides only methods for positioning the background on the document and the background element, of that the opacity can be set.

Sample C# project

You can download a complete working “Visual Studio 2012 project” that demonstrates my printing library by using this link: (Click here to download the sample project.). NOTE: please change the file format of the downloading document from .docx to .zip

 
35 Comments

Posted by on March 6, 2013 in Printing, WPF

 

Tags: ,

Bind to WebBrowser’s Source Property

Unfortunately the Source property of the WebBrowser control is not a dependency property. Therefore it cannot be bound.
But there exists a workaround in order to bind a URI to a WebBrowser Control.
The workaround that exists is to create an attached property that can be bound and which changes reflect directly to the webbrowser’s source property.. This is done as follows:

public static readonly DependencyProperty BindableSourceProperty = DependencyProperty.RegisterAttached("BindableSource", typeof (string), typeof (WebBrowserUtility), new UIPropertyMetadata(null, OnBindableSourceChanged));

public static string GetBindableSource(DependencyObject obj)
{
    return (string)obj.GetValue(BindableSourceProperty);
}

public static void SetBindableSource(DependencyObject obj, string value)
{
    obj.SetValue(BindableSourceProperty, value);
}

private static void OnBindableSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    var browser = o as WebBrowser;
    if (browser == null)
        return;

    var uri = (string)e.NewValue;

    try
    {
        browser.Source = !string.IsNullOrEmpty(uri) ? new Uri(uri) : null;
    }
    catch (ObjectDisposedException) {}
}

This attached property can be bound as follows:

<WebBrowser Name="browse" Utilities:WebBrowserUtility.BindableSource="{Binding Path=MyStrigUrlProperty}" />

Nice isn’t it?

 
Leave a comment

Posted by on September 18, 2012 in C-Sharp, WPF

 

Tags:

ScrollViewer for ItemsControl

In a ItemsControl the ScrollViewer is not visible, not by setting ScrollViewer.HorizontalScrollBarVisibility to “Auto” nor by setting it to “Visible”.
The only solution is to wrap the ItemsControl into a ScrollViewer:

<ScrollViewer VerticalScrollBarVisibility="Auto">
     <ItemsControl ItemsSource="{Binding Path=MyList}"/>
</ScrollViewer>
 
Leave a comment

Posted by on September 18, 2012 in WPF

 

Tags:

Show a List as ReadOnly

I have often the situation when I need showing a list and I do use a ListBox I do not need that the selected item is highlighted. If I use a ListBox for displaying a list of data then this looks like:

image

But as you can see there is a highlighted item which we want to avoid. How can we avoid that? This is achieved by using an ItemsControl. Then we have solved the problem and the list looks like:

image

 
Leave a comment

Posted by on May 9, 2012 in C-Sharp, WPF

 

Tags: ,

Validating Binding

Writing XAML Code is very cool, but if you have a control in which you have to write several times

<TextBox Text="{Binding Path=... ValidatesOnDataErrors = true, 
                ValidatesOnException = true, 
                UpdateSourceTrigger = PropertyChanged}" />

might be very stressul and unclear in case of searching errors. Therefore I have created following class, called ValidatingBinding, that inherits from Binding and sets at creation time the properties ValidatesOnDataError and ValidatesOnExceptions to true

public class ValidatingBinding : Binding
{
    public ValidatingBinding()
    {
        ValidatesOnDataErrors = true;
        ValidatesOnExceptions = true;
        //and if needed (be careful, might be a performance issue)
        //UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    }
}

Doing that you will be able to write following in XAML and omit to set the properties ValidatesOnDataError and ValidatesOnExceptions to true.

<TextBox Text="{namespace:ValidatingBinding Path=... }"/>

As you can see, this is much shorther than the previous example. Reviewing such XAML Code is more clear if you do not rewrite such duplicate property setting.

 
Leave a comment

Posted by on February 23, 2012 in WPF

 

Tags: ,

Generic IMultiValueConverter

In addition to the first part of this post series I want to present the generic pendant for IMultiValueConverter:

public abstract class MultiValueConverter<TTarget, TParameter> : IMultiValueConverter
{
    protected abstract TTarget Convert(object[] values,
                                       Type targetType,
                                       TParameter parameter,
                                       CultureInfo culture);
    protected abstract object[] ConvertBack(TTarget value,
                                       Type[] targetTypes,
                                       TParameter parameter,
                                       CultureInfo culture);

    object IMultiValueConverter.Convert(object[] values,
                                        Type targetType,
                                        object parameter,
                                        CultureInfo culture)
    {
        if (parameter != null && !(parameter is TParameter))
            throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type as ConvertParameter. The passing type was {0} but the expected is {1}", parameter.GetType(), typeof(TParameter)));

        return Convert(values, targetType, (TParameter)parameter, culture);
   }

    object[] IMultiValueConverter.ConvertBack(object value,
                                        Type[] targetTypes,
                                        object parameter,
                                        CultureInfo culture)
    {
        if (!(value is TTarget))
            throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type.
                                        The passing type was {0} but the expected is {1}", value.GetType(), typeof(TTarget)));
        if (parameter != null && !(parameter is TParameter))
           throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type as ConvertParameter. The passing type was {0} but the expected is {1}", parameter.GetType(), typeof(TParameter)));

        return ConvertBack((TTarget)value, targetTypes, (TParameter)parameter, culture);
    }
}

The usage is the same as you can see in the previous post. Have a look.
Like in the previous section following snippet will make it easies to use if you do not need all generic types

public abstract class MultiValueConverter<TTarget> : MultiValueConverter<TTarget, object> {}
 
Leave a comment

Posted by on October 6, 2011 in C-Sharp, WPF

 

Tags:

Generic IValueConverter

During WPF-Application developement I thought that it might be useful to have a generic IValueConverter to have the possibility to check some errors at runtime already. Therefore I implemented following generic ValueConverter:

public abstract class ValueConverter <TSource, TTarget, TParameter> : IValueConverter
{
    protected abstract TTarget Convert(TSource value,
                                   Type targetType,
                                   TParameter parameter,
                                   CultureInfo culture);
    protected abstract TSource ConvertBack(TTarget value,
                                   Type targetType,
                                   TParameter parameter,
                                   CultureInfo culture);

    object IValueConverter.Convert(object value,
                                   Type targetType,
                                   object parameter,
                                   CultureInfo culture)
    {
        if (value != null && !(value is TSource))
            throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type. The passing type was {0} but the expected is {1}", value.GetType(), typeof(TSource)));
        if (parameter != null && !(parameter is TParameter))
            throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type as ConvertParameter. The passing type was {0} but the expected is {1}", parameter.GetType(), typeof(TParameter)));

        return Convert((TSource)value, targetType, (TParameter)parameter, culture);
    }

    object IValueConverter.ConvertBack(object value,
                                       Type targetType,
                                       object parameter,
                                       CultureInfo culture)
    {
        if (value != null && !(value is TTarget))
            throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type. The passing type was {0} but the expected is {1}", value.GetType(), typeof(TTarget)));
        if (parameter != null && !(parameter is TParameter))
            throw new InvalidCastException(string.Format("In order to use the generic IValueConverter you have to use the correct type as ConvertParameter. The passing type was {0} but the expected is {1}", parameter.GetType(), typeof(TParameter)));

        return ConvertBack((TTarget)value, targetType, (TParameter)parameter, culture);
    }
}

See the second post of this series regarding generic IMultiValueConverter

The tree Generic parameters defines the type that is passed, returned and the type of the additional ConverterParameter.

In order to make it a lot easier to use you can reduce the number of generic parameters implementing following classes:

public abstract class ValueConverter<TSourceTarget> : ValueConverter<TSourceTarget, TSourceTarget> {}
public abstract class ValueConverter<TSource, TTarget> : ValueConverter<TSource, TTarget, object> {}

A very simple class to show you the functionality would be a InverseBooleanConverter:

public class InverseBooleanConverter<bool> : ValueConverter
{
    protected override bool Convert(bool value, Type targetType, object parameter, CultureInfo culture)
    {
        return !value;
    }

    protected override bool ConvertBack(bool value, Type targetType, object parameter, CultureInfo culture)
    {
        return !value;
    }
}

or a BooleanToVisibilityConverter

public class BoolToVisibilityConverter<bool,Visibility> : ValueConverter
{
    protected override Visibility Convert(bool value, Type targetType, object parameter, CultureInfo culture)
    {
        return value ? Visibility.Visible : Visibility.Collapsed;
    }

    protected override bool ConvertBack(Visibility value, Type targetType, object parameter, CultureInfo culture)
    {
        return value == Visibility.Visible;
    }
}

Continue reading on the second post of this series regarding generic IMultiValueConverter

 
Leave a comment

Posted by on October 5, 2011 in C-Sharp, WPF

 

Tags: