2011年10月27日木曜日

◆アプリケーション開始時に処理を行う

アプリケーション開始時に処理を行うには、Applicationクラスの Application_Initialize()メソッドにに処理を書けば良さそうだ。

これらは、お約束通りpartialになっているのだが直接的にそのひな形を作る方法は無く、以下のような手順で代用する?

    1. 適当な画面をソリューションエクスプローラーでダブルクリックしてデザイナーを表示させる。
    2. 「ツールバー」「コードの記述」「アクセス制御メソッド」にて「画面名_CanRun」をクリック。
      image
    3. Applicationクラスが表示されるので追加された「画面名_CanRn」メソッドは削除(必要がなければ)
    4. 右上のドロップダウンから「Application_Initialize()」メソッドを選択して追加。
    5. 必要な処理を追加

今回は以下のように適当なメッセージを表示させてみた。

        partial void Application_Initialize()
{
System.Diagnostics.Debug.WriteLine("◆開始しますよ");
Microsoft.LightSwitch.Threading.Dispatchers.Main.BeginInvoke(
() => { System.Windows.MessageBox.Show("◆開始しますよ"); });
}

image

2011年10月21日金曜日

◆Grid行の複数選択を可能にする

以下に解説がある。
How To Enable Multiple Row Selection In LightSwitch's Grids - MSDN サンプル ギャラリー

基本的には前回、以下でやったのと同様な方法でGridコントロールをFindして準備OKになったイベントを拾ってSelectionModeプロパティにDataGridSelectionMode.Extendedを設定してやるというパターンだ。
LightSwitch: ◆ConverterとFocusイベントを使って表示形式をカスタマイズする

SelectionModeプロパティの設定自体は問題ないのだが、サンプルの後半で選択状態に応じてボタンの有効・無効を切り替えている処理はなんとなくメタメタな気がする。

CanExecuteイベントハンドラーで有効無効をBool値で返すだけだと思うのだが、そもそも行選択の状態を変更したときにCanExecuteが発生してくれない。
image

新規行を選択したときに「編集」「削除」ボタンは無効になるのでCanExecuteとは別の仕掛けがあるのだろうか・・・。
image

CanExecute編集用のメニューも、状況に応じて違いがあり混乱する。
imageimage

デフォルトのボタンとユーザー追加したボタンで違うのかとも思ったがそうでも無さそう。(デフォルトのボタンでも画面テンプレートによって違う)

なんとなくどうでも良くなってきたので放置。

 

そもそも、これまではIDEでコントロールのプロパティをポチっと設定するだけでできていたものにたいして此の様な手間をかけるというのは方向性が間違っている。

現時点ではLightSwitchをそのような用途に使うべきでは無いのだろう。

SQLServer Management Studio のデータ参照・更新機能が簡単にカスタマイズ出来る様になった、っと捉えれば非常に便利なツールだ。

それ以上のことをやろうとすると我々一般ユーザーには他の開発ツール以上に敷居が高くなってしまうようだ。

2011年10月10日月曜日

◆ConverterとFocusイベントを使って表示形式をカスタマイズする

簡単に全体の機能を触ってみて気になるのが、表示項目の形式を簡単に変えられないこと。
また、入力項目のValidationも一応コーディングのインタフェースは標準で用意されているが、
image
昨今のアプリケーションであれば、ある程度のValidationはノンコーディングで可能になっていることを考えると、簡単さが売りと思われるLightSwitchで必ず(必須、最大長以外)コーディングが必要になるのは残念だ。

Validationはとりあえずコーディングインタフェースがあるから良いとしても表示形式を調整できないのはちょっと困る。

という事で、表示形式のカスタマイズ方法を探ってみた。
基本的にはLightSwitchもWPFアプリなのだろうからコンバーターとかを使うのかと思い、とりあえず以下のように実現した。
LightSwitchに関してはまだ情報も少なく、これが妥当なやり方なのかも判らないが一応こんな方法でも出来たよと言う事で。

<データ>

いつものように、NorthwindのCustomersをデータソースに使用。

