How to make a report viewer on Avalonia and FastReport Open Source. Part 1


The well-known WPF platform allows .Net developers to create their own non-standard program interfaces by using the XAML language in form design. Many developers have liked this, but the trouble is that WPF does not fit into the modern concept of cross-platform. To fill this gap, there was created a UI (user interface) framework that allows you to create applications on the .Net Framework with a graphical interface on XAML for all popular platforms: Windows, Linux, OSX, and in the future Android, iOS. In essence, this is a cross-platform implementation of the WPF approach. Avalonia is evolving, new controls that are in WPF are constantly appearing. Of course there are differences in the use of Avalonia compared to WPF, however, they are not radical. We now turn to the topic of the article.

In this article, we will look at the way to use the Fast Report Open Source report generator in the Avalonia project. We will create an application - report viewer in a format prepared to be shown. Reports in Fast Report Open Source have two formats - frx and fpx. The first is a report template that is created in a special designer, the second one is a report prepared for display, which includes a template and data for its content.

Install Avalonia.
I will make a reservation right away, we will consider programming on Avalonia in the context of Microsoft Visual Studio. Therefore, the first question you will face is how to install Avalonia?
There are two ways to install this framework: via VisualStudio and from the GIT repository. The first method is very simple. In Visual Studio, open the Tools -> Extensions and Updates menu:

In the tree on the left, open the Online node, and in the search bar in the right corner enter Avalonia. And install the extension found.
The second method is more complicated. You clone the Avalonia repository from GitHub: https://github.com/AvaloniaUI/Avalonia, open the Windows command line and enter the command:
dotnet new --install [path-to-repository]
Where [path-to-repository] is the path to the cloned or downloaded Avalonia repository.

How to create a project
Both ways of installing Avalonia will lead to the same result - you will have access to Avalonia project templates.
And here again we have two options for creating an Avalonia application: from the studio or from the command prompt.
The first option looks like this:


The second option. Create a folder. In CMD, move to this folder using the cd command and execute one more command:
dotnet new avalonia.app -o Viewer
where the Viewer is the name of the project.

It is clear from the screenshot above that we have two templates available:
  • Avalonia Application - a simple framework of the application with the minimum set of required files;
  • Avalonia MVVM Application - an application based on the Model-View-ViewModel template with ReactiveUI.

We are interested in a simple frame.
Once we have created the application, you will need to restore the packages. To do this, open the project, select the menu Tools -> NuGet Package Manager -> Package Manager Console. In the window that appears, we enter this command:
Dotnet restore
Avalonia can work as .Net Framework, and with .Net Core. You can see what target framework is currently being used and change it in the csproj project file:
   <TargetFramework>netcoreapp2.1</TargetFramework>
Any framework will be suitable for our project. So leave the default setting.
Since we are creating the report viewer FastReport.Net, then we need to install the FastReport.OpenSource library. Open the NuGet package manager using the context menu on the project file. We need to install the following packages:
  • Avalonia;
  • Avalonia.Desktop;
  • FastReport.OpenSource;
  • Microsoft.NetCore.App.

Avalonia is positioned as a cross-platform analog of WPF. But, since this is still a third-party product, visual design of the form in Visual Studio is not supported. Therefore, we will have to add all controls using tags in the XAML representation of the program window. If you are well-versed in XAML and know the controls you need, then it is even faster than dragging visual components with your mouse.
So, initially we have the document MainWindow.xaml with the following code:
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="AvaloniaFROSViewer">
    Hello World!
</Window>
If you start the application now, you will only see the text “Hello World!” To place the components in the window, we need a Grid:
<Window xmlns="https://github.com/avaloniaui"
Title="Report Viewer" Height="900" Width="800" SizeToContent="Width" CanResize="false" >
  <Grid>
</Grid>
</Window>
All subsequent code will be placed inside the <Grid> tag. To manage the report we will need a number of buttons:
  • Open report;
  • Go to the first page;
  • Go back to the page;
  • Go to the next page;
  • Go to the last page;
  • Zoom in;
  • Zoom out.

These buttons need to be grouped somehow. It is best to make an improvised toolbar using the panel. Unfortunately, there is no regular ToolBar control yet. So that the buttons are in the same size and style, we place on them images corresponding to the purpose. Thus the code of our toolbar will be as follows:
<StackPanel Orientation="Horizontal" HorizontalAlignment="left" VerticalAlignment="Top" Background="#AA000000">
      <Button Name="Open" Margin="5" Background="Transparent">
        <Image Source="Resources/001.ico" Width="16" Height="16"></Image>
      </Button>
      <Button Name="First" Click="First_Click" Margin="5" Background="Transparent">
        <Image Source="Resources/frst.png"></Image>
      </Button>
      <Button Name="Prev" Click="Prev_Click" Margin="5" Background="Transparent">
        <Image Source="Resources/prr.png"></Image>
      </Button>
       <Button Name="Next" Click="Next_Click" Margin="5" Background="Transparent">
        <Image Source="Resources/nxt.png"></Image>
      </Button>
      <Button Name="Last" Click="Last_Click" Margin="5" Background="Transparent">
        <Image Source="Resources/lst.png"></Image>
      </Button>
      <Button Name="ZoomIn" Click="Zoom_in_Click" Margin="5" Background="Transparent">
        +
      </Button>
      <Button Name="ZoomOut" Click="Zoom_out_Click" Margin="5" Background="Transparent">
        -
      </Button>
     </StackPanel>
