Ich habe einen SSRS-Bericht, der mehr als 20 Spalten in einem Tablix enthält. Unsere Benutzer haben entschieden, dass die Daten in Ordnung sind, aber sie möchten, dass die Spalten verschoben werden (seufz!).
Es scheint, als ob es einfach sein sollte, die Spalten neu anzuordnen (Spalte 3 in Spalte 1 verschieben, Spalten 4 und 5 tauschen usw.). Drag & Drop scheint jedoch nicht zu funktionieren, und die einzige Lösung scheint darin zu bestehen, die ursprüngliche Spalte zu löschen und an der richtigen Stelle einzufügen (und bereits für die Spalte erstellte Ausdrücke und Formatierungen erneut anzuwenden).
Gibt es einen einfacheren Weg, dies zu tun? Bitte beachten Sie, dass ich keine programmgesteuerte Lösung möchte, sondern diese nur einmal im Entwurfsmodus ändern muss.
Es gibt eine Möglichkeit, Spalten im Designer zu verschieben:
Wenn Sie XML lesen können (verstehen Sie einfach, wo Tags beginnen und/oder enden usw.), können Sie die Aufgabe problemlos ausführen. Sie können die folgenden Schritte ausführen:
<Tablix Name="Tablix1"> ....</Tablix >
<Textbox Name="...">...</Texbox>
" - Tags suchen, die in den <TablixCells><TablixCell><CellContents>....
- Tags verschachtelt sind<Textbox...>...</Texbox>
Ändern. Der neue Bericht wird dann in einer neuen Spaltenreihenfolge angezeigt.Eigentlich müssen Sie das gesamte <TablixCell>
Element für die Spalte (alles zwischen dem <TablixCell>
und </TablixCell>
einschließlich der <TablixCell>
und </TablixCell>
markiert sich selbst).
Um beispielsweise die Spalten im folgenden Beispiel neu anzuordnen, damit die Spalte "Produkt-ID" vor der Spalte "Produktname" angezeigt wird, müssen Sie den gesamten Abschnitt um das Zellenelement "Produktname" auswählen und ausschneiden (alles vom ersten <TablixCell>
zum ersten </TablixCell>
) und dann einfügen nach das </TablixCell>
für die Spalte "ProductID".
Beachten Sie, dass es einen vollständigen Satz von <TablixCell>
-Elemente für jede in Tablix definierte Zeile; jeder ist in einem separaten <TablixRow>
Element. Wenn Sie die Standardüberschriftenspalte (in der die Spaltennamen festgelegt sind) verlassen haben, wird das erste <TablixRow>
definiert diese Kopfzeile und die zweite definiert die Daten in den Spalten und ist diejenige, die Sie bearbeiten möchten. Nachdem Sie die Datenspalten neu angeordnet haben, müssen Sie entweder dasselbe für die Kopfspalte tun (sofern vorhanden) oder die Spalten einfach mit dem Designer umbenennen, damit sie den Daten in den Spalten entsprechen.
In Wirklichkeit ist dies jedoch so kompliziert, dass es wahrscheinlich einfacher ist, eine Spalte zu verschieben, indem Sie mit dem Designer eine neue Spalte einfügen, in die die Spalte verschoben werden soll, diese mit der richtigen Datenquelle für diese Spalte festlegen und dann die ursprüngliche Spalte löschen . Für das folgende Beispiel fügen Sie eine neue Spalte nach Produkt-ID ein und setzen sie in die Datenquellenspalte Produktname (wodurch in der Kopfzeile "Produktname" festgelegt wird) ), und löschen Sie dann die ursprüngliche Spalte Produktname auf der linken Seite.
...
<TablixCell>
<CellContents>
<Textbox Name="ProductName">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>=Fields!ProductName.Value</Value>
<Style />
</TextRun>
</TextRuns>
<Style />
</Paragraph>
</Paragraphs>
<rd:DefaultName>ProductName</rd:DefaultName>
<Style>
<Border>
<Color>LightGrey</Color>
<Style>Solid</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
<TablixCell>
<CellContents>
<Textbox Name="ProductID">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>=Fields!ProductID.Value</Value>
<Style />
</TextRun>
</TextRuns>
<Style />
</Paragraph>
</Paragraphs>
<rd:DefaultName>ProductID</rd:DefaultName>
<Style>
<Border>
<Color>LightGrey</Color>
<Style>Solid</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
...
nach dem Ausschneiden/Einfügen erhalten Sie dann:
...
<TablixCell>
<CellContents>
<Textbox Name="ProductID">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>=Fields!ProductID.Value</Value>
<Style />
</TextRun>
</TextRuns>
<Style />
</Paragraph>
</Paragraphs>
<rd:DefaultName>ProductID</rd:DefaultName>
<Style>
<Border>
<Color>LightGrey</Color>
<Style>Solid</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
<TablixCell>
<CellContents>
<Textbox Name="ProductName">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value>=Fields!ProductName.Value</Value>
<Style />
</TextRun>
</TextRuns>
<Style />
</Paragraph>
</Paragraphs>
<rd:DefaultName>ProductName</rd:DefaultName>
<Style>
<Border>
<Color>LightGrey</Color>
<Style>Solid</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
...
Ein weiterer Hinweis zur Arbeit in der RDL:
Wenn Sie einen Fehler machen, wird im Bericht eine Fehlermeldung angezeigt und die Daten werden nicht angezeigt.
Sofern Sie nicht mit RDL (Report Definition Language, eine Art von XML) vertraut sind, können diese Arten von Fehlern sehr frustrierend sein, wenn Sie den Bericht manchmal unbrauchbar machen.
Es ist weitaus sicherer, die oben genannten Methoden zum Hinzufügen neuer Spalten und Löschen der alten Spalten im Designer zu verwenden. Dies hält Sie von der RDL fern und verringert die Wahrscheinlichkeit, dass der Bericht beschädigt wird.
Ich bin heute auf diese Situation gestoßen, als ich versuchte, Spalten durch Ziehen der Spaltenüberschrift des Tablix neu anzuordnen. Das funktioniert nicht! Ich habe jedoch festgestellt, dass es möglich ist, eine Zelle zu ziehen und (vorsichtig) auf eine andere Zelle abzulegen, bevor die Zellen vertauscht werden. Auf diese Weise können Sie Spalten neu anordnen, indem Sie die Kopf- und Inhaltszellen vertauschen, ohne neue leere Spalten erstellen zu müssen. Dies ist besser, wenn Sie nicht möchten, dass die Breite des Berichtskörpers zunimmt und leere Seiten in PDF = Das Rendern kann natürlich wieder repariert werden. Um eine Zelle zu ziehen, klicken Sie einfach auf die Zelle, gehen Sie aber nicht in den Bearbeitungsmodus, bewegen Sie die Maus über die Ränder und ziehen Sie, sobald Sie den 'Verschieben'-Cursor erhalten. Dies gilt für Berichtsdesigner für Visual Studio 2017 verfügbar.
Meine Lösung:
using System;
using System.IO;
using System.Linq;
using System.Xml;
namespace MoveSsrsColumns
{
class TablixColumnReorderer
{
readonly XmlDocument _xData = new XmlDocument();
readonly XmlNamespaceManager _nsManager;
readonly XmlElement _tablixNode;
public TablixColumnReorderer(string rdlFileName, string tablixName)
{
using (var fs = new FileStream(rdlFileName, FileMode.Open))
using (var xr = XmlReader.Create(fs))
_xData.Load(xr);
_nsManager = new XmlNamespaceManager(_xData.NameTable);
_nsManager.AddNamespace("def", "http://schemas.Microsoft.com/sqlserver/reporting/2010/01/reportdefinition");
_tablixNode =
_xData.SelectNodes(string.Format(TablixXPath, tablixName)_nsManager)
?.Cast<XmlElement>().FirstOrDefault()
?? throw new ApplicationException("Tablix node notfound");
}
const string TablixXPath = @"
/def:Report
/def:ReportSections
/def:ReportSection
/def:Body
/def:ReportItems
/def:Tablix[@Name='{0}']";
const string SearchColumnXPath = @"
def:TablixBody
/def:TablixRows
/def:TablixRow
/def:TablixCells
/def:TablixCell
/def:CellContents
/def:*[@Name='{0}']";
const string ParentTablixCellXPath = "parent::def:CellContents/parent::def:TablixCell";
int FindColumn(string columnControlName)
{
var columnControl = _tablixNode
.SelectNodes(string.Format(SearchColumnXPath, columnControlName), _nsManager)
?.Cast<XmlElement>()
.Single();
if (columnControl==null)
throw new ArgumentException($"Column with control {columnControlName} notfound");
if (!(columnControl.SelectSingleNode(ParentTablixCellXPath, _nsManager) is XmlElement tablixCell))
throw new ArgumentException($"Tablix cell for column with control {columnControlName} notfound");
var columnIndex = ((XmlElement) tablixCell.ParentNode)
?.ChildNodes
.Cast<XmlElement>()
.TakeWhile(e=>e!=tablixCell)
.Count() ?? -1;
if (columnIndex==-1)
throw new ArgumentException($"Cannot get index for column with control {columnControlName}");
return columnIndex;
}
public void SetPosition(string sourceColumnControlName, string destinationColumnControlName)
{
SetPosition(FindColumn(sourceColumnControlName), FindColumn(destinationColumnControlName));
}
public void SetPosition(string sourceColumnControlName, int destinationColumnIndex)
{
SetPosition(FindColumn(sourceColumnControlName), destinationColumnIndex);
}
public void SetPosition(int sourceColumnIndex, string destinationColumnControlName)
{
SetPosition(sourceColumnIndex, FindColumn(destinationColumnControlName));
}
const string TablixCellsXPath = "def:TablixBody/def:TablixColumns";
const string TablixRowCellsXPath = "def:TablixBody/def:TablixRows/def:TablixRow/def:TablixCells";
public void SetPosition(int sourceColumnIndex, int destinationColumnIndex)
{
var tablixColumnsNode = _tablixNode
.SelectSingleNode(TablixCellsXPath, _nsManager) as XmlElement
?? throw new ApplicationException("TablixColumns node notfound");
tablixColumnsNode.InsertBefore(
tablixColumnsNode.ChildNodes[sourceColumnIndex],
tablixColumnsNode.ChildNodes[destinationColumnIndex]
);
var tablixRowsCells = _tablixNode
.SelectNodes(TablixRowCellsXPath, _nsManager)
?.Cast<XmlElement>()
?? throw new ApplicationException("Tablix rows cells notfound");
foreach (var cells in tablixRowsCells)
cells.InsertBefore(
cells.ChildNodes[sourceColumnIndex],
cells.ChildNodes[destinationColumnIndex]
);
}
public void Save(string rdlFileName)
{
using (var fs = new FileStream(rdlFileName, FileMode.Create))
using (var xw = XmlWriter.Create(fs, new XmlWriterSettings
{
Indent = true,
IndentChars = " "
}))
_xData.Save(xw);
}
}
}
Verwendung:
public static void Main(string[] args)
{
var tcr = new TablixColumnReorderer("myreport.rdl", "Tablix1");
tcr.SetPosition("bill_number", 0);
tcr.SetPosition("account", 1);
tcr.SetPosition("to_date", 2);
tcr.Save("myreport#2.rdl");
Console.WriteLine("done");
Console.ReadKey(true);
}