有没有一种简单的方法可以在SSRS设计模式下重新排列Tablix列?

时间:2012-09-28 15:21:24

标签: ssrs-2008 reporting-services ssrs-tablix

我有一个SSRS报告,其中包含一个Tablix中的20多列。我们的用户已经确定数据没问题,但他们希望列移动(叹息!)。

似乎应该很容易重新排列列(将列3移到第1列,交换第4列和第5列等)。但是,拖放似乎不起作用,唯一的解决方案似乎是删除原始列并将其重新插入正确的位置(并重新应用已为列创建的任何表达式和格式)。

有没有更简单的方法呢?请注意,我不需要程序化解决方案,只需要在设计模式下更改一次。

6 个答案:

答案 0 :(得分:96)

有一种方法可以在设计师中移动列:

  1. 插入要在目标位置移动的空白列数
  2. 左键单击要移动的单元格(不是标题列)
  3. 右键单击并选择“剪切”命令
  4. 右键单击目标列顶部,然后选择粘贴
  5. 删除现在空的旧列

答案 1 :(得分:29)

如果您可以阅读XML(只需了解标签的开始和结束位置等)​​,您就可以轻松完成任务。您可以采取以下一系列步骤:

  1. 首先通过将原始报告复制到另一个文件来备份原始报告。
  2. 右键单击解决方案资源管理器中的报告,然后选择“查看代码”
  3. 这打开了报告的RDL ---不要害怕它只是一个简单的xml文件
  4. 现在在RDL文件中找到“Tablix1”标记 - 查找<Tablix Name="Tablix1"> ....</Tablix >
  5. 您现在需要查找嵌套在<Textbox Name="...">...</Texbox>代码中的不同“<TablixCells><TablixCell><CellContents>....”代码
  6. 现在,只需重新排列这些<Textbox...>...</Texbox>的顺序,您就可以轻松地重新排列报告的列,并且您将获得包含新列排序的新报告。

答案 2 :(得分:5)

实际上,您需要移动(剪切并粘贴)列的整个<TablixCell>元素(<TablixCell></TablixCell>之间的所有内容,包括<TablixCell>和{{ 1}}标签本身)。

例如,要重新排列以下示例中的列以使“产品ID”列在“<产品名称”列的之前,您可以选择并剪切“ProductName”周围的整个部分“单元格元素(从第一个</TablixCell>到第一个<TablixCell>的所有内容),然后将粘贴到 </TablixCell>以获取”ProductID“列。
请注意,Tablix中定义的每一行都有一整套</TablixCell>元素;每个都在一个单独的<TablixCell>元素中。如果您保留默认标题列(设置列名称的位置),则第一个<TablixRow>定义该标题行,第二个定义列中的数据,它是您要编辑的标题行。重新排列数据列之后,您需要对标题列执行相同的操作(如果有的话),或者只需使用设计器重命名列以匹配列中现在的数据。

实际上,这非常复杂,只需使用设计器插入要移动列的新列,使用适当的数据源设置该列,然后删除,就可以更轻松地移动列原始专栏。对于下面的示例,您将在产品ID 之后插入一个新列,将其设置为 ProductName 数据源列(这将在标题行中将其设置为“产品名称” ),然后删除左侧的原始产品名称列。

<TablixRow>

剪切/粘贴后,您最终会得到:

...
<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>
...

答案 3 :(得分:3)

关于在RDL工作的另一个注意事项:
如果您弄错了,报告将显示错误消息,并且不会显示数据。

除非您熟悉RDL(报表定义语言,一种XML),否则这些类型的错误可能会非常令人沮丧,有时会导致报表无法使用。

使用添加新列并删除上面提到的设计器中的旧方法更安全。这可以让您远离RDL,从而减少损坏报告的可能性。

答案 4 :(得分:1)

我今天遇到这种情况,因为我试图通过拖动Tablix的列标题来对列进行重新排序,但这是行不通的!但是,我发现可以拖动一个单元格并将其(小心地)放到另一个单元格上,然后进行单元格交换。这样,您可以通过交换标题和内容单元格来重新排列列,而不必创建新的空列,如果您不希望报表主体宽度增加并在PDF渲染中产生空页,则更好。再次。要拖动单元格,请单击该单元格但不进入编辑模式,然后将鼠标悬停在边框上,并在获得“移动”光标后拖动。这适用于Visual Studio 2017可用的报表设计器。

答案 5 :(得分:0)

我的解决方案:

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);
}