Each button has a “Name” attribute. It is necessary to access an object from a C # window script. We will look at this later. Also note that all buttons except the first one have a Click event, in which the name of the handler method from the script is specified. Specially for making the example, for the first button, we will define a click event handler in the script code.
We have 4 buttons to navigate the report, but this is not always enough. What if the report has a half hundreds of pages, and we need to view the 25th one? Therefore, we add a text box in which you can enter the page number and go to it. Also, this field will always display the current page number. Place this field between the Prev and Next buttons:
<TextBox Name="PageNumber" Height="30" Margin="5" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="50" KeyDown="PageNumber_KeyDown"/>
For the text field, we use a keystroke event on the keyboard.
So, we have created controls, now we will add an Image object in which we will display the report pages in PNG format. But, knowing that the report will be scaled, you need to provide scroll bars. It is implemented like this:
    <ScrollViewer VerticalScrollBarVisibility="Visible"
                   HorizontalScrollBarVisibility="Visible" Margin="0,35,0,0" HorizontalContentAlignment="Left" VerticalContentAlignment="Top" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Left" Width="800">
        <Image Name="img" RenderTransformOrigin="0.5,0.5" Stretch="None" />
     </ScrollViewer>
The ScrollViewer control will allow us to scroll the image if the size of the Image object goes beyond the ScrollViewer bounds.
This concludes with the design of the window and go to the script - MainWindow.xaml.cs.
We will need the following libraries:

using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
using FastReport;
using System.Collections.Generic;
using FastReport.Export.Image;
using System.IO;
using Avalonia.Input;
using Avalonia.Interactivity;

Now consider the component initialization method:

private Button OpenBtn;
private Image img;
private TextBox PageNumber;

private void InitializeComponent()
{
     AvaloniaXamlLoader.Load(this);
     this.OpenBtn = this.FindControl<Button>("Open");
     OpenBtn.Click += OpenFile;

     this.img = this.FindControl<Image>("img");
     this.PageNumber = this.FindControl<TextBox>("PageNumber");
 }
To access the controls in Avalonia, you will have to create objects of the same type in the script, then find the necessary controls from the xaml code by name and assign them to the created objects. After that, you can work with the properties of these objects.
Consider the Open button as an example. At the beginning, we declare an object of type Button. When the window is initialized, we find a Button control with the name Open and assign it to the OpenBtn object created earlier. Here we sign the OpenFile method on the Click event of the OpenBtn button.
Here is the OpenFile method itself:

private async void OpenFile(object sender, RoutedEventArgs e)
        {
            OpenFileDialog myDialog = new OpenFileDialog();
            myDialog.Filters.Add(new FileDialogFilter() { Name = "Prepared export(*.FPX)", Extensions = new List<string> { "fpx" } });
            myDialog.AllowMultiple = false;

            var result = await myDialog.ShowAsync(this);
            LoadReport(result[0]);
        }
This is an asynchronous method, since the dialog box in Avalonia is called asynchronously. Here we create a dialog box and set the filtering of files in it by the fpx extension. Let me remind you that this is an extension of the report files in a format prepared for the display, that is, they contain, in addition to the template, also data to fill in the template. Next, we prohibit multiple selection of files. As a result, the dialog box will return to us the list of selected files with the full path. But, since multiple choices are prohibited for us, we take only the first element of the array of strings and pass it to the LoadReport report loading method:
        void LoadReport(string report_name)
        {
            Report rep = new Report();
            rep.LoadPrepared(report_name);
            Report = rep;
        }
First we create the report object. Then we load into it a file with the fpx extension at the specified path. And at the end we assign the resulting report to the Report object. Let’s consider this Report object:
private Report report;
private FastReport.Export.Image.ImageExport ex;
public Report Report
        {
            get { return report; }
            set
            {
                ex = new FastReport.Export.Image.ImageExport();
                ex.HasMultipleFiles = true;
                report = value;
                SetContent(report);
                SetImage();
            }
        }
First of all, we declare a report object and a report export object in image format. Next, create a Report property with a rather interesting set method. In it, we create an export instance. Set the property of the multiplicity of export files (for a set of report pages). Assign the report and call the methods SetContent () and SetImage (). The first one prepares content, i.e. Images. The second one sets the image to the Image object.
  private void SetContent(Report report)
        {
            ex.ImageFormat = ImageExportFormat.Png;
            ex.Resolution = 96;
            ex.Export(report, Directory.GetCurrentDirectory() + "/test.png");
            foreach (string file in ex.GeneratedFiles)
            {
                pages.Add(new Avalonia.Media.Imaging.Bitmap(file));
            }
        }

public List<Avalonia.Media.Imaging.Bitmap> pages = new List<Avalonia.Media.Imaging.Bitmap>();
In this method, we customize the image format in the export and its resolution and export it to the test.png file. But since we have allowed a plurality of files for export, all subsequent files will have an additional index in order: test.2.png, test.3.png, etc. Next, in the loop, we iterate through all the created files and add them to the list of Bitmap objects. Thus, we will have a list of report pages and easily select the desired page by the ordinal index in the list.
        public void SetImage()
        {
            img.Source = pages[0];
        }
This method simply sets the Source property for the Image object. For now, let's set for it the first image from the list of pages.
This concludes the first part of the article. In the second part, we will continue to create a report viewer based on AvaloniaUI and FastReport.OpenSource.
How to make a report viewer on Avalonia and FastReport Open Source. Part 2

Comments

Popular posts

FastReport Designer Community Edition

How to use FastReport Open Source in ASP.NET Core Web API application