Есть ли простой способ переупорядочить столбцы таблиц в режиме проектирования SSRS?
У меня есть отчет SSRS, содержащий более 20 столбцов в таблице. Наши пользователи решили, что данные в порядке, но они хотят, чтобы столбцы перемещались (вздох!).
Кажется, что нужно легко перестроить столбцы (переместить столбец 3 в столбец 1, обменять столбцы 4 и 5 и т.д.). Но перетаскивание не работает, и единственное решение, похоже, удаляет исходный столбец и снова вставляет его в нужное место (и повторно применяет любые выражения и форматирование, уже созданные для столбца).
Есть ли более простой способ сделать это? Обратите внимание, что я не хочу программного решения, но просто нужно изменить его один раз в режиме разработки.
Ответы
Ответ 1
Существует способ перемещения столбцов через конструктор:
- введите количество пустых столбцов, которые вы хотите переместить в место назначения
- shift-left-click на ячейки (НЕ в столбце заголовка), которые вы хотите переместить
- щелкните правой кнопкой мыши и выберите команду "Вырезать"
- щелкните правой кнопкой мыши в верхней части столбцов назначения и выберите Вставить
- удалить теперь пустые старые столбцы
Ответ 2
Если вы можете читать XML (просто понимаете, где начинаются и заканчиваются теги и т.д.), Вы легко можете выполнить задачу. Вы можете предпринять следующие шаги:
- Прежде всего сделайте резервную копию исходного отчета, скопировав его в другой файл.
- Щелкните правой кнопкой мыши свой отчет в обозревателе решений и выберите "Просмотреть код"
- Это открывает RDL отчета - не пугайтесь, это всего лишь простой XML файл
- Теперь найдите в файле RDL тег "Tablix1" - найдите
<Tablix Name="Tablix1">....</Tablix >
- Теперь вам нужно искать разные теги "
<Textbox Name="...">...</Texbox>
", вложенные в теги <TablixCells><TablixCell><CellContents>....
- Теперь вы можете легко изменить порядок столбцов отчета, просто изменив порядок этих
<Textbox...>...</Texbox>
и вы получите новый отчет с новым порядком столбцов.
Ответ 3
На самом деле вам нужно переместить (вырезать и вставить) весь элемент <TablixCell>
для столбца (все между <TablixCell>
и </TablixCell>
, включая теги <TablixCell>
и </TablixCell>
).
Например, чтобы переупорядочить столбцы в приведенном ниже примере, чтобы столбец "Идентификатор продукта" пришел до в столбце "Имя продукта", вы должны выбрать и разрезать весь раздел вокруг "ProductName" "элемент ячейки (все от первого <TablixCell>
до первого </TablixCell>
), а затем вставьте после </TablixCell>
для столбца" ProductID ".
Обратите внимание, что существует полный набор элементов <TablixCell>
для каждой строки, определенной в Tablix; каждый из них находится в отдельном элементе <TablixRow>
. Если вы оставили столбец заголовка по умолчанию (где установлены имена столбцов), то первый <TablixRow>
определяет эту строку заголовка, а второй определяет данные в столбцах, и это тот, который вы хотите отредактировать. После того, как вы изменили столбцы данных, вам потребуется либо сделать то же самое для столбца заголовка (если он у вас есть), либо просто переименовать столбцы, используя конструктор, чтобы сопоставлять данные в столбцах.
Действительно, это настолько запутанно, что, возможно, проще перемещать столбец, просто используя конструктор, чтобы вставить новый столбец, куда вы хотите переместить столбец, установить его с соответствующим источником данных для этого столбца, а затем удалить оригинальная колонка. В следующем примере вы введете новый столбец после Идентификатор продукта, установите его в столбец источника данных ProductName (который будет устанавливать его "Имя продукта" в строке заголовка), а затем удалите исходный столбец Имя продукта слева.
...
<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>
...
после вырезания/вставки, вы в итоге получите:
...
<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>
...
Ответ 4
Еще одна заметка о работе в RDL:
Если вы ошибетесь, отчет отобразит сообщение об ошибке и не отобразит данные.
Если вы не знакомы с RDL (языком определения отчета, типом XML), эти типы ошибок могут очень расстраивать, чтобы иногда обрабатывать отчет, который не может использоваться.
Намного безопаснее использовать новые столбцы и удалить старый метод в упомянутом выше дизайнере. Это избавляет вас от RDL, уменьшая ваши шансы повредить отчет.
Ответ 5
Я столкнулся с этой ситуацией сегодня, когда пытался изменить порядок столбцов, перетаскивая заголовок столбца таблицы, это не работает! Однако я обнаружил, что можно перетащить ячейку и (осторожно) поместить ее в другую ячейку, а затем поменять ячейки. Таким образом, вы можете переупорядочивать столбцы, меняя ячейки заголовка и содержимого без необходимости создавать новые пустые столбцы, что лучше, если вы не хотите, чтобы ширина тела отчета увеличивалась, и создавались пустые страницы при рендеринге PDF, конечно, это можно исправить снова. Чтобы перетащить ячейку, щелкните ее один раз, но не переходите в режим редактирования, затем наведите курсор мыши на границы и перетащите, как только вы получите курсор перемещения. Это применимо к конструктору отчетов, доступному для Visual Studio 2017.
Ответ 6
Мое решение:
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);
}
}
}
Использование:
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);
}