[jQuery]+[ASP.NET MVC] Chart + Grid の Excel出力

以前、[ASP.NET]チャート+グリッドのExcel出力という記事を書きましたが、今回はそれの jQuery 版を書きます。弊社では Ignite UI という jQuery コントロールが提供しておりますが、今回はそれにMVCヘルパーを使ってチャートとグリッドを1ページ内に表示し、サーバ処理でそのチャート画像とグリッドの内容を Excel 出力する方法をご紹介します。

 

概要

本ポストでは、Ignite UIの基本的な使用方法やチャートとグリッドの表示方法についての詳しい説明は割愛させていただき、以下の点にフォーカスしたいと思います。

・チャート(Canvasタグ)の画像化とサーバへの送信
・グリッドの Excel 出力と画像埋め込み

基本的なコントロールの表示方法は以下のサンプルページや、Infragsiticsスタータープロジェクトをご確認下さい。
※末尾の”こちら”リンクからダウンロードできるサンプルプロジェクトもご参考に。

[オンラインサンプル]
・グリッド MVC ヘルパー:http://jp.igniteui.com/grid/aspnet-mvc-helper
・チャート MVC ヘルパー:http://jp.igniteui.com/data-chart/aspnet-mvc-helper

 

チャートとグリッドの表示

今回のサンプルでは、Ignite UI のスターターテンプレートプロジェクトを作成し、そこから各クラスをサンプルように少しカスタマイズしました。スターターテンプレートには、グリッドやチャートなど基本的なコントロールが既に View、Model、Controller の連携も含めて作成されているため、非常に簡単にMVCモデルのサンプルアプリを作成することができます。

image
スターターテンプレートプロジェクト

スターターテンプレートの View 部分(index.cshtml)を以下のように書きました。※スクリプトの読み込み部分などは割愛しています。

    <h3>Sample Application</h3>
    @(Html.Infragistics().Loader()
        .ScriptPath(Url.Content("~/Infragistics/js/"))
        .CssPath(Url.Content("~/Infragistics/css/"))
        .Render()
    )

    @(Html.Infragistics().DataChart(Model.CountSummaries.AsQueryable())
        .ID("igDataChart1")
        .Height("300px")
        .Width("500px")
        .PlotAreaBackground("White")
        
        .Legend(legend => legend.ID("Legend"))
        .Axes(a =>
        {
            a.CategoryX("NameAxis").Label(cl => cl.CountryName);

            a.NumericY("CountAxis");
                //.MinimumValue(0)
                //.MaximumValue(Model.Customers.Count());
        })
        .Series(s =>
        {
            s.Line("Line1")
                .Title("1995年")
                .XAxis("NameAxis")
                .YAxis("CountAxis")
                .ShowTooltip(true)
                .IsHighlightingEnabled(true)
                .IsTransitionInEnabled(true)
                .ValueMemberPath("Pop1995");
            s.Line("Line2")
                .Title("2005年")
                .XAxis("NameAxis")
                .YAxis("CountAxis")
                .ShowTooltip(true)
                .IsHighlightingEnabled(true)
                .IsTransitionInEnabled(true)
                .ValueMemberPath("Pop2005");
            s.Line("Line3")
                .Title("2015年")
                .XAxis("NameAxis")
                .YAxis("CountAxis")
                .ShowTooltip(true)
                .IsHighlightingEnabled(true)
                .IsTransitionInEnabled(true)
                .ValueMemberPath("Pop2015");
            s.Line("Line4")
                .Title("2025年")
                .XAxis("NameAxis")
                .YAxis("CountAxis")
                .ShowTooltip(true)
                .IsHighlightingEnabled(true)
                .IsTransitionInEnabled(true)
                .ValueMemberPath("Pop2025");
                
            //積層エリアチャートで出す場合
            //s.StackedArea("Chart1")
            //    .Title("1995")
            //    .XAxis("NameAxis")
            //    .YAxis("CountAxis")
            //    .ShowTooltip(true)
            //            .Series(ss =>
            //            {
            //                ss.Fragment("Pop1995")
            //                    .Title("1995")
            //                    .ValueMemberPath("Pop1995");
            //                ss.Fragment("Pop2005")
            //                    .Title("2005")
            //                    .ValueMemberPath("Pop2005");
            //                ss.Fragment("Pop2015")
            //                    .Title("2015")
            //                    .ValueMemberPath("Pop2015");
            //                ss.Fragment("Pop2025")
            //                    .Title("2025")
            //                    .ValueMemberPath("Pop2025");
            //            });

        })
        .DataBind()
        .Render()
    )
    <table>
        <tr>
            <td>
            @(Html.Infragistics().Grid(Model.CountSummaries.AsQueryable())
                .Width("550px")
                .DefaultColumnWidth("120px")
                .DataBind()
                .Columns(column =>
                    {
                        column.For(x => x.CountryName).HeaderText("Area").Width("150px");
                        column.For(x => x.Pop1995).HeaderText("1995年").Width("100px");
                        column.For(x => x.Pop2005).HeaderText("2005年").Width("100px");
                        column.For(x => x.Pop2015).HeaderText("2015年").Width("100px");
                        column.For(x => x.Pop2025).HeaderText("2025年").Width("100px");
                    })
                .Render()
            )
            </td>   
            <td id="Legend" style="float: left"/>
        </tr>
    </table>
    
    <input id="Button1" type="button" value="Excel出力" onclick="excelExport();" />
    <div style="display: none;" class="dl-parent"></div>

上から、チャート、グリッド、Excel 出力ボタンが並んでいます。Model や Controller 部分の記述は割愛しますが、以下の画像のグリッドに表示されているデータを生成し、それぞれにバインドしています。(※コードが必要な場合は本ポスト末尾の添付サンプルご参照ください。)
ちなみに、igDataChart の定義内の Series の定義をコメント化されている部分と差し替えると積層チャートエリアチャートとして表示させることができます。

image

 

チャートの画像化とサーバへの送信

チャートとグリッドが表示されたので、ここからエクセル出力処理に入ります。エクセル処理にはInfragistics.Excelエンジン を利用し、サーバ側でエクセルを生成しクライアント側へFileResponseとして返すことを想定します。

まず、表示されているチャートを、サーバ側で生成するエクセルに埋め込むために画像としてサーバへ送る必要があります。方法としては、

・チャートを画像化し、画像のバイナリデータをサーバへPOSTする。
・チャートを画像化し、サーバのディレクトリへ画像をアップロードしたあと、エクセル出力するよう POST する。

が考えられます。画像のアップロードは、弊社の igUpload コントロールや jQuery の Upload プラグインなどありますが、扱いが面倒そうなので前者の方法で実装します。Excel 出力ボタン押下時の JavaScript は以下の通り。

<script type="text/javascript">
    function excelExport() {
        //Canvasのイメージをエクスポート
        var pngImage = $("#igDataChart1").igDataChart("exportImage");
        var pngImageSrc = pngImage.src; //画像のバイナリデータがBase64でエンコードされたものを取得
        pngImageSrc = pngImageSrc.replace('data:image/png;base64,', ''); //サーバ側でBase64でデコードするように識別子を取り除く

        // POST処理
        var form = document.getElementById("form1");
        var imageText = document.createElement("input");
        // 画像のバイナリデータをimageTextへ設定
        imageText.setAttribute("name", "imageText");
        imageText.setAttribute("type", "hidden");
        imageText.setAttribute("value", pngImageSrc);
        form.appendChild(imageText);

        form.action = "Home/ExcelExport";
        form.method = "post";
        form.submit();
        return false;

    }
</script>

コメントの表記通りですが、チャートを画像化し、その Base64 エンコードのデータをポストデータとして Controller の ExcelExport へ POST しています。

チャートの画像化は exportImage() というメソッド(これは Infragistics 独自のもの)を使っていますが、canvas.toDataURL() といった具合で JavaScript で canvas の内容を画像化することができます。

また、POST処理に関しては、ダミーのフォーム form1 を用意しており、そこに imageText という名前のエレメントを追加し POST しています。

 

グリッドの Excel 出力と画像埋め込み

