Friday, October 2, 2020

Azure Files Storage Development Part III

We've switched to using Azure Files for file storage with our ResearchStory web application. I've already written a couple of posts (here and here) about some of the things I've figured out along the way. The most recent issue I sadly discovered only after pushing code from development into production. Turns out there is a 4MB upload limit. Files can be much larger in the cloud but you need to transfer them up in 4MB (or smaller) chunks. Of course all of the files I tested with during dev were smaller than this.

Once in production I started getting RequestBodyTooLarge exceptions:

The request body is too large and exceeds the maximum permissible limit.

Searching on that exception you get this page and explanation:

Cause - There's a 4-MB limit for each call to the Azure Storage service. If your file is larger than 4 MB, you must break it in chunks.

Trying to track down possible fixes, I started with the Azure Files documentation and specially the v12.x client libraries. The overall documentation is pretty sparse. There are some basic examples but nothing complex. The Azure.Storage.Files.Shares reference documentation enumerates all of the classes, methods and parameters but just gives simple descriptions for each of them.

For example the first two parameters for the UploadRangeAsync method are commented as follows:

range   HttpRange
Specifies the range of bytes to be written. Both the start and end of the range must be specified.

content Stream
A Stream containing the content of the range to upload.

The rest of the descriptions are also this useless and there is no mention on this page of the 4MB limit (at least at the time of this blog post). This limit is documented in the REST API put-range reference but it is not documented for any of the SDK Upload* methods. There are also no examples of working around this.

After much trial and error I was able to create the following method to work around the file upload limits. In the code below _dirClient is an already initialized ShareDirectoryClient set to the folder I'm uploading to.

If the incoming stream is larger than 4MB the code reads 4MB chunks from it and uploads them until done. The HttpRange parameter specifies where the bytes will be added to the file already uploaded to Azure. The index has to be incremented to point to the end of the Azure file so the new bytes will be appended.

public async Task WriteFileAsync(string filename, Stream stream) {

    //  Azure allows for 4MB max uploads  (4 x 1024 x 1024 = 4194304)
    const int uploadLimit = 4194304;

    stream.Seek(0, SeekOrigin.Begin);   // ensure stream is at the beginning
    var fileClient = await _dirClient.CreateFileAsync(filename, stream.Length);

    // If stream is below the limit upload directly
    if (stream.Length <= uploadLimit) {
        await fileClient.Value.UploadRangeAsync(new HttpRange(0, stream.Length), stream);
        return;
    }

    int bytesRead;
    long index = 0;
    byte[] buffer = new byte[uploadLimit];

    // Stream is larger than the limit so we need to upload in chunks
    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) {
        // Create a memory stream for the buffer to upload
        using MemoryStream ms = new MemoryStream(buffer, 0, bytesRead);
        await fileClient.Value.UploadRangeAsync(new HttpRange(index, ms.Length), ms);
        index += ms.Length; // increment the index to the account for bytes already written
    }
}

Azure Files seems like a pretty good product but the SDK documentation and examples are lacking. Hopefully this improves in the future. Until then maybe this post can help someone else.

Thursday, October 1, 2020

Azure Files Storage Development Part II

Since posting last time about using two file separate subfolders for development and production I've decided to modify the approach somewhat. I still want to be able to develop and test directly against Azure but realize that there are times when I might not want to use the cloud for development. One scenario is that I might be disconnected from the internet and I don't want to be unable to develop and debug code. The other consideration is cost. While file storage is fairly cheap on Azure it still costs more than just using my local hard drive.

So (with some inspiration from this blog post) I decided to create an abstraction layer. I created a generic storage interface for the basic storage functionality I needed (list files/create/delete/read/write) and then specific wrappers for Azure Files and local files.

Here is a simple UML for the StorageFolder interface:

IStorageFolder UML

In Startup.cs I can either instantiate one of the concrete implementations based on the environment and then use Dependency Injection with the interface.

services.AddScoped<IStorageClient, AzureStorageClient>(client => {
	var shareName = WebEnvironment.IsDevelopment() ? "dev" : "prod";
	var connectionString = Configuration.GetConnectionString("StorageConnection");
	var shareClient = new ShareClient(connectionString, shareName);
	return new AzureStorageClient(shareClient);
});

I've uploaded the full implementation at this Github Gist but here are the two implementations for creating a subfolder

