WebSurfer's Home

Filter by APML

DataObjectTypeName と Delete 操作

by WebSurfer 24. May 2013 17:12

ObjectDataSource コントロールの DataObjectTypeName プロパティを利用する場合、それがバインドされるデータバウンドコントロール(GridView, ListView など)の DataKeyNames プロパティを適切に設定しないと、Delete 操作に失敗するという話です。

DataObjectTypeName を使った場合、ウィザードベースでは DataKeyNames プロパティは自動的に設定されないので注意が必要です。DataKeyNames プロパティを設定しなくても Insert, Update は成功するので、何が原因なのか見当がつかず結構ハマりました。

再びハマって時間を無駄にすることがないように、何故 Update と Insert は問題なく Delete だけがダメなのか、何故 DataKeyNames を設定すると問題が解決するのかを備忘録として書いておきます。

先の記事「SqlDataSource などのパラメータ」に書きましたように、ListView などのデータバウンドコントロールは、DataKeyNames プロパティ、子コントロール、ビューステートなどからパラメータ名と値を取得し、Keys, Values, OldValues, NewValues という IDictionary コレクションを作成します。

Update, Insert, Delete 操作を行うためにはパラメータが必要ですが、それらの値は Keys, Values, OldValues, NewValues コレクションから取得され、ObjectDataSource コントロールの UpdateMethod, InsertMethod, DeleteMethod プロパティに指定されている各メソッドの引数として渡されます。

普通、各メソッドの引数には、ObjectDataSource コントロールのパラメータコレクションから複数のパラメータを渡すことが多いですが、代わりに DataObjectTypeName に指定したオブジェクトを 1 つ生成して、それをメソッドの引数として渡すこともできます。

その具体例を以下のコードに示します。

以下のクラスファイルには、ObjectDataSource と Forms 認証用のデータベースの間に位置して Select, Update, Insert, Delete 操作を行う AggregateData クラスというビジネスロジックと、ObjectDataSource の DataObjectTypeName プロパティに設定される UserInfo クラスの定義が含まれています。

using System.Collections.Generic;
using System.ComponentModel;
using System.Web.Security;

public class UserInfo
{
    public string UserName { get; set; }
    public string Email { get; set; }
}


public class AggregateData
{
    public AggregateData()
    {

    }

    [DataObjectMethod(DataObjectMethodType.Select, true)]
    public List<UserInfo> GetAllUserData()
    {
        List<UserInfo> Data = new List<UserInfo>();
        MembershipUserCollection users = Membership.GetAllUsers();

        foreach (MembershipUser user in users)
        {
            UserInfo info = new UserInfo();

            info.UserName = user.UserName;
            info.Email = user.Email;
            Data.Add(info);
        }
        return Data;
    }

    [DataObjectMethod(DataObjectMethodType.Update, true)]
    public void UpdateUserData(UserInfo user)
    {
        // 省略
    }

    [DataObjectMethod(DataObjectMethodType.Delete, true)]
    public void DeleteUserData(UserInfo user)
    {
        // 省略
    }

    [DataObjectMethod(DataObjectMethodType.Insert, true)]
    public void InsertUserData(UserInfo user)
    {
        // 省略
    }
}

