Printing in Silverlight

14 07 2014

Printing in Silverlight

Printing is a very basic requirement for business application now days. At the advent of Silverlight there was no direct support for printing. But in Silverlight version 4 Microsoft have provided Print Document assembly for achieving the printing functionality.

What if the application still uses Silverlight version 3.0 or earlier versions?

In this article I will discuss the alternate approach for printing in Silverlight version 3.0 and earlier and then move on to a more sophisticated approach using Print Document provided in Silverlight 4.0.

The alternative is bit simple but not that much mature. Here we will take the entire image of the control that needs to be printed and let the user to save it using the save dialog. But one major problem with this approach is related to scaling of the image. In a case that have a large number of records to be printed like 100 records in data grid than the height of the jpeg produce would be very high and when it would be printed then it would automatically scale down the size of the image to fit in one single print page. The clarity for the scaled down image would not be good enough for reading purpose. So to overcome this thing I developed an algorithm to split the big heighted image into fixed slices and then I have used PdfSharp library to send the each sliced images to separate pages in pdf file. Finally the user will be able to save the Pdf file from where he can print the document. But here the chances to cut down the data when the image is splitted would remain.

Due to such pain and alternate unorganized approaches Microsoft finally Gifted Silverlight 4.0 with Print Document class. This class provides printing capabilities in Silverlight Applications. Refer http://msdn.microsoft.com/en-us/library/system.windows.printing.printdocument(VS.95).aspx for more details.

I strived hard to develop one such common framework related to print document class till I found one fantastic article by Pete Brown in his blog Creating a Simple Report Writer in Silverlight 4 http://10rem.net/blog/2010/05/09/creating-a-simple-report-writer-in-silverlight-4

This framework allows printing for one item source with one data template. My requirement forced me to print multiple grids at the same time in one single print job. So I analyzed the framework and with some modifications finally achieved it. I have converted some of its properties to arrays which use combination of multiple Data Templates and Item Sources to print multiple grids in one single print job.

Thanks Peter for such a fabulous use of Print Document.

Advertisements




Screen Readers with Silverlight Applications

13 01 2012

Screen Readers with Silverlight Applications.


Web based applications fly across global boundaries due to which accessibility and ease of use becomes one of the most important factor that drives the sales count of the service we provide.

As per 508 Compliance if we require our service served to audience with disabilities it should follow various aspects.

For thorough details please refer

http://en.wikipedia.org/wiki/Section_508_Amendment_to_the_Rehabilitation_Act_of_1973

This article is specifically targeted for users interested to know

How do we provide support for Screen Readers software with the applications developed in one of the most cutting edge technology Silverlight?

Traditional web based applications that emits html to browsers easily comply with Screen readers based on specifications followed for HTML.
With Silverlight based applications the Silverlight plug-in takes care for screen readers. We just need to follow some methodologies provided in form of Automation Properties in Silverlight. Automation Properties provide several methods and attachable properties which help us define access keys, instruction text for screen readers and much more.

For details please refer

http://msdn.microsoft.com/en-us/library/system.windows.automation.automationproperties(v=vs.95).aspx

By following these properties the work is pretty much simple.

Sample 1

<TextBox  id=” nameTextBox” AutomationProperties.Name=”Please enter name” />

Screen reader will voice over “Please enter name” when this textbox receives focus.

Sample 2

<TextBlock id=”nameTextBlock” Text=”Please enter name” />
<TextBox  id=”nameTextBox” AutomationProperties.LabeledBy="{Binding ElementName= nameTextBlock }" />

Screen reader will voice over “Please enter name” when this textbox receives focus. Here is the case when we want to directly bind the AutomationProperties.Name property of textbox to any control, in this case a label beside text box.

Pretty much simply till here.

But wait!

Silverlight SDK and Controls are very powerful and includes number of controls that can be bound directly to objects. Here is where most of the screen readers fail while generating voice over for such control. I will depict the problem with one of the control “Combo Box”

Problem
If your combo box is bound to any dictionary object including collection than screen readers will work perfectly. But if combo box is bound to any object like “Person” than screen readers will fail.

Sample Code

<ComboBox Name="personsComboBox" ItemsSource="{Binding}" DisplayMemberPath="Name"/>

 

