[WindowsForms] タッチ対応のドット絵おえかきアプリを作ろう!

こんにちは!今回は WindowsForms 向けのグリッドコントロールを使って、タッチ対応のドット絵おえかきアプリの作り方をご紹介したいと思います。

image

おえかきの様子はこちらから(HTML5対応ブラウザのみ再生可能)


いかがですか?
筆者の絵心はさておき、ここでは 80列 × 80行 のグリッドを使っていますが、一見するととてもグリッドコントロールを使っているようには見えないですよね。
お固い業務システムで使われるUI部品も使い方次第では面白いものができそうですね!

ポイントとしては、

・グリッドコントロールを使って、セルでドットを表現。
・条件付きのセル外観を指定することで、セルの背景色が変化。
・パン操作のイベントをハンドリングしてセルの値を変える。
・タッチ操作が完了した時のイベントをハンドリングして一連のタッチ操作ごとに状態を記憶する。※Undo(戻るボタン)に対応するため。

です。

それぞれどのように実装しているのか、コードと共に説明していきます。
※本記事の最後に、このサンプルプロジェクトのリンクがございますので、ご興味ある方はサンプルをダンロードしてみて下さい。

 

グリッドでドット絵の表現

まず、グリッドのドット絵の表現部分です。今回のサンプルでは、タッチ対応している UltraWinGrid コントロールを使い、12px × 12px のセルを 6400個( 80列 × 80行 )用意します。

以下は Load イベントでのコードです。※デザイナ側で既にグリッドや各ボタンは配置済みです。

        const int columnsCountWidth = 80;  //列数
        const int columnsCountHeight = 80; //行数
        const int cellSize = 12; //1セルのサイズ

        //Undo用のデータ保持スタック
        private Stack<DataTable> dtHistory = new Stack<DataTable>();

        private void Form1_Load(object sender, EventArgs e)
        {

            this.Width = 1000;
            this.Height = 1000;

            //データバインド
            DataTable dt = GetData();
            this.ultraGrid1.DataSource = dt;

            dtHistory.Push((DataTable)(dt.Copy()));
            dt.AcceptChanges();

            // **********************************************
            // 条件付き書式でタッチ後のセルの外観(Appearance)を定義
            // **********************************************
            //条件合致の際の外観(Appearance)
            Infragistics.Win.Appearance appearanceForCondition = new Infragistics.Win.Appearance();
            appearanceForCondition.BackColor = Color.Black;
            appearanceForCondition.ForeColor = Color.Black;
            //条件式と外観の設定
            Infragistics.Win.ConditionValueAppearance conditionValueAppearance = new Infragistics.Win.ConditionValueAppearance(new Infragistics.Win.ICondition[] {
            ((Infragistics.Win.ICondition)(new Infragistics.Win.OperatorCondition(Infragistics.Win.ConditionOperator.Equals, "1", true)))}, new Infragistics.Win.Appearance[] {
            appearanceForCondition});


            // **********************************************
            // その他列の設定
            // **********************************************
            //キー列は非表示
            this.ultraGrid1.DisplayLayout.Bands[0].Columns["Number"].Hidden = true;
            //セルは選択のみ
            ultraGrid1.DisplayLayout.Override.CellClickAction = Infragistics.Win.UltraWinGrid.CellClickAction.CellSelect;
            //選択操作を無効にする。
            ultraGrid1.DisplayLayout.Override.SelectTypeCell = SelectType.None;
            ultraGrid1.DisplayLayout.Override.SelectTypeRow = SelectType.None;
            ultraGrid1.DisplayLayout.Override.SelectTypeCol = SelectType.None;
            ultraGrid1.DisplayLayout.Override.ActiveCellAppearance = null;
            ultraGrid1.DisplayLayout.Override.ActiveRowAppearance = null;
            //各セルの起きさの設定
            ultraGrid1.DisplayLayout.Override.MinRowHeight = 1;
            ultraGrid1.DisplayLayout.Override.DefaultColWidth = cellSize;
            ultraGrid1.DisplayLayout.Override.DefaultRowHeight = cellSize;
            //ヘッダ非表示
            ultraGrid1.DisplayLayout.Bands[0].HeaderVisible = false;
            ultraGrid1.DisplayLayout.Bands[0].ColHeadersVisible = false;
            ultraGrid1.DisplayLayout.GroupByBox.Hidden = true;

            //各列ごとの定義
            for (int index = 1; index <= columnsCountWidth + 1; index++)
            {
                //条件付き外観の適用
                this.ultraGrid1.DisplayLayout.Bands[0].Columns[index].ValueBasedAppearance = conditionValueAppearance;
                //ReadOnlyに設定
                ultraGrid1.DisplayLayout.Bands[0].Columns[index].CellActivation = Infragistics.Win.UltraWinGrid.Activation.ActivateOnly;    
            }

            //イベントハンドリング
            ultraGrid1.PanGesture += ultraGrid1_PanGesture;
            ultraGrid1.GestureCompleted += ultraGrid1_GestureCompleted;
        }