続いて、サーバ側の処理です。

        [HttpPost]
        public FileResult ExcelExport(string imageText)
        {
            // ******************************************  
            // エクセルファイルへチャート画像を埋め込む
            // ******************************************

            // クライアントより取得したチャートのイメージを取得
            byte[] bs = System.Convert.FromBase64String(imageText);
            ImageConverter imgconv = new ImageConverter();
            Image img = (Image)imgconv.ConvertFrom(bs);

            // Excel用の画像シェイプに埋め込む
            Infragistics.Documents.Excel.WorksheetImage imageShape =
                new Infragistics.Documents.Excel.WorksheetImage(img);

            //Excel出力用のワークブックとワークシートを準備  
            Infragistics.Documents.Excel.Workbook workbook = new Infragistics.Documents.Excel.Workbook(WorkbookFormat.Excel2007);
            Infragistics.Documents.Excel.Worksheet worksheet1 = workbook.Worksheets.Add("Sheet1");

            // チャート画像出力先の開始位置の指定  
            Infragistics.Documents.Excel.WorksheetCell cellB2 = worksheet1.Rows[1].Cells[1]; //チャート画像の出力先の開始位置  
            imageShape.TopLeftCornerCell = cellB2;
            imageShape.TopLeftCornerPosition = new PointF(0.0F, 0.0F);

            // チャート画像出力先の終了位置の指定  
            Infragistics.Documents.Excel.WorksheetCell cellH17 = worksheet1.Rows[16].Cells[7]; //チャート画像の出力先の終了位置  
            imageShape.BottomRightCornerCell = cellH17;
            imageShape.BottomRightCornerPosition = new PointF(100.0F, 100.0F);

            // シートに画像を追加  
            worksheet1.Shapes.Add(imageShape);


            // ******************************************  
            // エクセルファイルへグリッドの内容を書き込む
            // ******************************************

            //ヘッダ部分の書き込み
            foreach (var cell in worksheet1.GetRegion("B20:F20"))
            {
                cell.CellFormat.Fill = CellFill.CreateSolidFill(Color.Gray);
                cell.CellFormat.Font.ColorInfo = new WorkbookColorInfo(Color.White);
            }
            worksheet1.Rows[19].Cells[1].Value = "CountryName";
            worksheet1.Rows[19].Cells[2].Value = "Pop1995";
            worksheet1.Rows[19].Cells[3].Value = "Pop2005";
            worksheet1.Rows[19].Cells[4].Value = "Pop2015";
            worksheet1.Rows[19].Cells[5].Value = "Pop2025";

            //各列の幅定義
            worksheet1.Columns[0].Width = 1000;
            worksheet1.Columns[1].Width = 5000;
            worksheet1.Columns[2].Width = 3000;
            worksheet1.Columns[3].Width = 3000;
            worksheet1.Columns[4].Width = 3000;
            worksheet1.Columns[5].Width = 3000;

            //データ部分の書き込み
            int i = 20;
            foreach (CountSummary list in Lists)
            {
                worksheet1.RowsIdea.Cells[1].Value = list.CountryName;
                worksheet1.RowsIdea.Cells[2].Value = list.Pop1995;
                worksheet1.RowsIdea.Cells[3].Value = list.Pop2005;
                worksheet1.RowsIdea.Cells[4].Value = list.Pop2015;
                worksheet1.RowsIdea.Cells[5].Value = list.Pop2025;
                i++;
            }

            // ******************************************  
            // エクセル出力  
            // ******************************************  
            // メモリストリーム  
            System.IO.MemoryStream theStream = new System.IO.MemoryStream();

            // ストリームに作成したワークブックを保存  
            workbook.Save(theStream);

            // レスポンスの作成 
            byte[] byteArr = (byte[])Array.CreateInstance(typeof(byte), theStream.Length);
            theStream.Position = 0;
            theStream.Read(byteArr, 0, (int)theStream.Length);
            theStream.Close();
            
            string fileName = "ExportedFile.xlsx";
            return File(byteArr, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
            
        }

上記コードは Excel 出力ボタンが押された時の Controller 側の記述です。

まず初めに POST された画像のバイナリデータよ読み込み Base64 でデコードし画像に戻しています。(※パラメタの名前(imageText)は、ポストデータを作成したときに追加した Input エレメント の名前と一致する必要があります。)

続いて、Excelエンジンを利用して新規でワークブック/シートを作成し、作成したシートに対して位置を指定したうえでクライアントから渡されたチャート画像を貼り付けています。

オンラインサンプル Excel ワークシートの作成:
http://jp.igniteui.com/infragistics-excel/create-excel-worksheet

グリッドの内容をエクセルへ出力する部分については、全て手組みで全セルへ値設定を行っています。値の取得元については、サンプルコードでは上記コード外で定義している IEnumerable<CountSummary> 型の Lists をぐるぐる回していますが、バインドしているデータが取得できればOKです。(※クライアント側で値の変更がある場合は要注意です。)
上記のサンプルコードでは、セルの背景色の設定や列幅の変更を行っておりますが、Excel エンジンでできる事はここでもできます。

オンラインヘルプ Excel エンジン:
http://help.jp.infragistics.com/Help/Doc/ASPNET/2013.2/CLR4.0/html/Web_Infragistics_Excel_Engine.html

最後に、生成した Excel ファイルをストリームに保存して、FileResult として返却すれば Excel 出力処理の完成です。

image
出力された Excel ファイル

Excel ファイルが出力されました。

が・・・よく見ると凡例が出力されていませんね。これは、凡例部分はチャートの Canvas 部分とは別の Div タグで構成されており画像化できなかったためです。無念です。。。
現状考えられる策としては、予め凡例を含んだExcelのテンプレートファイルをサーバ側で用意しておき、Excelを新規作成ではなくファイル読み込みにすることです。
もし Div タグ内をクライアント上で画像化できれば同じプロセスでExcelファイルへ含めることができるので、アイデアある方がいらしたら是非コメント下さい!

Excel エンジンはかなり優秀で、テンプレートとなるファイルを読み込んで利用したりオートシェイプを挿入したりセルのフォーマットを変更したりとかなり柔軟にExcel操作を行うます。皆さんも是非お試しあれ!

 

[サンプルプログラム]
※今回作成したサンプルは こちら からダウンロードいただけます。

NetAdvantage トライアル版ダウンロード
NetAdvantage は無料トライアルを用意しています。是非一度お試し下さい。

NetAdvantage ダウンロード

[WindowsForms] WinGrid 活用術 3 - 外観の変更

こんにちは!WinGridに関する連載3回目の今回はグリッドの外観の設定方法についていくつか紹介していきます。前回のバリデーションの紹介 で作成したサンプルを使って外観の変更を施していきたいと思います。

プロパティ設定による外観の設定

まずはプロパティ設定による基本的な外観の変更方法を紹介します。
WinGridの外観の変更は Appearance クラスから変更できます。この Appearance クラスは様々なエレメント(例えば、行 や セル や アイコン)や様々な状態(例えば、アクティブ状態 や ホバー状態 や 選択状態)ごとに用意されており、これらを変更することで細かな外観の制御を行うことができます。

例として、以下ではエラーのセルの背景色、編集中のセルの背景色、アクティブ状態(≒選択状態)の行の背景色および文字色、マウスホバー状態の行の背景色を設定しています。

            //エラーセルの外観変更()
            Infragistics.Win.Appearance errorCell = (Infragistics.Win.Appearance)ultraGrid1.DisplayLayout.Override.DataErrorCellAppearance;
            errorCell.BackGradientStyle = Infragistics.Win.GradientStyle.VerticalBump; //グラデーション設定
            errorCell.BackColor = Color.FromArgb(255, 181, 193); //ピンク
            errorCell.BackColor2 = Color.White; //白

            //その他、行とセルのの外観変更
            ultraGrid1.DisplayLayout.Override.EditCellAppearance.BackColor = Color.Yellow;          //編集中のセルの背景色
            ultraGrid1.DisplayLayout.Override.ActiveRowAppearance.BackColor = Color.LightYellow;    //アクティブ行の行背景色
            ultraGrid1.DisplayLayout.Override.ActiveRowAppearance.ForeColor = Color.Blue;           //アクティブ行の文字色
            ultraGrid1.DisplayLayout.Override.HotTrackRowAppearance.BackColor = Color.LightSkyBlue; //ホバー時の行背景色

image

このように、各エレメントの各状態ごとにAppearanceの設定を行っていくことでデザインしていくことができます。

[参考] オンラインヘルプ - WinGridのスタイル設定:
http://help.jp.infragistics.com/Doc/Win/2013.2/CLR4.0/?page=WinGrid_Styling_WinGrid.html

 

Application Styling Framwork による外観設定

上記ではプロパティを一つずつ設定する方法を紹介しましたが、この方法では以下のようなデメリットが考えられます。

・デザインの実装に時間がかかる。
・各画面間でのデザインを一定に保てない。
・デザイン担当とロジック担当を分けられない。
・後でデザインの変更を行うのが難しい。

Application Styling Framwork はこれらを解決するためのフレームワークで、Webでいうところの CSS のようなイメージでお使いいただけるものです。
仕組みとしてはデザインの定義情報の集合体であるスタイルライブラリ(*.isl)を作成し、アプリケーション内で以下のようなコードで読み込ませるだけで、画面内のすべてのInfragisticsコントロール(および.NET標準コントロールの外観の一部)の外観を設定することができます。

        public Form1()
        {
            InitializeComponent();
            //スタイルライブラリの読み込み
            Infragistics.Win.AppStyling.StyleManager.Load("../../IG.isl");
        }

image 
スタイルライブラリ適用前

image
スタイルライブラリ適用後

スタイルライブラリ適用前後でかなりイメージが変わりましたね!
スタイルライブラリ適用後は黒を基調としたイメージとなっていますが、これは私が作ったものではなく製品をインストールすると最初から用意されている ”IG” というテンプレートを使ったものです。テンプレートは30種類近く用意されており、そのままお使いいただけますが、AppStylist という専用ツールを使って編集することができます。

テンプレートの格納場所(”全てのユーザ”に対してインストールした場合):
C:\Users\Public\Documents\Infragistics\2013.2\Windows Forms\AppStylist for Windows Forms\Styles

以下のイメージは Metro.isl と Office2007Silver.isl をそれぞれ読み込ませたイメージです。

image
Metro.isl を読み込ませた状態

image 
Office2007Silver.isl を読み込ませた状態

 

これらのイメージは読み込むスタイルライブラリを変えただけで、モジュール自体は同一のものです。
開発環境、テスト環境、本番環境、といった各環境ごとに一目で環境の違いが分かるようにしたい場合は、配信するスタイルライブラリを変えるだけで対応できますし、後から外観の変更を行う場合にも、画面モジュールを修正することなく対応できます。

[参考]オンラインヘルプ アプリケーションのスタイリング:
http://help.jp.infragistics.com/Doc/Win/2013.2/CLR4.0/?page=Win_Styling_Your_Application2.html

 

AppStylist の利用

上で紹介したスタイルライブラリの中身はXMLで構成されており、各エレメントやその状態ごとに細かくデザインが定義されています。テキストエディタでこれらを編集することも無理ではないですが、ノンコーディングでマウス操作のみで簡単にデザインを行える AppStylist というツールが用意されています。

image
AppStylist 利用イメージ

画面中央のプレビューキャンバスでデザインしたい部分にマウスカーソルを合わせると、そのエレメントに影響を与えているスタイル定義のロールの一覧がポップアップされるので、そこから編集するロールを選択し、背景色や前景色、画像などを設定していくのが基本的な使い方となります。※1つのエレメントに対して複数のロールが適用されており、より下位の階層で定義されているスタイルが勝ちます。
詳しい利用方法については、以下のヘルプページをご参照ください。

[参考]オンラインヘルプ - AppStylist の具体的な利用方法:
http://help.jp.infragistics.com/Doc/Win/2013.2/CLR4.0/?page=Win_Styling_Your_Application.html

 

最後に

稀に「なぜ Label コントロールが製品の中に入っているのか?.NET標準のLabelコントロールとどのような違いがあるのか?」などといったご質問をいただくことがあります。実際、.NET標準で用意されている Label コントロールと機能的には変わらないのですが、Infragisticsコントロールで画面UIを統一することで上記でご紹介したような仕組みをすべてのUIに反映させる事ができデザインを統一することができるのです。
開発者からは、どうしてもコントロールの機能が注目されがちですが、利用する側のユーザは外観によってアプリケーションの受ける印象がかなり変わります。デザインの完成度、デザインコスト、画面間での統一性、メンテナンス性、様々な面で優秀でWindowsFormsアプリ作るなら是非一度はお試しいただきたいものです。AppStylist を最大限に利用してカッコいいWindowsFormsアプリを作ってみてください。

今回作成したサンプルのダウンロードは こちら から

 

無料トライアルダウンロードはこちら
NetAdvantage ダウンロード

[WindowsForms] WinGrid 活用術 2 - バリデーション

こんにちは!前回の データの表示/登録 に続いて、今回は WinGrid におけるバリデーション方法をご紹介します。

バリデーションというのは、要するに入力項目の審査のことですね。例えば単純な最大値や最大桁数や禁則文字のチェックであったり、別の項目がAの時はBしか入力できないなどといった相関チェックであったり、同じ値を登録させないような重複チェックであったり・・・とにかくアプリケーションとしてユーザに登録させたくないデータが入力されたときに、それは登録できないんだよ、と伝える仕組みのことです。
今回は WinGrid の各セルに対する入力チェックのかけ方をいくつかご紹介します。

 

プロパティ設定による入力制限

まず、プロパティで設定できる基本的な入力制限を紹介します。
WinGridの各列のバインド列はデータソースの対応するカラムのタイプに応じて自動的に適切な(数値型や文字型や日付型など)データタイプが設定され、例えば数値項目であれば数字と小数点以外入力できなかったり、日付項目であれば実在する日付しか入力できなくなったりと、データタイプに応じて自動的に入力制限がかかります。(もちろん、設定次第では解除することも他の入力制限をかけることもできます。)
これは、バインドした項目のデータタイプに応じてセル編集時に利用されるエディターが、数値編集用であったり日付編集用のものに自動的に変わるためです。この編集時に利用されるエディターに対してマスク処理などを施すこともできますが、以下のコードのように各列毎に設定できるプロパティを利用することで簡単に入力制限をかけられるものもあります。

            //最小値、最大値の設定(※数値項目、日付項目の場合のみ有効)
            ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].MinValue = 0;
            ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].MaxValue = 99999;

            //最大文字数(※文字列項目の場合のみ有効)
            ultraGrid1.DisplayLayout.Bands[0].Columns["String1"].MaxLength = 20;

            //正規表現を使った文字制限
            ultraGrid1.DisplayLayout.Bands[0].Columns["String1"].RegexPattern = "^[a-zA-Z0-9!-/:-@¥[-`{-~]+$";