<画面>

Customersをデータソースに、「一覧および詳細画面」のテンプレートを使って画面を作る。
コントロールツリーの「CustomersItemDetails」で「追加」ボタンをクリックし「新しいカスタムコントロール」をクリックする。

image

以下のネームスペースを展開して「TextBox」コントロールを追加する。
imageimage

ここでは「Country2」という名前にした。
image
 

ツールバーの「コードの記述」から「CustomersListDetail_Created」をクリックしてコードエディタを開く。

image

表示された「コードエディタ」にて以下のようなコーディングを追加した。

    public partial class CustomersListDetail
{
partial void CustomersListDetail_Created()
{
// ここにコードを書き込んでください。
this.FindControl("Country2").SetBinding(TextBox.TextProperty,
"Screen.Customers.SelectedItem.Country",
new CountryConverter(),
System.Windows.Data.BindingMode.TwoWay);

this.FindControl("Country2").ControlAvailable += Country2FieldAvailable;

}


private void Country2FieldAvailable(object sender, ControlAvailableEventArgs e)
{
((System.Windows.Controls.Control)e.Control).GotFocus += Country2GotFocus;
((System.Windows.Controls.Control)e.Control).LostFocus += Country2LostFocus;
}

private void Country2GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
var txtObj = (System.Windows.Controls.TextBox)sender;
txtObj.Text = (string)(new CountryConverter()).ConvertBack(txtObj.Text, null, null, null);

((System.Windows.Controls.TextBox)sender).SelectAll();
}

private void Country2LostFocus(object sender, System.Windows.RoutedEventArgs e)
{
var txtObj = (System.Windows.Controls.TextBox)sender;
txtObj.Text = (string)(new CountryConverter()).Convert(txtObj.Text, null, null, null);

}

class CountryConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return "(" + value + ")";
}

public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return (((string)value).Replace("(", "")).Replace(")", "");
}
}

}

6行目から9行目で「Country2」コントロールを検索し、Textプロパティに「Country」の値をバインドしている。
その時、コンバータとして自前の「CountryConverter」を指定。


また、Focusに応じても表示を切り替えたいので「GotFocus」と「LostFocus」にイベントハンドラーを登録する。
登録するタイミングとして、コントロールが有効になった後に行う必要があるようなので一旦11行目で「ControlAvailable」イベントを拾って、そこで登録処理を行う。


あとはConverterクラスを追加して「Convert」メソッドと「ConvertBack」を実装すればOKだ。
ここでは動作の確認だけなので便宜的に「Country」に括弧をつけて表示させている。


これで実行すると以下のような感じになる。


imageimage


とりあえず良い感じだが、ちょっとフォーマットしたいだけの要求には大げさな感じ。


もう少しなんとかならないものだろうか・・・。

2011年10月3日月曜日

◆承認、アクセス許可機能

ガイドツアーのサンプルで出てきた承認、アクセス機能を纏めておく。

<データ>

「Northwind」から「Categories」と「Products」を追加しそれぞれのデータに対して「編集可能グリッド画面」を作成する。
2011-10-03 16h08_58

<プロパテイ設定>

「ソリューションエクスプローラー」にて「プロパティ」をダブルクリックしてデザイナー画面を表示させる。
image

「画面ナビゲーション」タブをクリックすると以下のような画面が表示される。
2011-10-03 16h16_58

ここで、「タスク」フォルダにある画面は上記で作成した画面であり、実行時に表示される。
その下の「管理」フォルダにある画面(役割、ユーザー)は自動生成されたものであり通常は表示されない。
そこで、まずこれを開発時にも表示するようにしてみる。

この「管理」画面を表示するにはビルトインの「SecurityAdministration」権限が必要になっているので、開発ユーザーに対してその権限を許可する。
手順としては、「アクセス制御」タブをクリックし「フォーム認証を使用する」をチェックし、「SecurityAdministration」の「デバッグ様に許可」チェックボックスをONにする。(Windows認証でも良いと思うが、説明しやすいのでここではフォーム認証を使う)
image
また、同画面にて以下のように、画面に対する権限を作成する。
image