public class Person : INotifyPropertyChanged
    {
        private string name;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                if (value != name)
                {
                    name = value;
                    OnPropertyChnaged("Name");
                }
            }
        }

        private string surname;
        public string Surname
        {
            get
            {
                return surname;
            }
            set
            {
                if (value != surname)
                {
                    surname = value;
                    OnPropertyChnaged("Surname");
                }
            }
        }

        private Int32 age;
        public Int32 Age
        {
            get
            {
                return age;
            }
            set
            {
                if (value != age)
                {
                    age = value;
                    OnPropertyChnaged("Age");
                }
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

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

        #endregion

    }

Solution

Screen Readers works on strings attached to the controls using attachable property AutomationProperties.Name as described in above example code. With the case were we are binding objects directly to control, screen readers fail and voice over the entire path (Namespace.ObjecName) were the object reside. In order to overcome this issue simply override ToString() method for your objects.

Adding this code with the Person object designed above will ask screen readers to voice over “Name” property.

public override string ToString()
        {
            return Name;
        }

Tricky but yet simple.

Enjoy Coding.





Animating Grid Column

12 02 2011

Troubleshooting InvalidOperationException: DoubleAnimation cannot be used to animate property Width due to incompatible type.

Silverlight has great support for Animation and you might have experienced animating most of the controls on one or the other aspect. But animating ColumnDefinition.Width or RowDefinition.Height properties for Grid control is not straight forward. To achieve this one has to make a tweak.

Problem : InvalidOperationException: DoubleAnimation cannot be used to animate property Width due to incompatible type.

Reason : Width property of grid is not of type double but is of type GridLength. Further GridLength has a property called Value of type Double, but Value is ReadOnly so to change the width or height you have to create a new instance of the GridLength object and set it to the Width Property of the grid.

Solution: To achieve this we simply create a dependency property in our page and in the change event of that property we set the new instance of GridLength to width property of the grid.


<Grid x:Name="myGrid">
        <Grid.Resources>
            <Storyboard x:Name="myStoryboardC">
                <DoubleAnimation From="150" To="0" Duration="00:00:1"
                Storyboard.TargetName="myPageName"
                Storyboard.TargetProperty="ColumnWidth">
                    <DoubleAnimation.EasingFunction>
                        <CubicEase EasingMode="EaseOut"/>
                    </DoubleAnimation.EasingFunction>
                </DoubleAnimation>
            </Storyboard>
            <Storyboard x:Name="myStoryboardE">
                <DoubleAnimation From="0" To="150" Duration="00:00:1"
                Storyboard.TargetName= “myPageName"
                Storyboard.TargetProperty="ColumnWidth">
                    <DoubleAnimation.EasingFunction>
                        <CubicEase EasingMode="EaseOut"/>
                    </DoubleAnimation.EasingFunction>
                </DoubleAnimation>
            </Storyboard>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition x:Name="Column1" Width="150" MaxWidth="250" MinWidth="0">
            </ColumnDefinition>
            <ColumnDefinition Width="15" MaxWidth="15" MinWidth="15"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Rectangle x:Name="myRectangle"
     Fill="Blue" Width="200" Height="30" Grid.Column="0"/>

        <layoutToolkit:Accordion x:Name="acc" SelectionMode="ZeroOrMore" Grid.Column="0">
            <layoutToolkit:AccordionItem Header="hi">
                <StackPanel>
                    <TextBlock Text="1"/>
                    <TextBlock Text="2"/>
                    <TextBlock Text="3"/>
                </StackPanel>
            </layoutToolkit:AccordionItem>
            <layoutToolkit:AccordionItem Header="hi" Content="Task 2"/>
            <layoutToolkit:AccordionItem Header="hi" Content="Task 3"/>
        </layoutToolkit:Accordion>

        <Button x:Name="btnEC" Content="&lt;" Grid.Column="1" Click="Button_Click" />

        <data:DataGrid x:Name="dataGrid1" Margin="0" Grid.Column="2">
            <data:DataGrid.RowDetailsTemplate>
                <DataTemplate>
                    <StackPanel Background="LightBlue">
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="This item has details." />
                        </StackPanel>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Here is some data: " />
                            <TextBlock Text="{Binding FirstName}" />
                            <TextBlock Text=" LastName" />
                            <TextBlock Text="{Binding LastName}" />
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </data:DataGrid.RowDetailsTemplate>
        </data:DataGrid>

    </Grid>


public partial class MainPage : UserControl
    {

        bool toogle;
        public MainPage()
        {
            InitializeComponent();
            this.Name = "myPageName";
        }

        public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth",
                                                                                                    typeof(double),
                                                                                                    typeof(MainPage),
                                                                                                    new PropertyMetadata
                                                                                                      (ColumnWidthChanged));
        public double ColumnWidth
        {
            get
            {
                return (double)GetValue(ColumnWidthProperty);
            }
            set
            {
                SetValue(ColumnWidthProperty, value);
            }
        }

        private static void ColumnWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var mainPage = (MainPage)d;
            mainPage.Column1.Width = new GridLength((double)e.NewValue);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (!toogle)
            {
                myStoryboardC.Begin();
                btnEC.Content = ">";
                toogle = !toogle;
            }
            else
            {
                myStoryboardE.Begin();
                btnEC.Content = "<";
                toogle = !toogle;
            }
        }
    }





Show loading while downloading XAP modules in prism.

4 09 2010

Show loading while downloading XAP modules in prism.

Scenario : To show progress bar when modules (xap’s) in prism are downloaded.