Loadイベント内で行っているおおまかな流れとしては、

・グリッドに対してデータ(ここではDataTable)をバインド
・各列に対して条件付き外観を設定
・ドット絵おえかきアプリに必要なグリッドコントロールの各種設定
・タッチ対応のイベントハンドラの追加

です。

条件付き外観の設定部分(21行目~31行目)では、セルに”1”という値が入った場合は、背景色と文字色を黒にするという条件付き外観(ConditionValueAppearance)を全ての列に対して適用しています。
ユーザがグリッド上を指でなぞった際に、なぞられたセルの値を”1”に変えることで、セルが黒く塗りつぶされるという仕組みです。

 

パン操作イベントとセルの値変更

続いて、ユーザが指でグリッドをなぞっている時に、タッチされているセルの値を変える処理を加えます。
弊社の WindowsForms コントロールでは、ズームや回転、2本指タップなど様々なタッチジェスチャをサポートしています。

オンラインヘルプ タッチジェスチャのイベント:
http://help.jp.infragistics.com/Doc/Win/2014.1/CLR4.0/default.aspx?page=Touch_Gestures_Events.html

今回は、ユーザのパン操作をを取得したいので、PanGesturイベントをハンドリングします。

        // **********************************************
        // PanGestureイベント(文字の描画)
        // **********************************************
        void ultraGrid1_PanGesture(object sender, Infragistics.Win.Touch.PanGestureEventArgs e)
        {
            //タッチされているエレメントの取得
            UIElement el = ultraGrid1.DisplayLayout.UIElement.ElementFromPoint(e.Location);

            UltraGridCell cell = GetCell(el);// エレメントからセルの取得

            if (cell != null)//セルが取得できたかどうかの判定
            {

                cell.Value = "1";// 値を設定

            }
            e.Handled = true;
        }

        //エレメントからセルを取得
        private UltraGridCell GetCell(UIElement element)
        {
            if (element == null || element.Parent == null)

                return null;

            if (element.Parent is CellUIElement)

                return ((CellUIElement)element.Parent).Cell;
            else
                return GetCell(element.Parent);
        }

PanGestureイベントでは、引数からタッチされているロケーション情報が取得できるため、グリッドの Element FromPoint メソッドから指の直下にあるエレメントを取得し、そのエレメントがCellUIElementであった場合にそのセル値を”1”に変更しています。
また、本来のパン操作により範囲選択のような挙動が起きてしまうため、イベント内の最後で e.Handled に True を設定し、そのデフォルト動作をキャンセルさせています。

 

タッチ操作完了時のイベントとUndoの組み込み

このおえかきアプリでは、動画にもあるようにUndo動作(戻るボタン)を組み込んでいます。PanGesture イベントにて連続的にセルの値を変更していますが、Undoを実装するためには一連のパン操作が終わったタイミングで、グリッドの状態を記録していく必要があります。今回は、一覧のタッチ操作が終わったタイミングで呼ばれるイベント GestureCompleted でDataTableのスタックにグリッドの状態を記録していく方法をとっています。(Redoも行いたい場合は、スタックじゃなくてリストとか用意しましょう。)