前のコード例で使用されている 2 つのクラスを使用する aspx ページを次のコード例を以下に示します。上のクラスファイルをベースにして、ほとんどのコードを Visual Studio のウィザードで自動生成できますが、ListView の DataKeyNames プロパティは自動生成されたコードには含まれないことに注意してください。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ObjectDataSource ID="ObjectDataSource1" 
            runat="server" 
            DataObjectTypeName="UserInfo" 
            DeleteMethod="DeleteUserData" 
            InsertMethod="InsertUserData" 
            SelectMethod="GetAllUserData" 
            TypeName="AggregateData" 
            UpdateMethod="UpdateUserData">
        </asp:ObjectDataSource>

        <asp:ListView ID="ListView1" 
            runat="server" 
            DataSourceID="ObjectDataSource1" 
            InsertItemPosition="LastItem" 
            DataKeyNames="UserName">
            <EditItemTemplate>
                <tr style="">
                    <td>
                        <asp:Button ID="UpdateButton" 
                            runat="server" 
                            CommandName="Update" 
                            Text="更新" />
                        <asp:Button ID="CancelButton" 
                            runat="server" 
                            CommandName="Cancel" 
                            Text="キャンセル" />
                    </td>
                    <td>
                        <asp:Label ID="UserNameLabel" 
                            runat="server" 
                            Text='<%# Bind("UserName") %>' />
                    </td>
                    <td>
                        <asp:TextBox ID="EmailTextBox" 
                            runat="server" 
                            Text='<%# Bind("Email") %>' />
                    </td>
                </tr>
            </EditItemTemplate>
            <InsertItemTemplate>
                <tr style="">
                    <td>
                        <asp:Button ID="InsertButton" 
                            runat="server" 
                            CommandName="Insert" 
                            Text="挿入" />
                        <asp:Button ID="CancelButton" 
                            runat="server" 
                            CommandName="Cancel" 
                            Text="クリア" />
                    </td>
                    <td>
                        <asp:TextBox ID="UserNameTextBox" 
                            runat="server" 
                            Text='<%# Bind("UserName") %>' />
                    </td>
                    <td>
                        <asp:TextBox ID="EmailTextBox" 
                            runat="server" 
                            Text='<%# Bind("Email") %>' />
                    </td>
                </tr>
            </InsertItemTemplate>
            <ItemTemplate>
                <tr style="">
                    <td>
                        <asp:Button ID="DeleteButton" 
                            runat="server" 
                            CommandName="Delete" 
                            Text="削除" />
                        <asp:Button ID="EditButton" 
                            runat="server" 
                            CommandName="Edit" 
                            Text="編集" />
                    </td>
                    <td>
                        <asp:Label ID="UserNameLabel" 
                            runat="server" 
                            Text='<%# Eval("UserName") %>' />
                    </td>
                    <td>
                        <asp:Label 
                            ID="EmailLabel" 
                            runat="server" 
                            Text='<%# Eval("Email") %>' />
                    </td>
                </tr>
            </ItemTemplate>
            <LayoutTemplate>
                <table runat="server">
                    <tr runat="server">
                        <td runat="server">
                            <table ID="itemPlaceholderContainer" 
                                runat="server" 
                                border="0" 
                                style="">
                                <tr runat="server" style="">
                                    <th runat="server">
                                    </th>
                                    <th runat="server">
                                        UserName</th>
                                    <th runat="server">
                                        Email</th>
                                </tr>
                                <tr ID="itemPlaceholder" 
                                    runat="server">
                                </tr>
                            </table>
                        </td>
                    </tr>
                    <tr runat="server">
                        <td runat="server" style="">
                        </td>
                    </tr>
                </table>
            </LayoutTemplate>
        </asp:ListView>
    </div>
    </form>
</body>
</html>

上記のコードのように、ObjectDataSource の DataObjectTypeName プロパティに UserInfo クラスを指定することによって、UserInfo クラスから生成されたオブジェクトが AggregateData クラスの UpdateUserData, InsertUserData, DeleteUserData メソッドの引数として渡されるようになります。

そのオブジェクトを生成する際に、ListView の Keys, Values, OldValues, NewValues コレクションからデータを取得します。Keys, Values, OldValues, NewValues コレクションのどれからデータを得るかは、Update、Insert、Delete 操作によって異なります。具体的には以下のとおりです。

  • Update: NewValues
  • Insert: Values
  • Delete: Keys

Keys コレクションには ListView の DataKeyNames プロパティに指定した項目(上のコードの例では UserName )の値が入ります。DataKeyNames プロパティを指定しないと Keys コレクションは空になります。Keys コレクションが空でも、DeleteUserData メソッドの引数 user に渡す UserInfo オブジェクト自体は生成されますが、Keys が空なのでオブジェクト中のフィールドは空になり(user.UserName は null になり)Delete 操作に失敗します。

