LangWars Example (C# / uwp10)
Note
This demo is available in your FlexCel installation at <FlexCel Install Folder>\samples\csharp\VS2022\uwp10\LangWars and also at https://github.com/tmssoftware/TMS-FlexCel.NET-demos/tree/master/csharp/VS2022/uwp10/LangWars
Overview
This example shows how to do a simple report doing FlexCel. It will fetch the most used tags from Stack Overflow and rank and provide a chart to visualize them. You can work with online or offline data, in case you don't have access to Stack Overflow.
In this example, we are exporting the results to an html file and showing the results in a web browser. We could also export to pdf, as shown in the "FlexView" demo.
Files
App.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace LangWars
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
// Ensure the current window is active
Window.Current.Activate();
}
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
deferral.Complete();
}
}
}
FileShare.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
namespace LangWars
{
static class FileShare
{
public static void Register()
{
var dtm = DataTransferManager.GetForCurrentView();
dtm.DataRequested += dtm_DataRequested;
}
public static void Share()
{
DataTransferManager.ShowShareUI();
}
public static async void dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
{
try
{
args.Request.Data.Properties.Title = "Language Wars";
args.Request.Data.Properties.Description = "Send the file with statistics to another app.";
args.Request.Data.Properties.FileTypes.Add(".xlsx");
var file = await ReportGenerator.TempXlsPath.GetFileAsync(ReportGenerator.TempXlsName);
args.Request.Data.SetStorageItems(new IStorageFile[] { file });
}
catch (FileNotFoundException)
{
args.Request.FailWithDisplayText("There is no generated file to share. Make sure to press the 'Fight' button first.");
}
catch (Exception ex)
{
args.Request.FailWithDisplayText("There was an error: " + ex.Message);
}
}
}
}
LangData.cs
using System;
using System.Runtime.Serialization;
using System.Collections.Generic;
namespace LangWars
{
[DataContract]
class LangDataList
{
[DataMember]
public LangData[] items{ get; set; }
}
[DataContract]
class LangData
{
[DataMember]
public string name{ get; set; }
[DataMember]
public int count{ get; set; }
}
}
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace LangWars
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
bool Offline => !OnlineButton.IsChecked.GetValueOrDefault(false);
public MainPage()
{
this.InitializeComponent();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); //Make all encodings available. Not really necessary as the app will work anyway with other encodings.
this.NavigationCacheMode = NavigationCacheMode.Required;
ResultsWindow.NavigationCompleted += ResultsWindow_NavigationCompleted;
FileShare.Register();
}
private void ResultsWindow_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
if (!args.IsSuccess)
{
SetHTML("<html>" + WebUtility.HtmlEncode("Press the Fight button!") + "</html>");
}
}
private void StartProgress()
{
// see http://code.msdn.microsoft.com/windowsapps/How-to-put-a-ProgressRing-a92f2530
Progress.IsActive = true;
Progress.Visibility = Windows.UI.Xaml.Visibility.Visible;
WebViewBrush brush = new WebViewBrush();
brush.SourceName = "ResultsWindow";
brush.Redraw();
MaskRectangle.Fill = brush;
MaskRectangle.Visibility = Windows.UI.Xaml.Visibility.Visible;
ResultsWindow.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void EndProgress()
{
Progress.IsActive = false;
Progress.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
ResultsWindow.Visibility = Windows.UI.Xaml.Visibility.Visible;
MaskRectangle.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
//OpenLastReport();
}
private void OpenLastReport()
{
try
{
LoadHTML();
}
catch (Exception ex)
{
SetHTML("<html>" + WebUtility.HtmlEncode(ex.Message) + "</html>");
}
}
private async void appBarFightButton_Click(object sender, RoutedEventArgs e)
{
appBar.IsEnabled = false;
try
{
await new ReportGenerator().TryCreateReport(Offline, StartProgress, EndProgress, LoadHTML, SetHTML);
}
finally
{
appBar.IsEnabled = true;
}
}
void LoadHTML()
{
ResultsWindow.Navigate(new System.Uri("ms-appdata:///local/" + ReportGenerator.TempHtmlRelFolder + "/" + ReportGenerator.TempHtmlName));
}
void SetHTML(string html)
{
ResultsWindow.NavigateToString(html);
}
private void ShareButton_Click(object sender, RoutedEventArgs e)
{
FileShare.Share();
}
}
}
ReportGenerator.cs
using System;
using System.IO;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Threading.Tasks;
using FlexCel.Core;
using FlexCel.Report;
using FlexCel.XlsAdapter;
using Windows.Storage;
using FlexCel.Render;
namespace LangWars
{
class ReportGenerator
{
public const string TempHtmlRelFolder = "htm";
public const string TempHtmlName = "langwars.html";
public const string TempXlsName = "langwars.xlsx";
public async Task<bool> TryCreateReport(bool Offline, Action StartProgress, Action EndProgress, Action LoadHTML, Action<string> SetHTML)
{
try
{
StartProgress();
try
{
await Task.Run(() => CreateReport(Offline));
}
finally
{
EndProgress();
}
}
catch (Exception ex)
{
SetHTML("<html>" + WebUtility.HtmlEncode(ex.Message) + "</html>");
return false;
}
LoadHTML();
return true;
}
async Task CreateReport(bool Offline)
{
var Langs = Offline ? await LoadData() : await FetchData();
var xls = await RunReport(Langs);
xls.Save(Path.Combine(TempXlsPath.Path, TempXlsName)); //we save it to share it and to display it on startup.
await GenerateHTML(xls);
}
async Task<LangDataList> FetchData()
{
const string url = "https://api.stackexchange.com/2.1/tags?order=desc&sort=popular&site=stackoverflow&pagesize=5";
var handler = new HttpClientHandler();
if (handler.SupportsAutomaticDecompression)
{
handler.AutomaticDecompression = DecompressionMethods.GZip |
DecompressionMethods.Deflate;
}
var client = new HttpClient(handler);
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri(url)));
string s = await response.Content.ReadAsStringAsync();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(LangDataList));
return (LangDataList)ser.ReadObject(await response.Content.ReadAsStreamAsync());
}
async Task<LangDataList> LoadData()
{
var offlineFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Data/OfflineData.txt"));
using (var offlineData = await offlineFile.OpenStreamForReadAsync())
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(LangDataList));
return (LangDataList)ser.ReadObject(offlineData);
}
}
async Task<ExcelFile> RunReport(LangDataList langs)
{
ExcelFile Result = new XlsFile(true);
var template = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Templates/report.template.xlsx"));
using (var templateStream = await template.OpenStreamForReadAsync())
{
Result.Open(templateStream);
using (FlexCelReport fr = new FlexCelReport(true))
{
fr.AddTable("lang", langs.items);
fr.Run(Result);
}
}
return Result;
}
public static StorageFolder TempXlsPath
{
get
{
return ApplicationData.Current.LocalFolder;
}
}
public async static Task<StorageFolder> TempHtmlPath()
{
return await ApplicationData.Current.LocalFolder.CreateFolderAsync(TempHtmlRelFolder, CreationCollisionOption.OpenIfExists);
}
public async static Task<string> TempHtmlPathString()
{
var folder = await TempHtmlPath();
return folder.Path;
}
async Task GenerateHTML(ExcelFile xls)
{
using (FlexCelHtmlExport html = new FlexCelHtmlExport(xls, true))
{
html.SavedImagesFormat = THtmlImageFormat.Svg; //vectorial so it can zoom.
html.EmbedImages = true;
//see http://blogs.windows.com/windows_phone/b/wpdev/archive/2011/03/14/managing-the-windows-phone-browser-viewport.aspx
html.ExtraInfo.Meta = new string[] { "<meta name=\"viewport\" content=\"width=device-width\" />" };
html.Export(Path.Combine(await TempHtmlPathString(), TempHtmlName), ".");
}
}
}
}