まず、初めのForm_Loadイベントのグリッドのデータバインドの直後で、事前に用意したDataTableのスタックに、初期状態を追加(Push)しています。
続いて、GestureCompleted イベントでの処理は以下のとおり。

        // **********************************************
        // GestureCompletedイベント
        // **********************************************
        void ultraGrid1_GestureCompleted(object sender, Infragistics.Win.Touch.GestureCompletedEventArgs e)
        {
            // **********************************************
            // 指を話したタイミングで最新のデータ状態を記憶する。
            // **********************************************
            DataTable dt = ultraGrid1.DataSource as DataTable;
            dt.AcceptChanges();
            dtHistory.Push((DataTable)(dt.Copy()));

        }

ポイントとしては、バインドしているDataTableを直接 Push するのではなく、Copy() を入れるということです。※こうしないと常に最新のグリッドのDataTableが参照されます。

最後に、戻るボタン押下時の処理を追加します。

        // **********************************************
        // 戻るボタン押下時
        // **********************************************
        private void ultraButton2_Click(object sender, EventArgs e)
        {
            if (dtHistory.Count <= 1)
                return;

            dtHistory.Pop(); //最新データを削除
            ultraGrid1.DataSource = dtHistory.Peek().Copy(); //一つ前のデータを取得しデータソースに設定
        }

一番最後に積まれたデータを削除し、1つ前のデータをグリッドの再バインドする事で、1つ前の状態に戻しています。

その他、最初からボタンの処理を組み込んだら、

        // **********************************************
        // 最初からボタン押下時
        // **********************************************
        private void ultraButton1_Click(object sender, EventArgs e)
        {
            //初期データバインド
            DataTable dt = GetData();
            this.ultraGrid1.DataSource = dt;
            dtHistory.Clear();
            dtHistory.Push((DataTable)(dt.Copy()));
            dt.AcceptChanges();        
        }

完成です!

へー、こんなことできるんだ!と感心いただけた方もそうでもなかった方も、ご紹介したように WindowsForms でも UI 部品でタッチ対応しているものもあるという事を頭の片隅におきつつ、チャンスがあれば是非使ってみてください。

image 

 

関連記事:

WindowsFormsコントロールのタッチサポートについて(イベント編)

WindowsFormsコントロールのタッチサポートについて(デザイン編)

 

本稿のサンプル:
WinGrid_DotPaintApp.zip

 

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

製品のビルドバージョンの扱いとバージョンユーティリティについて

弊社の製品は、年に数回のメジャーリリースと不具合修正を含むサービスリリースを各開発プラットフォーム向けにご提供しております。(2014年現在、メジャーリリースは年に2~3回、サービスリリースは年に9回程度のご提供となっております。)

新しいメジャーバージョンのインストールを行った場合には、既にインストール済みのバージョンの製品は更新されずに新しいバージョンの製品がインストールされ、サービスリリースの適用を行った場合はアセンブリが更新されビルドバージョンの後方の下4桁が上がります。

(例)
メジャーバージョンアップ:13.2.20132.1009 → 14.1.20141.1013
  ※上記の場合、13.2.20132.1009は残ります。
サービスリリース適用:14.1.20141.1013 → 14.1.20141.2011

[メジャーリリースに関する注意点]
新しいメジャーバージョンをインストールした場合には、古いメジャーバージョンは残り、1開発端末内に複数のメジャーバージョンを同居させることができます。
しかし、1つのアプリケーションのプロジェクト内に複数のメジャーバージョンを同居させることはできません。
例)
1つの開発端末にバージョン 13.1, 13.2, 14.1 をインストール → OK
1つのアプリケーション(プロジェクト)からバージョン 13.2 と 14.1 を参照 → NG

[サービスリリースに関する注意点]
サービスリリースをインストールした場合、一つ前のサービスリリースは上書きされビルドバージョンの下4桁が進みます。したがって、1つの開発端末内に(同一のメジャーバージョンかつ)複数のビルドバージョンを同居させることはできません。
例)
1つの開発端末に 13.2.20132.1009 と 14.1.20141.1013 が同居 → OK
1つの開発端末に 14.1.20141.1013 と 14.1.20141.2011 が同居 → NG