これで実行してみると、以下のように「管理」フォルダが表示されるのが判る。
また、開発時はログイン画面などは表示されず、自動的にデバッグユーザー(テストユーザー)でログインされる。(右下に表示)
image

試しに「ユーザー」をクリックして適当なユーザーを追加してみる。
2011-10-03 16h36_04

一旦保存し、次に「役割」画面にて「管理者」ロールを作成し「アクセス許可」に「セキュリティ管理」を追加。
image

同様に、「一般ユーザー」ロールを作成し「アクセス許可」に「カテゴリー権限」と「プロダクト権限」を追加する。
image

どうやら「役割」側から「ユーザー」は設定できないようなので(ロールから先に作れば良いのね・・)、ここで一旦保存し、またユーザーに戻る。
ここで、「test1」を「管理者」、「test2」を「一般ユーザー」にしてみる。(ロールが表示されない場合は最新の情報に更新をクリックすると良いようだ)
image

最後に「保存」ボタンをクリックする。

<コーディング>

実際に今作成した権限を利用して画面表示を制御する。

    1. 「ソリューションエクスプローラー」にて「EditableCategoriesGrid」画面をダブルクリックしてデザイナー画面を表示する。
    2. 「ツールバー」の「コードの記述」から「EditableCategoriesGrid_CanRun」メソッドを選択。
      image
    3. 表示されたコードエディタで以下のコーディングを追加。
              partial void EditableCategoriesGrid_CanRun(ref bool result)
      {
      // 結果を目的のフィールド値に設定します
      if(this.User.HasPermission(Permissions.ViewCategories))
      {
      result = true;
      }else{
      result = false;
      }
      }


      ちなみに、この時点で実行してみると「EditableCategoriesGrid」画面が表示されなくなるのが判る。

    4. 同様に、「EditableProdustsGrid」画面をダブルクリックしてコーディングを追加する。












<発行>


開発環境では簡単にユーザーを切り替える機能が見つからなかったので、とりあえず「発行して」から動作確認してみようと思う。(テストユーザーに権限を付与することで確認は可能だが)




    1. 「ビルド」メニューで「発行」をクリック

    2. 「クライアント構成」で「デスクトップ」を指定して「次へ」

    3. 「アプリケーションサーバーの構成」で「ローカル」を指定して「次へ」

    4. 「発行の出力」で「データベースに直接発行する」を指定して「次へ」

    5. 「データーベース接続」で接続文字列はデフォルトのまま「次へ」

    6. 「認証」では、先程すでに管理者を作成済みなので「アプリケーション管理者は既に作成されている」を選択し「次へ」(管理者を作成していない場合はここで指定する)

    7. 「必須コンポーネント」はデフォルトのまま「次へ」

    8. 「その他の接続」もデフォルトのまま「次へ」

    9. 「証明書の指定」もそのまま「次へ」

    10. 「概要」にて「発行」ボタンをクリック

<SETUP>


プロジェクトのフォルダに「Publish」というフォルダが作成されて「Setup.exe」が配置されているので実行してセットアップ。
image


セットアップが終わるとアプリケーションが起動されてログイン画面が表示されるのでログイン。
って、ログイン出来ない・・・。


ん~、どうやら開発時のユーザーをセットアップしてくれるなんてことはしないのね。(DBを確認してもUserテーブルは空だった)


仕方が無いので「発行ウィザード」に戻り、手順6にて「test1」ユーザーを追加したのちに再発行し、Setupを再実行。




「test1」でログインし、開発環境で行ったのとまったく同様に役割とユーザーを追加する。


追加した「test2」ユーザーでログインすると以下のような表示となる。(ちなみに、ユーザーの切替は再起動するしか無さげ)
2011-10-03 18h59_35

2011年9月30日金曜日

◆詳細画面にリンクする

データ項目をリンクラベルで表示して詳細画面を表示してみる。