最小値、最大値、最大文字数の設定と、正規表現を使った入力制限をかけています。この正規表現を使った方法で入力制限をかけると、以下のように入力自体はできるもののエラーメッセージがダイアログ表示されフォーカスアウトできないというような挙動となります。※この時、CellDataError イベントをハンドルすることでフォーカスをとどめるかどうかや、元の値に戻すかどうかなど細かい制御を加えることができます。(この辺り、標準の DataGridView と一緒ですね。

image

 

イベントハンドリングによる入力制御

セル編集時のイベントハンドリングによるバリデーションは大きく分けて2パターン考えられます。

1つ目はセル編集が終わりフォーカスを抜けようとする際にチェックを行い、エラーであった場合はその場でダイアログ表示を行い、エラーを修正するまでは編集状態を抜けられないようにする。

2つ目はセル編集を抜けて(内部的に)セルの値をコミットしにいくタイミングでチェックし、エラーであった場合その旨をユーザへ通知し、(場合によっては値を元の正常な値に戻して)フォーカスを抜けるというものです。

image

どちらも一長一短で、前者はエラーであった場合でもフォーカスが留まるのでその場ですぐに修正する場合は使いやすいのですが、エラー値でもいいからフォーカスを抜けたいときに抜けられない、スピード重視の連続入力が行ないえないというデメリットがあります。おそらく多くの方が経験されたことがあるかと思いますが、入力エラーでフォーカスが抜けられないがために画面を閉じることすら許されないという悲惨な状況を生みかねないため、個人的には後者のほうが良いと思っています。(後者の場合はどのようにユーザにエラーを伝えるかがポイントですね!)
それぞれ実装方法をチェックいていきます。

フォーカス抜けられなくするエラー告知のコード例

        //セルの編集から抜け出るときにチェック(この場合、エラーを改善しない限りフォーカスを抜けられない)
        void ultraGrid1_BeforeExitEditMode(object sender, Infragistics.Win.UltraWinGrid.BeforeExitEditModeEventArgs e)
        {
            // String1列に"a"が含まれていないかどうかチェック
            if (ultraGrid1.ActiveCell.Column.Key == "String1")
            {
                string text = ultraGrid1.ActiveCell.EditorResolved.CurrentEditText;
                if (text.Contains("a"))
                {
                    MessageBox.Show(text + " は \"a\" を含んでいるため入力できません。");
                    e.Cancel = true;
                }
            }
        }

フォーカスを抜けられるエラー告知のコード例

        //セルの編集から抜け出た後にチェック(この場合、値が変更間のものに戻り、フォーカスは次へ遷移する)
        void ultraGrid1_BeforeCellUpdate(object sender, Infragistics.Win.UltraWinGrid.BeforeCellUpdateEventArgs e)
        {
            // String1列に"a"が含まれていないかどうかチェック
            if (e.Cell.Column.Key == "String1"){
                if(e.NewValue.ToString().Contains("a")){
                    MessageBox.Show(e.NewValue.ToString() + " は \"a\" を含んでいるため入力できません。");
                    e.Cancel = true;
                }
            }
        }

後者の場合、「e.Cancel=true;」により値をロールバックさせていますが、場合によっては苦労して入力したデータがロールバックで消えてしまうことでユーザへ大変なストレスを与える事もあります。また、各セルの入力エラーごとにダイアログを出力しているとスピード重視で連続セル入力を行っている場合は、こちらも大変なストレスになります。
そこで、もう一つエラーダイアログを出さずに、また入力した値をの残しつつエラーが存在することを伝える方法として、バインドしている DataTable の ErrorInfo を利用してセルにエラー表示する方法をご紹介します。

 

DataTable の ErrorInfo を使ったエラー表示

まず、DataErrorInfoをセル単位または行単位とセル単位で有効に設定します。

            // DataErrorInfoを利用する。
            ultraGrid1.DisplayLayout.Override.SupportDataErrorInfo = Infragistics.Win.UltraWinGrid.SupportDataErrorInfo.RowsAndCells;

続いて、BeforeCellUpdate イベントでダイアログ表示していた箇所を以下のように変更します。

        //セルの編集から抜け出た後にチェック(この場合、値が変更間のものに戻り、フォーカスは次へ遷移する)
        void ultraGrid1_BeforeCellUpdate(object sender, Infragistics.Win.UltraWinGrid.BeforeCellUpdateEventArgs e)
        {
            //// String1列に"a"が含まれていないかどうかチェック
            //if (e.Cell.Column.Key == "String1"){
            //    if(e.NewValue.ToString().Contains("a")){
            //        MessageBox.Show(e.NewValue.ToString() + " は \"a\" を含んでいるため入力できません。");
            //        e.Cancel = true;
            //    }
            //}

            // String1列に"a"が含まれていないかどうかチェック
            if (e.Cell.Column.Key == "String1")
            {
                if (e.NewValue.ToString().Contains("a"))
                {
                    // セルにエラー貼り付け
                    e.Cell.Row.DataErrorInfo.SetColumnError(
                        e.Cell.Column.Key,
                        e.NewValue.ToString() + " は \"a\" を含んでいるため入力できません。"
                        );
                }
                else
                {
                    // セルにエラー解除
                    e.Cell.Row.DataErrorInfo.SetColumnError(e.Cell.Column.Key, null);
                }
            }
        }

対象の行の DataErrorInfo から SetColumnError() メソッドでセルに対してエラー情報を貼り付けています。また、忘れてはいけないのがエラーの解除で、エラーではなかった場合にエラー情報をクリアしてあげないと正常値を入力いてもいつまでたってもエラー表示が残ってしまいます。

実行結果がこちら。

image

入力エラーのセルが一目瞭然ですね。!マークのところにマウスカーソルを合わせると上図のようにチップテキストとして設定したエラーメッセージが表示されます。
Webの入力フォームで入力エラーだった場合に項目の右隣か下に赤文字でエラーメッセージが表示されるものをよく見かけますが、これならスムーズに入力を続けられますね。

続いてエラーセルをより分かりやすくするためにエラーセルの外観を変更してみます。WinGridでは外観変更には Appearance クラスを変更しますが、エラーセルの外観は DataErrorCellAppearance により変更できます。

            //エラーセルの外観変更()
            Infragistics.Win.Appearance ap = (Infragistics.Win.Appearance)ultraGrid1.DisplayLayout.Override.DataErrorCellAppearance;
            ap.BackGradientStyle = Infragistics.Win.GradientStyle.VerticalBump; //グラデーション設定
            ap.BackColor = Color.FromArgb(255, 181, 193); //ピンク
            ap.BackColor2 = Color.White; //白

image

入力エラーのあるセルの所在がかなり見やすくなりましたね。

最後に、確定ボタン押した時のチェックについてです。
フォーカスアウトのタイミングで実装している入力チェックを再度同じロジックを通してチェックするという方法でもよいのですが、DataErrorInfo を利用している場合は、以下のように書くだけで入力したタイミングで行っていたチェック結果を利用してエラー有無を確認することができます。

以下のコードでは、先頭のエラー情報を拾ってダイアログ表示を行っています。

        // *****************************
        // 確定ボタン押下
        // *****************************
        private void ultraButton3_Click(object sender, EventArgs e)
        {

            // バインドしている DataTable の取り出し
            DataTable dt = ultraGrid1.DataSource as DataTable;

            // エラーが存在しないかどうか確認
            DataRow[] rows = dt.GetErrors();
            if (rows.Length != 0)
            {
                //存在する場合は先頭のエラー情報を取得
                String msg = rows[0].GetColumnError(rows[0].GetColumnsInError()[0]);
                MessageBox.Show(msg); //エラー表示
                return;
            }

            // DB更新確認
            if (MessageBox.Show(
                "編集内容を確定しますか?",
                "登録確認",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question)
                == DialogResult.No)
            {
                return;
            }
            
            // ・・・省略

image

実はこれは.NET標準の DataGridView でも同じような事ができるのですが、WinGrid を使って実装するとどんな感じになるのかという紹介をさせていただきました。また、何よりこの ErrorInfo を使ったバリデーションはユーザにも喜ばれるしコード量も減らせるとても良いものなので、使ったことないという方がいらっしゃれば是非この記事を参考に試していただければと思います。

次回はWinGridの外観のカスタマイズについてご紹介しようと思います。

今回作成したサンプルのダウンロードは こちら から

 

無料トライアルダウンロードはこちら
NetAdvantage ダウンロード

[WindowsForms] WinGrid 活用術 1 - データの表示と登録

こんにちは!最近は WindowsForms の Blog を書くことも少なくなってきていましたが、皆様から寄せられるお問い合わせのプラットフォーム別件数を見ると、まだまだ WindowsForms は現役であると感じます。また、VB6.0などで作られたレガシーシステムを最新の.NET Frameworkへマイグレーションするというような案件も、まだ良く耳にします。

本ポストから数回に分けて、WindowsFormsにフォーカスした内容で、特に実際の業務アプリにどのように組み込むのかというところを意識した内容で、WinGrid を使って簡単なサンプルアプリを作成していきたいと思います。ちょっとした小技やサンプルコードなど利用できるところがあればご参考にしていただければ幸いです。
また、WinGridを使ったことが無いという方にはとっかかりとして見ていただけると思います。

 

WinGrid概要

WinGrid は弊社コントロールの中でも最も歴史の深いコントロールです。フィルターやソートやグループ化などの基本的なオプションは全て簡単なプロパティ設定で実装でき、また、 Excel や PDF 形式でのエクスポートなどグリッド周辺の多様な要件にコストをかけず対応できるコントロールとなっています。このポストでは、基本的なプロパティ設定などにはあまり触れず、オンラインヘルプなどには無い業務アプリケーションに近い視点からサンプルを作成していきたいと思います。

WinGridの紹介ページ:
http://jp.infragistics.com/dotnet/netadvantage/winforms/wingrid.aspx#Overview

オンラインヘルプ WinGrid:
http://help.jp.infragistics.com/Doc/Win/2013.2/CLR4.0/?page=Win_WinGrid_Using_WinGrid.html

 

データの表示

前置きが長くなりましたが、データの表示と登録を行うサンプルを作っていきたいと思います。
WinGridは、様々なデータソース形式に対応しており、SQLサーバ等に直接接続してしまえば、特にデータの表示や更新処理を自分でプログラムを書くことなく実装できてしまいます。しかし、実際に業務アプリを組む際には、データの加工や各種項目チェック処理を独自ロジックで組み込むために、一度データベースから取得したものを加工してグリッドへ表示し、登録時に再度データを加工し各テーブルへ更新を行うという方が多いのではないかと思います。その流れを想定して、今回はプログラム上で DataTable を作成し、それをグリッドコントロールへバインドするという方法をとっています。

まず、グリッドをデザイナへ配置し、コードビハインド側を以下のように記述します。

        private void Form1_Load(object sender, EventArgs e)
        {
            // グリッドの初期設定
            ultraGrid1.DisplayLayout.Override.AllowRowFiltering = Infragistics.Win.DefaultableBoolean.True; //フィルタ設定
            ultraGrid1.DisplayLayout.Override.AllowGroupBy = Infragistics.Win.DefaultableBoolean.True; //グループ化設定
            ultraGrid1.DisplayLayout.Override.AllowColMoving = Infragistics.Win.UltraWinGrid.AllowColMoving.WithinBand; //列移動設定
            ultraGrid1.DisplayLayout.Override.AllowColSizing = Infragistics.Win.UltraWinGrid.AllowColSizing.Synchronized; //列サイズ設定
            
            // バインドするサンプルデータの設定
            DataTable dt = GetData();
            dt.AcceptChanges(); // バインド時にデータテーブルを全コミット
            this.ultraGrid1.DataSource = dt;

            // キー項目(Number列)を非表示化(もしくは編集不可へ)
            //ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].Hidden = true;
            ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].CellActivation = Infragistics.Win.UltraWinGrid.Activation.NoEdit; //もしくは編集不可        
        }

        // *****************************
        // バインドするDatatableを生成
        // *****************************
        private DataTable GetData()
        {
            //Create DataTable
            DataTable dtSample1 = new DataTable("Sample");

            //Create Columns
            dtSample1.Columns.Add("Number", typeof(System.Int32));
            dtSample1.Columns.Add("Check", typeof(System.Boolean));
            dtSample1.Columns.Add("String1", typeof(System.String));
            dtSample1.Columns.Add("String2", typeof(System.String));
            dtSample1.Columns.Add("String3", typeof(System.String));
            
            //Create Rows(20レコード生成する)
            for (int index = 0; index <= 19; index++)
            {
                dtSample1.Rows.Add(new object[] {
                                                    index,
                                                    false,
                                                    "String1_" + index.ToString(),
                                                    "String2_" + index.ToString(),
                                                    "String3_" + index.ToString()
                                                });
            }
            //Number列をキーとする。
            dtSample1.PrimaryKey = new DataColumn[] { dtSample1.Columns["Number"] };

            return dtSample1;
        }

GetData() メソッド内でグリッドへ表示するDataTableを生成しておりますが、実際の業務アプリではここで実DBから値を取得するようなイメージになります。
グリッドコントロールへのバインドは DataSource プロパティに作成/取得したデータを任意のデータ形式で割り当てるだけで、グリッドの表示が行えます。ひとつひとつ列を定義してゆくこともできますが、今回のようにデータをバインドした際に、バインドしたデータが保持している列定義に合わせて自動でグリッドの列定義を生成することができます。また、今回は Number 列をDataTableのキー項目としているため、グリッド上で Number 列を編集できないよう設定しています。

上記のコードのみで、以下のような結果となります。

image

 

 

 

各列がバインドしたDataTableの情報より自動生成され、Check列はチェックボックス列として生成されていることが確認できます。また、この時点で編集不可として設定していない Number 以外の列はセル編集することができます。

 

行追加と行削除

続いて、ここに行を追加または削除する機能を付けます。
WinGridでは行削除・行追加についてもプロパティのみで対応することもできます。 AllowAddNew プロパティを有効にすることで画面下部に新規追加するための領域が現れ、そこから行追加を行うことができるのですが、Built-inであるが故の制限もあるためコードビハインドから追加していく方法を紹介します。

ボタンコントロールをフォームに追加し、それぞれのボタンのクリックイベントのハンドラを追加し、以下のよう行追加・削除の処理を記述します。

        // *****************************
        // 行追加ボタン押下
        // *****************************
        private void ultraButton1_Click(object sender, EventArgs e)
        {
            // バインドしている DataTable の取り出し
            DataTable dt = ultraGrid1.DataSource as DataTable;
            // 新規行追加 (キー項目だけは重複しないように注意)
            dt.Rows.Add(new object[] {dt.Rows.Count,false,"","",""});

        }

        // *****************************
        // 行削除ボタン押下
        // *****************************
        private void ultraButton2_Click(object sender, EventArgs e)
        {
            // 対象行チェック
            if (ultraGrid1.ActiveRow == null)
            {
                MessageBox.Show("削除対象行が選択されていません。");
                return;
            }

            int activeRowIndex = ultraGrid1.ActiveRow.Index;

            // 削除処理(自動的に確認ダイアログが出る。)
            ultraGrid1.ActiveRow.Delete();

            // アクティブ行を次の行へ移動
            if (activeRowIndex < ultraGrid1.Rows.Count)
                ultraGrid1.ActiveRow = ultraGrid1.Rows[activeRowIndex];
                        
        }

WinGrid はデータバインドが前提のコントロールとなっているため、 Grid.Rows.Add(※例です) のような形で行追加するのではなく、バインドされているデータソース側で行追加を行います。この際、初期値としてキー項目は値が重複しないように設定しておく必要があります。
また、行削除については、Delete() メソッドを利用するか、データソース側の対象データを直接削除するかです。上記コードでは現在選択されている行(ActiveRow)に対して Delete() を行っています。そもそも行自体が選択されていない場合はその旨をダイアログ表示しています。

 

データの更新

最後にデータの更新処理です。今回のサンプルではセルの編集と行追加と行削除ができるため、Update, Insert, Delete と一通りの更新処理が発生します。
今回のサンプルでは DataRowState を利用して、変更のあった行のみを対象に更新処理を入れていきます。

        // *****************************
        // 行追削除ボタン押下
        // *****************************
        private void ultraButton3_Click(object sender, EventArgs e)
        {
            // DB更新確認
            if (MessageBox.Show(
                "編集内容を確定しますか?",
                "登録確認",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question)
                == DialogResult.No)
            {
                return;
            }
            
            // バインドしている DataTable の取り出し
            DataTable dt = ultraGrid1.DataSource as DataTable;
            
            // 変更(更新、追加、削除)があった行のみの取り出し
            DataTable data = dt.GetChanges();

            if (data == null){
                MessageBox.Show("更新対象がありませんでした。");
                return;
            }
            
            foreach (DataRow row in data.Rows){
                switch (row.RowState) {
                    case DataRowState.Modified:
                        //更新処理へ
                        System.Diagnostics.Debug.WriteLine("RowModified Number:" + row["Number"].ToString());
                        break;
                    case DataRowState.Added:
                        //新規追加処理へ
                        System.Diagnostics.Debug.WriteLine("RowAdded Number:" + row["Number"].ToString());
                        break;
                    case DataRowState.Deleted:
                        //削除処理へ
                        System.Diagnostics.Debug.WriteLine("RowDeleted Number:" + row["Number",DataRowVersion.Original].ToString());
                        break;
                    default:
                        break;

                }
            }

            //更新処理完了
            dt.AcceptChanges();
            MessageBox.Show("更新しました。");
            
        }

上記コードでは、まず更新確認のダイアログを出した後に、バインドしている DataTable の変更を GetChanges() で取り出し、更新対象の有無判定を行っています。更新対象があった場合は、GetChanges() によって取り出された各変更行から RowState で 更新 or 登録 or 削除 を判定しています。 

※DataRowState についての詳細はこちらから:MSDN DataTableの編集

ここでは実際にデータベースへ書き込みに行く処理は割愛していますが、row[“Key値”] といった具合で対象行の値を取得することができるのでこの値を元に各処理を呼び出すような形になるかと思います。
※削除された行については、row["Key値",DataRowVersion.Original] のように第2引数を指定し削除される前の状態からキー値を取得します。

 

元に戻す

もはやWinGridとは関係ありませんが、DataTable の RowState を使った更新制御をしていると、データを元に戻す事が非常に楽に実装できます。
DataTable.RejectChanges() メソッドは最後にコミット( AcceptChanges() )されて以降の変更をすべてロールバックします。バインドしているデータソースがロールバックされると自動的にグリッドの内容も編集前の状態に戻ります。

        // *****************************
        // 元に戻すボタン押下
        // *****************************
        private void ultraButton4_Click(object sender, EventArgs e)
        {
            // バインドしている DataTable の取り出し
            DataTable dt = ultraGrid1.DataSource as DataTable;
            //元に戻す
            dt.RejectChanges();
        }

 

image

完成です!細かい制御は抜きにして、グリッドコントロールを使った更新ありの一覧画面の基本的な流れができたと思います。
書いたコードを見返してみると、その大部分が WinGrid コントロール独自のものではなく、DataTableに対する操作や確認ダイアログの記述で、一般的なコードとなっています。また、フィルタやソートなどの+αの機能も簡単なプロパティ設定で実装できるため、DBの参照/更新や業務ロジックにのみ集中すればOKで、UI側の実装にはあまり時間をかけずに済みます。(気がつけば WinGrid 活用術というよりも DataTable 活用術な内容ですね。。。

今回のサンプルのダウンロードは こちら から

 

次回は、ここにバリデーションを加えていきます。エラーにひっかけ方や、特にWinGridにおける効果的なエラーの表現方法をご紹介したいと思います。

 

無料トライアルダウンロードはこちら
NetAdvantage ダウンロード

[ASP.NET] HTML要素の属性追加について

ASP.NET コントロールは、サーバ側のスクリプトによりコントロール(またはそれに紐づく要素)に対して HTML 属性を追加することができます。弊社のASP.NET向けコントロールもこれを踏襲しており、コントロールやその要素に対してHTML属性を付加することができます。

MSDN - ASP.NET Web ページのコントロールの HTML 属性を設定する
http://msdn.microsoft.com/ja-jp/library/7a9d6h4f%28v=vs.100%29.aspx

 

今回は、WebDataTreeを例に実際に属性追加を行ってみます。

image

WebDataTreeは、階層データを表示するために使用できるコントロールで、展開可能なツリーノードによって構成されています。今回はサーバサイドでノードを構成する際に、各ノードに対して属性追加し付加情報をクライアントサイドで取り出せるようにします。

まずはノードの作成です。以下では Page_Load でルートノードとそれに紐づく2つの子ノードを作成しています。各ノードに対して、Attributs.Add() で ExtraIDという属性を新たに追加しています。

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            //ルートノードの作成
            DataTreeNode root = new DataTreeNode();
            root.Text = "root1";
            root.Value = "root_1";
            root.Attributes.Add("ExtraID", "みかん"); //ExtraID属性を追加
            
            //ルートノードに子ノードを追加
            DataTreeNode node1 = new DataTreeNode();
            node1.Text = "test1";
            node1.Value = "test_1";
            node1.Attributes.Add("ExtraID", "れもん"); //ExtraID属性を追加
            root.Nodes.Add(node1);

            //ルートノードに子ノードを追加
            DataTreeNode node2 = new DataTreeNode();
            node2.Text = "test2";
            node2.Value = "test_2";
            node2.Attributes.Add("ExtraID", "りんご"); //ExtraID属性を追加
            root.Nodes.Add(node2);

            //ルートノードをWebDataTreeに追加
            this.WebDataTree1.Nodes.Add(root);
        }
    }

