Я думаю, что ответ - нет, в VS Crystal Reports нет. Похоже, что есть API для других версий, например это
В качестве альтернативы я изменил использование большого количества кода в формуле отчета вместо использования пользовательских функций. Затем я могу обновить формулу отчета, используя ReportDocument.DataDefinition.FormulaFields..Text
В моем случае я хочу обновить только одну формулу в каждом отчете с именем «Период». Я создал файл PeriodFormula.txt и включил его в проект с помощью Build Action = EmbeddedResource.
Я создал этот класс для чтения текстового файла и обновления всех отчетов в данном каталоге. В настоящее время он жестко задан для обновления только формулы периода, но его можно легко изменить, чтобы он работал из списка и т. Д.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
namespace RMReports
{
public class CustomFunctionUpdater
{
/// <summary>
/// Update all rpt files in the given directory and all subdirectories.
/// Currently only updates the Period formula.
/// </summary>
/// <param name="directoryPath"></param>
public static void UpdateAllReports(String directoryPath)
{
Debug.WriteLine(string.Format("Starting update on all reports within {0}", directoryPath));
const string formulaName = "Period";
int reportsUpdated = 0;
string formulaText = GetFormulaText(formulaName);
foreach (String filename in Directory.GetFiles(directoryPath, "*.rpt", SearchOption.AllDirectories))
{
try
{
if (UpdateReportFunction(filename, formulaName, formulaText))
{
reportsUpdated++;
Debug.WriteLine(string.Format("Updated: {0}", filename));
}
else
Debug.WriteLine(string.Format("No update to: {0}", filename));
}
catch(Exception ex)
{
Debug.WriteLine(string.Format("Failed to update: {0}. Error: {1}", filename, ex.Message));
}
}
Debug.WriteLine(string.Format("done. {0} reports updated", reportsUpdated));
}
/// <summary>
/// Opens the given report file, updates the specified formula with the given text
/// and saves the report.
/// </summary>
/// <param name="reportFilename">The report file to update</param>
/// <param name="formulaName">The name of the formula to update</param>
/// <param name="formulaText">The new text of the formula to update</param>
/// <returns>Whether the report was updated. If the formula doesn't exist this will be false.</returns>
public static bool UpdateReportFunction(String reportFilename, String formulaName, string formulaText)
{
if (String.IsNullOrEmpty(formulaText)) return false;
if (!File.Exists(reportFilename)) throw new FileNotFoundException("reportFilename", reportFilename);
bool updated = false;
ReportDocument document = new ReportDocument();
try
{
document.Load(reportFilename, OpenReportMethod.OpenReportByDefault);
foreach (FormulaFieldDefinition f in document.DataDefinition.FormulaFields)
{
if (f.Name != formulaName) continue;
if (f.Text == formulaText) break; // no update needed
f.Text = formulaText;
updated = true;
break;
}
if (updated)
document.SaveAs(reportFilename);
}
finally
{
if (document.IsLoaded)
document.Close();
}
return updated;
}
public static void UpdateReportFunction(String reportFilename, String formulaName)
{
string formulaText = GetFormulaText(formulaName);
UpdateReportFunction(reportFilename, formulaName, formulaText);
}
/// <summary>
/// Reads the text for the given formula from the current assembly. Assumes the formula
/// exists in a file named [formulaName]Formula.txt that's been compiled as an embedded resource
/// in the current assembly, e.g. DoStuffFormula.txt for a formula named DoStuff.
/// </summary>
/// <param name="formulaName"></param>
/// <returns></returns>
public static String GetFormulaText(String formulaName)
{
string resourceName = Assembly.GetExecutingAssembly().GetName().Name + "." + formulaName + "Formula.txt";
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (stream==null) return null;
return (new StreamReader(stream)).ReadToEnd();
}
}
}
Затем я использую это, чтобы обновить все мои отчеты (которые находятся в папках под папкой «отчеты»).
DirectoryInfo d = Directory.GetParent(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
string reportDirectory = Path.Combine(d.Parent.FullName, "reports");
CustomFunctionUpdater.UpdateAllReports(reportDirectory);
Надеюсь, кто-то найдет это полезным!