サンプルは前回のものを使用する。

    1. 「ソリューションエクスプローラー」にて「顧客注文一覧」画面をダブルクリックしてデザイナー画面を表示させる。
    2. コントロールツリーにて「Orders Item」の中の「Order ID」を選択し「プロパティウィンドウ」にて「リンクとして表示」をチェックする。
      image
    3. これで実行すると以下のような画面が表示されて、「Order ID」をクリックすると詳細画面が表示される。
      ちなみに、この詳細画面は特に自分で作らなくても自動生成されるようだ。
      2011-09-30 16h07_222011-09-30 16h07_49

 

次に、ここで表示される詳細画面はデザイナーから簡単にカスタマイズできるものではなさそうなので、独自の詳細画面を作り差し替えてみる。

    1. 「ソリューションエクスプローラー」にて「画面」フォルダを右クリックして表示されたメニューから「画面の追加」をクリックする。
      image
    2. 「新しい画面の追加」ダイアログにて以下のように設定して「OK」ボタンをクリックする。
      image
    3. 画面が差し替わったのが分かるようにプロパティを一つ追加する。
      ツールバーの「データ項目の追加」をクリックし以下のローカルプロパティを追加する。
      image
    4. 追加された「タイトル」を画面ツリーにドラッグ。
      image
    5. 「プロパティ」ウインドウで表示名を「注文詳細」に変更。
      image
    6. 「プロパティ」画面にて「読取り専用コントロールの使用」をチェックしておく。(特に深い意味は無いが)
      image
    7. これで実行すると以下のような画面が表示されて「Order ID」のリンクをクリックすると差し替えた詳細画面が表示される。
      読み取り専用に変えたためラインが何も表示されず雑然とした感が強いが、ココらへんを調整する術がGUI上からは見当たらない。
      全般的に見栄えを変更する機能には力が入れられていない気がする。
      image
    8. ここで、詳細画面を作っただけで自動的に表示画面が差し替えられるのは、手順2にて「規定の詳細画面として使用」にチェックが入っているためだと思う。
      明示的に切り替えるには「顧客別注文一覧」の画面ツリーにて「Order ID」のプロパティ「ターゲット画面」に「注文詳細」を指定すれば良い。
      image

◆ボタンの有効無効を制御する

状況に応じてボタンを非活性にする方法をメモしておく。

サンプルは前回のものを使用する。

注文の件数が4件を超える顧客には新規の注文を制限してみる。

    1. 「ソリューションエクスプローラー」にて「顧客注文一覧」画面をダブルクリックしてデザイナー画面を表示する。
      image
    2. 「Orders By Ship Via1」データグリッドの「追加」ボタンを右クリックして表示されたメニューから「CanExecuteコードの編集」をクリックする。
      image
    3. 表示されたソース画面にて以下のコーディングを追加する。
              partial void OrdersByShipVia1AddAndEditNew_CanExecute(ref bool result)
      {
      // Write your code here.
      if (OrdersByShipVia1.Count > 4) { result = false; }

      }

    4. これを実行して4件以上の注文がある顧客を選択すると「追加」ボタンが非活性になる。
      image

◆クエリーとのデータ連結

LightSwitchではテーブル同士の(というかEntity同士)のリレーションは、データソースのテーブルに外部キーが設定してあれば自動的に張られる。

そのデータソースを基にすれば、簡単にテーブル同士が連携した画面を作ることが出来る。
しかし、実アプリケーションでは単純にテーブルの全データを使うのではなく、ある条件で絞り込んだ上で使用することが多い。(例えば、削除フラグが立っているデータは除くとか)

Accessとかであればテーブルとクエリを連結したり、クエリ同士を連結したりということがGUIで簡単にできる。

LightSwitchでこれを実現するにはいくぶん直感的でない作業が必要になるので、その方法を纏めておく。

<データ>

まずはいつもどおりNorthwindから以下のテーブルを追加してデータソースを作る。

  • Customers
  • Employees
  • Orders
  • Order_Details

2011-09-30 11h51_40

<画面>

テンプレートに「一覧および詳細画面」を指定して画面を作成。
image