ルートノードに対しては”みかん”、1つ目の子ノードには”れもん”、2つ目の子ノードには”りんご”という文字列が ExtraID 属性として追加されました。

ここで実行してみます。

image実行結果

image
実行時のHTMLの各ノード( li エレメント)

実行時のHTMLを覗いてみるとしっかりExtraIDが追加されていますね!

続いて、Javascriptでこれらの属性の中身を取り出す処理を書きます。今回は、クライアントサイドのノードクリックのイベントで、クリックされたノードのExtraID属性を取り出します。

function WebDataTree1_NodeClick(sender, eventArgs)
{
    //クリックされたノードのExtraID属性の取り出し
    extraID = eventArgs.getNode().get_element().getAttribute("extraid");
    alert(extraID); //表示
}

get_element() メソッドによりクリックされたノードに対するHTML要素(<li>)を取得したら、あとは getAttribute() メソッドにより ExtraID を取り出し、ダイアログ表示しています。

image 実行結果

 

 

 

 

今回はWebDataTreeを例として使いましたが、どのコントロールに対しても行えます。サーバサイドからクライアントサイドに渡したい情報を付加する方法として使えます。

※注意点
追加した属性はポストバック対象とならないので、フルポストバックした際に追加した属性は引き継がれず消えてしまいます。サーバサイドで値を引きまわすための領域としては使えません。

 

無料トライアルダウンロードはこちら
NetAdvantage トライアル版
NetAdvantage ダウンロード

コントロールの日本語表示(ローカライズ)について

弊社のUIコントロールは、英語圏で開発されておりますが日本を含む各世界国向けにローカライズされております。コントロールのデザインや挙動は全て同一ですが、ユーザインタフェースに必要な表記の中で世界共通にできない部分(※)は、各国向けにローカライズされています。
※例えば、グリッドのフィルタの ”を含む” や “~と等しい” など

image
ローカライズされている文字(赤下線)

 

■ローカライズのためのリソースファイル

開発環境で開発していた時は日本語表示されていたのに本番環境(或いは各クライアント端末)へインストールしたら英語表記になってしまった、、、というお問い合わせをよく頂きます。弊社コントロール(日本語版)を開発端末にインストールすると、日本語リソースファイルも一緒にGACへ登録されます。開発環境で実行している際にはそのリソースファイルを参照できるため日本語表示となりますが、弊社製品がインストールされていない実行環境では、日本語リソースファイルが見つからず英語表記となってしまうというものです。
弊社製品は、WindowsForms, ASP.NET, Silvertlight, WPF, jQuery, etc… など各プラットフォームで製品を展開しておりますが、完全な日本語表示には基本的に全ての製品においてこの日本語のリソースファイルが必要になります。

  

■リソースファイルの追加

言語リソースファイルは、製品インストールフォルダのBinフォルダ(細いパスはプラットフォームにより異なります)の中に格納されております。日本語のリソースファイルは “ja” という名前のフォルダの中に格納されています。

image
ASP.NETの場合の例

弊社製品をインストールしていない実行環境に対してこれらのファイルをどのように配備するかは、プラットフォームや配信方法により異なりますが、基本的には GAC に登録するか製品のアセンブリが配備されるフォルダと同階層に ja フォルダを配置する事で対応いただけます。

  

■リソースのカスタマイズ

用意されているリソース文字ではなく、表示される文字列をカスタマイズしたいという場合には、リソース文字列をカスタマイズすることができます。カスタマイズ方法については、弊社オンラインヘルプにもございますが過去のブログ記事に纏めて紹介されているものがございますので、以下をご確認下さいませ。

