在SSRS设计模式中,是否有一种简单的方法来重新排列tablix列?

78

我有一个包含超过20个列的选项卡表格的SSRS报表。我们的用户认为数据没问题,但是他们想要移动这些列(叹气!)。

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

是否有更简单的方法来实现这一点?请注意,我不想要编程解决方案,只需要在设计模式下更改一次即可。


1
@ensisNoctis 还差得远呢。在 PaulStock,我只能将我的列全部删除并重新添加,因为它们是分组列。 - Michael Plautz
6个回答

134

通过设计师有一种移动列的方法:

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

3
尝试对行执行此操作;效果很好。比编辑XML文件要安全/容易得多… - ARich
3
这应该是最佳答案。比玩弄XML要安全得多。 - april4181
1
无法与某些公式表达式一起使用,例如当我尝试这样做时,RunningValue神秘地丢失了它的第三个参数,因此我不得不手动恢复它。我想基于XML的方法会保留这个参数。 - underscore_d
1
我无法让这在列中的行组中工作 :( - Michael Plautz
1
工作得很好。但不要忘记将整个报告的宽度调整回先前的值,以防在生成PDF时出现意外。 - Marcel
显示剩余6条评论

31

如果你能够阅读 XML(只要理解标签何时开始或结束等),你就可以轻松完成任务。您可以按照以下步骤操作:

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

18
再举一个例子,显示微软在为其开发人员提供合理的报告工具方面投入极少。+1 - Yuck
5
给阅读此回复的任何人一个提示,您需要将包含列名的元素与包含详细信息的元素分别移动。列宽也会单独存储,您应用的任何可见性规则也是如此...这种方法开始看起来并不那么容易了... - Mike
2
Scott Blasingame的回答更为简单,如果你觉得这种XML方法有点太复杂了! - T. Sar

5
实际上,您需要移动(剪切和粘贴)整个 <TablixCell> 元素以进行列的操作(包括 <TablixCell></TablixCell> 标签本身之间的所有内容)。
例如,在下面的示例中重新排列列,使 "Product ID" 列在 "Product Name" 列之前,您需要选择并剪切围绕 "ProductName" 单元格元素的整个部分(从第一个 <TablixCell> 到第一个 </TablixCell>),然后将其粘贴到 "ProductID" 列的 </TablixCell> 后面。
请注意,对于 Tablix 中定义的每一行都有一个完整的 <TablixCell> 元素集;每个元素都在单独的 <TablixRow> 元素中。如果您保留了默认的标题列(其中设置列名),则第一个 <TablixRow> 定义该标题行,第二个定义列中的数据,这是您想要编辑的。一旦您重新排列了数据列,您将需要对标题列执行相同的操作(如果有的话),或者只需使用设计器将列重命名为与现在在列中的数据匹配即可。
但实际上,这太复杂了,可能更容易通过使用设计器在您想要移动列的位置插入新列,将其设置为该列的正确数据源,然后删除原始列来移动列。对于下面的示例,您需要在 Product ID 后插入一个新列,将其设置为 ProductName 数据源列(这将在标题行中将其设置为 "Product Name"),然后删除左侧的原始 Product Name 列。
...
<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>
...

3

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

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

最安全的方法是在设计师中使用添加新列和删除旧列的方法,如上所述。这样可以让您远离RDL,减少损坏报告的机会。


2
我今天遇到了一个情况,我试图通过拖动表格的列标题来重新排序列,但是它不起作用!然而,我发现可以拖动一个单元格(小心地)将其放在另一个单元格上,然后单元格会交换。这样,您可以通过交换标题和内容单元格来重新排列列,而无需创建新的空列。如果您不希望报告正文宽度增加并在PDF渲染中产生空白页面,则这种方法更好,当然可以再次修复。要拖动单元格,请单击单元格但不进入编辑模式,然后将鼠标悬停在边框周围,一旦获得“移动”光标就可以拖动。这适用于Visual Studio 2017提供的报告设计器。最初的回答。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接