実行すると以下のような画面が表示される。
2011-09-30 13h12_54

 

ここで、現在グリッド表示部分は「Orders」テーブルからそのまま表示されているが、これをクエリにしてみる。

<クエリ>

デザイン画面に戻って、左ペインの「Orders」から「クエ入りの編集」リンクをクリックする。
image

表示された「クエリデザイナ」画面にてフィルタを追加する。
ここでは便宜的に以下のフィルタを追加した。
image

これで実行すると以下のように「Ship Via」が「1」で絞りこまれた注文一覧が表示される。
image

これだけで良ければ直感的かつ簡単で良いのだが(実際これでも良いだろう)、ここで使用したクエリは画面レベルのクエリで、他の画面などでは使用できないし、フィルタが追加されていることがデザイン画面では容易に把握できないためある程度の規模になってくると見通しを悪くする可能性がありそうだ。

そこで、これをアプリケーション全体をスコープとするクエリに作り替えてみる。

  1. まずは今作ったフィルタを削除する。
  2. 「ソリューションエクスプローラー」の「Orders」テーブルを右クリックして表示されたメニューから「クエリの追加」を選択する。
    image
  3. クエリの名前を「OrdersByShipVia1」と指定し、先ほどと同じフィルタを追加する。
    2011-09-30 13h38_31
  4. 「ソリューションエクスプローラー」にて「顧客別注文一覧」画面をダブルクリックしてデザイナー画面を表示させる。
  5. デザイナー画面、左ペインの「Orders」を右クリックして表示されたメニューから「削除」をクリックする。
    image
  6. デザイナー画面上部のツールバーにて「データ項目の追加」をクリックし表示された「データ項目の追加」画面にて「OrdersByShipVia1」クエリを選択しOKをクリックする。
    image
  7. 左ペインに追加された「OrdersByShipVia1」を画面ツリーの「Details Column」の下にドラッグして追加する。
    image
  8. これで一旦表示してみると以下のような画面が表示される。
    2011-09-30 13h51_36
  9. 「Ship Via」は1のデータが表示されているが、顧客を選択しなおしても「Orders」のデータが変わらないのが分かる。
    「Customers」と「OrdersByShipVia1」の関連ができていないのである。
    ココらへんがもう少し簡単になれば良いと思うのだが、とりあえず以下のような手順になりそうだ。
  10. 「ソリューションエクスプローラー」にて「OrdersByShipVia1」をダブルクリックしてデザイナー画面を開く。
  11. 表示されたデザイナー画面にてフィルターを以下のように追加編集する。
    (CutsomerIDを使ったパラメータクエリにする)
    image
  12. 「ソリューションエクスプローラー」にて「顧客別注文一覧」をダブルクリックして表示されたデザイン画面の左ペインにて「OrdersShipVia1」の「CustomerID」パラメータを選択する。
    image
  13. 「プロパティ」ウインドウにて「パラメータバインド」をクリックし、表示されたメニューで「Customers」を選択。
    ピリオドを入力し「SelectedItem」を選択。
    ピリオドを入力し「CustomerID」を選択。
    imageimageimage
    これで左ペインを見ると「CustomerID」に矢印が引かれ、連結されたことが分かる。
    image
  14. 以上を実行すると画面は以下のようになる。
    2011-09-30 14h12_35
    こんどは「Customers」の選択に応じて「Orders」の表示が変わるのが分かる。
  15. 最後にレイアウトが今ひとつなので、これを調整する。
    ちなみに、今のレイアウトは以下の状態に成っている。
    2011-09-30 14h15_17
    これは、ルートノード「顧客別注文一覧」の下に4つのノードがあることを表している。
    また、ルートノードは「列のレイアウト」となっているので、子どもの4つのノードは横並びになる。(HorizontalAlighが指定されたStackパネルが使われていると思われる)
  16. データグリッドを以下のようにドラッグし、「Details Column」の子ノードにする。
    image
  17. 以上で実行すると初期表示と同様のレイアウトで表示される。
    2011-09-30 14h25_33