ローカライズ可能なリソース文字列 [Win][WPF][Silverlight] (by Daizen Ikehara

 

■カルチャーの設定について

日本語表記の部分が全てリソースファイルから取得されているわけではありません。例えば、「月、火、水」などの曜日表記や「yyyy年MM月dd日」の日付表記は国によって異なりますがこれらは OS が情報を持っており各コントロールの CultureInfo の設定により決定されるため、これらの表記についてはリソースファイルには左右されません。
(CultureInfoの設定や多言語対応については別の機会に書きたいと思います!)

無料トライアルダウンロードはこちら
NetAdvantage トライアル版
NetAdvantage ダウンロード

[WindowsForms] GanttViewのグリッド部分のカスタマイズ

プロジェクト管理や工程管理など、タスクの進捗やスケジュールを確認するためのガントチャート。弊社製品では各プラットフォームにて用意されていますが、今日はWindowsForms向けのUltraGanttViewについてTipsを紹介します。

image 

グリッド部分のカスタマイズについては、UltraGanttView の GridSettings クラスにより、背景色やフォーマットの設定を行う事ができます。

            //選択セルの色設定
            this.ultraGanttView1.GridSettings.SelectionOverlayColor = Color.Green;
            //DateTime列のフォーマットを変更
            this.ultraGanttView1.GridSettings.ColumnSettings["DateTime"].Format = "yyyy/MM/dd HH:mm:ss";

しかし、この方法でのカスタマイズでは出来る範囲は限られており、より細かなプロパティ設定や、グリッドイベントをハンドルして処理を行うというような事ができません。

UltraGanttViewコントロールのグリッド部分は内部的には UltraGrid が利用されており、以下のコードで UltraGrid を抜き出すことができます。この抜き出したグリッドに対してカスタマイズを行う事で、UltraGrid と同じ容量でイベントハンドリングやグリッド特有のカスタマイズを行っていくことができます。

[グリッドの取得]

            //Gridの取得
            var grid = this.ultraGanttView1.GetType().InvokeMember("grid", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic, null, ultraGanttView1, null) as UltraGrid;

[取得したグリッドのカスタマイズ]

            // **********************
            // GridSettingsでは実現できないカスタマイズを実装
            // **********************
            // 編集時のマスク文字の指定
            grid.DisplayLayout.Bands[0].Columns["DateTime"].MaskInput = "mm/dd hh:mm:ss";
            // リサイズ方法の指定
            grid.DisplayLayout.Override.RowSizing = RowSizing.AutoFree;
            // 編集時に利用されるコントロールの指定
            grid.DisplayLayout.Bands[0].Columns["DateTime"].EditorComponent = new UltraCalendarCombo();
            // イベントハンドリング
            grid.AfterExitEditMode += grid_AfterExitEditMode;

この方法を使うことで、UltraGrid と同じ要領で複雑なカスタマイズを組み込んでいくことができます!

[関連ページ]

GridSettingsによるカスタマイズ

ガントチャートコントロールのカスタム列

弊社製品は機能制限なしのトライアル版もご用意しています。ダウンロードはこちらから。

Infragistics 製品トライアルダウンロード

[Reporting] 請求書印刷の作り方(改ページやグループ化について)

本ポストでは、Reporting製品について業務的なシナリオ(請求書印刷)に沿って一つサンプルを作っていきたいと思います。一連の流れを説明しつつReporting製品を使ってどのような感覚でレポートの出力機能を実装できるのか、このポストに目を通していただきイメージを掴んでいただけると幸いです。
Infragistics Reporting は表形式や統合チャートを含めた表現力豊かなレポートを出力するためのツールです。各プラットフォーム(WindowsForms, ASP.NET, WPF, Silverlight, HTML5)に対応したViewerも用意されており、直感的なユーザ操作でレポートを閲覧または PDF や Excel 形式での出力に対応しています。

先日、お客様からこんなお問い合わせをいただきました。
「請求書の鑑と明細を交互に出力するようなイメージ、できますか?」

要点としては、1回の印刷処理(またはプレビュー表示)で2つのフォーマットを交互に(鑑と明細をペアで)印字し明細側でレコードが1ページ内で収まらない程の量ある場合は改ページされるといったイメージで印刷がおこなえるのかということです。
改ページのイメージ:鑑、明細、鑑、明細、明細、鑑、明細・・・

実際に作成したサンプルの実行結果は以下のとおり。

 image   image

image   image   image

見た目については、サンプルですのでご容赦下さい・・・

今回はサンプルとして WindowsForms の Viewer を利用してレポートを表示しています。直接印刷ボタンや印刷ダイアログボタンは自分で配置してロジックを組んでいますが、画面下のボタンセット(ページ切り替えやエクスポートなど)はViewerに付随するもので機能としては既に製品に組み込まれています。

まずはデータの準備から

レポートファイルを作成する前に、まずは表示するデータを準備します。SQLデータソース、EntityFrameWork、RIAサービスなど様々なデータソースに対応していますが、今回は ObjectsSource クラスを利用してデータを生成します。

 

Public Class ObjectsSource

    Public Function GetProducts() As IEnumerable(Of Product)
        Dim products As New List(Of Product)()

        'データの作成
        For i As Integer = 0 To 29
            products.Add(New Product() With { _
             .Code = IIf(CInt(i / 10) = 2, 1, i / 10), _
             .MyDate = DateTime.Today.AddDays(i), _
             .Number = i * 2 + i * 3 _
            })

        Next
        Return products
    End Function
End Class

Public Class Product
    'コード
    Public Property Code() As Integer
        Get
            Return m_Code
        End Get
        Set(value As Integer)
            m_Code = value
        End Set
    End Property
    Private m_Code As Integer

    '日付
    Public Property MyDate() As DateTime
        Get
            Return m_MyDate
        End Get
        Set(value As DateTime)
            m_MyDate = value
        End Set
    End Property
    Private m_MyDate As DateTime

    '数値
    Public Property Number() As Integer
        Get
            Return m_Number
        End Get
        Set(value As Integer)
            m_Number = value
        End Set
    End Property
    Private m_Number As Integer
End Class

Code(文字型)、MyDate(日付型)、Number(数値型)の3項目を準備し、各項目の値が変わるように30レコード生成しています。
※明細の改ページを見せるために Code=”1” のデータが多くなるようにしています。

様々なデータソースのタイプに対応していますが詳細は以下のヘルプページをご確認下さいませ。
オンラインヘルプ データソースを使用した作業方法
http://help.jp.infragistics.com/Doc/reporting/2013.2/CLR4.0/?page=How_to_Work_With_Data_Sources.html

レポートファイルの作成

新しい項目の追加から、Infragisticsレポートを追加します。(Infragistics Reportingがインストールされている必要があります。)
※プロジェクトではなくWebSiteに追加する場合は少し特殊な手順となりますので、お困りの際はお問い合わせ下さいませ。

image

レポートファイルにデータを準備

追加されたレポートファイル(.igr)をデザイナで開き以下の手順で作成した ObjectsSource を追加します。
※ダブルクリックでデザイナではなくXMLエディタとして開かれてい待った場合は、右クリック→”ファイルを開くアプリケーションの選択”からInfragistics Reportingエディターを選択してください。

image image image

image

グループ化の設定

下準備ができました。ここからは、ツールボックスタブからラベルや表を配置しつつレポートをデザインしていきます。が、今回はその前にやっておかなければならない事があります。鑑→明細、鑑→明細&明細といった具合で印字していくために”グループ化”を行います。

オンラインヘルプ グループ化の方法:
http://help.jp.infragistics.com/Doc/reporting/2011.2/CLR4.0/default.aspx?page=How_to_Group_Data.html

まず、デザイナ上の”ここにクリック / ドロップ”とかいてある箇所をクリックし、その後”グループの追加”を押下すると新たにグループ化のための領域が作成され、以下のようなイメージになります。

image image

 

 

 

 

 

 

 

更に、”グループの設定に式を追加する”をクリックすると、式アシスタントダイアログが表示されますので、ここで改ページ条件となるキーを設定します。今回作成するサンプルではCode値が変化したら改ページを行うという設定にしています。更にDefaultで用意されているセクションを小さくたたみ(今回はグループ化するため使用しません。)、新しく作成したグループの領域を大きくしたら、デザインの前準備が完了です。

image image

レポートのデザイン

ようやくデザインまで来ました。ツールボックスタブからラベルや表を配置しつつレポートをデザインしていきます。直感的な操作で配置していくことができますので、ここは割愛します。各ラベルや表を配置し、項目のバインドを行うと以下のようなイメージとなりました。

image

 

 

 

※今回はわかりやすくするためにレポートヘッダなどは使用せずに一つのセクション内に全て定義していますが、レポートヘッダやフッタを利用して固定出力させることも可能です。

ここでは請求書(鑑)と請求書(明細)の2パターンのフォーマットを1つのセクションに用意しています。ここでポイントとなるのは、”改ページ”(ツールボックスの中のコントロール群の中にあります。)を利用していることです。用意した2パターンのフォーマットを1回のグループ出力(今回のサンプルでいうところの、Code値ごとの出力)で鑑→明細の順で改ページされ出力されるような設定になっています。鑑のほうでは Number の Summary しか表示していないため、1グループにつき1つの値しか表示されませんが、明細のほうでは同一のCode値をもつレコードの MyDate と Number が繰り返し表示されてゆきます。

この状態で実際にレポート出力したものが、本ポストの一番初めにお見せしたイメージです。
鑑(Code=0) → 明細(Code=0) → 鑑(Code=1) → 明細(Code=1) → 明細(Code=1)
といった順序で出力されていることがお分かるかと思います。

ちなみに、レポートのデザイナではこの作成中のレポートを実際にデータと紐付けてプレビューする機能もあるので、作成中に仕上がりを確認しながら進めるということができます。

image

アプリケーションへの組み込み

いよいよ作成したレポートをアプリケーションから呼び出します。ここについては、各プラットフォーム毎に設定方法は異なるため、オンラインヘルプやサンプルなどでご確認いただきご不明点があれば弊社サポート窓口までお問い合わせいただければ対応致します。

今回のサンプルは WindowsForms の UltraReportViewer を利用します。設定方法は非常に簡単でツールボックスからデザイナへ投げ入れ、DefinitionUri を設定するだけです。DefinitionUri を設定するコンボにはプロジェクト内に追加されている igrファイル へのパスがアイテムとして自動で追加されているので、基本的にはコンボに出てきたアイテムを選ぶだけで済みます。※レポート処理をサーバ側で行わせる場合には別途設定が必要になります。

image

最後に、コード側でViewerからレポートを表示させてあげれば完了です!
各ボタン押下時の処理は以下の通り。

    '表示クリック
    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        UltraReportViewer1.Show()
    End Sub

    '印刷ダイアログ
    Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
        Me.UltraReportViewer1.PrintWithDialog()
    End Sub

    '直接印刷クリック時
    Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
        Dim localPrintServer As New LocalPrintServer()
        Dim defaultPrintQueue As PrintQueue = localPrintServer.DefaultPrintQueue
        Dim printerName As String = defaultPrintQueue.Name

        Dim currentPageSettings As PageSettings = UltraReportViewer1.GetCurrentPageSettings()
        Dim paperSettings As New PaperSettings(currentPageSettings.PaperSize, currentPageSettings.PageOrientation)

        Dim paperSize As New PaperSize(New Measure(20, Unit.Centimeters), New Measure(45, Unit.Centimeters))

        Me.UltraReportViewer1.Print(paperSettings, printerName, PageRange.All)

    End Sub

その他、書式設定や計算式について

セル単位で書式文字列を変更することで、通貨や数値など細いフォーマッティングが行えます。
また、和暦対応しており、以下の様にFormatの指定で”gg”を設定すると元号表記となります。※明治以降の元号に対応。ラベルや

image image

各ラベルやセルでは数学式や変数(PageCountなど)を使った計算式を組み込む事ができ、データソースを変えずにレポート内である程度の計算や分析を行うことができます。

image

いかがでしたでしょうか。予想以上に長くなってしまったので、後半細いところは省略させていただきましたが、イメージは伝わましたでしょうか。

実はオンラインヘルプにも「請求書レポートの作成」というトピックがあるのですが、集計単位やグループ化や改ページの観点で内容は異なるため本ポストを書きました。途中省略した各バインド項目の設定や細い手順についてはオンラインヘルプの方が参考になるかもしれませんので、実際に開発される方はこちらもチェックしてみて下さい!

オンラインヘルプ 請求書の作成:
http://help.jp.infragistics.com/Doc/reporting/2013.2/CLR4.0/?page=Creating_an_Invoice_Report.html

 

 

 

今回のサンプルプロジェクトのダンロードは こちら から!

無料トライアルダウンロードはこちら
NetAdvantage トライアル版
NetAdvantage ダウンロード

[ASP.NET] チャート + グリッドのExcel出力

image

上の図のような「チャート+グリッド」といった画面構成はよくあるパターンかと思いますが、本ポストではこの「チャート+グリッド」をこのイメージのままエクセルへ出力する方法について書きたいと思います。

この「チャート+グリッド」イメージは弊社コントロールの UltraChart と WebDataGrid を利用していますが、それぞれの実装方法については割愛させていただきます。※実装コードを確認したい方は 添付サンプル をご参照下さいませ。

[各コントロールのオンラインサンプル]

WebDataGrid: http://samples.jp.infragistics.com/aspnet/Samples/WebDataGrid/Organization/ExcelStyleFiltering/Default.aspx?cn=data-grid&sid=28d09818-7853-46fb-ba0a-a930e003aa83

UltraChart: http://samples.jp.infragistics.com/aspnet/Samples/WebCharts/Display/AreaCharts/StackedAreaCharts/Default.aspx?cn=chart&sid=425897b1-1d3a-483f-b291-c563c4bd9ee7

WebDataGrid コントロールには、表示している内容をそのままエクセル出力するための WebExcelExporter コントロールが用意されています。最もシンプルな利用方法としては、以下のコード1行だけで、グリッドの内容を元にエクセルブックを生成してクライアントへダウンロードさせること(レスポンスの書き換え)まで行ってくれます。

this.WebExcelExporter.Export(this.WebDataGrid1);

image 
グリッドのみエクセル出力した場合

今回はグリッドだけでなくチャートも同じシート内に一緒に出力したいため、事前にチャートを画像として保存し、エクセルのシートへ貼り付ける必要があります。
具体的には以下のようなコードになります。

        // ************************************************
        // Excel出力
        // ************************************************
        protected void LinkButton1_Click(object sender, EventArgs e)
        {
            //チャートから画像を出力する
            string fileName = "chartImageForExport.bmp";
            UltraChart1.SaveTo(fileName, System.Drawing.Imaging.ImageFormat.Bmp);

            //Saveした画像の取得
            System.Drawing.Image image = System.Drawing.Image.FromFile(fileName);
            Infragistics.Documents.Excel.WorksheetImage imageShape =
              new Infragistics.Documents.Excel.WorksheetImage(image);

            //Excel出力用のワークブックとワークシートを準備
            Infragistics.Documents.Excel.Workbook workbook = new Infragistics.Documents.Excel.Workbook();
            Infragistics.Documents.Excel.Worksheet worksheet1 = workbook.Worksheets.Add("Sheet1");

            // チャート画像出力先の開始位置の指定
            Infragistics.Documents.Excel.WorksheetCell cellB2 = worksheet1.Rows[1].Cells[1]; //チャート画像の出力先の開始位置
            imageShape.TopLeftCornerCell = cellB2;
            imageShape.TopLeftCornerPosition = new PointF(0.0F, 0.0F);

            // チャート画像出力先の終了位置の指定
            Infragistics.Documents.Excel.WorksheetCell cellJ17 = worksheet1.Rows[16].Cells[9]; //チャート画像の出力先の終了位置
            imageShape.BottomRightCornerCell = cellJ17;
            imageShape.BottomRightCornerPosition = new PointF(100.0F, 100.0F);

            // シートに画像を追加
            worksheet1.Shapes.Add(imageShape);
            
            //Worksheet1の19行目のB列目からグリッドの内容をエクスポート(レスポンスヘッダは書きかえない)
            WebExcelExporter1.Export(worksheet1, 18, 1, WebDataGrid1);

            // ******************************************
            // エクセル出力
            // ******************************************
            // メモリストリーム
            System.IO.MemoryStream theStream = new System.IO.MemoryStream();

            // ストリームに作成したワークブックを保存
            workbook.Save(theStream);

            // レスポンスヘッダの書きかえ
            byte[] byteArr = (byte[])Array.CreateInstance(typeof(byte), theStream.Length);
            theStream.Position = 0;
            theStream.Read(byteArr, 0, (int)theStream.Length);
            theStream.Close();
            Response.Clear();
            Response.AddHeader("content-disposition", "attachment; filename=ExportedTo.xlsx");
            Response.BinaryWrite(byteArr);
            Response.End();
        }

まず初めに、UltraChart の SaveTo メソッドでチャートのイメージをビットマップ形式でサーバ側に保存しています※パスは変数 FileName へ。(6~13行目)

次に 弊社のエクセルエンジン(Infragistics.Document.Excel)を使いエクセルブック及びそれに紐づくエクセルシートを生成しています。(15~17行目)

続いて、生成したシートに対して開始位置と終了位置を指定しつつ、出力したチャートの画像を貼り付けています。(19~30行目)

その後、WebExcelExporter の Export メソッドにて、引数で出力対象となるシートと出力開始位置(ここでは19行目のB列から)をしていしてグリッドの内容をエクスポートしています※ブックかシートを引数で渡している時はレスポンスの自動書き換えは発生しない。(32~33行目)

そして最後に生成されたエクセルブックをレスポンスとしてクライアントへ返すことで、チャート+グリッドのエクセル出力を実現しています。(35~52行目)

image

こういったエクスポート機能はユーザに喜ばれる需要のあるモノかと思いますが、比較的簡単に実現できるんです!

 

 

 

[サンプルプログラム]
※今回作成したサンプルは こちら からダウンロードいただけます。

NetAdvantage トライアル版ダウンロード
NetAdvantage は無料トライアルを用意しています。是非一度お試し下さい。

NetAdvantage ダウンロード

[WPF / Silverlight] XamCalendar: 日付表示形式のカスタマイズ(和暦表示も可能!?

先日、XamXalendar コントロールについて、日付部分の表示形式をカスタマイズしたい(和暦表示変換するロジックを独自に入れたい)というご相談をいただきました。
XamCalendarでは以下の図のように、日単位、月単位、年単位の表示を行うことができ、各日付や年月をクリックするとより詳細な表示へドリルダウンし、反対にヘッダー部分をクリックするとより広範囲の情報を表示するな挙動となっています。

image

今回は上の図の赤枠の箇所、現在の表示状態を示すキャプション部分と今日日付を選択する今日日付ボタンの日付表示をカスタマイズしていきます。本ポストのタイトルにも書いていますが、(弊社コントロールはプロパティ設定による和暦表示には対応していないものの)和暦表示のカスタムロジックを自前で用意すれば和暦表示を行うこともできます!

また、以下で紹介している再テンプレート方法とValueConverterクラスの利用は、カレンダーコントロールに限らず幅広く応用できるものですので、参考にしていただけるかと思います。

   

■再テンプレートを使う!

ウィンドウにXamCalendarを1つ配置した後、早速該当箇所に手を施していきたいと思いますが、ここで再テンプレートという大技を使います。

カスタマイズを加えたい該当の箇所は内部的にはButtonコントロールで構成されているのですが、この部分に適用されているスタイル定義がプロパティとして公開されているわけではないため、このボタンの部分を含むスタイル定義を再定義し変更しなければなりません。(弊社では、製品がデフォルトで使用しているスタイルを再定義して改変することを再テンプレートと呼んでいます。)
弊社のXAML製品は、各コントロールを構成しているXAML要素のスタイル定義を全て公開しており、それらはインストールフォルダの ”DefaultStyles” ディレクトリに格納されています。今回の対象となるXamCalendarの該当箇所のスタイル定義は “インストールフォルダ\DefaultStyles\XamCalendar” 下の generic.shared.xaml にです。このデフォルトスタイルファイルをプロジェクトへ追加し、以下のコードの様にResourceDictionaryに登録することで、アプリケーション内でこの部分のスタイルをオーバライドします。(ファイル全体ではなく、該当の部分をコピー&ペーストで同一リソース内に定義する方法でも問題ありません。)

        <Grid.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="generic.shared.xaml"/> 
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Grid.Resources>

ここまでが、再テンプレートの基本的な流れです。

まずはじめに今日日付ボタンの日付フォーマットを変更します。
TargetType に "igEditors:XamCalendar" が設定されているスタイル(generic.shared.xaml のかなり下の方)が該当のスタイルとなっているので、このスタイルの中の今日日付ボタンの箇所(<!--Today button-->というコメントが入っています。)を以下のように修正します。

              <!--Today button-->
              <Button Grid.Row="1"
		              IsTabStop="False"
		              Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Today}"
		              Style="{TemplateBinding TodayButtonStyleResolved}"
		              Visibility="{TemplateBinding TodayButtonVisibility}">
                <Button.ContentTemplate>
                  <DataTemplate>
                    <TextBlock Text="{Binding StringFormat='yyyy-MM-dd'}"/>
                  </DataTemplate>
                </Button.ContentTemplate>
	              <ig:Commanding.Command>
		              <igEditorsPrim:CalendarCommandSource EventName="Click" CommandType="Today"/>
	              </ig:Commanding.Command>
              </Button>

<Button.ContentTemplate>の箇所を加えており、ボタンの更に内部で利用されているTextBlockのTextプロパティに対してフォーマットを仕込んでいます。(ハイフン区切り ’yyyy-MM-dd’ に変えてみました。)

これで、まずは日付ボタンのフォーマッティングが完了です。

image

■ValueConverterを使う!

続いてはヘッダー部分の日付フォーマットを変更しようと思いますが、こちらは少し複雑です。
安易に先ほどと同様の方法でフォーマッティングを行うと、 月単位 → 年単位 という様に表示単位を変えた時に表示形式が年に変わらず、テストフェーズで怒られます。
XamCalendar には CurrentMode というプロパティがあり、現在の表示モードが月単位なのか、年単位なのか、それ以外のものなのか判断できるので、このケースではこのプロパティを参照してケースバイケースでフォーマッティング方式を変えなければいけません・・・

そこで Converter を利用して、CurrentMode を参照しつつ適切なフォーマッティングを行い変換後の文字列を返すということを実装します。(今回のケースでは複数の値をコンバータクラスへ渡したいため、 IValueConverter ではなく IMultiValueConverter を利用します。)

まずは以下のように IMultiValueConverter インタフェースを継承したクラスを作成します。
クラス内では、
第一引数 Values[0] に、対象の日付を
第二引数 Values[1] に、CurrentMode(現在の表示単位)を
第三引数 Values[2] に、カスタマイズ無しの状態で出力していた値
を利用してフォーマットを行っています。簡単な例として”yyyyねん”といったフォーマッティングを行っていますが、ここで和暦変換のロジック独自に実装すれば和暦表示にすることも可能です。

    public class MyConverter : IMultiValueConverter
    {

        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            //対象の日付
            DateTime targetDate = (DateTime)values[0];
            //モード
            Infragistics.Controls.Editors.CalendarZoomMode mode = (Infragistics.Controls.Editors.CalendarZoomMode)values[1];
            //本来出力しようとしていた値
            String defaultValue = (string)values[2];

            if (values[0].GetType() == typeof(DateTime))
            {
                if (mode == Infragistics.Controls.Editors.CalendarZoomMode.Months)
                {
                    //月表示の場合
                    return targetDate.ToString("yyyyねん");
                }
                else
                {
                    if (mode == Infragistics.Controls.Editors.CalendarZoomMode.Days)
                    {
                        //非表示の場合
                        return targetDate.ToString("yyyyねんMMがつ");
                    }
                }
                //それ以外
                return defaultValue;
            }
            return defaultValue;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

続いて、定義したConverterを利用して、該当箇所のスタイルに変更を加えます。

名前空間の定義

xmlns:c="clr-namespace:コンバータが定義されているのネームスペース"

リソースの定義

<c:MyConverter x:Key="Formatter" />

TargetType に "igEditors:CalendarItemGroupTitle" が設定されているスタイル(generic.shared.xaml の割りと下の方)が該当のスタイルとなっているので、このスタイルの中のヘッダのボタンの箇所<!--  CalendarItemGroup FirstDayInGroup  -->というコメントが入っています。)を以下のように修正します。

<!--  CalendarItemGroup FirstDayInGroup  -->
<Button x:Name="headerContent"  Grid.Column="1" 
		Foreground="{TemplateBinding ComputedForeground}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
		IsTabStop="False"
    igPrim:XamlHelper.Focusable="False"
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    Style="{StaticResource BorderlessButtonStyle}"
    igPrim:XamlHelper.SnapsToDevicePixels="{TemplateBinding igPrim:XamlHelper.SnapsToDevicePixels}"
 
    Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type igPrim:CalendarItemGroup}}, Path=FirstDateOfGroup}">
    <button.contenttemplate>
        <datatemplate>
            <textblock>
                <textblock.text>
                    <multibinding converter="{staticresource formatter}">
                        <binding path="."/>
                        <binding relativesource="{relativesource ancestortype={x:type igprim:calendaritemgroup}}" path="currentmode"/>
                        <binding relativesource="{relativesource ancestortype={x:type igprim:calendaritemgroup}}" path="title"/>
                        <binding relativesource="{relativesource ancestortype={x:type igprim:calendaritemgroup}}" path="firstdateofgroup"/>
                        <binding relativesource="{relativesource ancestortype={x:type igprim:calendaritemgroup}}" path="lastdateofgroup"/>
                    </multibinding>
                </textblock.text>
            </textblock>
        </datatemplate>
    </button.contenttemplate>
	<ig:Commanding.Command>
		<igEditorsPrim:CalendarCommandSource EventName="Click" CommandType="ZoomOutCalendarMode" ParameterBinding="{Binding Path=Group, RelativeSource={RelativeSource TemplatedParent}}"/>
	</ig:Commanding.Command>
