//private void AssignSectionProperties(Document document, SectionProperties secProperties) //{ // if (document == null) // { // throw new ArgumentNullException("document"); // } // if (secProperties != null) // { // PageSize pageSize = secProperties.Descendants<PageSize>().FirstOrDefault(); // if (pageSize != null) // { // pageSize.Remove(); // } // PageMargin pageMargin = secProperties.Descendants<PageMargin>().FirstOrDefault(); // if (pageMargin != null) // { // pageMargin.Remove(); // } // document.AppendChild(new Paragraph(new ParagraphProperties(new SectionProperties(pageSize, pageMargin)))); // } //} private static Paragraph CreateParagraph(OpenXmlCompositeElement openXmlCompositeElement, List <Run> runs) { ParagraphProperties paragraphProperties = openXmlCompositeElement.Descendants <ParagraphProperties>().FirstOrDefault(); Paragraph para = null; if (paragraphProperties != null) { para = new Paragraph(paragraphProperties.CloneNode(true)); foreach (Run run in runs) { para.AppendChild <Run>(run); } } else { para = new Paragraph(); foreach (Run run in runs) { para.AppendChild <Run>(run); } } return(para); }
/// <summary> /// Sets the unique content control ids. /// </summary> /// <param name="element">The element.</param> /// <param name="existingIds">The existing ids.</param> public static void SetUniquecontentControlIds(OpenXmlCompositeElement element, IList <int> existingIds) { var randomizer = new Random(); foreach (var sdtId in element.Descendants <SdtId>()) { if (existingIds.Contains(sdtId.Val)) { var randomId = randomizer.Next(int.MaxValue); while (existingIds.Contains(randomId)) { randomizer.Next(int.MaxValue); } sdtId.Val.Value = randomId; } else { existingIds.Add(sdtId.Val); } } }
private static Text SetTextElement(OpenXmlCompositeElement elem, string name, string text) { var textElems = elem.Descendants <Text>(); textElems.Skip(1).ToList().ForEach(e => e.Remove()); var textElem = textElems.FirstOrDefault(); if (textElem == null && elem.ChildElements.Count > 0) { throw new InvalidOperationException($"[{name}] No text element: {string.Join(", ", elem.ChildElements.Select(o => o.GetType()))}"); } if (textElem == null) { textElem = new Text(); elem.Append( new Paragraph( new Run( //new RunProperties() { Languages = new Languages() { Val = "el-GR" } }, textElem))); } textElem.Text = text; return(textElem); }
/// <summary> /// Sets the unique content control ids. /// </summary> /// <param name="element">The element.</param> /// <param name="existingIds">The existing ids.</param> public static void SetUniquecontentControlIds(OpenXmlCompositeElement element, IList<int> existingIds) { var randomizer = new Random(); foreach (var sdtId in element.Descendants<SdtId>()) { if (existingIds.Contains(sdtId.Val)) { var randomId = randomizer.Next(int.MaxValue); while (existingIds.Contains(randomId)) { randomizer.Next(int.MaxValue); } sdtId.Val.Value = randomId; } else { existingIds.Add(sdtId.Val); } } }
/// <summary> /// Determines whether element is placeholder. /// </summary> /// <param name="xmlCompositeElement"></param> /// <returns></returns> public static bool IsPlaceholder(this OpenXmlCompositeElement xmlCompositeElement) { return(xmlCompositeElement.Descendants <P.PlaceholderShape>().Any()); }
/// <summary> /// Determines whether element is table. /// </summary> /// <param name="compositeElement"></param> public static bool IsTable(this OpenXmlCompositeElement compositeElement) { return(compositeElement.Descendants <D.Table>().Any()); }
/// <summary> /// Sets the SDT content keeping permission elements. /// </summary> /// <param name="openXmlCompositeElement">The open XML composite element.</param> /// <param name="newChildren">The new children.</param> private void SetSdtContentKeepingPermissionElements(OpenXmlCompositeElement openXmlCompositeElement, List<OpenXmlElement> newChildren) { PermStart start = openXmlCompositeElement.Descendants<PermStart>().FirstOrDefault(); PermEnd end = openXmlCompositeElement.Descendants<PermEnd>().FirstOrDefault(); openXmlCompositeElement.RemoveAllChildren(); if (start != null) { openXmlCompositeElement.AppendChild(start); } foreach (var newChild in newChildren) { openXmlCompositeElement.AppendChild(newChild); } if (end != null) { openXmlCompositeElement.AppendChild(end); } }
/// <summary> /// Creates the run. /// </summary> /// <param name="openXmlCompositeElement">The open XML composite element.</param> /// <param name="content">The content.</param> /// <returns></returns> private static Run CreateRun(OpenXmlCompositeElement openXmlCompositeElement, string content) { RunProperties runProperties = openXmlCompositeElement.Descendants<RunProperties>().FirstOrDefault(); Run run = null; if (runProperties != null) { run = new Run(runProperties.CloneNode(true), new Text(content)); } else { run = new Run(new Text(content)); } return run; }
/// <summary> /// Updates the category value chart series. /// </summary> /// <param name="series">The series.</param> /// <param name="newSeriesIndex">New index of the series.</param> /// <param name="categoryHeadingRange">The category heading range.</param> /// <param name="axisDataRange">The axis data range.</param> /// <param name="seriesDataRange">The series data range.</param> /// <exception cref="ArgumentNullException">series</exception> /// <exception cref="InvalidOperationException">Only valid for series of type BarChartSeries, LineChartSeries, AreaChartSeries & PieChartSeries</exception> public static void UpdateCategoryValueChartSeries(this OpenXmlCompositeElement series, uint newSeriesIndex, CompositeRangeReference categoryHeadingRange, CompositeRangeReference axisDataRange, CompositeRangeReference seriesDataRange) { if (series == null) { throw new ArgumentNullException("series"); } if (!(series is BarChartSeries) && !(series is LineChartSeries) && !(series is AreaChartSeries) && !(series is PieChartSeries)) { throw new InvalidOperationException("Only valid for series of type BarChartSeries, LineChartSeries, AreaChartSeries & PieChartSeries"); } // Updates the supplied series Index and Order var index = series.Descendants <Index>().FirstOrDefault(); if (index != null) { index.Val = (UInt32Value)newSeriesIndex; } var order = series.Descendants <Order>().FirstOrDefault(); if (order != null) { order.Val = (UInt32Value)newSeriesIndex; } // Set the formula on the SeriesText ('Legend Entries(Series)' in Excel). // This is set to reference the column header in Excel and determines the Category Heading var seriesText = series.Descendants <SeriesText>().FirstOrDefault(); if (seriesText != null) { seriesText.StringReference = new StringReference(); seriesText.StringReference.Formula = new Formula(); seriesText.StringReference.Formula.Text = categoryHeadingRange.Reference; } // Set the formula on the Category Axis var categoryAxisData = series.Descendants <CategoryAxisData>().FirstOrDefault(); if (categoryAxisData != null) { categoryAxisData.StringReference = new StringReference(); categoryAxisData.StringReference.Formula = new Formula(); categoryAxisData.StringReference.Formula.Text = axisDataRange.Reference; } // Set the Formula on the data var values = series.Descendants <Values>().FirstOrDefault(); if (values != null) { values.NumberReference = new NumberReference(); values.NumberReference.Formula = new Formula(); values.NumberReference.Formula.Text = seriesDataRange.Reference; } }
/// <summary> /// Sets the SDT content keeping permission elements. /// </summary> /// <param name="openXmlCompositeElement">The open XML composite element.</param> /// <param name="newChildren">The new children.</param> private static void SetSdtContentKeepingPermissionElements(OpenXmlCompositeElement openXmlCompositeElement, IEnumerable<Run> newChildren) { var start = openXmlCompositeElement.Descendants<PermStart>().FirstOrDefault(); var end = openXmlCompositeElement.Descendants<PermEnd>().FirstOrDefault(); openXmlCompositeElement.RemoveAllChildren(); if (start != null) { openXmlCompositeElement.AppendChild(start); } foreach (var newChild in newChildren) { openXmlCompositeElement.AppendChild(newChild); } if (end != null) { openXmlCompositeElement.AppendChild(end); } }
public NonPlaceholderTransform(OpenXmlCompositeElement xmlElement) { _offset = xmlElement.Descendants <DocumentFormat.OpenXml.Drawing.Offset>().First(); //TODO: make lazy _extents = xmlElement.Descendants <DocumentFormat.OpenXml.Drawing.Extents>().First(); }
/// <summary> /// Processes the dynamic chart. /// </summary> /// <param name="tableDataSheetName">Name of the table data sheet.</param> /// <param name="tableData">The table data.</param> /// <param name="chartModel">The chart model.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">chartModel</exception> private static bool ProcessDynamicChart(string tableDataSheetName, TableData tableData, ChartModel chartModel) { if (chartModel == null) { throw new ArgumentNullException("chartModel"); } TempDiagnostics.Output(string.Format("Processing chart in sheet '{0}'", tableDataSheetName)); var seriesFactory = new SeriesFactory(chartModel); if (seriesFactory.SourceSeriesCount > 0) { if (tableData.TreatRowAsSeries == false) { // A COLUMN of data represents a series... // Find the Range that representes the first Axis Data for all of the series... CompositeRangeReference category1AxisRange = DetermineCategory1AxisRange(tableData, tableData.TreatRowAsSeries, tableDataSheetName); foreach (TableColumn column in tableData.Columns) { // Skip if we are on the category column if (column.DataRegion.ExcelColumnStart == category1AxisRange.MinColumnIndex) { continue; } // Get information about the series that will be based on the column var seriesInfo = new ChartSeriesInfo(column); // We need a column that has not been actively excluded if (seriesInfo.BaseOnChartSeriesIndex >= 0 && !seriesInfo.SuppressSeries) { // Get the template series or get a copy if already used OpenXmlCompositeElement clonedSeries = seriesFactory.GetOrCloneSourceSeries(seriesInfo.BaseOnChartSeriesIndex); //TODO: Allow ability to assign colour palettes (as opposed to colours) to dynamically generated series Color?seriesColour = seriesInfo.SeriesColour; if (!seriesColour.HasValue) { int useCount = seriesFactory.GetUseCount(seriesInfo.BaseOnChartSeriesIndex); seriesColour = new Color?((ColourPalette.GetColour(ColourPaletteType.GamTechnicalChartPalette, useCount - 1))); } var seriesTextRange = new CompositeRangeReference(new RangeReference(tableDataSheetName, (uint)column.DataRegion.ExcelRowStart, (uint)column.DataRegion.ExcelColumnStart)); // Series data is first column only var seriesValuesRange = new CompositeRangeReference(new RangeReference(tableDataSheetName, (uint)column.DataRegion.ExcelRowStart + 1, (uint)column.DataRegion.ExcelColumnStart, (uint)column.DataRegion.ExcelRowEnd, (uint)column.DataRegion.ExcelColumnStart)); // Determine data ranges to be used within chart series. ChartDataRangeInfo dataRangeInfo = new ChartDataRangeInfo { SeriesTextRange = seriesTextRange, CategoryAxisDataRange = category1AxisRange, SeriesValuesRange = seriesValuesRange, }; // update all formula on the series UpdateChartSeriesDataReferences(clonedSeries, dataRangeInfo, new SolidColorBrush(seriesColour.Value)); } } } else { // A ROW of data represents a series... // Find the Range that representes the first Axis Data for all of the series... CompositeRangeReference category1AxisRange = DetermineCategory1AxisRange(tableData, tableData.TreatRowAsSeries, tableDataSheetName); TableColumn seriesTextColumn = tableData.Columns[FindNonExcludedColumnIndex(tableData, 1)]; // Build a list of columns to include in the series var seriesTableColumns = new List <TableColumn>(); int seriesValuesColumnIndex = FindNonExcludedColumnIndex(tableData, 2); TableColumn firstSeriesValuesColumn = tableData.Columns[seriesValuesColumnIndex]; seriesTableColumns.Add(firstSeriesValuesColumn); // Count up to last column, excluding those marked for exclusion while (seriesValuesColumnIndex < (tableData.Columns.Count - 1)) { seriesValuesColumnIndex++; TableColumn tableColumn = tableData.Columns[seriesValuesColumnIndex]; ChartExcludeOption excludeOption = tableColumn.ChartOptions.GetOptionOrDefault <ChartExcludeOption>(); if (excludeOption == null || excludeOption.Exclude == false) { seriesTableColumns.Add(tableColumn); } } foreach (TableDataRowInfo rowInfo in tableData.RowData) { // Extract chart series related properties from row data var seriesInfo = new ChartSeriesInfo(rowInfo.RowData); // Determine where data has been written into Excel uint rowIndex = (uint)tableData.MapContainer.ExcelRowStart + rowInfo.TableRowIndex - 1; // Skip if we are on the category column if (rowIndex == category1AxisRange.MinRowIndex) { continue; } // We need a column that has not been actively excluded if (seriesInfo.BaseOnChartSeriesIndex >= 0 && !seriesInfo.SuppressSeries) { // Get the template series or get a copy if already used OpenXmlCompositeElement clonedSeries = seriesFactory.GetOrCloneSourceSeries(seriesInfo.BaseOnChartSeriesIndex); //TODO: Allow ability to assign colour palettes (as opposed to colours) to dynamically generated series Color?seriesColour = seriesInfo.SeriesColour; if (!seriesColour.HasValue) { int useCount = seriesFactory.GetUseCount(seriesInfo.BaseOnChartSeriesIndex); seriesColour = new Color?((ColourPalette.GetColour(ColourPaletteType.GamTechnicalChartPalette, useCount - 1))); } // SeriesText is the series heading used in legends var seriesTextRange = new CompositeRangeReference(new RangeReference(tableDataSheetName, rowIndex, (uint)seriesTextColumn.DataRegion.ExcelColumnStart)); // From column after category to last column in table. var seriesValuesRange = new CompositeRangeReference(); foreach (TableColumn tableColumn in seriesTableColumns) { seriesValuesRange.Update(tableDataSheetName, rowIndex, (uint)tableColumn.DataRegion.ExcelColumnStart, rowIndex, (uint)tableColumn.DataRegion.ExcelColumnStart); } // Determine data ranges to be used within chart series. ChartDataRangeInfo dataRangeInfo = new ChartDataRangeInfo { SeriesTextRange = seriesTextRange, CategoryAxisDataRange = category1AxisRange, SeriesValuesRange = seriesValuesRange, }; // update all formula on the series ( I know we're constantly swapping between brush and color here.... Address later) UpdateChartSeriesDataReferences(clonedSeries, dataRangeInfo, new SolidColorBrush(seriesColour.Value)); } } } // Remove all un-used template series and set the order of remaining. uint seriesIndex = 0; for (int idx = 0; idx < seriesFactory.SourceSeriesCount; idx++) { OpenXmlCompositeElement templateSeries = seriesFactory.GetSourceSeriesElement(idx); // Value is the use-count, if zero then the template needs removing. if (seriesFactory.GetUseCount(idx) == 0) { // Remove the template series. templateSeries.Remove(); } else { // Set template series index and order to initial value, then set clones var templateIndex = templateSeries.Descendants <DrawingCharts.Index>().FirstOrDefault(); if (templateIndex != null) { templateIndex.Val = seriesIndex; } var templateOrder = templateSeries.Descendants <DrawingCharts.Order>().FirstOrDefault(); if (templateOrder != null) { templateOrder.Val = seriesIndex; } seriesIndex++; OpenXmlElement lastElement = templateSeries; foreach (var clonedElement in seriesFactory.GetClonedSeriesElements(idx)) { var index = clonedElement.Descendants <DrawingCharts.Index>().FirstOrDefault(); if (index != null) { index.Val = seriesIndex; } var order = clonedElement.Descendants <DrawingCharts.Order>().FirstOrDefault(); if (order != null) { order.Val = seriesIndex; } lastElement.InsertAfterSelf <OpenXmlElement>(clonedElement); lastElement = clonedElement; seriesIndex++; } } } } return(true); }
public NonPlaceholderTransform(OpenXmlCompositeElement sdkCompositeElement) { _offset = sdkCompositeElement.Descendants <A.Offset>().First(); _extents = sdkCompositeElement.Descendants <A.Extents>().First(); }
public static void UpdateCategoryValueChartSeries(this OpenXmlCompositeElement series, bool isRowSeries, string sheetName, uint seriesCount, uint itemPosition, uint xCount, uint xOffset, uint yCount, uint yOffset) { if (series == null) { throw new ArgumentNullException("series"); } if (!(series is BarChartSeries) && !(series is LineChartSeries)) { throw new InvalidOperationException("Only valid for series of type BarChartSeries & LineChartSeries"); } // Updates the supplied series Index and Order var index = series.Descendants <Index>().FirstOrDefault(); if (index != null) { index.Val = (UInt32Value)seriesCount; } var order = series.Descendants <Order>().FirstOrDefault(); if (order != null) { order.Val = (UInt32Value)seriesCount; } // Set the SeriesText ('Legend Entries(Series)' in Excel). // This is set to reference the column header in Excel and determines the Category. var seriesText = series.Descendants <SeriesText>().FirstOrDefault(); if (seriesText != null) { seriesText.StringReference = new StringReference(); seriesText.StringReference.Formula = new Formula(); if (isRowSeries) { seriesText.StringReference.Formula.Text = string.Format("'{0}'!$A${1}", sheetName, itemPosition + 1); } else { // a column is a series seriesText.StringReference.Formula.Text = string.Format("'{0}'!${1}${2}", sheetName, CellExtensions.GetColumnLetter(itemPosition + 1), yOffset); } } // set the formula for the category axis, currently always the 1st column of data var categoryAxisData = series.Descendants <CategoryAxisData>().FirstOrDefault(); if (categoryAxisData != null) { categoryAxisData.StringReference = new StringReference(); categoryAxisData.StringReference.Formula = new Formula(); if (isRowSeries) { // if row is the series, the category are the dynamic columns // so for example B3:F3 categoryAxisData.StringReference.Formula.Text = string.Format("'{0}'!${1}${2}:${3}${2}", sheetName, CellExtensions.GetColumnLetter(xOffset + 1), yOffset, CellExtensions.GetColumnLetter(xCount + 1)); } else { // if row isnt series, then its a category, the default behaviour // so assuming 1st column is the category then formula is A4:A10 with 4 being the starting row in the sheet categoryAxisData.StringReference.Formula.Text = string.Format("'{0}'!$A${1}:$A${2}", sheetName, yOffset + 1, yCount + yOffset); } } var values = series.Descendants <Values>().FirstOrDefault(); if (values != null) { values.NumberReference = new NumberReference(); values.NumberReference.Formula = new Formula(); if (isRowSeries) { values.NumberReference.Formula.Text = string.Format("'{0}'!${1}${2}:${3}${2}", sheetName, CellExtensions.GetColumnLetter(xOffset + 1), itemPosition + 1, CellExtensions.GetColumnLetter(xCount + xOffset)); } else { values.NumberReference.Formula.Text = string.Format("'{0}'!${1}${2}:${1}${3}", sheetName, CellExtensions.GetColumnLetter(itemPosition + 1), yOffset + 1, yCount + yOffset); } } }
/// <summary> /// Creates the paragraph. /// </summary> /// <param name="openXmlCompositeElement">The open XML composite element.</param> /// <param name="runs">The runs.</param> /// <returns>Returns the paragraph element</returns> private static Paragraph CreateParagraph(OpenXmlCompositeElement openXmlCompositeElement, List<Run> runs) { var paragraphProperties = openXmlCompositeElement.Descendants<ParagraphProperties>().FirstOrDefault(); Paragraph para; if (paragraphProperties != null) { para = new Paragraph(paragraphProperties.CloneNode(true)); foreach (var run in runs) { para.AppendChild(run); } } else { para = new Paragraph(); foreach (var run in runs) { para.AppendChild(run); } } return para; }
/// <summary> /// Creates the run. /// </summary> /// <param name="openXmlCompositeElement">The open XML composite element.</param> /// <param name="content">The content.</param> /// <returns>Returns a run object</returns> private static Run CreateRun(OpenXmlCompositeElement openXmlCompositeElement, string content) { var runProperties = openXmlCompositeElement.Descendants<RunProperties>().FirstOrDefault(); var run = runProperties != null ? new Run(runProperties.CloneNode(true), new Text(content)) : new Run(new Text(content)); return run; }
/// <summary> /// Creates the run. /// </summary> /// <param name="openXmlCompositeElement">The open XML composite element.</param> /// <param name="content">The content.</param> /// <returns></returns> private static Run CreateRun(OpenXmlCompositeElement openXmlCompositeElement, string content) { RunProperties runProperties = openXmlCompositeElement.Descendants <RunProperties>().FirstOrDefault(); return(runProperties != null ? new Run(runProperties.CloneNode(true), new Text(content)) : new Run(new Text(content))); }
/// <summary> /// Finds and replaces text in the target element /// </summary> /// <param name="element">Target element</param> /// <param name="find">Text to find and replace</param> /// <param name="replaceWith">Text to replace with</param> private static void ReplaceText(OpenXmlCompositeElement element, string find, string replaceWith) { if (find.IsNullOrEmptyOrWhiteSpace()) { return; } if (replaceWith == null) { replaceWith = string.Empty; } var texts = element.Descendants <Text>(); for (var t = 0; t < texts.Count(); t++) { var txt = texts.ElementAt(t); for (var c = 0; c < txt.Text.Length; c++) { var match = IsMatch(texts, t, c, find); if (match != null) { // now replace the text var lines = replaceWith.Replace(Environment.NewLine, "\r") .Split('\n', '\r'); var skip = lines[lines.Length - 1].Length - 1; if (c > 0) { lines[0] = txt.Text.Substring(0, c) + lines[0]; } if (match.EndCharIndex + 1 < texts.ElementAt(match.EndElementIndex).Text.Length) { lines[lines.Length - 1] = lines[lines.Length - 1] + texts.ElementAt(match.EndElementIndex) .Text.Substring(match.EndCharIndex + 1); } txt.Space = new EnumValue <SpaceProcessingModeValues>(SpaceProcessingModeValues .Preserve); txt.Text = lines[0]; for (var i = t + 1; i <= match.EndElementIndex; i++) { texts.ElementAt(i).Text = string.Empty; } if (replaceWith.IsNullOrEmptyOrWhiteSpace()) { txt.Parent.Parent.Remove(); } if (lines.Count() > 1) { OpenXmlElement currEl = txt; var run = currEl.Parent as Run; var sampleRun = (Run)run.Clone(); var sampleRunProperties = (RunProperties)sampleRun.GetFirstChild <RunProperties>().Clone(); var sampleTxt = (Text)sampleRun.GetFirstChild <Text>().Clone(); var paragraph = run.Parent as Paragraph; var sampleParagraph = (Paragraph)paragraph.Clone(); sampleParagraph.RemoveAllChildren <Run>(); currEl = paragraph; for (var i = 1; i < lines.Count(); i++) { var paragraphBefore = currEl; var paragraphAfter = (Paragraph)sampleParagraph.Clone(); var newRun = new Run(); var newRunProperties = (RunProperties)sampleRunProperties.Clone(); var newTxt = (Text)sampleTxt.Clone(); newTxt.Space = SpaceProcessingModeValues.Preserve; newTxt.Text = lines[i]; paragraphAfter.InsertAfter(newRun, paragraphAfter.LastChild); newRun.InsertAt(newRunProperties, 0); newRun.InsertAt(newTxt, 1); paragraphBefore.InsertAfterSelf(paragraphAfter); currEl = paragraphAfter; } c = skip; } else { c += skip; } } } } }
/// <summary> /// Sets the color of the series using a solidcolor brush /// If a null brush is supplied any color is removed so the color will be automatic /// </summary> /// <param name="line">The line.</param> /// <param name="brush">The brush.</param> public static void UpdateLineBrush(this OpenXmlCompositeElement line, Brush brush) { if (line == null) { return; } // If we have a BarChart, we really want tp update the SolidFill (not the Outline.SolidFill) BarChartSeries barChartSeries = line as BarChartSeries; if (barChartSeries != null) { // For BarCharts, we update the SolidFill barChartSeries.UpdateSeriesColour((SolidColorBrush)brush); } else { // Update the Outline.SolidFill + set the SolidFill to the same colour var chartShapeProperties = line.Descendants <ChartShapeProperties>().FirstOrDefault(); if (brush == null && !(brush is SolidColorBrush)) { if (chartShapeProperties != null) { line.RemoveChild <ChartShapeProperties>(chartShapeProperties); } return; } // the series title, this is the name of the column header var seriesText = line.Descendants <SeriesText>().FirstOrDefault(); if (chartShapeProperties == null) { chartShapeProperties = new ChartShapeProperties(); // if there's a series text then insert afterwards if (seriesText == null) { line.InsertAt <ChartShapeProperties>(chartShapeProperties, 0); } else { line.InsertAfter <ChartShapeProperties>(chartShapeProperties, seriesText); } } var outline = chartShapeProperties.Descendants <Outline>().FirstOrDefault(); if (outline == null) { outline = new Outline(); chartShapeProperties.InsertAt(outline, 0); } var outlineSolidFill = outline.Descendants <SolidFill>().FirstOrDefault(); if (outlineSolidFill == null) { outlineSolidFill = new SolidFill(); outline.Append(outlineSolidFill); } // Update the fill with the supplied brush colour outlineSolidFill.UpdateSolidFill((SolidColorBrush)brush); // Clones the OutlineSolidFill as the SolidFill of the series... var solidFill = chartShapeProperties.GetFirstChild <SolidFill>(); if (solidFill != null) { chartShapeProperties.RemoveChild(solidFill); } chartShapeProperties.InsertAt(outlineSolidFill.CloneNode(true), 0); } }