【OpenXml】Converting Pptx Dashed Borders to WPF Dashed Borders

【OpenXml】Converting Pptx Dashed Borders to WPF Dashed Borders

As the title says.

Last updated 5/2/2022 8:59 PM
RyzenAdorer
3 min read
Category
.NET
Tags
.NET C# WPF OpenXML

1. Installing the OpenXML SDK

First, install the required NuGet package for the OpenXML SDK. We have open-sourced an OpenXML flattening layer for parsing pptx files. You can install it using either of the following methods:

  • Via the NuGet Package Manager Console:
Install-Package dotnetCampus.DocumentFormat.OpenXml.Flatten -Version 2.0.0
  • Via a csproj reference:
<PackageReference Include="dotnetCampus.DocumentFormat.OpenXml.Flatten" Version="2.0.0" />

2. Parsing the Pptx

Here I use the 7 types of straight lines in PPTX, each set to one of the 7 available dash types. The display effect in PPTX is as follows:

The parsing code is as follows, with the main logic part:

private void PptxToGeometry(string filePath)
{
    if (!File.Exists(filePath) || !filePath.EndsWith(".pptx", StringComparison.OrdinalIgnoreCase))
    {
        return;
    }

    var lines = new List<Line>();
    using var presentationDocument = PresentationDocument.Open(filePath, false);
    var presentationPart = presentationDocument.PresentationPart;
    var presentation = presentationPart?.Presentation;
    var slideIdList = presentation?.SlideIdList;
    if (slideIdList == null)
    {
        return;
    }
    foreach (var slideId in slideIdList.ChildElements.OfType<SlideId>())
    {
        var slidePart = (SlidePart)presentationPart.GetPartById(slideId.RelationshipId);
        var slide = slidePart.Slide;
        foreach (var shapeProperties in slide.Descendants<ShapeProperties>())
        {
            var presetGeometry = shapeProperties.GetFirstChild<PresetGeometry>();
            if (presetGeometry != null && presetGeometry.Preset.HasValue)
            {
                if (presetGeometry.Preset == ShapeTypeValues.StraightConnector1)
                {
                    var transform2D = shapeProperties.GetFirstChild<Transform2D>();
                    var extents = transform2D?.GetFirstChild<Extents>();
                    if (extents != null)
                    {
                        var width = new Emu(extents.Cx!.Value).ToPixel().Value;
                        var height = new Emu(extents.Cy!.Value).ToPixel().Value;


                        var presetDash = shapeProperties.GetFirstChild<Outline>()?.GetFirstChild<PresetDash>()?.Val;
                        var dashArray = GetDashArrayByPresetLineDashValues(presetDash);
                        var line = ConverterToGeometry( width, height, dashArray);
                        lines.Add(line);
                    }
                }
            }
        }
    }

    this.ListBox.ItemsSource = lines;
}

Method to map PPTX dash types to WPF dash arrays:

private DoubleCollection GetDashArrayByPresetLineDashValues(PresetLineDashValues presetLineDashValues)
{
    DoubleCollection dashStyle = presetLineDashValues switch
    {
        PresetLineDashValues.Solid => new(),
        PresetLineDashValues.Dot => new() { 0, 2 },
        PresetLineDashValues.Dash => new() { 3, 3 },
        PresetLineDashValues.LargeDash => new() { 8, 3 },
        PresetLineDashValues.DashDot => new() { 3, 3, 1, 3 },
        PresetLineDashValues.LargeDashDot => new() { 7.5, 3.5, 1, 3.5 },
        PresetLineDashValues.LargeDashDotDot => new() { 8, 3, 1, 3, 1, 3 },
        PresetLineDashValues.SystemDash => new() { 3, 1 },
        PresetLineDashValues.SystemDot => new() { 1, 1 },
        PresetLineDashValues.SystemDashDot => new() { 2, 2, 0, 2 },
        PresetLineDashValues.SystemDashDotDot => new() { 2, 2, 0, 2 },
        _ => new DoubleCollection()
    };
    return dashStyle;
}

Final method to draw the line:

private Line ConverterToGeometry(double width, double height, DoubleCollection dashDoubleCollection)
{
    var line = new Line
    {
        X1 = 0,
        Y1 = 0,
        X2 = width,
        Y2 = height,
        StrokeDashArray = dashDoubleCollection,
        Stroke = Stroke,
        StrokeThickness = StrokeThickness
    };
    return line;
}

Final effect:

We can see that the effect is very close. Of course, you can fine-tune the values to be more precise by simply adjusting the corresponding values in the GetDashArrayByPresetLineDashValues method.

3. Afterword

Actually, the OpenXML documentation provides the PresetDash values, roughly as follows:

However, these values do not correspond to the DoubleCollection used for setting dashes in WPF. Therefore, the mapping values above are all my own fine-tuning.

4. Source Code

BlogCodeSample/PptDashConverToWpfSample at main · ZhengDaoWang/BlogCodeSample

Keep Exploring

Related Reading

More Articles
Same category / Same tag 5/27/2025

WPF Achieve a Danger Reminder Effect

When our program is released and users are performing dangerous operations, our software should provide some reminder effects, such as a red border edge, similar to the warning effect in Amap.

Continue Reading
Same category / Same tag 6/20/2024

CodeWF.EventBus: Lightweight Event Bus for Smoother Communication

CodeWF.EventBus is a flexible event bus library that enables decoupled communication between modules. It supports various .NET project types such as WPF, WinForms, ASP.NET Core, etc. With a clean design, it easily implements command publishing and subscribing, as well as requests and responses. Through orderly event handling, it ensures events are properly processed. Simplify your code and improve system maintainability.

Continue Reading