Here is the Azure Files implementation. The StorageFolder class is instantiated pointing to a specific folder. So creating a subfolder is relative to that directory.

public class AzureFolder : IStorageFolder {
	private readonly ShareDirectoryClient _dirClient;

	public AzureFolder(ShareDirectoryClient directoryClient) {
		_dirClient = directoryClient;
	}

	public async Task<IStorageFolder> CreateSubfolderAsync(string folderName) {
		var directoryClient = await _dirClient.CreateSubdirectoryAsync(folderName);
		return await Task.FromResult(new AzureFolder(directoryClient.Value));
	}
}

Here is the local storage implementation. Since most of the calling code is from a website I wanted to use async where available. The local file API doesn't use async but using Task.Run() you can simulate that behavior and allow for a common interface.

public class LocalFolder : IStorageFolder {
	private readonly DirectoryInfo _dirInfo;

	public LocalFolder(DirectoryInfo dirInfo) {
		_dirInfo = dirInfo;
	}

	public async Task<IStorageFolder> CreateSubfolderAsync(string folderName) {
		var path = Path.Combine(_dirInfo.FullName, folderName);
		await Task.Run(() => Directory.CreateDirectory(path));
		return new LocalFolder(new DirectoryInfo(path));
	}
}

Wednesday, September 23, 2020

Azure Files Storage Development

We've been using an App Service in Azure since first developing our site (ResearchStory). An App Service allows you to run your code in the cloud with out having to maintain a full server or virtual machine. You get local storage but it's not unlimited (there is different storage levels by tier see: Azure App Service Tiers). It's also not easy to manage remotely. You can use Kudu and extensions (like Azure Web Apps Disk Usage) to view the data in a web browser but it's still a bit disconnected.

So we've decided to add Azure Files for storage of logs and generated reports. There are some samples and basic documentation but not a lot of extended examples. In particular I tried to find an example of how others were using the code to develop locally as well as use it in production yet keep the data separated.

I had first considered two storage accounts with separate connection strings like we do for database development but that seemed overly complicated. In the end, I created two root subfolders (one 'dev' and one 'prod') inside the Azure file share. During startup the code sets the root folder based on the environment and adds a scoped reference to DI.


services.AddScoped(client => {
	var shareName = WebEnvironment.IsDevelopment() ? "dev" : "prod";
	var connectionString = Configuration.GetConnectionString("StorageConnection");
	return new ShareClient(connectionString, shareName);
});

Then any code that needs to interact with the storage system will get it injected already configured to the right subfolder.


public async Task OnGetAsync(int id, [FromServices] ShareClient shareClient) {
	...
	var dirClient = shareClient.GetDirectoryClient("reports");
	await dirClient.CreateIfNotExistsAsync();
	...
}

Thursday, August 6, 2020

Displaying Git Commit and Build Info in an ASP.Net website

Scott Hanselman wrote a really good blog post on March 6, 2020 called Adding a git commit hash and Azure DevOps Build Number and Build ID to an ASP.NET website. In the post he covered passing build information into an ASP.Net web app so that it could be displayed on a web page to confirm which version of your code was running in the cloud. If you work with multiple machines, deployment slots or even if you just forget when you last pushed your code up you know how important it can be to confirm which version is running.

His article was well written, contained lots of great information and steps to follow but could it be improved. As I set out to implement it I found a couple of ways it could be.

My build process on Azure DevOps was the classic (legacy) UI build pipeline using MSBuild. I've wanted to update it to use dotnet for awhile so I figured this was a good reason to do so. I created a new Pipeline using the ASP.NET template. That created a new yaml file but it was using MSBuild. After some googling I was able to replace that with a simple dotnet driven pipeline.