</Button>

Multibingingの各バインド設定(18行目~21行目)に以下のような表記がありますが、これは Visual Tree 上の先祖のエレメントを辿って calendaritemgroup 取得していて、更に Path の指定で各プロパティを Converter クラスへ渡しています。

<binding relativesource="{relativesource ancestortype={x:type igprim:calendaritemgroup}}" path="currentmode"/>

今回の例では受け手側(Converter)では第4引数(firstdateofgroup)、第5引数(lastdateofgroup)を利用していませんが、これらはそれぞれ表示範囲の最初の日付と最後の日付を渡しているので、もし”平成10年~平成22年”というような表記をする場合には必要になります。

さて、実行してみましょう。

image

Converterで指定した通りにフォーマットが行われました。Converter を使ったフォーマッティングでは自在に返却値を変えられるので和暦表示でも独創的なフォーマットでも何でもOKです!

また、今回はXamCalendarを例に 再テンプレート と Converter の使い方をご紹介しましたが、この2つのカスタマイズ方法を使えば、かなり幅広くカスタマイズできるようになります。是非、今後の実装のご参考にしていただければと思います!

※今回のサンプル(WPF, C#)は こちら から!


[再テンプレートの注意点]
再テンプレートはカスタマイズの自由度がかなり高くなる反面、実装は複雑で、コントロール自体の内部のTree構成やデザイン、イベント等を簡単に変える事ができてしまうため、実装には注意が必要です。
また、製品のバージョンを上げる際には、これらのテンプレートファイル自体に変更が加えられる可能性があるため、再度デフォルトテンプレートを取得し同様のカスタマイズを加える作業が必要となります。

関連リンク:
再テンプレートに関するお知らせ

NetAdvantage は購入前にもトライアル利用が可能です!是非一度お試しください。

NetAdvantage トライアル版ダウンロード
NetAdvantage ダウンロード

[ASP.NET] Editor系コントロールのクリップボード操作

今日はWebのEditor系コントロールのクリップボード操作について簡単なTipsを書きたい思います。

弊社のASP.NETのEditor系コントロールは、WebTextEditor、WebMaskEditor、WebComboEditor、、、等いろいろございますが、今日はWebMaskEditorを例に、ユーザがコピー操作を行った時にクリップボードへ追加されるテキストの内容を変える方法を紹介します。(コントロール内部で使っているEditor部分(Inputタグ)を抽出した先は、弊社コントロールに限らず汎用的に使えるはずです。)

WebMaskEditorは、文字通りマスク文字列を指定することで入力制限をかけたり表示文字列のフォーマットを指定できたりするエディターコントロールです。例えば、InputMaskプロパティに”a-a-a”(”a”は任意の一文字という意味)と指定しておくと”AB1”という文字を入力した際に表示上は”A-B-1”となります。

image image

この状態で文字列を選択してコピーするとマスク文字(“_”や“-”)も含んだ状態でコピーされるため、コピー操作が行われた時のイベントをハンドルして純粋な入力値”AB1”をコピーさせます。

[ソースコード]

        <ig:WebMaskEditor ID="WebMaskEditor1" runat="server" InputMask="a-a-a">
            <ClientEvents Initialize="txtMask_Initialize" />
        </ig:WebMaskEditor>
<script type="text/javascript">
    function txtMask_Initialize(oEdit, text) {
        //WebMaskEditorの初期化イベントでコントロール内部の
        //Editorエレメント(input)を取得してoncopyイベントを追加
        oEdit.get_inputElement().oncopy = WebMuskEditor_onCopy;
    }

    function WebMuskEditor_onCopy(sender) {
        //WebMaskEditorの取得
        editor = $find("WebMaskEditor1");

        //コピーする文字列の整形
        targetString = editor.getSelectedText().split("-").join(""); //半角スペースは削除
        targetString = targetString.split("_").join(""); //リテラル文字も削除

        if (targetString.length > 0) {
            //クリップボードに追加
            clipboardData.setData('text', targetString);
            alert(targetString + " をコピーしました。");
        }
        return false; //元のコピー動作自体はキャンセル
    }
</script>

WebMaskEditorの初期化イベントでコントロール内部のEditor部分(Inputエレメント)にonCopyイベントハンドラを追加しています。イベント内では、現在選択されている文字列から、マスク文字を削除し clipboardData.setData() メソッドでクリップボードに整形後の文字列を追加し、元のコピーイベント自体はキャンセルしています。

image

弊社のコントロールに限らずとも、編集に使われているInputエレメントさえ取得できれば同様のスクリプトでコピー文字列の操作が行えます。

 

 

 

 

 

NetAdvantage トライアル版ダウンロード
NetAdvantage は無料トライアルを用意しています。是非一度お試し下さい。

NetAdvantage ダウンロード

[WPF] XamDataGrid: Filterをカスタマイズしよう!

XamDataGridにはビルトインのフィルター機能があり、簡単なプロパティ設定で、入力式のフィルタからエクセルライクなものまで様々なフィルタ機能を利用することができます。

image

 

ビルトインのフィルタでは以下左の図で ”(カスタム)” という項目を選択するとカスタムフィルター専用のダイアログ(以下右の図)が表示され、指定列に対してカスタムな絞り込み条件を追加していくことができます。
今回はこのビルトインのカスタムフィルタダイアログを独自に作成した範囲指定専用のフィルタダイアログに置き換えてみたいと思います。

image image

まずはじめに、Window内にXamDataGridを配置し、フィルタ機能を有効 ( AllowRecordFiltering = True ) に設定します。(データバインド部分についての説明は割愛します。本記事に添付のサンプルをご参照下さい。)

        <!--XamDatagridの設定-->
        <igDP:XamDataGrid 
                          Name="xgrid1" DataSource="{Binding People}">
            <igDP:XamDataGrid.FieldSettings>
                <igDP:FieldSettings AllowRecordFiltering="True" FilterOperandUIType="Combo"/>
            </igDP:XamDataGrid.FieldSettings>

            
            <igDP:XamDataGrid.FieldLayoutSettings>
                <igDP:FieldLayoutSettings   SelectionTypeRecord="Single"/>
            </igDP:XamDataGrid.FieldLayoutSettings>

            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout>
                    <igDP:FieldLayout.Fields>
                        <igDP:Field  Name="Name">
                            
                        </igDP:Field>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>

 

これだけでフィルタ機能が有効になります。

次に、フィルタのドロップダウンを展開した際に表示される ”(カスタム)” という文字を ”(範囲選択)” に変換します。各フィルタアイテムのテキスト値を IValueConverter を利用して変換する手もありますが、ここでは、フィルタのリスト部分が表示される際に発生する RecordFilterDropDownPopulating イベントを利用して変換します。

        //フィルタメニューが出るタイミングのイベント
        void xgrid1_RecordFilterDropDownPopulating(object sender, Infragistics.Windows.DataPresenter.Events.RecordFilterDropDownPopulatingEventArgs e)
        {
            //(カスタム)を(範囲選択)に変更
            e.DropDownItems[0].DisplayText = "(範囲選択)";
        }

※ DropDownItems 指定をインデックス固定で行っていますが、ケースによってはループ文で検証したほうが良いです。このコレクションはこのイベントの中で追加や削除することができます。

現時点ではフィルタアイテムの ”(カスタム)” という表示が ”(範囲選択)” になっただけなので、 ”(範囲選択)” をクリックするとビルトインのカスタムフィルタダイアログが表示されます。そこで CustomFilterSelectionControlOpening イベントでこのビルトインのダイアログ表示をキャンセルし、自作したダイアログ(今回は XamDialogWindow を利用)を表示させたいと思います。

まずはXamDialogWindowの自作ダイアログの定義部分。ポイントは、IsModal プロパティを False に設定し、ダイアログ表示時にダイアログ外を操作させないことです。

        <!--XamDialogWindowの設定-->
        <ig:XamDialogWindow Height="136" HorizontalAlignment="Left" Name="xamDialogWindow1" Width="315"
                            CloseButtonVisibility="Hidden" MaximizeButtonVisibility="Hidden" MinimizeButtonVisibility="Hidden" IsModal="True">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="71*" />
                    <ColumnDefinition Width="228*" />
                </Grid.ColumnDefinitions>
                <TextBlock Height="23" Margin="15,10,17,0" Name="textBlock1" Text="From" VerticalAlignment="Top" />
                <TextBox Height="24" HorizontalAlignment="Right" Margin="0,9,12,0" Name="textBox1" VerticalAlignment="Top" Width="224" Grid.ColumnSpan="2" />
                <Button Content="OK" Height="23" HorizontalAlignment="Left" Margin="142,65,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" Grid.Column="1" />
                <TextBlock Height="23" Margin="15,39,14,0" Name="textBlock2" Text="To" VerticalAlignment="Top" />
                <TextBox Height="24" HorizontalAlignment="Right" Margin="0,36,12,0" Name="textBox2" VerticalAlignment="Top" Width="224" Grid.ColumnSpan="2" />
                <Button Content="Cancel" Height="23" HorizontalAlignment="Left" Margin="61,65,0,0" Name="button2" VerticalAlignment="Top" Width="75" Click="button2_Click" Grid.Column="1" />
            </Grid>
        </ig:XamDialogWindow>

こんな感じになりました。

image

 

続いて、RecordFilterDropDownPopulating イベントでビルトインのダイアログ表示をキャンセルし、自作のダイアログを表示。

        // カスタムダイアログが出るイベント
        void xgrid1_CustomFilterSelectionControlOpening(object sender, Infragistics.Windows.DataPresenter.Events.CustomFilterSelectionControlOpeningEventArgs e)
        {
            // 本来出るダイアログ自体はキャンセルする。
            e.Cancel = true;
            
            // 現在のカスタムフィルタから範囲設定の情報を抽出
            string pGreaterThanOrEqualTo = "";
            string pLessThanOrEqualTo = "";
            foreach (ICondition c in e.RecordFilter.Conditions)
            {
                // 現在のカスタムフィルタの登録されている条件を走査
                if (((Infragistics.Windows.Controls.ComparisonCondition)(c)).Operator == ComparisonOperator.GreaterThanOrEqualTo)
                {
                    // Fromの情報があった場合
                    pGreaterThanOrEqualTo = ((Infragistics.Windows.Controls.ComparisonCondition)(c)).Value.ToString();
                }
                if (((Infragistics.Windows.Controls.ComparisonCondition)(c)).Operator == ComparisonOperator.LessThanOrEqualTo)
                {
                    // Toの情報があった場合
                    pLessThanOrEqualTo = ((Infragistics.Windows.Controls.ComparisonCondition)(c)).Value.ToString();
                }
            }

            // 自作ダイアログに設定
            this.textBox1.Text = pGreaterThanOrEqualTo;
            this.textBox2.Text = pLessThanOrEqualTo;
            // 自作ダイアログの表示
            xamDialogWindow1.Visibility = System.Windows.Visibility.Visible;
        }

ここでは、グリッドに登録されている現在のフィルタ条件を全てループ文で走査し、 From と To に対応する条件が見つかった場合は、ダイアログ内の対応する項目に設定しています。

最後に、自作ダイアログ内の OKボタン と Cancelボタン が押された際の処理を書けば完了です!

        //OKボタン押下時
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            RecordFilter rf = xgrid1.FieldLayouts[0].RecordFilters["Name"];

            for (int i = rf.Conditions.Count - 1 ; i >= 0 ; i--)
            {
                //一旦条件をクリアする
                if (((Infragistics.Windows.Controls.ComparisonCondition)(rf.ConditionsIdea)).Operator == ComparisonOperator.GreaterThanOrEqualTo
                || ((Infragistics.Windows.Controls.ComparisonCondition)(rf.ConditionsIdea)).Operator == ComparisonOperator.LessThanOrEqualTo)
                {
                    rf.Conditions.Remove(rf.ConditionsIdea);
                }

            }
            
            //修正した条件を適用する。
            if(textBox1.Text.Trim() != ""){
                rf.Conditions.Add(new ComparisonCondition(ComparisonOperator.GreaterThanOrEqualTo, textBox1.Text));
            }
            if(textBox2.Text.Trim() != ""){
                rf.Conditions.Add(new ComparisonCondition(ComparisonOperator.LessThanOrEqualTo, textBox2.Text));
            }

            //閉じる
            xamDialogWindow1.Visibility = System.Windows.Visibility.Hidden;

        }

        //Cancelボタン押下時
        private void button2_Click(object sender, RoutedEventArgs e)
        {
            //閉じる
            xamDialogWindow1.Visibility = System.Windows.Visibility.Hidden;
        }