Problem : Currently in prism the default ModuleManager class does not expose a progress event that can be tracked to show some progressing of the modules (xap’s) being downloaded.

Solution : Prism’s ModuleManager internally uses instance of XapModuleTypeLoader to load modules (xap’s) asynchronously. Thanks to the community of prism for providing CreateDownloader() as virtual method.

Straight to the point I will jot down the steps to achieve the above mentioned goal.

Step 1 – Create an event and an event argument classes that will be used in custom class below


public class ModuleDownloadProgressEvent : CompositePresentationEvent<ModuleDownloadProgressArgs>
{
}

public class ModuleDownloadProgressArgs
{
    public ModuleDownloadProgressArgs(int BytesReceived, bool IsComplete) { _BytesReceived = BytesReceived; _IsComplete = IsComplete; }
    private int _BytesReceived;
    public int BytesReceived { get { return _BytesReceived; } set { _BytesReceived = value; } }
    private bool _IsComplete;
    public bool IsComplete { get { return _IsComplete; } set { _IsComplete = value; } }
}

Step 2 – Create a class that derives from IFileDownloader.


public class CustomFileDownloader : IFileDownloader
{
    private readonly WebClient webClient = new WebClient();
    private readonly IEventAggregator eventAgg;

    public CustomFileDownloader()
    {
        eventAgg = ServiceLocator.Current.GetInstance<IEventAggregator>();
    }

    private event EventHandler<DownloadCompletedEventArgs> _downloadCompleted;
    public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted
    {
        add
        {
            if (this._downloadCompleted == null)
            {
                this.webClient.OpenReadCompleted += this.WebClient_OpenReadCompleted;
                this.webClient.DownloadProgressChanged += this.DownloadProgressChanged;
            }

            this._downloadCompleted += value;
        }

        remove
        {
            this._downloadCompleted -= value;
            if (this._downloadCompleted == null)
            {
                this.webClient.OpenReadCompleted -= this.WebClient_OpenReadCompleted;
                this.webClient.DownloadProgressChanged -= this.DownloadProgressChanged;
            }
        }
    }

    void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        RaiseDownloadProgressChanged(e.ProgressPercentage, false);
    }

    private void RaiseDownloadProgressChanged(int val, bool isComplete)
    {
        eventAgg.GetEvent<ModuleDownloadProgressEvent>().Publish(new ModuleDownloadProgressArgs(val, isComplete));
    }

    public void DownloadAsync(Uri uri, object userToken)
    {
        this.webClient.OpenReadAsync(uri, userToken);
    }

    private void WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
    {
        this._downloadCompleted(this, ConvertArgs(e));
        RaiseDownloadProgressChanged(0, true);
    }

    private static DownloadCompletedEventArgs ConvertArgs(OpenReadCompletedEventArgs args)
    {
        return new DownloadCompletedEventArgs(args.Error == null ? args.Result : null, args.Error, args.Cancelled, args.UserState);
    }
}

Step 3 – Create a class that derives from XapModuleTypeLoader and override its virtual method CreateDownloader returning your class object prepared in step 1


public class CustomXapModuleTypeLoader : XapModuleTypeLoader
{
    protected override IFileDownloader CreateDownloader()
    {
        return new CustomFileDownloader();
    }
}

Step 4 – Create a class that derives from ModuleManager and override its virtual method ModuleTypeLoaders replacing default XapModuleTypeLoader with your class object prepared in step 2.


public class CustomModuleManager : ModuleManager
{
    public CustomModuleManager(IModuleInitializer moduleInitializer, IModuleCatalog moduleCatalog, ILoggerFacade loggerFacade)
        : base(moduleInitializer, moduleCatalog, loggerFacade) { }
    private System.Collections.Generic.IEnumerable<IModuleTypeLoader> typeLoaders;
    public override System.Collections.Generic.IEnumerable<IModuleTypeLoader> ModuleTypeLoaders
    {
        get
        {
            if (this.typeLoaders == null)
            {
                this.typeLoaders = new List<IModuleTypeLoader>()
                                      {
                                          new CustomXapModuleTypeLoader()
                                      };
            }

            return this.typeLoaders;
        }
        set
        {
            this.typeLoaders = value;
        }
    }
}

Step 5 – Register your custom ModuleManager class prepared in step 4, in overridden method ConfigureContainer() of your Bootstrapper class.


	base.RegisterTypeIfMissing(typeof(IModuleManager), typeof(CustomModuleManager), true);

Step 6 – Create a method in Shell that will respond to the ModuleDownloadProgressEvent event when published


public void ShowHide(ModuleDownloadProgressArgs e)
{
	// Your Dream Goes Here
}

Step 7 – Finally in the Shell subscribe to the ModuleDownloadProgressEvent event prepared in Step 1.


	eventAggregator.GetEvent<ModuleDownloadProgressEvent>().Subscribe(ShowHide, ThreadOption.UIThread);

That’s it!!!

Enjoy Prism.