ノンコーディングでの挿入をサポートする GridView
こんな感じで、ノンコーディングにできるように、GridView クラスを拡張してみた。TemplateField を使う場合、挿入行には EditItemTemplate が適用される。InsertItemTemplate を用意してやればそっちが適用される (デザイナでは表示されないけど)。
<my:InsertableGridView ID="InsertableGridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="PrimaryKey" DataSourceID="SqlDataSource1"> <Columns> <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" ShowInsertButton="True" /> <asp:BoundField DataField="PrimaryKey" HeaderText="PrimaryKey" InsertVisible="False" ReadOnly="True" SortExpression="PrimaryKey" /> <asp:BoundField DataField="Column1" HeaderText="Column1" SortExpression="Column1" /> </Columns> </my:InsertableGridView> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConflictDetection="CompareAllValues" OldValuesParameterFormatString="original_{0}" ConnectionString="<%$ ConnectionStrings:SampleDBConnectionString %>" DeleteCommand="DELETE FROM [Table1] WHERE [PrimaryKey] = @original_PrimaryKey AND (([Column1] = @original_Column1) OR ([Column1] IS NULL AND @original_Column1 IS NULL))" InsertCommand="INSERT INTO [Table1] ([Column1]) VALUES (@Column1)" SelectCommand="SELECT * FROM [Table1]" UpdateCommand="UPDATE [Table1] SET [Column1] = @Column1 WHERE [PrimaryKey] = @original_PrimaryKey AND (([Column1] = @original_Column1) OR ([Column1] IS NULL AND @original_Column1 IS NULL))"> <DeleteParameters> <asp:Parameter Name="original_PrimaryKey" Type="Int64" /> <asp:Parameter Name="original_Column1" Type="String" /> </DeleteParameters> <UpdateParameters> <asp:Parameter Name="Column1" Type="String" /> <asp:Parameter Name="original_PrimaryKey" Type="Int64" /> <asp:Parameter Name="original_Column1" Type="String" /> </UpdateParameters> <InsertParameters> <asp:Parameter Name="Column1" Type="String" /> </InsertParameters> </asp:SqlDataSource>
拡張したクラスがこれ。
using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Reflection; using System.Resources; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace MoroMoro.Web.UI.WebControls { [ToolboxData("<{0}:InsertableGridView runat=\"server\"><Columns><asp:CommandField ShowInsertButton=\"true\" ShowDeleteButton=\"True\" ShowEditButton=\"True\"/></columns></{0}:InsertableGridView>")] public class InsertableGridView : GridView { #region Static Members private static readonly object EventRowInserting = new object(); private static readonly object EventRowInserted = new object(); private static string GetResourceStringBySystemDotWeb(string name) { ResourceManager resourceManager = new ResourceManager("System.Web", typeof(GridView).Assembly); return resourceManager.GetString(name); } #endregion #region Fields private IOrderedDictionary _insertValues; private GridViewRow _insertRow; private string _modelValidationGroup; #endregion #region Properties [DefaultValue(true)] public bool ShowInsertRow { get { object showInsertRow = this.ViewState["ShowInsertRow"]; if (showInsertRow != null) { return (bool)showInsertRow; } return true; } set { bool showInsertRow = this.ShowInsertRow; if (value != showInsertRow) { this.ViewState["ShowInsertRow"] = value; if (base.Initialized) { base.RequiresDataBinding = true; } } } } private bool IsInsertMode { get { return ((bool?)this.ViewState["IsInsertMode"]).GetValueOrDefault(false); } set { this.ViewState["IsInsertMode"] = value; } } #endregion #region Events public event GridViewInsertEventHandler RowInserting { add { base.Events.AddHandler(EventRowInserting, value); } remove { base.Events.RemoveHandler(EventRowInserting, value); } } public event GridViewInsertedEventHandler RowInserted { add { base.Events.AddHandler(EventRowInserted, value); } remove { base.Events.RemoveHandler(EventRowInserted, value); } } #endregion #region Constructors public InsertableGridView() : base() { this._insertValues = null; this._insertRow = null; this._modelValidationGroup = null; } #endregion #region Methods protected override void OnRowCommand(GridViewCommandEventArgs e) { base.OnRowCommand(e); if (string.Equals(e.CommandName, "new", StringComparison.OrdinalIgnoreCase)) { this.IsInsertMode = true; this.EditIndex = -1; this.RequiresDataBinding = true; } else if (string.Equals(e.CommandName, "insert", StringComparison.OrdinalIgnoreCase)) { bool causesValidation = false; string validationGroup = string.Empty; IButtonControl commandSource = e.CommandSource as IButtonControl; if (commandSource != null) { causesValidation = commandSource.CausesValidation; validationGroup = commandSource.ValidationGroup; } this._modelValidationGroup = null; if (causesValidation) { this.Page.Validate(validationGroup); if (this.EnableModelValidation) { this._modelValidationGroup = validationGroup; } } this.HandleInsert(causesValidation); } else { this.IsInsertMode = false; } } private void HandleInsert(bool causesValidation) { if ((!causesValidation || (this.Page == null)) || this.Page.IsValid) { DataSourceView data = null; if (this.IsBoundUsingDataSourceID) { data = this.GetData(); if (data == null) { string dataSourceReturnedNullViewMessageFormat = InsertableGridView.GetResourceStringBySystemDotWeb("GridView_DataSourceReturnedNullView"); throw new HttpException(string.Format(dataSourceReturnedNullViewMessageFormat, this.ID)); } } GridViewInsertEventArgs e = new GridViewInsertEventArgs(); if (this.IsBoundUsingDataSourceID) { base.ExtractRowValues(e.Values, this._insertRow, true, true); } this.OnRowInserting(e); if (!e.Cancel && this.IsBoundUsingDataSourceID) { this._insertValues = e.Values; data.Insert(this._insertValues, new DataSourceViewOperationCallback(this.HandleInsertCallback)); } } } private bool HandleInsertCallback(int affectedRows, Exception ex) { GridViewInsertedEventArgs e = new GridViewInsertedEventArgs(affectedRows, ex); e.SetValues(this._insertValues); this.OnRowInserted(e); this._insertValues = null; if ((ex != null) && !e.ExceptionHandled) { if (this.PageIsValidAfterModelException()) { return false; } e.KeepInInsertMode = true; } if (!e.KeepInInsertMode) { this.IsInsertMode = false; base.RequiresDataBinding = true; } return true; } protected virtual void OnRowInserting(GridViewInsertEventArgs e) { GridViewInsertEventHandler handler = (GridViewInsertEventHandler)base.Events[EventRowInserting]; if (handler != null) { handler(this, e); } else if (!base.IsBoundUsingDataSourceID && !e.Cancel) { string unhandledEventMessageFormat = InsertableGridView.GetResourceStringBySystemDotWeb("GridView_UnhandledEvent"); throw new HttpException(string.Format(unhandledEventMessageFormat, this.ID, "RowInserting")); } } protected virtual void OnRowInserted(GridViewInsertedEventArgs e) { GridViewInsertedEventHandler handler = (GridViewInsertedEventHandler)base.Events[EventRowInserted]; if (handler != null) { handler(this, e); } } private bool PageIsValidAfterModelException() { if (this._modelValidationGroup == null) { return true; } this.Page.Validate(this._modelValidationGroup); return this.Page.IsValid; } protected override int CreateChildControls(IEnumerable dataSource, bool dataBinding) { if (!this.ShowInsertRow) { return base.CreateChildControls(dataSource, dataBinding); } this.ValidateSettings(); CommandFieldsManager commandFieldsManager = new CommandFieldsManager(CommandFieldsManager.ExtractCommandFields(this.Columns)); commandFieldsManager.DisableInsertButton(); int count = base.CreateChildControls(dataSource, dataBinding); if (count == 0) { this.BuildEmptyTable(dataBinding); } commandFieldsManager.EnableInsertButton(); this.BuildInsertRow(dataBinding); commandFieldsManager.Reset(); return count; } private void BuildEmptyTable(bool dataBinding) { DataControlField[] fields = new DataControlField[this.Columns.Count]; this.Columns.CopyTo(fields, 0); Table tableControl = new Table(); if (this.ShowHeader) { GridViewRow headerRow = this.CreateRow(-1, -1, DataControlRowType.Header, DataControlRowState.Normal); this.InitializeRow(headerRow, fields); GridViewRowEventArgs headerRowArgs = new GridViewRowEventArgs(headerRow); this.OnRowCreated(headerRowArgs); tableControl.Rows.Add(headerRow); if (dataBinding) { this.OnRowDataBound(headerRowArgs); } } if (this.ShowFooter) { GridViewRow footerRow = this.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal); this.InitializeRow(footerRow, fields); GridViewRowEventArgs footerRowArgs = new GridViewRowEventArgs(footerRow); this.OnRowCreated(footerRowArgs); tableControl.Rows.Add(footerRow); if (dataBinding) { this.OnRowDataBound(footerRowArgs); } } this.Controls.Clear(); this.Controls.Add(tableControl); } private void BuildInsertRow(bool dataBinding) { DataControlField[] fields = new DataControlField[this.Columns.Count]; this.Columns.CopyTo(fields, 0); Table childTable = (Table)this.Controls[0]; this._insertRow = this.CreateRow(-1, -1, DataControlRowType.DataRow, (this.IsInsertMode ? DataControlRowState.Insert : DataControlRowState.Normal)); this.InitializeRow(this._insertRow, fields); GridViewRowEventArgs insertRowArgs = new GridViewRowEventArgs(this._insertRow); this.OnRowCreated(insertRowArgs); int insertRowIndex = this.GetInsertIndexOfInsertRow(childTable); childTable.Rows.AddAt(insertRowIndex, this._insertRow); if (dataBinding) { this.OnRowDataBound(insertRowArgs); } } protected override void InitializeRow(GridViewRow row, DataControlField[] fields) { base.InitializeRow(row, fields); if (row.RowState != DataControlRowState.Insert) { return; } for (int i = 0; i < fields.Length; i++) { DataControlField field = fields[i]; if (!field.InsertVisible) { row.Cells[i].Controls.Clear(); } } } private int GetInsertIndexOfInsertRow(Table childTable) { bool visibleTopPager = (this.PagerSettings.Position == PagerPosition.Top) || (this.PagerSettings.Position == PagerPosition.TopAndBottom); bool scanedTopPager = false; int i = 0; while (i < childTable.Rows.Count) { GridViewRow row = (GridViewRow)childTable.Rows[i]; bool isHeader = (row.RowType == DataControlRowType.Header); bool isDataRow = (row.RowType == DataControlRowType.DataRow); bool isSeparator = (row.RowType == DataControlRowType.Separator); bool isTopPager = (row.RowType == DataControlRowType.Pager) && visibleTopPager && !scanedTopPager; scanedTopPager = isTopPager ? true : scanedTopPager; if (!isHeader && !isDataRow && !isSeparator && !isTopPager) { break; } i++; } return i; } private void ValidateSettings() { if (!this.ShowInsertRow) { return; } const string messageFormat = "ShowInsertRow プロパティが true の場合、{0} プロパティを true に設定することはできません。"; if (this.AutoGenerateSelectButton) { throw new NotSupportedException(string.Format(messageFormat, "AutoGenerateSelectButton")); } if (this.AutoGenerateEditButton) { throw new NotSupportedException(string.Format(messageFormat, "AutoGenerateEditButton")); } if (this.AutoGenerateDeleteButton) { throw new NotSupportedException(string.Format(messageFormat, "AutoGenerateDeleteButton")); } } #endregion #region Inner Types private sealed class CommandFieldsManager { #region Static Methods public static List<CommandField> ExtractCommandFields(DataControlFieldCollection columns) { List<CommandField> commandFields = new List<CommandField>(); foreach (DataControlField column in columns) { CommandField commandField = column as CommandField; if (commandField != null) { commandFields.Add(commandField); } } return commandFields; } #endregion #region Fields private readonly IList<CommandFieldManager> _targets; #endregion #region Constructors public CommandFieldsManager(IEnumerable<CommandField> fields) { this._targets = new List<CommandFieldManager>(); foreach (CommandField field in fields) { this._targets.Add(new CommandFieldManager(field)); } } #endregion #region Methods public void DisableInsertButton() { foreach (CommandFieldManager target in this._targets) { target.DisableInsertButton(); } } public void EnableInsertButton() { foreach (CommandFieldManager target in this._targets) { target.EnableInsertButton(); } } public void Reset() { foreach (CommandFieldManager target in this._targets) { target.Reset(); } } #endregion } private sealed class CommandFieldManager { #region Fields private readonly CommandField _target; private readonly bool _originalShowInsertButton; private readonly bool _originalShowCancelButton; private readonly bool _originalShowSelectButton; private readonly bool _originalShowEditButton; private readonly bool _originalShowDeleteButton; #endregion #region Constructors public CommandFieldManager(CommandField target) { this._target = target; this._originalShowInsertButton = target.ShowInsertButton; this._originalShowCancelButton = target.ShowCancelButton; this._originalShowSelectButton = target.ShowSelectButton; this._originalShowEditButton = target.ShowEditButton; this._originalShowDeleteButton = target.ShowDeleteButton; } #endregion #region Methods public void DisableInsertButton() { StateBag viewState = GetViewState(); viewState["ShowInsertButton"] = false; viewState["ShowCancelButton"] = false; viewState["ShowSelectButton"] = this._originalShowSelectButton; viewState["ShowEditButton"] = this._originalShowEditButton; viewState["ShowDeleteButton"] = this._originalShowDeleteButton; } public void EnableInsertButton() { StateBag viewState = GetViewState(); viewState["ShowInsertButton"] = this._originalShowInsertButton; viewState["ShowCancelButton"] = this._originalShowCancelButton; viewState["ShowSelectButton"] = false; viewState["ShowEditButton"] = false; viewState["ShowDeleteButton"] = false; } public void Reset() { StateBag viewState = GetViewState(); viewState["ShowInsertButton"] = this._originalShowInsertButton; viewState["ShowCancelButton"] = this._originalShowCancelButton; viewState["ShowSelectButton"] = this._originalShowSelectButton; viewState["ShowEditButton"] = this._originalShowEditButton; viewState["ShowDeleteButton"] = this._originalShowDeleteButton; } private StateBag GetViewState() { PropertyInfo viewStateProperty = typeof(CommandField).GetProperty("ViewState", BindingFlags.NonPublic | BindingFlags.Instance); StateBag viewState = (StateBag)viewStateProperty.GetValue(this._target, null); return viewState; } #endregion } #endregion } public class GridViewInsertEventArgs : CancelEventArgs { private readonly OrderedDictionary _values; public IOrderedDictionary Values { get { return this._values; } } public GridViewInsertEventArgs() { this._values = new OrderedDictionary(); } } public class GridViewInsertedEventArgs : EventArgs { private int _affectedRows; private Exception _exception; private bool _exceptionHandled; private IOrderedDictionary _values; private bool _keepInInsertMode; public int AffectedRows { get { return this._affectedRows; } } public Exception Exception { get { return this._exception; } } public bool ExceptionHandled { get { return this._exceptionHandled; } set { this._exceptionHandled = value; } } public IOrderedDictionary Values { get { return this._values; } } public bool KeepInInsertMode { get { return this._keepInInsertMode; } set { this._keepInInsertMode = value; } } public GridViewInsertedEventArgs(int affectedRows, Exception e) { this._affectedRows = affectedRows; this._exception = e; this._exceptionHandled = false; this._values = new OrderedDictionary(); this._keepInInsertMode = false; } internal void SetValues(IOrderedDictionary values) { this._values = values; } } public delegate void GridViewInsertEventHandler(object sender, GridViewInsertEventArgs e); public delegate void GridViewInsertedEventHandler(object sender, GridViewInsertedEventArgs e); }
それにしても、なんで GridView は挿入をサポートしてないんだろ。