※上記の「条件を一旦クリアする」の箇所が SyntaxHighlighter の都合上?おかしな表示になっています。正しいコードは添付されているサンプルコードより取得下さいませ。

OKボタン が押された際は、グリッドの現在のカスタムフィルタの条件を全てクリアし、 From 及び To で入力があった場合はそれぞれ新たにカスタムフィルタ条件を追加しています。

完成!!

image

今回ご紹介したシナリオは、実際にお客様から問合せいただいて、ご案内をしたものです。ビルトインの機能だけでもかなり充実していますが、こういった独自の作り込みもできますので是非ご参考にしてみてください!

※今回のサンプルは こちら から!!

NetAdvantage トライアル版ダウンロード
NetAdvantage は無料トライアルを用意しています。是非一度お試し下さい。

NetAdvantage ダウンロード

WebDataGrid: ビルトインのフォーカス移動ルールを変更する

WebDataGrid では、Tab キー押下で次セル移動、Enter キー押下で下セル移動など、予めユーザのキー操作に対するグリッドの振るまいがいくつか組み込まれています。このデフォルト動作を変更したいというお問い合わせは頻繁にいただくものなので、その方法を紹介します。

image

WebDataGrid のクライアント側のイベントをハンドリングし Javascript でこれらの動作を変更していきます。