このメジャーリリース及びサービスリリースは各プラットフォーム向けに提供されておりますが、インストールされている製品のバージョンをアップグレードした場合、それに伴い製品を利用しているアプリケーション自体も更新する必要があります。
具体的には、WindowsForms, ASP.NET, WPF, Silverlight のアプリケーションでは、弊社製品のアセンブリ参照を新しくインストールされたバージョンに書き換え、 Ignite UI (jQuery) を利用しているアプリケーションではプロジェクトで使用している Javascript ファイル及び CSS ファイルを置き換える必要がございます。
※Ignite UI でも MVC を利用している場合は、MVC モジュールの参照アセンブリを更新する必要があります。

 

バージョンユーティリティについて

上記で説明した”参照の書き換え”作業は、手動で行っていただく事も可能ですが、弊社のバージョンユーティリティというツールをお使いいただく事で簡単に書き換えを行う事ができます。
このツールの基本的な説明・利用方法については以下のヘルプページに記述がございます。

旧バージョンからのアップグレードについて:
http://jp.infragistics.com/project_upgrade.aspx

また、上記ヘルプ内で紹介されているバージョンユーティリティは、現在はVisualStudio上でアドオンとしてご利用いただく事ができます。非常に簡単にバージョンの上げ下げを行う事ができますので、サービスリリースの適用の際などには是非ご参考にしていただければと思います。

具体的には、以下の手順でバージョン操作が行えます。

