WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

ASP.NET MVC5 で boorstrap datepicker 利用

by WebSurfer 16. August 2021 14:14

ASP.NET MVC5 で Controller で Model に日付を設定して View に渡し、Html ヘルパーの EditorFor を使ってテキストボックスにその日付を表示したとします。そのテキストボックスに bootstrap datepicker を適用してカレンダーを表示した際、 Model に設定した日付がカレンダー上で選択されるようにする方法を備忘録として残しておきます。

boorstrap datepicker

自動的に Model に設定した日付が bootstrap datepicker のカレンダー上で選択されて表示されると思っていたのですが、何もしないと Model に設定した日付ではなくてブラウザに表示された当日の日付になってしまいます。

その原因は、例えば new DateTime(2021, 3, 23) としてそれを Model に設定すると、テキストボックスに 2021/03/23 0:00:00 と表示される(当該 html input 要素で value="2021/06/24 0:00:00" となる)、即ち 0:00:00 部分が bootstrap datepicker にとって余計で日付を判定できなかったからのようです。

なので、当該 html input 要素で value="2021/03/23" となるようにすれば bootstrap datepicker のカレンダー上もその日付を選択して表示するようになります。

そのためには、Model の当該プロパティに DisplayFormatAttribute 属性を付与してやります。具体的には以下のようにします。その結果が上の画像です。

(1) Model

using System;
using System.ComponentModel.DataAnnotations;

namespace Mvc5App.Models
{
    public class BootstrapDatepickerModel
    {
        [Display(Name = "日付")]
        [DisplayFormat(ApplyFormatInEditMode = true,
            DataFormatString = "{0:yyyy/MM/dd}")]
        public DateTime? OrderDate { get; set; }
    }
}

Visual Studio 2019 のテンプレートを使って ASP.NET Web アプリを作ると、Bootstrap.js と Bootstrap.css が自動的にプロジェクトに含まれますが、bootstrap datepicker はその中には含まれていませんので注意してください。別途 github のサイト uxsolutions/bootstrap-datepicker 等からダウンロードする必要があります。この記事では、この記事を書いている時点での最新版 1.9.0 を使用しました。

また、bootstrap datepicker は Boorstrap 本体のバージョン 4 以上には対応してないそうです (Bootstrap 4 および 5 でも使う方法があるそうですが未検証・未確認です)。この記事で使った Bootstrap のバージョンは、MVC5 アプリのテンプレートに含まれている v3.4.1 です。

ご参考に View と Controller / Action Metod のコードも以下に載せておきます。

(2) View

@section Scripts { ... } 内の link 要素、script 要素による外部 bootstrap datepicker の js ファイル、css ファイルへの参照に注目してください。

@model Mvc5App.Models.BootstrapDatepickerModel

@{
    ViewBag.Title = "Datepicker";
}

<h2>Datepicker</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>BootstrapDatepickerModel</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.OrderDate,
                htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.OrderDate,
                    new { htmlAttributes = new { @class = "form-control datepicker" } })
                @Html.ValidationMessageFor(model => model.OrderDate, "",
                    new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    <link href="~/Content/bootstrap-datepicker.css" rel="stylesheet" />
    <script src="~/Scripts/bootstrap-datepicker.js"></script>
    <script src="~/Scripts/locales/bootstrap-datepicker.ja.min.js"></script>

    @Scripts.Render("~/bundles/jqueryval")

    <script type="text/javascript">
        //<![CDATA[
        $(function () {
            $('.datepicker').datepicker({
                format: 'yyyy/mm/dd',
                language: 'ja'
            });
        })
        //]]>
    </script>
}

(3) Controller / Action Method

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Mvc5App.DAL;
using System.Data.Entity;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using Mvc5App.Models;
using System;

namespace Mvc5App.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Datepicker()
        {
            var model = new BootstrapDatepickerModel();
            model.OrderDate = new DateTime(2021, 3, 23);

            // model.OrderDate が null なら今日の日付を表示する
            if (model.OrderDate == null)
            {
                model.OrderDate = DateTime.Now;
            }

            return View(model);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Datepicker(BootstrapDatepickerModel model)
        {
            if (ModelState.IsValid)
            {
                return RedirectToAction("Index");

            }

            return View(model);
        }
    }
}

Tags: , , ,

MVC

GridView と jQuery UI DatePicker

by WebSurfer 26. October 2014 15:47

ASP.NET の GridView で jQuery UI の Datepicker を使用する例の紹介です。

GridView に jQuery UI の Datepicker

GridView と組み合わせるなら Ajax Control Toolkit の CalendarExtender を使う方が簡単で、かつ 日付表示の日本語対応 もやりやすいと思いますが、外観の統一を図るなどの理由で jQuery UI の Datapicker を使用したい場合があると思います。

その場合、どのように GridView 内で Datapicker を表示する TextBox を探して、その jQuery オブジェクトを取得し、それに datepicker() メソッドを適用するかを書きます。

通常、GridView では編集モードになった行に TextBox を表示してユーザー入力を受け付けるようにします。その時に datepicker() メソッドを当該 TextBox に適用すればいいわけです。

