How To: Customizing Story Layout by Overriding ViewInsertionManager
There are three ways to alter the appearances of story documents in the Syndicated Client Experiences (SCE) Reader Sample:
- Providing a FlowDocumentStyleProvider – this is by far the easiest method. In the sample application, simply edit the XAML based styles found in “Resources/articleresources.xaml.”
- Overriding NitfToFlowDocumentConverter – this allows you to have direct control over the conversion of XML to a FlowDocument. If you are using a different format than NITF for your stories, you will need to do this anyway. Note that non-textual content such as images and ads is not inserted during this phase (though in some circumstances it may be appropriate).
- Overriding ViewInsertionManager – ViewInsertionManger inserts non-text content into the document after the document completes its initial layout pass.
This document will walk you through the process of overriding ViewInsertionManager. Some of the more interesting scenarios involving ViewInsertionManager include adding videos and charts into the FlowDocument. For the sake of simplicity, this "How To" will focus on a much simpler scenario: making images larger.
The current ViewInsertionManager logic always snaps images to column widths. Images never get displayed larger than their native size. Some publishers wish to allow images to scale larger than the native size. This document will show you how.
Subclass ViewInsertionManager
- Create a new class named MyViewInsertionManager
- In the Visual Studio Solution Explorer, right click on SceReaderSample, select Add >> Class.
- In the name field, type MyViewInsertionManager and press enter
- Delete everything in MyViewInsertionManager.cs and add the following code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using Microsoft.SceReader.Controls;
using Microsoft.SceReader.Data;
namespace SceReaderSample
{
class MyViewInsertionManager : ViewerInsertionManager
{
public MyViewInsertionManager(StoryViewer sv) : base(sv)
{
}
protected override FigureLength EstimateImageFigureWidth(ImageData imageData, FigureHorizontalAnchor figureHorizontalAnchor, FigureVerticalAnchor figureVerticalAnchor, int columns, double columnGap, double columnWidth)
{
//increase image width by 25% and round up
int imageColumnSpan = (int)Math.Round((imageData.Width * 1.25 / columnWidth));
//make sure it's a least one column and don't let the image span all the columns on the page
imageColumnSpan = Math.Max(1, Math.Min(imageColumnSpan, columns - 1));
FigureLength figureWidth = new FigureLength(imageColumnSpan, FigureUnitType.Column);
return figureWidth;
}
}
}
- Save the changes and build your application (press F6). Make sure the changes do not prevent it from building.
What did I just do?
ViewInsertionManager includes some default rules which determine image sizing within an article. These include:
- Snap images to column boundaries
- Never display an image larger than it’s reported size
The code above changes rule #2. It allows the images to be rescaled up to 25% larger. It also rounds the column span to the nearest integer instead of always rounding down.
Subclass StoryViewer
- Create a new class named MyStoryViewer
- In the Visual Studio Solution Explorer, right click on SceReaderSample, select Add >> Class.
- In the name field, type MyStoryViewer and press enter
- Delete everything in MyStoryViewer.cs and add the following code:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SceReader.Controls;
namespace SceReaderSample
{
class MyStoryViewer : StoryViewer
{
protected override IInsertStoryDocumentTemplate TemplateInserter
{
get
{
EnsureTemplateInserter();
return _myTemplateInserter;
}
}
protected override void EnsureTemplateInserter()
{
if (StoryTemplatesEnabled)
{
if (_myTemplateInserter == null)
{
_myTemplateInserter = new MyViewInsertionManager(this);
}
}
}
private IInsertStoryDocumentTemplate _myTemplateInserter;
}
}
- Save the changes and build your application (press F6). Make sure the changes do not prevent it from building.
What did I just do?
The StoryViewer class handles the coordination between ViewInsertionManager and the story FlowDocument. The MyStoryViewer class will now use MyViewInsertionManager to insert additional content into the FlowDocument.
Change Story’s DataTemplate
- In the SceReaderSample project, open "Resources/BaseResources.xaml"
- Add the namespace reference highlighted below to the top level ResourceDictionary element:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SceReader="clr-namespace:SceReader;assembly=SceReader"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:SceReaderData="clr-namespace:SceReader.Data;assembly=SceReader"
xmlns:SceReaderControls="clr-namespace:SceReader.Controls;assembly=SceReader"
xmlns:SceReaderView="clr-namespace:SceReader.View;assembly=SceReader"
xmlns:Annotations="clr-namespace:System.Windows.Annotations;assembly=PresentationFramework"
xmlns:Local="clr-namespace:SceReaderSample"
>
- Replace all instances of "SceReaderControls:StoryViewer" with "Local:MyStoryViewer"
- Repeat steps 2 and 3 for the file "Resources/StoryViewerResources.xaml"
- Add the namespace reference
- Replace all instances of "SceReaderControls:StoryViewer" with "Local:MyStoryViewer"
- Build and run your application by pressing F5
What did I just do?
In WPF, data visualization is controlled via DataTemplates. In the sample application, the fundamental DataTemplates are found in BaseResources.xaml. By switching the template for Type ViewStory to use MyStoryViewer instead of StoryViewer, a MyStoryViewer object is instaniated whenever the application navigates to a story. In addition, you needed to provide a ControlTemplate for MyStoryViewer. You did this in step #4 above.
The images below show the effects of the change.