1.アップグレード前の状態
サービスリリースを適用した後の場合、参照エラーとなっていることがあります。
(※新しいメジャーバージョンをインストールした場合は、古いバージョンも残っているためエラーにはなりません。参照を書き換えない限り古いバージョンを参照しています。

image

2.バージョンユーティリティの起動
右クリックでInfragisticsバージョン○○へのアップグレードを選択すると、バージョンユーティリティが起動します。

image

3.バージョンユーティリティの操作
開発端末にインストールされている最新のバージョンへバージョンアップする場合は、アップグレードボタンをクリックすると処理が始まります。
もし開発端末に複数のメジャーバージョンがインストールされている場合は、オプション選択からどのメジャーバージョンにアップ(またはダウン)するのか調整する事ができます。

image

4.バージョン操作後

image

これでバージョン操作の完了です。アプリケーションを実行し動作を確認してください。

[Xamarin.iOS] [NucliOS] バーコード表示アプリを作ってみる

先月 Xamarin の勉強会(すまべん特別編「Xamarin 2.0であそぼう!」@関東)に参加させていただきました。Xamarin開発におけるポイントや、MVVMフレームワークの活用事例などを聞くことができましたが、個人的には Xamarin Studio が予想以上にIDEとしての使い勝手がよさそうだった事と、思っていたよりも既に実業務で利用されていた事が印象に残りました。

さて、弊社 Infragistics でも iOS 向けの NucliOS というコントロールを出しており、これがなんと Xamarin 対応しております。そこで今回は、Xamarin Studio 上で NucliOSコントロールをどのように利用するのか簡単なサンプルを交えてご紹介します。また、Xamrin.iOSの基本的な開発方法についても少し書いているので、Xamarin や iOS 開発に興味のある方は是非チェックしてください。

igBarcodeView を使う

今回は、先日リリースされた NucliOS Volume2014.1 の新コントロール igBarcodeView を使って、入力した文字列をバーコード表示する簡単なサンプルアプリを作成します。※Xamarin Studio for Macを利用しています。

NucliOSの30日間無償トライアル:
http://jp.infragistics.com/products/NUCLiOS.aspx#Overview

igBarcodeView オンラインヘルプ:
http://help.jp.infragistics.com/iOS/2014.1/IGBarcodeView.html

それでは早速 Xamarin Studio for Mac を使ってサンプルを作成していきます。今回は、 iPhone の Single View Application でサンプルを作成します。

image

参照の追加

プロジェクトが作成できたら、コントロールを追加する前に igBarcodeView に必要な Infragistics アセンブリの参照設定を行う必要があります。
XCodeで NucriOS を利用するときは、”IG.framework” か “IGChart.framework” のフレームワークを追加する必要がありますが、Xamarinでは “IG.dll” か “IGChart.dll” のDLLファイルを参照に追加する必要があります。
今回使うの igBarcodeView コントロールはチャートではないため “IG.dll” のみを参照追加します。

image
参照の追加

image 
IG.dll の追加

image
“全て”タブに表示されていない場合

これで、Infragistics クラスを利用できるようになりました。実際にコードを記述する ViewController ファイルに名前空間をインポートしておきます。

image

 

 

コントロールの追加

標準コントロールであれば、XCodeのインタフェースビルダー等利用することでデザイナ上でコントロールの配置を行うことができますが、NucliOSではコード上でコントロールの配置を動的に行う必要があります。

こちら に従い以下コードを ViewDidLoad の中に埋め込みます。
igBarcodeView コントロールをサイズ指定しつつインスタンス化し、バーコードとして表示する文字を設定した後に、Viewに追加しています。

this.View.BackgroundColor = UIColor.White;
RectangleF barcodeRect = new RectangleF(0, 0, 300, 200);
IGBarcodeView barcode = IGBarcodeView.CreateBarcodeFrame(IGBarcodeType.IGBarcodeTypeCode128, barcodeRect);
barcode.AutoresizingMask = UIViewAutoresizing.FlexibleWidth|UIViewAutoresizing.FlexibleHeight|
                                      UIViewAutoresizing.FlexibleLeftMargin|UIViewAutoresizing.FlexibleBottomMargin|
                                      UIViewAutoresizing.FlexibleRightMargin|UIViewAutoresizing.FlexibleTopMargin;
barcode.SetValue ("Getting Started");
barcode.Center = this.View.Center;
this.View.Add (barcode);

 

image

この時点で一度実行してみます。

image

しっかりバーコードが表示されました!

iOSネイティブコントロールとの連携

続いては、ここにテキストボックスとボタンを追加し、ボタンを押した時にテキストボックスに入力されている値をバーコードとして表示させるように変更してみます。ここからは弊社コントロール云々というより Xamarin の基本的な扱い方になります。

まずはテキストボックスとボタンを配置します。(Xamarin 4.3.4以降で iOS Designer が追加されていますが、ここではXCodeの InterfaceBuilder というデザイナを使用しています。iOS Desinerの有効化方法は こちら で紹介されています。)
Xamarin上でxibファイルをダブルクリックするとXCodeのデザイナが立ち上がるので、そこで Button と TextField コントロールを追加します。

 image
***ViewController.xibをダブルクリック

image 
XCodeの InterfaceBuilder 上でコントロールを追加

image 
ボタンの表示を変更

コントロールを配置したら、ヘッダファイル(*.h)との紐づけを行います。

image Ctrlキーを押しながらヘッダファイルへ紐づけ

 

image 
テキスト は Outlet を選択し code と名前を付ける

image 
ボタンは Action を選択し、showBarcode と名前を付ける

このあたりの手順は、XCodeでの開発手法と同じですがコードビハインド側で配置したコントロールにアクセスしたり(outlet)、イベントをハンドルしたり(Action)するためのものです。

ここでXamarinに戻ってデザイナファイル(*.designer.cs)を確認してみると、先ほど追加した outlet と Action が自動的に反映されています。

image

この時点で一度 Xamarin 側で実行して表示を確認してみます。

image

デザイナ上で追加したテキストとボタンがしっかり表示されていますね。
ここに、ボタンが押された時にテキストの値をバーコードに表示する処理を加えます。

image 
igBarcodeSampleViewController.designer.cs

image
igBarcodeSampleViewController.cs

igBarcodeView の定義をデザイナファイルへ移動し、追加した Action(showBarcode) の中でTextFieldの値を igBarcodeView に設定しています。

実行してみます。

image     image

左がテキスト入力後、右がボタン押下後です。確かにバーコードが変わったように見えますがキーボードが邪魔でよくわかりません。ボタンを押した時にキーボードを閉じるように処理を加えましょう。
Objective-c では”[self.textView resignFirstResponder]”でキーボードをしまう事ができますが、これをC#で書いてみると、、、

image

ありました。これで再度実行してみます。

image     image

できました!このあたりは Objective-C と同じ要領でできるようです。

今回はXamarin Studio for Mac と XCode の InterfaceBuilder を使ってサンプルを作成しましたが、デザイン部分とコード部分の連携はスムーズに開発できるという印象でした。現時点では、Visual Studio のアドイン(Xamarin for Visual Studio)の方で開発を進めると少し勝手は違うようですが、今後 iOS 向けのデザイナが搭載されてVS上で画面デザインが行えるようになるそうで、今後に期待が持てます!

NucliOS はここで紹介したバーコードコントロール以外にも、チャートやゲージ、グリッドなどニーズの多いコントロールが用意されています。iOSのネイティブアプリ開発をされている方、興味のある方は是非一度トライしていただければと思います。

NucliOS チャート:
http://www.infragistics.com/products/ios/features/charting

NucliOS グリッド:
http://www.infragistics.com/products/ios/features/grid

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

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

Posted: 14 May 2014, 12:28 | 0 Comments
Filed under: , , ,
[ASP.NET] 最新バージョン(14.1)での旧スタイル(13.2以前)の適用方法

先日 Infragistics 2014 Volume1 が発表されました。WebプラットフォームではjQueryコントロールである Ignite UI にて新機能・新コントロールなど大幅なアップデートがありましたが、ASP.NET 向けコントロールでもデフォルトのルックアンドフィールが大きく変わりました。

image  image

左側がバージョン13.2までのデフォルトスタイルで、右側がバージョン14.1のデフォルトスタイルです。水色基調の立体感のある少しポップな感じのデザインから、黒基調のクールなフラットデザインに変わりましたね!上の図ではタブ、ドロップダウン、グリッドを配置していますが、ASP.NET向けコントロールが一律でこのようなデザインに変わりました。この見た目は、弊社のjQueryコントロールである Ignite UI の見た目と同じであり、これでプラットフォームを跨いで見た目が統一されたことになります。

image
Ignite UI グリッドコントロールの見た目

バージョンアップしたいけどスタイルは変えたくない!

細かいアップデートや不具合修正の適用を目的に、最新バージョンである14.1にしたいが、既にアプリがリリースされていてデフォルトスタイルを引き継ぎたいという要望は多々あるかと思います。14.1にバージョンアップし、DefaultStyleを再読み込みすると新しいスタイルが読み込まれてしまいますので、そんなときの対策を以下に示します※スタイルの再読み込み( CSS 群である ig_res フォルダ内を一度削除し、デザイナ表示時に再びデフォルトスタイルのインポート)を行わなければ昔のスタイルのままですが、それでは CSS 群が最新版にアップグレードされていないことになってしまうため、バージョンアップ時は CSS 群の再読み込みを行うべきです。

[13.2以前の旧スタイル対適用方法(全体)]

1.バージョン14.1にアップグレード後、Web.Config で読み込むスタイルを Default らIG2007 に変更します。

image

2.Infragisticsコントロールを含むASPXファイルをデザイナで開くと、自動的に旧スタイルのCSS群 “IG2007” が ig_res フォルダにインポートされます。

image

これでプロジェクトに含まれるすべてのIGコントロールのスタイルが13.2以前のスタイルに変更されました。

[13.2以前の旧スタイル対適用方法(コントロール単位)]

コントロール単位でも旧スタイルを適用することができます。以下のようにコントロールの StyleSetName プロパティからCSSのインポートからコントロール単位の設定まで行えます。

1.Infragistics コントロールのうちの一つ(何でもよい)のプロパティウィンドウから StyleSetName を選択し、インポートをクリックします。

 image

2.「スタイルセットをインポート」ダイアログで ”IG2007” にチェックをつけ、インポートボタンを押します。※このIG2007というのが、13.2以前のデフォルトスタイルです。

image

これで、コントロールごとにスタイルの設定を行うことができました。

旧製品のデフォルトスタイルの適用方法をご紹介しましたが、是非この機会に最新のデフォルトスタイルを適用してみて、フラットデザインへの移行を検討されてみてはいかがでしょうか。

 

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

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

Posted: 12 May 2014, 17:56 | 0 Comments
Filed under: ,
[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 ダウンロード