一方、UpdateUserData, InsertUserData メソッドに渡すオブジェクトには、それぞれ NewValues, Values コレクションから値が渡されます。それらには、DataKeyNames プロパティを指定しなくても、UserName 他すべての新しい値が含まれているので、Update, Delete 操作は問題なく完了するというわけです。

なお、Delete 操作の場合、UserName の値は Label からでなく DataKeyNames から取得するので、ItemTemplate の中の Text='<%# Eval("UserName") %>' は Eval のままで(Bind にしなくても)問題ありません。

Tags: ,

ASP.NET

更新操作中の例外の処置

by WebSurfer 16. August 2011 17:25

ユーザー入力を使ってデータベースの更新を行うケースで、ユーザー入力の検証結果が NG の場合、先にデータバインドコントロール(ここでは DetailsView を例にします)にユーザーが入力したデータを維持したままユーザーに再入力を促すにはどうすればよいかという話です。

ユーザーが入力したデータを維持したままユーザーに再入力を促す

ASP.NET には、ユーザー入力を検証するために RegularExpressionValidator, CustomValidator 等の検証のためのコントロールが用意されており、通常はこれらを組み合わせて使えば簡単に要件は満たせます。

ただし、検証するタイミングが更新の直前でなければならず、かつ DB ベースから値を取得して検証し、結果 NG ならロールバックしなければならないような場合は、上記の検証コントロールを使って目的を果たすのは難しいです。

その場合は、ObjectDataSource と型付 DataSet + TableAdapter を DetailsView と組み合わせた 3 層構造とし、検証やロールバック等の処理は TableAdapter を拡張したメソッドを作成して行うのが良いと思いますが、検証結果が NG の時 DetailsView にユーザーが入力したデータを維持するには工夫が必要です。

例えば、拡張したメソッドの戻り値などで検証結果 NG を判断して、DetailsView を挿入モードに保ってユーザーに再入力を促すことはできます。しかしながら、その前に DetailsView に DataBind が起こって、せっかくユーザーが入力したデータがすべて消えてしまいます。

それを避けるためには、拡張したメソッドで検証結果 NG と判定された場合は、そこで例外をスローすることです。ただし、スローしっぱなしではサーバーエラーで終わってしまうので、スローされた例外を処置しなければなりません。

処置と言っても、拡張したメソッド内で try - catch で行うのではありません。拡張したメソッドは、検証結果 NG の場合に特定の例外をスローするだけです。

処置は ItemInserted イベントのイベントハンドラに渡された DetailsViewInsertedEventArgs オブジェクトを使用して行います。

検証結果 NG でスローされた特定の例外を捕捉した場合のみ ExceptionHandled プロパティを true に設定することで例外は処置されたと見なされます。

以下に検証に使用した aspx ページのコードをアップしておきます。ArgumentNullException と NorthwindDataException のみを捕捉し、ExceptionHandled プロパティを true に設定しています。