編集モードになった行は GridView.EditIndex プロパティで判定できますが、Page.Load のタイミングでは早すぎて判定できません。

GridView.RowEditing のタイミングでは、ユーザー入力用の TextBox が生成されていないので FindControl では取得できず、このタイミングでもダメです。

GridView.RowCreated のタイミングでは、最終的な TextBox.ClientID が取得できないので、これまたダメです。(このタイミングでは TextBox.ClientID にはまだ名前つきコンテナの id が付与されておらず TextBox1_x のようになります。最終的な TextBox.ClientID が取得できないと、クライアントスクリプトでその jQuery オブジェクトを取得できません)

GridView.PreLender のタイミングなら、編集モードになっているか否かおよび編集モードになっている行のインデックスは正しく判定できます。最終的な TextBox.ClientID も取得できます。

GridView の[編集]ボタンクリックでポストバックされた際に、GridView.PreLender イベントのハンドラで編集モードになっている行の中の Datapicker を適用する TextBox を探し、その ClientID を取得し、RegisterClientScriptBlock メソッド を使って当該 TextBox に datepicker() メソッドを適用するスクリプトを登録してやることで Datapicker を表示することができます。

以下のコードの GridView1_PreRender イベントハンドラののコードような感じです。GridView で[編集]ボタンをクリックして表示される TextBox にフォーカスを当てると、上の画像のように Datapicker が表示されます。

<%@ 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">    
  protected void GridView1_PreRender(object sender, EventArgs e)
  {
    int index = GridView1.EditIndex;

    if (index >= 0)
    {
      TextBox tb = 
        (TextBox)GridView1.Rows[index].FindControl("TextBox1");

      if (tb != null)
      {
        ClientScriptManager cs = Page.ClientScript;
        Type cstype = this.GetType();
        string csname = "jQueryUiDatepicker";

        if (!cs.IsClientScriptBlockRegistered(cstype, csname))
        {
          string cstext = "$(function () { $('#" 
              + tb.ClientID + "').datepicker(); });";

          cs.RegisterClientScriptBlock(
              cstype, 
              csname, 
              cstext, 
              true);
        }
      }
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>jQuery UI Datepicker in GridView</title>
  <script src="Scripts/jquery.js" type="text/javascript">
  </script>
  <script src="Scripts/jquery-ui.js" type="text/javascript">
  </script>
  <link href="css/jquery-ui.css" rel="stylesheet" type="text/css" />
</head>
<body>
  <form id="form1" runat="server">
  <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:Northwind %>" 
    SelectCommand="SELECT TOP 10 
      [OrderID], [CustomerID], [ShippedDate] 
      FROM [Orders]" 
    UpdateCommand="UPDATE [Orders] 
      SET [CustomerID] = @CustomerID, 
          [ShippedDate] = @ShippedDate 
      WHERE [OrderID] = @OrderID">
    <UpdateParameters>
      <asp:Parameter Name="CustomerID" Type="String" />
      <asp:Parameter Name="ShippedDate" Type="DateTime" />
      <asp:Parameter Name="OrderID" Type="Int32" />
    </UpdateParameters>
  </asp:SqlDataSource>

  <asp:GridView ID="GridView1" 
    runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="OrderID" 
    DataSourceID="SqlDataSource1" 
    OnPreRender="GridView1_PreRender">
    <Columns>
      <asp:CommandField ShowEditButton="True" />
      <asp:BoundField DataField="OrderID" 
        HeaderText="OrderID" 
        InsertVisible="False" 
        ReadOnly="True" 
        SortExpression="OrderID" />
      <asp:BoundField DataField="CustomerID" 
        HeaderText="CustomerID" 
        SortExpression="CustomerID" />
      <asp:TemplateField HeaderText="ShippedDate" 
        SortExpression="ShippedDate">
        <EditItemTemplate>
          <asp:TextBox ID="TextBox1" 
            runat="server" 
            Text='<%# Bind("ShippedDate") %>'>
          </asp:TextBox>
        </EditItemTemplate>
        <ItemTemplate>
          <asp:Label ID="Label1" 
            runat="server" 
            Text='<%# Bind("ShippedDate") %>'>
          </asp:Label>
        </ItemTemplate>
      </asp:TemplateField>
    </Columns>
  </asp:GridView>
  </form>
</body>
</html>

サーバー側のコードで TextBox.ClientID を取得してピンポイントなスクリプトを設定しなくても、クライアント側で jQuery のセレクタで当該 TextBox を探して、それに datepicker() 適用する方が簡単ですが、ClientID の命名規則が変わるとうまくいかなくなる可能性があります。

そのリスクが許容できるのであれば、GridView1_PreRender イベントハンドラに代えて、以下のようなクライアントスクリプトを使っても同等なことが可能です。

<script type="text/javascript">
//<![CDATA[
  $(function () { 
    $('#<%=GridView1.ClientID%> input:text[id*=TextBox1]')
      .datepicker(); 
  });
//]]>
</script>

Tags: , ,

JavaScript

About this blog

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

Calendar

<<  November 2024  >>
MoTuWeThFrSaSu
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

View posts in large calendar