まず、Enter キーを押下した際に下セルへフォーカスが移動するのを抑止します。ここでは、ActiveCellChanging というアクティブセル(見た目上のフォーカスを持っているセル)が変更される際に発生するイベントをハンドリングします。このイベント発生時にはまだアクティブセルの移動が完了していない状態で、なぜアクティブセルの変更が発生したのか(例えばクリック操作や tab キー操作など)や移動前と移動先のセルなどを判断した上で、イベントをキャンセルさせることができます。
今回は Enter キー( KeyCode = 13 )押下によってアクティブセルの変更が行われようとしている場合にキャンセルさせることで下セル移動の組み込み動作を抑止しています。

[ASPX: イベント定義部分]

            <Behaviors>
                <ig:EditingCore>
                    <Behaviors>
                        <ig:CellEditing>
                        </ig:CellEditing>
                    </Behaviors>
                </ig:EditingCore>
                <ig:Activation>
                    <ActivationClientEvents ActiveCellChanging="WebDataGrid1_Activation_ActiveCellChanging" />
                </ig:Activation>
            </Behaviors>

[Javascript]

function WebDataGrid1_Activation_ActiveCellChanging(sender, eventArgs) {
    // *****************************
    // EnterKey押下時のデフォルト動作(下のセルに移動)をキャンセルさせる。
    // *****************************
    if (eventArgs.get_browserEvent().keyCode == 13) {
        eventArgs.set_cancel(true);
    }
}

続いて、次セルへ移動させる処理を記述していきます。

上記の ActiveCellChanging イベントでキャンセルの後に続けて次セルへ移動させる処理を記述しても動きそうなものですが、それでは最下行で Enter キーを押下した際に無反応のなってしまうため、KeuDown イベントをハンドリングします。

[ASPX: イベント定義部分]

 

        <ig:WebDataGrid ID="WebDataGrid1" runat="server" Height="350px" Width="400px">
            <ClientEvents KeyDown="WebDataGrid1_Grid_KeyDown" />

[Javascript]

function WebDataGrid1_Grid_KeyDown(sender, eventArgs) {
    // *****************************
    // Enterキー押下時は右方向へ移動
    // *****************************
    // キーコードの判定()
    if (eventArgs.get_browserEvent().keyCode == 13) {
        eventArgs._cancel = true;
        // Gridオブジェクトの取得
        var grid = $find("WebDataGrid1");

        //各情報の取得
        var cell = grid.get_behaviors().get_activation().get_activeCell()   //アクティブセル
        var rowIndex = cell.get_row().get_index();   //アクティブ行のインデックス
        var columnIndex = cell.get_column().get_index();   //アクティブ列のインデックス
        var rowsCount = grid.get_rows()._rows.length;   //総行数
        var columnsCount = grid.get_columns()._items.length;   //総列数

        //各情報の取得
        if (columnIndex < columnsCount - 1) {
            //最終列以外の場合、一つ右へ移動
            var cellnew = grid.get_rows().get_row(rowIndex).get_cell(columnIndex + 1);
            grid.get_behaviors().get_activation().set_activeCell(cellnew);
        }
        else {
            if (rowIndex < rowsCount - 1) {
                //最終列でありかつ最終行ではないの場合、下の行の先頭セルへ移動
                var cellnew = grid.get_rows().get_row(rowIndex+1).get_cell(0);
                grid.get_behaviors().get_activation().set_activeCell(cellnew);
            }
        }
    }
}

 

上記コードで行っていることは以下のとおりです。

・Enter キー押下の場合、アクティブセルの移動処理を行う。
・現在、最終列(最も右のセル)以外にフォーカスがある場合、一つ右のセルへ移動
・現在、最終列であり、かつ最終行ではない場合、下の行の先頭セルへ移動

 

 

以上で Enter キー押下時に下セルではなく次セルへ移動させることができるようになりました!

この方法を応用すれば他のキーが押された場合の動作や Shift キーが併せて押されていた場合の前セル移動など、いろいろと要件に合わせて作り込むことができます。

※今回のサンプルプロジェクトのダンロードは こちら から!

 

無料トライアルダウンロードはこちら
NetAdvantage トライアル版
NetAdvantage ダウンロード

WebDataGrid: ファイル出力時のTips

弊社の ASP.NET 用のグリッドコントロール、WebDataGrid には付属する専用コントロール WebExcelExporter や WebDocumentExporter が用意されています。 これらは、WebDataGrid の内容を非常に簡単に(僅か1行で!)Excel 形式や PDF 形式、XPS 形式などで出力できるコントロールです。

        // Excelファイル出力はこれだけ!
        this.WebExcelExporter1.Export(this.WebDataGrid1);

とても便利な機能ですね!

上記のコード1文で、WebDataGrid から Excel ファイル等を生成し、更に HTTP レスポンスを書きかえてファイルダウンロードを自動で行わせてくれる素晴らしく便利なコントロールです。

しかし、WebDataGrid のセル編集を可能状態にしていた場合、注意しなければならない点がございます

WebDataGrid では、通常、セルの編集が行われた後のポストバック時にサーバ側でデータソースの更新を行い、レスポンスがクライアントサイドに返ってきたタイミングで更新された情報をグリッドにコントロール内部の Javascript で反映しています。
以下の図では、左側がグリッド編集後かつ未ポストバック状態で、2,3行目を編集しているため文字色が若干薄くかつ斜体になっています。(サーバ側のデータソースにコミットされていない状態を表しています。)また、右の図は単純なポストバックを起こした後の状態で、クライアント側で編集したデータがサーバ側のデータソースにコミットされたため薄字かつ斜体だった行が元の表示に戻っている事がわかります。
※分り易くするため、「BatchUpdating=true」のプロパティ設定をしています。

image image

 

ここで、ポストバック時にファイル出力のためにHTTPレスポンスを書きかえてしまうと、サーバ側ではデータソースが更新されているのにクライアント側では更新されていないことになってしまいます。こうなってしまうとオペレーション次第では修正したはずの内容が DB に適用されない、あるいはファイル出力されないといったような不具合に繋がってしまいます。

image

これを防ぐためには、通常行っている HTTP レスポンスを一度クライアント側に返してから、別のレスポンスでファイル出力を行う必要があります。具体的には以下のようなコードで実装することができます。

    /// ファイル出力ボタン押下時の処理
    protected void Button1_Click(object sender, EventArgs e)
    {
        //***********************************
        // Excelデータの作成
        //***********************************
        WorkbookFormat excelFormat = Infragistics.Documents.Excel.WorkbookFormat.Excel2007;

        Workbook wBook = new Infragistics.Documents.Excel.Workbook(excelFormat);

        this.WebExcelExporter1.ExportMode = Infragistics.Web.UI.GridControls.ExportMode.Custom;
        this.WebExcelExporter1.Export(this.WebDataGrid1, wBook);

        Session["WorkbookData"] = wBook;

        //***********************************
        // 一度クライアントにレスポンスを返した後、
        // 再度エクセルダウンロードをさせるスクリプトを組みこむ
        //***********************************
        // ClientScriptManagerを取得
        ClientScriptManager csMgr = Page.ClientScript;

        // ClientScriptを記述
        StringBuilder sb = new StringBuilder(string.Empty);
        sb.Append("<script type='text/javascript'>\r\n");
        sb.Append("window.onload=function(){\r\n");
        sb.Append("\tdocument.forms['form1'].action = 'Default.aspx?action=DownloadFile';\r\n");
        sb.Append("\tdocument.forms['form1'].submit();\r\n");
        sb.Append("}\r\n");
        sb.Append("</script>");

        csMgr.RegisterClientScriptBlock(this.GetType(), "SampleScript", sb.ToString(), false);
    }

上記コードでは、ファイル出力ボタンが押下された際のサーバ側イベントにて、生成したファイルをそのままクライアント出力するのではなく、一度セッションに格納しています。また、クライアントサイドの描画が終わったタイミングで自動的に再度リクエストを投げるような Javascript を埋め込んでいます。

そのため、再リクエストを受けた時の Page_Load イベントの記述が必要です。

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Session["Data"] = getData();

            WebDataGrid1.DataKeyFields = "BoundColumn_KEY";
        }

        if (Request["action"] == "DownloadFile" &&
        Session["WorkbookData"] != null)
        {            
            Workbook customWorkbook = (Workbook)Session["WorkbookData"];

            //Create the Stream class
            System.IO.MemoryStream theStream = new System.IO.MemoryStream();

            //Write the in memory Workbook object to the Stream
            customWorkbook.Save(theStream);

            //Create a Byte Array to contain the stream and send the exported sheet to the client
            byte[] byteArr = (byte[])Array.CreateInstance(typeof(byte), theStream.Length);
            theStream.Position = 0;
            theStream.Read(byteArr, 0, (int)theStream.Length);
            theStream.Close();
            Session["WorkbookData"] = null;
            Response.Clear();
            Response.AddHeader("content-disposition", "attachment; filename=ExportedTo.xlsx");
            Response.BinaryWrite(byteArr);
            Response.End();
        }

        // *************
        // Bind DataSet
        // *************
        this.WebDataGrid1.DataSource = Session["Data"];
    }

上記コードでは、再リクエストを受けた際に、セッションから直前に生成した Excel ファイルのデータを取り出し、HTTP レスポンスを書きかえてファイル出力を行っています。

image

これでファイル出力時にも正常にグリッドの内容を更新させる事が出来ました!

今回ご紹介した Tips は弊社コントロールに限った問題というわけではなく、HTTP レスポンスの書きかえを行うような処理がある場合には気をつけなければならないポイントです。いつか皆様お役に立てれば幸いです!

無料トライアルダウンロードはこちら
NetAdvantage トライアル版
NetAdvantage ダウンロード

Indigo Studio V2 きたる!

2012年12月に Version1 が公開された Indigo Studio ですが、Version2(以下V2) の公開が近づいてきています!

V2 に盛り込まれる機能で大きなものは以下の通りです。

  • プラットフォームピッカー
  • iOS プラットフォームパック
  • タッチジェスチャー対応
  • トランジション効果の追加
  • HTML5 デバイス対応の強化
  • PDF 形式でのドキュメントエクスポート

大きなポイントとしてはモバイルデバイス対応が大幅に強化されたことです。特に iOS プラットフォームパックが追加されたことにより、iPhone や iPad 向けのプロトタイプを、かなり手軽に、よりリアルに作る事ができるようになりました。(作成したプロトタイプは、ボタンひとつでWeb上にアップロードでき、iPhone や iPad 上から動作確認することができます!)

細かい点は、後ほど紹介させていただきたいと思います!

indigo-mobile-platform-html5-support-new indigo-ios-controls-key-features-image

■Indigo Studio 起動時に出るダイアログについて

2013/09/26現在、Indigo Studio を起動すると以下の図のように V2 のリリースを告知するダイアログが表示されます。

image

ここで ”Learn More” をクリックすると、米国の Infragistics サイトへジャンプし V2 へのアップグレードを促されます。V2 は、期限付きでのトライアル利用も可能ですが、有償版となっており、既に公開されている米国のサイトからでも購入することができてしまいます。(その場合は別途、米国法人の Infragistics アカウントの作成が必要となります。)
しかしながら、今後の製品の日本語対応や使い方がわからなかった時などのサポート要求を考慮し、日本での公開を待っていただき日本法人のアカウントでのご購入をお勧めしております。

日本での公開まで今しばらくお待ちいただければと思います。

また、Indigo Studio V2 は有償版として提供させていただく事となりますが、その際に既に V1 をご愛用いただいていたお客様には何らかアドバンテージが付く事になると思います。もし未だインストールしていないという方がいらしたら、是非今のうちに V1 のインストールをお済ませ下さい!

Indigo Mac バージョン ダウンロード Indigo Windows バージョン ダウンロード

※Indigo Studio V1 の紹介・使い方はこちらから