<%@ Page Language="C#" %>
<%@ Import Namespace="ProductsDataSetTableAdapters" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Page_Load(object sender, EventArgs e)
  {
    MessageLabel.Text = String.Empty;
  }
    
  protected void DetailsView1_ItemInserted(object sender, 
    DetailsViewInsertedEventArgs e)
  {
    if (e.Exception == null && e.AffectedRows == 1)
    {
      MessageLabel.Text = "Insert に成功";
    }
    else
    {
      if (e.Exception != null)
      {
        Exception innerEx = e.Exception.InnerException;
        if (innerEx is ArgumentNullException ||
          innerEx is ProductsTableAdapter.NorthwindDataException)
        {
          MessageLabel.Text = innerEx.Message;
          e.ExceptionHandled = true;
        }
      }
      else
      {
        MessageLabel.Text = "例外の発生なし。";
      }            
      e.KeepInInsertMode = true;
    }
  }
    
  protected void DetailsView1_ItemUpdated(object sender, 
    DetailsViewUpdatedEventArgs e)
  {
    if (e.Exception == null && e.AffectedRows == 1)
    {
      MessageLabel.Text = "Update に成功。";
    }
    else
    {
      if (e.Exception != null)
      {
        Exception innerEx = e.Exception.InnerException;
        if (innerEx is ArgumentNullException ||
          innerEx is ProductsTableAdapter.NorthwindDataException)
        {
          MessageLabel.Text = innerEx.Message;
          e.ExceptionHandled = true;
        }
      }
      else
      {
        MessageLabel.Text = "例外の発生なし。";
      }       
      e.KeepInEditMode = true;
    }
  }

  protected void DetailsView1_DataBinding(object sender, 
    EventArgs e)
  {
    // 例外が出ない場合は DataBind がかかる。
    // 結果、ユーザー入力が消える。
    MessageLabel.Text += " DataBinding イベントが発生";
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <h2>挿入、更新操作中の例外の処置</h2>
    <asp:ObjectDataSource ID="ObjectDataSource1" 
      runat="server"
      SelectMethod="GetData" 
      DeleteMethod="Delete" 
      InsertMethod="ThrowExceptionBeforeInsert" 
      UpdateMethod="ThrowExceptionBeforeUpdate"
      OldValuesParameterFormatString="original_{0}" 
      TypeName="ProductsDataSetTableAdapters.ProductsTableAdapter">
      <DeleteParameters>
        <asp:Parameter Name="Original_ProductID" Type="Int32" />
      </DeleteParameters>
      <InsertParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="SupplierID" Type="Int32" />
        <asp:Parameter Name="CategoryID" Type="Int32" />
        <asp:Parameter Name="QuantityPerUnit" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="UnitsInStock" Type="Int16" />
        <asp:Parameter Name="UnitsOnOrder" Type="Int16" />
        <asp:Parameter Name="ReorderLevel" Type="Int16" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
      </InsertParameters>
      <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="SupplierID" Type="Int32" />
        <asp:Parameter Name="CategoryID" Type="Int32" />
        <asp:Parameter Name="QuantityPerUnit" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="UnitsInStock" Type="Int16" />
        <asp:Parameter Name="UnitsOnOrder" Type="Int16" />
        <asp:Parameter Name="ReorderLevel" Type="Int16" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="Original_ProductID" Type="Int32" />
      </UpdateParameters>
    </asp:ObjectDataSource>
    <asp:DetailsView ID="DetailsView1" 
      runat="server" 
      AllowPaging="True" 
      AutoGenerateRows="False" 
      DataKeyNames="ProductID" 
      DataSourceID="ObjectDataSource1" 
      OnItemInserted="DetailsView1_ItemInserted" 
      OnItemUpdated="DetailsView1_ItemUpdated"
      OnDataBinding="DetailsView1_DataBinding">
      <Fields>
        <asp:BoundField DataField="ProductID" 
          HeaderText="ProductID" 
          InsertVisible="False" 
          ReadOnly="True" 
          SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" 
          HeaderText="ProductName" 
          SortExpression="ProductName" />
        <asp:BoundField DataField="SupplierID" 
          HeaderText="SupplierID" 
          SortExpression="SupplierID" />
        <asp:BoundField DataField="CategoryID" 
          HeaderText="CategoryID" 
          SortExpression="CategoryID" />
        <asp:BoundField DataField="QuantityPerUnit" 
          HeaderText="QuantityPerUnit" 
          SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice" 
          HeaderText="UnitPrice" 
          SortExpression="UnitPrice" />
        <asp:BoundField DataField="UnitsInStock" 
          HeaderText="UnitsInStock" 
          SortExpression="UnitsInStock" />
        <asp:BoundField DataField="UnitsOnOrder" 
          HeaderText="UnitsOnOrder" 
          SortExpression="UnitsOnOrder" />
        <asp:BoundField DataField="ReorderLevel" 
          HeaderText="ReorderLevel" 
          SortExpression="ReorderLevel" />
        <asp:CheckBoxField DataField="Discontinued" 
          HeaderText="Discontinued" 
          SortExpression="Discontinued" />
        <asp:CommandField ShowDeleteButton="True" 
          ShowEditButton="True" 
          ShowInsertButton="True" />
      </Fields>
    </asp:DetailsView>
    <asp:label id="MessageLabel"
      forecolor="Red"
      runat="server"/> 
  </div>
  </form>
</body>
</html>

TableAdapter を拡張したメソッドは以下の通りです。partial class として別ファイルにコードを書いて簡単に拡張できます。なお、以下のコードは検証用ですので、例外をスローするだけで、データーベースを更新するコードは含まれていません。

using System;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using System.ComponentModel;

namespace ProductsDataSetTableAdapters
{
  public partial class ProductsTableAdapter
  {
    public virtual int ThrowExceptionBeforeInsert(
      string ProductName, 
      Nullable<int> SupplierID, 
      Nullable<int> CategoryID, 
      string QuantityPerUnit, 
      Nullable<decimal> UnitPrice, 
      Nullable<short> UnitsInStock, 
      Nullable<short> UnitsOnOrder, 
      Nullable<short> ReorderLevel, 
      bool Discontinued)
    {
      if (ProductName == null)
      {
        throw new ArgumentNullException("ProductName");
      }
      else if (ProductName == "xxx")
      {
        throw new ApplicationException("ハンドルされない例外");
      }
      else if (ProductName == "zzz")
      {
        return 0;
      }
      else
      {
        throw new NorthwindDataException("Insert で例外。");
      }
    }

    public virtual int ThrowExceptionBeforeUpdate(
      Nullable<int> Original_ProductID,
      string ProductName,
      Nullable<int> SupplierID,
      Nullable<int> CategoryID,
      string QuantityPerUnit,
      Nullable<decimal> UnitPrice,
      Nullable<short> UnitsInStock,
      Nullable<short> UnitsOnOrder,
      Nullable<short> ReorderLevel,
      bool Discontinued)
    {
      if (ProductName == null)
      {
        throw new ArgumentNullException("ProductName");
      }
      else if (ProductName == "xxx")
      {
        throw new ApplicationException("ハンドルされない例外");
      }
      else if (ProductName == "zzz")
      {
        return 0;
      }
      else
      {
        throw new NorthwindDataException("Update で例外。");
      }
    }

    public class NorthwindDataException : Exception
    {
        public NorthwindDataException(string msg) : base(msg) { }
    }

  }
}

Tags:

Exception Handling

XML ファイルの更新操作

by WebSurfer 27. September 2010 23:14

XML ファイルをデータソースに使った場合、表示するだけなら XmlDataSource を使えば、ほぼコーディングレスで Web アプリケーションを作成できます。ただし、XmlDataSource には更新機能がないので、必要な場合は自力でコードを書いて更新機能を実装する必要があります。

MSDN ライブラリの XmlDataSource の説明には、GetXmlDocument メソッドを使って XmlDataDocument オブジェクトを取得し、それに変更を加えてから Save するという方法が紹介されていますが、GridView や ListView 上で編集して更新するにはその方法は難しそうです。

それより、XML ファイル操作用のクラス(選択、削除、挿入、更新操作を行うメソッドを実装)を作り、そのクラスを ObjectDataSource を経由 GridView や ListView にバインドして操作するのが簡単そうです。

そのサンプルは MSDN ライブラリ の「GridView で XML ファイルをデータ ソースとして使いレコードを編集する方法」に紹介されています。そのサンプルに削除、挿入機能を加えて、さらに ID も更新できるように拡張したコードを書いておきます。なお、言語はサンプルの VB.NET を C# に変更しました。

まず、XML ファイル操作用クラス(UserInfoTable クラス)の UpdateDataSet メソッドを、ID も更新できるように変更します。具体的には、引数に original_id を追加し、original_id で DataSet の行を検索し、ヒットした行の当該項目を id に書き換えるよう修正します。次に、削除と挿入操作のためのメソッドを追加します。修正、追加後の UserInfoTable クラスは以下のようになります。

using System;
using System.Data;
using System.Web;
using System.ComponentModel;

public class XmlDataSet
{
  public class UserInfoTable : IDisposable
  {
    const string strXmlFile = "~/App_Data/UserInfo.xml";
    private DataSet myDataSet;

    public UserInfoTable()
    {
      myDataSet = new DataSet();
      myDataSet.Locale = 
        System.Globalization.CultureInfo.InvariantCulture;
      string filePath = 
        HttpContext.Current.Server.MapPath(strXmlFile);
      myDataSet.ReadXml(filePath);
    }

    public virtual void Dispose(bool disposing)
    {
      if (disposing)
      {
        myDataSet.Dispose();
      }
    }

    public void Dispose()
    {
      Dispose(true);
      System.GC.SuppressFinalize(this);
    }

    ~UserInfoTable()
    {
      Dispose(false);
    }

    [DataObjectMethod(DataObjectMethodType.Select, true)]
    public DataSet GetDataSet()
    {
      return myDataSet;
    }

    [DataObjectMethod(DataObjectMethodType.Update, true)]
    public void UpdateDataSet(string id, string name, string original_id)
    {
      string strFillter = "ID='" + original_id + "'";
      DataRow[] rows = myDataSet.Tables[0].Select(strFillter);
      if (rows.Length > 0)
      {
        rows[0]["ID"] = id;
        rows[0]["NAME"] = name;
        Save();
      }
    }

    [DataObjectMethod(DataObjectMethodType.Delete, true)]
    public void DeleteItem(string original_id)
    {
      string strFillter = "ID='" + original_id + "'";
      DataRow[] rows = myDataSet.Tables[0].Select(strFillter);
      if (rows.Length > 0)
      {
        rows[0].Delete();
        Save();
      }
    }

    [DataObjectMethod(DataObjectMethodType.Insert, true)]
    public void InsertItem(string id, string name)
    {
      DataRow row = myDataSet.Tables[0].NewRow();
      row["ID"] = id;
      row["NAME"] = name;
      myDataSet.Tables[0].Rows.Add(row);
      Save();
    }

    private void Save()
    {
      string filePath = 
        HttpContext.Current.Server.MapPath(strXmlFile);
      myDataSet.WriteXml(filePath, XmlWriteMode.IgnoreSchema);
    }
  } 
}

新しい aspx ファイルを作成し、それに ObjectDataSource と ListView を配置します。UserInfoTable クラスをベースに、ウィザードで ObjectDataSource と ListView を設定していくと、以下のようなコードになるはずです。

<asp:ObjectDataSource ID="ObjectDataSource1" 
  runat="server" 
  DeleteMethod="DeleteItem" 
  InsertMethod="InsertItem" 
  SelectMethod="GetDataSet" 
  TypeName="XmlDataSet+UserInfoTable" 
  UpdateMethod="UpdateDataSet">
  <DeleteParameters>
    <asp:Parameter Name="original_id" Type="String" />
  </DeleteParameters>
  <InsertParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
  </InsertParameters>
  <UpdateParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
    <asp:Parameter Name="original_id" Type="String" />
  </UpdateParameters>
</asp:ObjectDataSource>
<asp:ListView ID="ListView1" 
  runat="server" 
  DataSourceID="ObjectDataSource1" 
  EnableModelValidation="True">
</asp:ListView>

しかしなから、このままではうまく動きません。問題点は以下のとおりです

  • ListView に DataKeyNames="id" の定義がないので、id の値が ObjectDataSource に渡されません。
  • ObjectDataSource に OldValuesParameterFormatString="original_{0}" の設定がないので、id の新旧の区別ができません。
  • ListView の中身(Template やその中の TextBox, Button など)は自動生成されません。自力でコードを書く必要があります。
  • ListView に挿入の行を表示するため、InsertItemPosition="LastItem" を追加します。

以上の点を修正したコードは以下のとおりです。

<asp:ObjectDataSource ID="ObjectDataSource1" 
  runat="server"
  OldValuesParameterFormatString="original_{0}"
  DeleteMethod="DeleteItem" 
  InsertMethod="InsertItem" 
  SelectMethod="GetDataSet" 
  TypeName="XmlDataSet+UserInfoTable" 
  UpdateMethod="UpdateDataSet">
  <DeleteParameters>
    <asp:Parameter Name="original_id" Type="String" />
  </DeleteParameters>
  <InsertParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
  </InsertParameters>
  <UpdateParameters>
    <asp:Parameter Name="id" Type="String" />
    <asp:Parameter Name="name" Type="String" />
    <asp:Parameter Name="original_id" Type="String" />
  </UpdateParameters>
</asp:ObjectDataSource>
<asp:ListView ID="ListView1" 
  runat="server"
  DataKeyNames="id"
  DataSourceID="ObjectDataSource1"
  InsertItemPosition="LastItem"
  EnableModelValidation="True">
  <ItemTemplate>
    <tr>
      <td>
        <asp:Button ID="DeleteButton" 
          runat="server" 
          CommandName="Delete" 
          Text="削除" />
        <asp:Button ID="EditButton" 
          runat="server" 
          CommandName="Edit" 
          Text="編集" />
      </td>
      <td>
        <asp:Label ID="idLabel" 
          runat="server" 
          Text='<%# Bind("id") %>' />
      </td>
      <td>
        <asp:Label ID="nameLabel" 
          runat="server" 
          Text='<%# Bind("name") %>' />
      </td>
    </tr>
  </ItemTemplate>
  <InsertItemTemplate>
    <tr>
      <td>
        <asp:Button ID="InsertButton" 
          runat="server" 
          CommandName="Insert" 
          Text="挿入" />
        <asp:Button ID="CancelButton" 
          runat="server" 
          CommandName="Cancel" 
          Text="クリア" />
      </td>
      <td>
        <asp:TextBox ID="idTextBox" 
          runat="server" 
          Text='<%# Bind("id") %>' />
      </td>
      <td>
        <asp:TextBox ID="nameTextBox" 
          runat="server" 
          Text='<%# Bind("name") %>' />
      </td>
    </tr>
  </InsertItemTemplate>
  <LayoutTemplate>
    <table id="Table2" runat="server">
      <tr id="Tr1" runat="server">
        <td id="Td1" runat="server">
          <table ID="itemPlaceholderContainer" 
            runat="server">
            <tr id="Tr2" runat="server">
              <th id="Th1" runat="server">
              </th>
              <th id="Th2" runat="server">
                id</th>
              <th id="Th3" runat="server">
                name</th>
            </tr>
            <tr ID="itemPlaceholder" runat="server">
            </tr>
          </table>
        </td>
      </tr>
    </table>
  </LayoutTemplate>
  <EditItemTemplate>
    <tr>
      <td>
        <asp:Button ID="UpdateButton" 
          runat="server" 
          CommandName="Update" 
          Text="更新" />
        <asp:Button ID="CancelButton" 
          runat="server" 
          CommandName="Cancel" 
          Text="キャンセル" />
      </td>
      <td>
        <asp:TextBox ID="idTextBox" 
          runat="server" 
          Text='<%# Bind("id") %>' />
      </td>
      <td>
        <asp:TextBox ID="nameTextBox" 
          runat="server" 
          Text='<%# Bind("name") %>' />
      </td>
    </tr>
  </EditItemTemplate>
</asp:ListView>

Tags: , ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  December 2024  >>
MoTuWeThFrSaSu
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

View posts in large calendar