The YAML pipeline shown below is about as basic as it gets (sidenote I had never used YAML before but it's pretty straighforward once you start working with it). Thi pipeline is triggered when a new commit happens on 'master'. It gets a windows vm and sets some variables and then starts the build. 'dotnet build' also does a NuGet restore first but you can break that out separate if you have packages from different sources. It then runs 'dotnet test' which runs through all of the unit tests. Then 'dotnet publish' to zip up the results and finally publishes the build artifact (the website zip). This artifact can be used in a Release deploy.


trigger:
- master

pool:
  vmImage: 'windows-latest'

variables:
  solution: '**/*.sln'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
- task: DotNetCoreCLI@2
  displayName: 'dotnet build $(buildConfiguration)'
  inputs:
    command: 'build'
    arguments: '--configuration $(buildConfiguration)'

# Run Unit Tests
- task: DotNetCoreCLI@2
  displayName: 'dotnet test'
  inputs:
    command: 'test'
    projects: '**/*Tests/*.csproj'
	
# Prepare output to send to the Release pipeline
- task: DotNetCoreCLI@2
  displayName: 'dotnet publish'
  inputs:
    command: publish
    publishWebProjects: True
    arguments: '--configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
    zipAfterPublish: True

# Take all the files in $(Build.ArtifactStagingDirectory) and upload them as an artifact of the build.
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'

Once that was all working I started to add the pieces Scott talks about in his article. I added '/p:SourceRevisionId=$(Build.SourceVersion)' to the build command to pass in the git commit hash as an assembly attribute. Using the code he provided I was able to read this value back out and display it on a webpage. Unfortunately this is the only variable that works this way. For the build number and id you can pass them in but you have to create custom attributes for each one along with specialized code to read them out. Scott doesn't include the code to read them back out instead preferring to create a file containing each of these values.

As I was working on implementing his code to output the build number and id into a file it occurred to me that it would probably be simpler to place all of the values in this buildinfo file. If I also format it as JSON it would super easy to read in these values in my application code. So starting with Scott's code I made the following changes.

In the build YAML I added the following task. It uses the echo command to create a minimal JSON file with the build number, build id and commit hash. I also wanted to include the build date as a separate field but after much searching (and build runs) was unable to figure out how to accomplish that.


- script: 'echo {"buildNumber":"$(Build.BuildNumber)","buildId":"$(Build.BuildId)","sourceVersion":"$(Build.SourceVersion)"} > .buildinfo.json'
  displayName: "Emit build info"
  workingDirectory: '$(Build.SourcesDirectory)/Neptune.Web'
  failOnStderr: true

I created the following small class to match the buildinfo JSON


    public class BuildInformation {
        public string BuildNumber { get; set; }
        public string BuildId { get; set; }
        public string SourceVersion { get; set; }
    }

I then simplified his 'AppVersionInfo' class into the following. It reads in the JSON on creation


    public class ApplicationInfo {

        private const string BuildFileName = ".buildinfo.json";
        private BuildInformation BuildInfo { get; set; }

        public ApplicationInfo(IHostEnvironment hostEnvironment) {
            var buildFilePath = Path.Combine(hostEnvironment.ContentRootPath, BuildFileName);
            if (File.Exists(buildFilePath)) {
                var fileContents = File.ReadAllText(buildFilePath);
                BuildInfo = JsonConvert.DeserializeObject<BuildInformation>(fileContents);
            }
        }

        /// <summary>
        /// Return the Build Id
        /// </summary>
        public string BuildId {
            get {
                return BuildInfo == null ? "123" : BuildInfo.BuildId;
            }
        }

        /// <summary>
        /// Return the Build Number
        /// </summary>
        public string BuildNumber {
            get {
                return BuildInfo == null ? DateTime.UtcNow.ToString("yyyyMMdd") + ".0" : BuildInfo.BuildNumber;
            }
        }

        /// <summary>
        /// Return the git hash of the commit that triggered the build
        /// </summary>
        public string GitHash {
            get {
                return BuildInfo == null ? "" : BuildInfo.SourceVersion;
            }
        }

        /// <summary>
        /// Return a short version (6 chars) of the git hash (or local)
        /// </summary>
        public string ShortGitHash {
            get {
                return GitHash.Length >= 6 ? GitHash.Substring(0, 6) : "local";
            }
        }
    }

As Scott does you add this class to Services in Startup.cs: 'services.AddSingleton();'

Then rather than displaying it in the footer I added it to an admin sys info page in a table. Note: the ApplicationInfo class is injected into the page.


@page
@inject ApplicationInfo appInfo

<h1>System Info</h1>
<table class="table">
    <tr>
        <td>Commit:</td>
        <td><a href="https://vs.com/commit/@appInfo.GitHash" target="_blank"><i class="fal fa-code-branch"></i> @appInfo.ShortGitHash</a></td>
    </tr>
    <tr>
        <td>Build:</td>
        <td><a href="https://vs.com/_build/results?buildId=@appInfo.BuildId&view=results" target="_blank"><i class="fab fa-simplybuilt"></i> @appInfo.BuildNumber</a></td>
    </tr>
    <tr>
        <td>Powered by:</td>
        <td>@System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription</td>
    </tr>
</table>

Friday, December 6, 2019

Enumerating a Collection with an Index

There is often the case where you want to loop over a collection of objects along with an indicator of where you are in the loop. While some languages have a built-in syntax for expressing this C# does not (yet). Using a bit of LINQ you can easily accomplish this.

Normally starting with a collection of things you might use a for loop to iterate over list and provide your index.

For example:


 var items = new List<string>();

 for (int i = 0; i < items.Count; i++) {
  var item = items[i];
  Console.WriteLine($"Item {i}: {item}");
 }

You can also use a foreach and use an temp variable to track the index.


 int i = 0;
 foreach (var item in items) {
  Console.WriteLine($"Item {i}: {item}");
  i++;
 }

Recently I discovered this gem. The following code uses a Linq Select statement to transform the list into tuples containing the index and the object as named properties. Not quite as nice as if this was built-in to the language but still nice to be able to reduce the code down.


 foreach (var (item, index) in items.Select((v, i) => (v, i))) {
  Console.WriteLine($"Item {index}: {item}");
 }

You can also add an extension method to encapsulate the Select and make it a little cleaner.


  /// <summary>
  /// Return the list as an enumerable of tuples with the item and it's index in the list. 
  /// </summary>
  /// <typeparam name="T">The typeof items in the list</typeparam>
  /// <param name="list">List to converted</param>
  /// <returns>Enumerable of tuples with the item and it's index</returns>
  public static IEnumerable<(T, int)> WithIndex<T>(this IEnumerable<T> list) => 
    list.Select((value, index) => (value, index));

And then use it like this:


 foreach (var (item, index) in items.WithIndex()) {
  Console.WriteLine($"Item {index}: {item}");
 }

Friday, September 14, 2018

SurveyGizmo Checkbox Options Selected

SurveyGizmo custom script has a function (sgapiGetQuestionOptionSelected) to return the selected option for a single or multiple select question. For a single select question it always returns a single number. Unfortunately for a multiple select question the return value depends on the number of options selected. If multiple items are selected an array is returned but if only one item is selected just that option's id is returned. Makes it harder to write code to check if certain items were checked.

So I wrote the following function to always return an array regardless of the number of items selected.


Friday, July 21, 2017

Colorful serialization

I was writing some code to serialize a C# class to JSON and needed to export a System.Drawing.Color field as a hex string. Thankfully it's easy to customize the output using Json.NET. I created the following custom converter to save colors as hex strings (like #FFF0B6)

And example usage:

Monday, March 13, 2017

Exclusive checkbox column

We had a customer that had a checkbox grid and wanted the checkboxes in one of the rows to be exclusive (e.g. when checked no other items in that column are checked). Most survey platforms have this functionality for a multiple choice checkbox question but not for columns in a checkbox grid. Some quick jQuery and JavaScript coding I had the following mostly generic function working.

Working demo: JSFiddle

Saturday, April 2, 2016

Find and Replace Placeholders in a Word Template

We have a template file that we use as a baseline in our report generation (see here for help creating a new Word document from a template). The template file has some placeholders for title, subtitle, author, etc. I wanted to be able to dynamically replace those with content when the rest of the document was generated. This took me way longer than I expected to figure out so here is hopefully some info to help the next person. First I found the relevant section in the XML (the Open XML Productivity Tools are a great help for this). Then with a bunch of trial and error I figured out how to find and replace the '[Document title]' placeholder.

Thursday, March 17, 2016

Custom validation in SurveyGizmo

SurveyGizmo allows you to add JavaScript directly to a survey page with the JavaScript Action. They use jQuery for survey interaction so that's already loaded on the page and available. Using this feature it's easy to customize a survey page.

One of the possible uses is for custom validation. For example to validate user input in some way that SurveyGizmo does not support out of the box. To do this you could start with the JavaScript below. It hooks the Next button click event and then hides or shows the error message based on a custom check. Note, we use the Next button click event and not the page submit event because that is called even when the Back button is selected.


Wednesday, March 16, 2016

Working with Arrays in SurveyGizmo Custom Scripting

As mentioned in my previous posts (here) we do a lot of work with SurveyGizmo's custom scripting feature. It's kind of like PHP and kind of like JavaScript but different enough that I forget how to do basic things like create arrays. So thought I would post some examples of working with arrays.

Some examples of working with single dimension arrays:

// Declare array with values
%%locations = array('Jacksonville, FL',
        'Toronto, Canada',
        'Orlando, FL',
        'Chicago, IL');

// Declare new empty array
%%copied = array();

foreach(%%locations as %%location) {
        // Add element to array
        %%copied[] = %%location;
}

And with a for loop (NOTE: You must use curly brackets when using a variable as the key).
for (%%i=0; %%i<%%count; %%i++) {
 %%output .= "<br>".%%locations{%%i};
}

And with a 2-dimension array (or hashtable):

// Declare array with values
%%locations = array('Toronto' => 'Ontario', 'Jacksonville' => 'FL',
        'Orlando' => 'FL', 'Chicago' => 'IL');

foreach (%%locations as %%key => %%value) {
        %%output .= "City: ".%%key." State/Province".%%value;
}

Tuesday, January 5, 2016

SurveyGizmo Custom Scripting

One nice feature of SurveyGizmo is their server-side custom scripting functionality (example below). It’s a PHP-like scripting language that allows you to modify the survey contents and operation at runtime. The script runs server-side before the page loads and offers a lot options for customizing a survey.

There are several things I would love to see with this feature:

  1. Syntax validation in the editor. Having to go through several survey pages to test your script and then find out you are missing a semi-colon or have a typo is no fun. A basic eval of the script would catch most of these errors.
  2. Using full PHP instead of PHP-lite. Having programmed a survey system I can understand why Survey Gizmo went with a more controlled subset of the language but when I need to use a function that isn’t there I’m not happy about it.
  3. Adding an option to run a script on page save. Currently, the scripts only run on page load so custom logic, validation and other items need to be on the next page. Sometimes it would be nice to just have them on the same page.

Having said that all in all its still pretty powerful functionality that we use with a lot of surveys at Researchscape.

Example of mapping the state question to another question that tracks the four main US Census regions.

Monday, January 4, 2016

SurveyGizmo Hacks

At Researchscape we’ve been using SurveyGizmo to run most of our surveys. It has a lot of flexibility built in but the customization options make it easy to adapt it for our needs. Going to start posting some quick hacks that I’ve used to customize various surveys.

First one up is a simple table customization. By default the table layout is dynamic but if you have a scale question with some labels (eg Strongly Disagree – 1, 2, 3, 4, Strongly Agree – 5) the column will not be equally sized. However we don’t want to change the whole table to a fixed layout which would squeeze the row heading.

Solution is to add a CSS class to the table question and in the Style section add custom CSS to the survey like follows:

.fixed-columns td:nth-child(n+2) {
    width: 8%;
}

Tuesday, December 15, 2015

Scratchpad Tools

Whenever I want to test out some small C# code I use the excellent LINQPad. It's great as a quick scratchpad for everything from a couple of lines of code to a small program. You can reference your own class libraries so it's great for performance testing. I also use it a lot to transform files. The free version is nice but if you can swing it, Pro is worth it just for the auto-completion.

For JavaScript testing and debugging it's hard to beat JSFiddle. I use it often and like the ability to save and share snippets of JS. I was interested when I found .NET Fiddle online. Could it offer the same versatility of LINQPad and the online sharing of JSFiddle.

I took the code from my last blog post and pasted it in .NET Fiddle. Interestingly by default code is evaluated and run as you enter it (you can turn this option off by setting Auto Run to 'No'). The first thing that occurred was that I got this error message:

Fatal Error: Memory usage limit was exceeded

Apparently the number of iterations in my test loop was too many. Once I dialed that back to 100,000 the code ran fine. This code is pretty basic and not creating a lot of large objects so this limit seems low. Also looks like no GC is running so this limit my hinder other tests.

It does have a 'Share' link that gives you either a direct link to the fiddle or a widget that you can embed directly in a blog post. Here is the widget for this post:


Definitely a nice tool for sharing code but think I will stick with LINQPad for most of my scratchpad needs.

Monday, December 7, 2015

All Your Base Are Belong To Us

I wanted to encode a number in Base 36. A simple enough task, but as these things sometimes do, this took me down the rabbit hole of wanting to create a generic routine. I needed a set of methods, one that would take an integer and convert it to an arbitrary base and another to convert it back to a Base 10 number. There are a bunch of examples floating around the web but I wanted to create my own (combining some of the best elements from the code I found). It's a relatively easy problem to solve but along the way I found some interesting tricks to speed up the process.

So here is my C# take on this old problem:

I used LINQPad to create a quick program to test it out.

Friday, November 20, 2015

N-Gram Extraction

For reporting purposes we do some simple word analysis. I've been looking to expand on that by analyzing the frequency of 2 and 3 word pairs. So I've been evaluating some n-gram extraction algorithms.

Wikipedia defines an n-gram as follows:

In the fields of computational linguistics and probability, an n-gram is a contiguous sequence of n items from a given sequence of text or speech. An n-gram of size 1 is referred to as a "unigram"; size 2 is a "bigram" (or, less commonly, a "digram"); size 3 is a "trigram"

Initially I had been using string.split() to break the text into words and process each one but found the overhead to be too high (lots of string copying). So I changed the approach to loop through each character in the text and build up words. To get the word pairs (or triplets) my first thought was to use Queues. First I had to have a queue that was a fixed size. It needed to drop items from the front when new ones were added to the end. From StackOverflow I found a simple example to do just that:

With that in place I could do something like the following:

That worked but still had some unnecessary overhead with a StringBuilder for each word and having to join the words back together to get the bigram. So I rewrote the code using pointers into the text and copying out the words (or word pairs) as I found them. This approach proved to be the fastest.

I also took one more pass and made the code more generic. Rather than using specific variables for each pairing I used an array to hold on each word location. This code was slower than the previous due to the loops and array access. For looking for larger number of n-grams (say 4+) I think this would be the better code to use.

Monday, July 27, 2015

Now more dynamic than ever

The dynamic keyword was introduced in C# 4 and I remember reading about it back then. Most of the articles (like this one) highlighted using it for late binding of method calls. Useful for calling external libraries in a different language or interfacing with old COM code but not something I would normally need in managed C# land.

Well the other day as I was reading about the Visitor pattern in C# I came across this article (Farewell Visitor) which used dynamic dispatch to simplify the visitor code. Very neat use of dynamic to essentially dynamically cast a variable to the underlying object type before calling a matching overridden method.

To see a quick example of this in action past the following code in LinqPad and check out the results.
void Main() {

 var shapes = new List();
 shapes.Add(new Shape());
 shapes.Add(new Circle());
 shapes.Add(new Square());
 
 // Only the Shape method gets called each time
 foreach (var element in shapes) {
  PrintShape(element);
 }
 
 Console.WriteLine();
 
 // Using dynamic the matching object method gets called
 foreach (dynamic element in shapes) {
  PrintShape(element);
 }
}

public void PrintShape(Shape shape) {
 Console.WriteLine("I am a Shape");
}

public void PrintShape(Circle circle) {
 Console.WriteLine("I am a Circle");
}

public void PrintShape(Square square) {
 Console.WriteLine("I am a Square");
}

// Simple shape and two subclasses
public class Shape {
}

public class Circle : Shape {
}

public class Square : Shape {
}

Friday, June 5, 2015

Use the Snipping Tool in Delay Mode

Over the years I've used a number of screen capture tools. They are great for demonstrating how to steps or documenting a bug. In the past, I would recommend Snagit as the best capture software but then in Windows Vista Microsoft added the Snipping Tool. Snagit remains my recommendation for as one of the best full featured capture tool (with video capture and image markup) but for quick screen capture the Snipping Tool is great.

One of the items that appears to be missing from the Snipping Tool is a delayed mode so you can capture a menu or dropdown list after it is expanded. Well it turns out you can do a delayed capture with the Snipping Tool even if it's not obvious how to trigger it.

First start the Snipping Tool. If it's in capture mode hit Cancel so the window looks like:


Next open the menu or drop down the list you want to capture then press the "Ctrl" + "Print Screen" buttons on your keyboard. This will trigger a new capture with the screen locked as it is so you can get an image of the menu.

In Windows 10 it looks like the delayed mode will become easier to do with an explicit UI button to trigger it (info from Neowin)




Monday, November 3, 2014

Creating Word documents with Open XML SDK


Creating a simple Word document and adding content is relatively easy using the Open XML SDK (2.5). Here's a basic Hello World example:

public static void CreateWordDocument(string filename) {
    // Create a document by supplying the filename. 
    using (var wordDocument = WordprocessingDocument.Create(filename, WordprocessingDocumentType.Document)) {
        // Add the MainDocumentPart, root Document and the Body.
        var mainPart = wordDocument.AddMainDocumentPart();
        var document = mainPart.Document = new Document();
        var body = document.AppendChild(new Body());

        // Add a Paragraph and a Run with the specified Text
        var para = body.AppendChild(new Paragraph());
        var run = para.AppendChild(new Run());
        run.AppendChild(new Text("Hello World"));
    }
}

That's great for creating a simple document but what about when you want to add some real content and style it. Once you try and do that you quickly learn that what you are creating with the Open XML SDK truly is an empty document. This is not like opening Word and selecting "New Blank document" which has a bunch of preset styles available. To start having your document look like a real Word document (with things like styles and bullet lists) you have to start adding in classes like StyleDefinitionsPart and NumberingDefinitionsPart (and lots of additional stuff). Even when you add in those additional elements it's hard to make a document look as a good as Word out of the box.

An easier solution is to create your own template and use that as the basis for your new document. That way you will get a bunch of stuff already defined. The simplest way to do this is to open Word and create a new blank document and then save this as a Word template (*.dotx). You can also go into Word and customize the styles and add items like a header page and use that in your template. Once you have a template the code to create that basic Hello World example can be altered like so to use it:

public static void CreateFromTemplate(string templateFilename, string documentFilename) {
    // WordprocessingDocument.Create will overwrite an existing file. 
    // If we are using Open we have to delete the file first 
    // if we want to copy that behavior.
    if (File.Exists(documentFilename)) {
        File.Delete(documentFilename);
    }

    // Copy the template to the output file name.
    File.Copy(templateFilename, documentFilename);

    // Now open the copied file
    using (var wordDocument = WordprocessingDocument.Open(documentFilename, true)) {
        // We need to change the file type from template to document.
        wordDocument.ChangeDocumentType(WordprocessingDocumentType.Document);

        // MainDocumentPart, root Document and Body already exist just access them
        var mainPart = wordDocument.MainDocumentPart;
        var document = mainPart.Document;
        var body = document.Body;

        // Add a Paragraph and a Run with the specified Text
        var para = body.AppendChild(new Paragraph());
        var run = para.AppendChild(new Run());
        run.AppendChild(new Text("Hello World"));

        document.Save();
    }
}

It's a little more verbose but now you have a document with lots of predefined style ready to add content.

Friday, October 10, 2014

Adding a formatted hyperlink to a Word document using Open XML

I was looking for a way to insert a hyperlink into a Word document I am creating using the Open XML SDK (v2.5). I found a couple of examples on the web and got them to work. The link that was created was clickable but it wasn't styled like a hyperlink. After some trial and error I figured out the issue. Since I was creating the document from scratch it has no styles defined in it. So I created a link in word and copied the hyperlink style.

Here is the example code:

const string outputFilename = @"c:\temp\linked.docx";
						
using (var wordDocument = WordprocessingDocument.Create(outputFilename, WordprocessingDocumentType.Document)) {
	var mainPart = wordDocument.AddMainDocumentPart();

	// Create the document structure and add some text.
	var doc = mainPart.Document = new Document();
	var body = doc.AppendChild(new Body());

	// Start a paragraph with some text
	var para = body.AppendChild(new Paragraph());
	var run = para.AppendChild(new Run());
	run.AppendChild(new Text("This is a formatted hyperlink: ") {
		Space = SpaceProcessingModeValues.Preserve // Need this so the trailing space is preserved.
	});

	// Create a hyperlink relationship. Pass the relationship id to the hyperlink below.
	var rel = wordDocument.MainDocumentPart.AddHyperlinkRelationship(new Uri("http://www.example.com/"), true);
				
	// Append the hyperlink with formatting.
	para.AppendChild(
		new Hyperlink(
			new Run(
				new RunProperties(
					// This should be enough if starting with a template
					new RunStyle { Val = "Hyperlink", }, 
					// Add these settings to style the link yourself
					new Underline { Val = UnderlineValues.Single },
					new Color { ThemeColor = ThemeColorValues.Hyperlink }),
				new Text { Text = "Click Here"}
			)) { History = OnOffValue.FromBoolean(true), Id = rel.Id });

	doc.Save();
}