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


69

我有一个SSRS报告,其中一个Tablix包含20多个列。我们的用户认为数据尚可,但是他们希望将列移动(叹气!)。

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

有没有更简单的方法可以做到这一点?请注意,我不需要编程解决方案,而只需要在设计模式下对其进行一次更改。


1
@ensisNoctis甚至没有关闭。在PaulStock,我只需要删除所有列并读取它们,因为我的列已分组。
Michael Plautz

Answers:


111

有一种在设计器中移动列的方法:

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

3
用行试了一下;像魅力一样运作。很多很多安全/容易不是编辑XML ...
ARich

3
这应该是最佳答案。比弄乱XML更安全。
2015年4

1
我同意这似乎更安全,尤其是如果您像我这样对SSRS较新并且只想快速解决问题。
Jhayes2118

1
不适用于某些公式表达式,例如,当我尝试运行此值时,RunningValue神秘地丢失了它的第三个参数,因此必须手动还原该参数。我想基于XML的方法将保留这一点。
underscore_d

1
为了充分利用此方法,我发现有必要在选择列文本框(如果需要,还可以选择标题)的同时按住SHIFT键,并且右键单击以选择CUT。如果我正确地完成所有这些工作,我会感到满意-但是所有这些细节都是非常微妙的,不直观的。感谢您的回答。
David Tansey

30

如果您可以阅读XML(只需了解标记在何处开始和/或结束等),就可以轻松完成任务。您可以执行以下一系列步骤:

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

17
+1是Microsoft投入很少资金为开发人员提供合理的报告工具的另一个示例。
Yuck

5
仅供参考阅读此响应的任何人,您必须将包含列名的元素与包含详细信息的元素分开移动。列的宽度也与您应用的任何可见性规则一样被单独存储...这种方法看起来不再那么容易了……
Mike

2
如果您发现此XML方法有点太复杂,那么Scott Blasingame的Asnwer会更简单!
T. Sar

5

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

例如,要重新排列列在下面的例子中,使“产品ID”列来之前的“产品名称”一栏,你会选择从先切“产品名称”电池元件周围的整个部分(一切<TablixCell>的第一</TablixCell>),然后将其粘贴</TablixCell>该“产品ID”列。
请注意,<TablixCell>在Tablix中定义的每一行都有一整套元素;每个都在一个单独的<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,从而减少了损坏报告的机会。


1

我今天遇到这种情况,当时我试图通过拖动Tablix的列标题来对列进行重新排序,但这是行不通的!但是,我发现可以拖动一个单元格并将其(小心地)放到另一个单元格上,然后进行单元格交换。这样,您可以通过交换标题和内容单元格来重新排列列,而不必创建新的空列,如果您不希望报表主体宽度增加并在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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.