TableDataSource.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. using System;
  2. using System.Data;
  3. using System.ComponentModel;
  4. using System.Reflection;
  5. using System.Collections;
  6. using FastReport.Utils;
  7. using System.IO;
  8. using System.Drawing.Design;
  9. namespace FastReport.Data
  10. {
  11. /// <summary>
  12. /// Represents a datasource based on <b>DataTable</b> class.
  13. /// </summary>
  14. /// <example>This example shows how to add a new table to the existing connection:
  15. /// <code>
  16. /// Report report1;
  17. /// DataConnectionBase conn = report1.Dictionary.Connections.FindByName("Connection1");
  18. /// TableDataSource table = new TableDataSource();
  19. /// table.TableName = "Employees";
  20. /// table.Name = "Table1";
  21. /// conn.Tables.Add(table);
  22. /// </code>
  23. /// </example>
  24. public partial class TableDataSource : DataSourceBase
  25. {
  26. #region Fields
  27. private DataTable table;
  28. private string tableName;
  29. private string selectCommand;
  30. private CommandParameterCollection parameters;
  31. private bool storeData;
  32. private bool ignoreConnection;
  33. private string qbSchema;
  34. #endregion
  35. #region Properties
  36. /// <summary>
  37. /// Gets or sets the underlying <b>DataTable</b> object.
  38. /// </summary>
  39. [Browsable(false)]
  40. public DataTable Table
  41. {
  42. get { return table; }
  43. set { table = value; }
  44. }
  45. /// <summary>
  46. /// Gets or sets the table name.
  47. /// </summary>
  48. [Category("Data")]
  49. public string TableName
  50. {
  51. get { return tableName; }
  52. set { tableName = value; }
  53. }
  54. /// <summary>
  55. /// Gets or sets SQL "select" command.
  56. /// </summary>
  57. /// <remarks>
  58. /// If this command contains parameters, you should specify them in the <see cref="Parameters"/>
  59. /// property.
  60. /// </remarks>
  61. [Category("Data")]
  62. [Editor("FastReport.TypeEditors.SqlEditor, FastReport", typeof(UITypeEditor))]
  63. public string SelectCommand
  64. {
  65. get { return selectCommand; }
  66. set { selectCommand = value; }
  67. }
  68. /// <summary>
  69. /// Gets a collection of parameters used by "select" command.
  70. /// </summary>
  71. /// <remarks>
  72. /// You must set up this property if the SQL query that you've specified in the
  73. /// <see cref="SelectCommand"/> property contains parameters.
  74. /// <para/>You can pass a value to the SQL parameter in two ways.
  75. /// <para/>The right way is to define a report parameter. You can do this in the
  76. /// "Data" window. Once you have defined the parameter, you can use it to pass a value
  77. /// to the SQL parameter. To do this, set the SQL parameter's <b>Expression</b> property
  78. /// to the report parameter's name (so it will look like [myReportParam]).
  79. /// To pass a value to the report parameter from your application, use the
  80. /// <see cref="Report.SetParameterValue"/> method.
  81. /// <para/>The other way (unrecommended) is to find a datasource object and set its parameter from a code:
  82. /// <code>
  83. /// TableDataSource ds = report.GetDataSource("My DataSource Name") as TableDataSource;
  84. /// ds.Parameters[0].Value = 10;
  85. /// </code>
  86. /// This way is not good because you hardcode the report object's name.
  87. /// </remarks>
  88. [Category("Data")]
  89. [Editor("FastReport.TypeEditors.CommandParametersEditor, FastReport", typeof(UITypeEditor))]
  90. public CommandParameterCollection Parameters
  91. {
  92. get { return parameters; }
  93. set { parameters = value; }
  94. }
  95. /// <summary>
  96. /// Gets or sets the parent <see cref="DataConnectionBase"/> object.
  97. /// </summary>
  98. [Browsable(false)]
  99. public DataConnectionBase Connection
  100. {
  101. get { return IgnoreConnection ? null : Parent as DataConnectionBase; }
  102. set { Parent = value; }
  103. }
  104. /// <summary>
  105. /// Gets or sets a value that determines whether it is necessary to store table data in a report file.
  106. /// </summary>
  107. [Category("Data")]
  108. [DefaultValue(false)]
  109. public bool StoreData
  110. {
  111. get { return storeData; }
  112. set { storeData = value; }
  113. }
  114. /// <summary>
  115. /// Gets or sets the table data.
  116. /// </summary>
  117. /// <remarks>
  118. /// This property is for internal use only.
  119. /// </remarks>
  120. [Browsable(false)]
  121. public virtual string TableData
  122. {
  123. get
  124. {
  125. string result = "";
  126. if (Table == null && Connection != null)
  127. {
  128. Connection.CreateTable(this);
  129. Connection.FillTable(this);
  130. }
  131. if (Table != null)
  132. {
  133. using (DataSet tempDs = new DataSet())
  134. {
  135. DataTable tempTable = Table.Copy();
  136. tempDs.Tables.Add(tempTable);
  137. using (MemoryStream stream = new MemoryStream())
  138. {
  139. tempDs.WriteXml(stream, XmlWriteMode.WriteSchema);
  140. result = Convert.ToBase64String(stream.ToArray());
  141. }
  142. tempTable.Dispose();
  143. }
  144. }
  145. return result;
  146. }
  147. set
  148. {
  149. if(!string.IsNullOrEmpty(value))
  150. {
  151. using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(value)))
  152. using (DataSet tempDs = new DataSet())
  153. {
  154. tempDs.ReadXml(stream);
  155. table = tempDs.Tables[0];
  156. Reference = table;
  157. tempDs.Tables.RemoveAt(0);
  158. }
  159. }
  160. }
  161. }
  162. /// <summary>
  163. /// If set, ignores the Connection (always returns null). Needed when we replace the
  164. /// existing connection-based datasource with datatable defined in an application.
  165. /// </summary>
  166. internal bool IgnoreConnection
  167. {
  168. get { return ignoreConnection; }
  169. set { ignoreConnection = value; }
  170. }
  171. /// <summary>
  172. /// Gets or sets the query builder schema.
  173. /// </summary>
  174. /// <remarks>
  175. /// This property is for internal use only.
  176. /// </remarks>
  177. [Browsable(false)]
  178. public string QbSchema
  179. {
  180. get { return qbSchema; }
  181. set { qbSchema = value; }
  182. }
  183. #endregion
  184. #region Private Methods
  185. private Column CreateColumn(DataColumn column)
  186. {
  187. Column c = new Column();
  188. c.Name = column.ColumnName;
  189. c.Alias = column.Caption;
  190. c.DataType = column.DataType;
  191. c.SetBindableControlType(c.DataType);
  192. return c;
  193. }
  194. private void DeleteTable()
  195. {
  196. if (Connection != null)
  197. Connection.DeleteTable(this);
  198. }
  199. private void CreateColumns()
  200. {
  201. Columns.Clear();
  202. if (Table != null)
  203. {
  204. foreach (DataColumn column in Table.Columns)
  205. {
  206. Column c = CreateColumn(column);
  207. Columns.Add(c);
  208. }
  209. }
  210. }
  211. #endregion
  212. #region Protected Methods
  213. /// <inheritdoc/>
  214. protected override void Dispose(bool disposing)
  215. {
  216. if (disposing)
  217. DeleteTable();
  218. base.Dispose(disposing);
  219. }
  220. /// <inheritdoc/>
  221. protected override object GetValue(Column column)
  222. {
  223. if (column == null)
  224. return null;
  225. if (column.Tag == null)
  226. {
  227. int index = Table.Columns.IndexOf(column.Name);
  228. if (index == -1)
  229. return null;
  230. column.Tag = index;
  231. }
  232. return CurrentRow == null ? null : ((DataRow)CurrentRow)[(int)column.Tag];
  233. }
  234. #endregion
  235. #region Public Methods
  236. /// <inheritdoc/>
  237. public override void InitSchema()
  238. {
  239. if (Connection != null)
  240. {
  241. if (!StoreData)
  242. {
  243. Connection.CreateTable(this);
  244. if (Table.Columns.Count == 0)
  245. Connection.FillTableSchema(Table, SelectCommand, Parameters);
  246. }
  247. }
  248. else
  249. table = Reference as DataTable;
  250. if (Columns.Count == 0)
  251. CreateColumns();
  252. foreach (Column column in Columns)
  253. {
  254. column.Tag = null;
  255. }
  256. }
  257. /// <inheritdoc/>
  258. public override void LoadData(ArrayList rows)
  259. {
  260. if (Connection != null)
  261. {
  262. if (!StoreData)
  263. Connection.FillTable(this);
  264. }
  265. else
  266. {
  267. TryToLoadData();
  268. }
  269. if (Table == null)
  270. throw new DataTableException(Alias);
  271. // custom load data via Load event
  272. OnLoad();
  273. bool needReload = ForceLoadData || rows.Count == 0 || Parameters.Count > 0;
  274. if (needReload)
  275. {
  276. // fill rows
  277. rows.Clear();
  278. foreach (DataRow row in Table.Rows)
  279. {
  280. if (row.RowState != DataRowState.Deleted && row.RowState != DataRowState.Detached)
  281. rows.Add(row);
  282. }
  283. }
  284. }
  285. /// <summary>
  286. /// Refresh the table schema.
  287. /// </summary>
  288. public void RefreshTable()
  289. {
  290. DeleteTable();
  291. InitSchema();
  292. RefreshColumns(true);
  293. }
  294. internal void RefreshColumns(bool enableNew)
  295. {
  296. if (Table != null)
  297. {
  298. // add new columns
  299. foreach (DataColumn column in Table.Columns)
  300. {
  301. if (Columns.FindByName(column.ColumnName) == null)
  302. {
  303. Column c = CreateColumn(column);
  304. c.Enabled = enableNew;
  305. Columns.Add(c);
  306. }
  307. }
  308. // delete obsolete columns
  309. int i = 0;
  310. while (i < Columns.Count)
  311. {
  312. Column c = Columns[i];
  313. if (!c.Calculated && !Table.Columns.Contains(c.Name))
  314. c.Dispose();
  315. else
  316. i++;
  317. }
  318. }
  319. }
  320. /// <inheritdoc/>
  321. public override void Serialize(FRWriter writer)
  322. {
  323. base.Serialize(writer);
  324. if (!String.IsNullOrEmpty(TableName))
  325. writer.WriteStr("TableName", TableName);
  326. if (!String.IsNullOrEmpty(SelectCommand))
  327. writer.WriteStr("SelectCommand", SelectCommand);
  328. if (!String.IsNullOrEmpty(QbSchema))
  329. writer.WriteStr("QbSchema", QbSchema);
  330. if (StoreData)
  331. {
  332. writer.WriteBool("StoreData", true);
  333. writer.WriteStr("TableData", TableData);
  334. }
  335. }
  336. /// <inheritdoc/>
  337. public override void SetParent(Base value)
  338. {
  339. base.SetParent(value);
  340. SetFlags(Flags.CanEdit, Connection != null);
  341. }
  342. /// <inheritdoc/>
  343. public override void InitializeComponent()
  344. {
  345. base.InitializeComponent();
  346. foreach (CommandParameter par in Parameters)
  347. {
  348. par.ResetLastValue();
  349. }
  350. }
  351. #endregion
  352. #region IParent Members
  353. /// <inheritdoc/>
  354. public override bool CanContain(Base child)
  355. {
  356. return base.CanContain(child) || child is CommandParameter;
  357. }
  358. /// <inheritdoc/>
  359. public override void GetChildObjects(ObjectCollection list)
  360. {
  361. base.GetChildObjects(list);
  362. foreach (CommandParameter p in Parameters)
  363. {
  364. list.Add(p);
  365. }
  366. }
  367. /// <inheritdoc/>
  368. public override void AddChild(Base child)
  369. {
  370. if (child is CommandParameter)
  371. Parameters.Add(child as CommandParameter);
  372. else
  373. base.AddChild(child);
  374. }
  375. /// <inheritdoc/>
  376. public override void RemoveChild(Base child)
  377. {
  378. if (child is CommandParameter)
  379. Parameters.Remove(child as CommandParameter);
  380. else
  381. base.RemoveChild(child);
  382. }
  383. #endregion
  384. /// <summary>
  385. /// Initializes a new instance of the <see cref="TableDataSource"/> class with default settings.
  386. /// </summary>
  387. public TableDataSource()
  388. {
  389. TableName = "";
  390. SelectCommand = "";
  391. QbSchema = "";
  392. parameters = new CommandParameterCollection(this);
  393. }
  394. }
  395. }