ページ

2011年11月25日金曜日

◆ASP.NET MVC Postデータを取得する

 

ポストページ表示用Actionメソッド

まずは、Post用のページを表示するためのActionメソッドを定義する。(ここではMyPostとした)

ちなみに、ActionメソッドはActionと入力しTab,Tabでコードスニペットを使うのが良さそうだ。

  1. public ActionResult MyPost()  
  2. {  
  3.     return View();  
  4. }  

次にこのメソッドの場所で右クリックして「ビューの追加」を選択。
2011-11-25 15h13_39


ポストページ表示用ビューサンプル


MyPost.aspx


  1. <body>  
  2.     <div>  
  3.     <% using (Html.BeginForm())  
  4.        {%>  
  5.            <label>名前:</label>  
  6.            <%: Html.TextBox("name","<初期値>") %>  
  7.            <input type="submit" value="送信" />  
  8.       <% } %>  
  9.       
  10.     </div>  
  11. </body>  

ここではまず、HtmlヘルパークラスのBeginFormメソッドを使ってFormタグを展開している。
その中に、ラベル、テキストボックス、ボタンを配置。
テキストボックスにはこれもまたHtmlヘルパークラスのTextBoxメソッドを使用した。
これで表示される画面とソースは以下の通り。
2011-11-25 16h39_42


2011-11-25 16h40_48


ヘルパークラスを使っても、それほど便利という感じでもないが、最近ではRazor記法とかが出てきているようなのでそちらを使ったほうが良いのだろう。


ちなみに、これも最近の機能っぽいが、<%:  %> とするとHTMLエンコードまでやってくれるらしい。


なお、<% using (Html.BeginForm("MyPost", "HelloWorld", FormMethod.Post))とすることでactionとmethodを明示的に指定することも可能。


ポストされたデータを処理するアクションメソッド


基本的には入力画面を表示したアクションメソッドと同じメソッド名になるのだが、区別するためにこちらには[HttpPost]属性を付加する。(というかActionPostスニペットで展開させるとそうなる)
また、ポストされたデータを受け取るためにメソッドに引数を指定する。ここでは画面入力された「name」の値を取得するために(string name)としている。


入力値はRequestプロパティから参照できるHttpRequestオブジェクトからも取得できる(Request.Form(“name”))ので引数の指定は必須では無いようにも思うが、シグネチャが被るので1つは指定しないといけないだろう。


取得したポストデータは画面に渡すためにViewData["HelloName"]に入れておく。


このケースではアクションメソッドの名前と、結果を表示するためのビューの名前が異なるため、Viewメソッドにビューの名前を引数で与えている。


  1. [HttpPost]  
  2. public ActionResult MyPost(string name)  
  3. {  
  4.     ViewData["HelloName"] = "Hello " + name;  
  5.     return View("Result");  
  6. }  

結果を表示するビュー


上記で設定されたViewDataを取ってきてそのまま表示するだけだ。


  1. <body>  
  2.     <div>  
  3.         <%:ViewData["HelloName"]%>  
  4.     </div>  
  5. </body>  

◆ASP.NET ユーザーパラメータを受け取る

ユーザーパラメータを受け取るにはActionメソッドの引数に指定すれば良い。

  1. public ActionResult Index(string id)  
  2. {  
  3.     ViewData["param1"] = "パラメータ1です。";  
  4.     ViewData["id"] = String.Format("ユーザーパラメータは({0})です。",id);  
  5.     return View();  
  6. }  

  1. <body>  
  2.     <div>  
  3.         Hello World !!  
  4.   
  5.         <%=Html.Encode(ViewData["param1"]) %>  
  6.   
  7.         <%=Html.Encode(ViewData["id"]) %>  
  8.   
  9.   
  10.     </div>  
  11. </body>  

2011-11-25 14h41_48


なお、パラメータを増やすにはGlobal.asaxのルーティング定義にパラメータを追加してあげれば良い。


image


image


image


2011-11-25 14h52_08

◆ASP.NET MVC コントローラーからビューにデータを渡す

コントローラー側は、ControllerベースクラスにViewDataプロパティが用意されている。
これは、ViewDataDictionaryクラスになっていて「Key」「Value」で値が設定できるので以下のようにKey名称を適当に決めて値をセットしてあげれば良い。

  1. public ActionResult Index()  
  2. {  
  3.     ViewData["param1"] = "パラメータ1です。";  
  4.     return View();  
  5. }  

受け取り側のビューでも同様にViewData[“param1”]とやれば値が取ってこれる。
HTMLに埋め込むには<%=……%>コードブロックを使えば良さげ。(かつてのASPっぽい感じかな)


また、HTMLエンコードにはヘルパークラスが提供されていて、Html.Encode()メソッドが使える。


  1. <body>  
  2.     <div>  
  3.         Hello World !!  
  4.   
  5.         <%=Html.Encode(ViewData["param1"]) %>  
  6.     </div>  
  7. </body>  

2011-11-25 14h17_12

◆ASP.NET MVC ルーティング

ルーティングはGlobal.asaxで定義されている。

  1. public class MvcApplication : System.Web.HttpApplication  
  2. {  
  3.     public static void RegisterRoutes(RouteCollection routes)  
  4.     {  
  5.         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");  
  6.   
  7.         routes.MapRoute(  
  8.             "Default", // ルート名  
  9.             "{controller}/{action}/{id}", // パラメーター付きの URL  
  10.             new { controller = "Home", action = "Index", id = UrlParameter.Optional } // パラメーターの既定値  
  11.         );  
  12.   
  13.     }  
  14.   
  15.     protected void Application_Start()  
  16.     {  
  17.         AreaRegistration.RegisterAllAreas();  
  18.   
  19.         RegisterRoutes(RouteTable.Routes);  
  20.     }  
  21. }  

RouteCollection型となっているので複数のルーティングを定義できるのだろう。
初期状態では”Default”というルーティングが定義されていて、
「コントローラー名/アクション名/パラメータ」でルーティングされるようだ。


ControllerにはHome、actionにはIndexが初期値として定義してあるので、例えば
「http://ホスト名/コントローラ名」でアクセスされたときにはIndexアクションが実行されてIndexページが返るという仕掛けなのだろう。

◆ASP.NET MVC でHelloWorld

 

以下のサイトを参考にしながら自分なりに勉強してみる。
連載:ASP.NET MVC入門 - @IT

新しいプロジェクトの作成

image

使っているVisualStudioのバージョンやインストールしているMVCモジュールのバージョンによってMVC2だったりMVC3だったりする。(ここではVisualStudio2010の素の状態を使っている)

デフォルトのフォルダ構成

2011-11-21 10h46_402011-11-21 10h49_26

コントローラークラスを作る

    1. 「ソリューションエクスプローラー」で「Controllers」フォルダを右クリックして「追加」「コントローラー」を選択。
      image
    2. 「コントローラーの追加」ダイアログで「コントローラー名」を指定する。
      xxxxControllerという名前にする必要がある。
      image
    3. 「Controllers」フォルダにコントローラーソースが追加される。
      image
      「AccountController」と「HomeController」はテンプレートが追加したもの。
    4. Viewを返す。
      デフォルトでActionResultオブジェクトを返すIndexメソッドが定義されるので、その中でとりあえずViewメソッドを呼んでやるとIndexメソッドに対応したIndex.aspxがブラウザに返るという仕組みのようだ。
      Viewメソッドは継承元のControllerクラスで定義されている。
      ActionResultは抽象クラスなので実際には、それを実装したViewResultオブジェクトを返している。

ビューを作る

Index.aspxを返すコントローラークラスが出来たので、あとはIndex.aspxの実体をViewとして作ってあげる。

    1. 追加された「HelloWorldController」クラスの「Index」メソッド上で右クリックして「ビューの追加」を選択する。
      2011-11-21 11h18_59
    2. 表示されたダイアログでオプションを全てOFFにした状態で「Index」ビューを追加する。
      2011-11-21 11h22_28
    3. 「Views」フォルダーに下にコントローラーと同名のフォルダが作られ、「Index.aspx」ページが追加される。
    4. デフォルトで以下のようなシンプルなHTMLの雛形が作られるので、とりあえず「Hello World!!」だけ追加してみた。
      1. <html xmlns="http://www.w3.org/1999/xhtml" >  
      2. <head runat="server">  
      3.     <title>Index</title>  
      4. </head>  
      5. <body>  
      6.     <div>  
      7.         Hello World !!  
      8.     </div>  
      9. </body>  
      10. </html>  

    5. コントローラーとViewの関係を簡単にまとめるとこんな感じだろうか。2011-11-25 11h25_02

動作確認



ここまでとりあえずF5キーを押して動作を確認すると、最初はひな形で作られた以下の画面が表示されるので、適切なドレスに変更する。
2011-11-25 11h59_56


今回の場合は、こんな感じになる。
2011-11-25 12h02_49

2011年11月13日日曜日

◆WPF バインディング2

<オブジェクトとのバインディング>

今度は、コントロール同士ではなくコードビハインド側で作成したオブジェクトとコントロールをバインドしてみる。

とりあえず、画面表示用に以下のXAMLを定義した。

  1. <Window x:Class="BindingSample.Window2"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window2" Height="300" Width="300" Name="MyWindow">  
  5.     <StackPanel>  
  6.         <StackPanel Orientation="Horizontal">  
  7.             <Label Content="名前:"/>  
  8.             <TextBox Text="{Binding Name}" />  
  9.         </StackPanel>  
  10.         <StackPanel Orientation="Horizontal">  
  11.             <Label Content="住所:"/>  
  12.             <TextBox Text="{Binding Address}" />  
  13.         </StackPanel>  
  14.     </StackPanel>  
  15. </Window>  

image


バインドするエンティティのクラスはとりあえず以下のとおり。


  1. class Person  
  2. {  
  3.     public string Name { getset; }  
  4.     public string Address { getset; }  
  5. }  

あとはコードビハインドで、このクラスのインスタンスを作り、WindowのContextに設定してあげれば良い。


  1. public Window2()  
  2. {  
  3.     InitializeComponent();  
  4.   
  5.     Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };  
  6.     this.MyWindow.DataContext = p;  
  7. }  

バインド先のTextBoxは自分の親を直近から探していって最初に見つかったDataContextを使う。(包含継承というらしい)


image


コントロールの時と同様にコードだけでバインドすると以下のようになる。


  1. public Window2()  
  2. {  
  3.     InitializeComponent();  
  4.   
  5.     Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };  
  6.     //this.MyWindow.DataContext = p;  
  7.     var bindName = new Binding();  
  8.     bindName.Path = new PropertyPath("Name");  
  9.     bindName.Source = p;  
  10.     txtName.SetBinding(TextBox.TextProperty, bindName);  
  11.   
  12.     var bindAddr = new Binding();  
  13.     bindAddr.Path = new PropertyPath("Address");  
  14.     bindAddr.Source = p;  
  15.     txtAddr.SetBinding(TextBox.TextProperty, bindAddr);  
  16. }  

9行目と14行目はDataContextを指定すれば不要になる。
また、Bindingクラスのコンストラクタ引数にPathを文字列で指定できるようなので8行目と13行目も省略できることになる。


  1. public Window2()  
  2. {  
  3.     InitializeComponent();  
  4.   
  5.     Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };  
  6.     this.MyWindow.DataContext = p;  
  7.     var bindName = new Binding("Name");  
  8.     txtName.SetBinding(TextBox.TextProperty, bindName);  
  9.   
  10.     var bindAddr = new Binding("Address");  
  11.     txtAddr.SetBinding(TextBox.TextProperty, bindAddr);  
  12. }  

少し簡単になった。


ちなみに、バインドモードは指定しなければDefaultという値になり状況に応じてWPFが決めてくれるらしい。TextBoxの様に入力可能なコントロールとバインドした場合は双方向になるとのこと。
ただし、TextBoxは依存関係プロパティになっていて変更通知機能があるのでTextBoxの変更をDataContextに伝えることができるが、DataContextへの変更はそのままではTextBoxに伝わらないのだと。


一般的にはDataContextに指定されたCLRオブジェクトに対してはINotifyPropertyChangedインタフェースを実装することにより変更通知を行う必要がある。


とりあえずDataContextの値を確認する為にボタンを追加してソースを以下の様に変更した。


image


  1. public partial class Window2 : Window  
  2. {  
  3.     Person p = new Person { Name = "原 辰徳", Address = "相模原市1-1" };  
  4.     public Window2()  
  5.     {  
  6.         InitializeComponent();  
  7.   
  8.         this.MyWindow.DataContext = p;  
  9.         var bindName = new Binding("Name");  
  10.         txtName.SetBinding(TextBox.TextProperty, bindName);  
  11.   
  12.         var bindAddr = new Binding("Address");  
  13.         txtAddr.SetBinding(TextBox.TextProperty, bindAddr);  
  14.     }  
  15.     //DataContextの値を確認  
  16.     private void Button_Click(object sender, RoutedEventArgs e)  
  17.     {  
  18.         MessageBox.Show(p.Name + ":" + p.Address,"DataContextの確認");  
  19.     }  
  20.     //名前を長島に  
  21.     private void Button_Click_1(object sender, RoutedEventArgs e)  
  22.     {  
  23.         p.Name = "長島茂雄";  
  24.     }  
  25. }  


これで確認してみると確かにDataContextへの変更はTextBoxに反映しないことが分かった。


DataContextに設定しているPersonクラスにINotifyPropertyChangedインタフェースを実装したのが以下のソース。
PropertyChangedイベントを発せさせれば良いだけだ。
(まったく決まりきった実装になるようだ)


  1. public class Person : INotifyPropertyChanged  
  2. {  
  3.     private string _name;  
  4.     private string _address;  
  5.     public string Name  
  6.     {  
  7.         get  
  8.         {  
  9.             return _name;  
  10.         }  
  11.         set  
  12.         {  
  13.             if (_name == value)  
  14.             {  
  15.                 return;  
  16.             }  
  17.             _name = value;  
  18.             OnPropertyChanged("Name");  
  19.         }  
  20.     }  
  21.     public string Address  
  22.     {  
  23.         get  
  24.         {  
  25.             return _address;  
  26.         }  
  27.         set  
  28.         {  
  29.             if (_address == value)  
  30.             {  
  31.                 return;  
  32.             }  
  33.             _address = value;  
  34.             OnPropertyChanged("Address");  
  35.         }  
  36.     }  
  37.   
  38.     public event PropertyChangedEventHandler PropertyChanged;  
  39.     protected void OnPropertyChanged(string propertyName)  
  40.     {  
  41.         var d = PropertyChanged;  
  42.         if (d != null)  
  43.         {  
  44.             d(thisnew PropertyChangedEventArgs(propertyName));  
  45.         }  
  46.     }  
  47. }  

確かにDataContextへの変更が即座にTextBoxへ反映されるようになる。


image

2011年11月6日日曜日

◆WPF バインディング

<コントロール同士のバインディング>

まずは、WPFのバインディングで最初の頃必ず出てきたテキストボックスとスライダーのバインディング。
スライダーを動かすと数値が変化し、数値を入力するとスライダーが動くというやつだ。(双方向のバインディング)

image

XAMLはこんな感じになる。

  1. <Window x:Class="BindingSample.Window1"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window1" Height="300" Width="300">  
  5.     <StackPanel>  
  6.         <TextBox Name="myValue">0</TextBox>  
  7.         <Slider Maximum="100" Minimum="0" Value="{Binding ElementName=myValue, Path=Text}" />  
  8.     </StackPanel>  
  9. </Window>  

スライダーのvalueプロパティにテキストボックスのTextプロパティをバインディングするには、
Value="{Binding ElementName=myValue, Path=Text}"
というようにバインドするコントロール名とプロパティ名を指定してあげる。
ちなみに、この{}で囲んだ書き方は「マークアップ拡張」と呼ばれるXAML独自の記法(多分)で、本来は以下のように書くべきもの。


  1. <Slider Maximum="100" Minimum="0">  
  2.     <Slider.Value>  
  3.         <Binding ElementName="myValue" Path="Text" />  
  4.     </Slider.Value>  
  5. </Slider>  


多分バインドは多用されることになるためシンタックスシュガー的に簡略記法を用意したのだろう。
ただし、私などは最初この省略形であるということが分からずにどうもピンと来なかった。
というか世の中のサンプルを見ていると様々な記法で書かれており(Pathというキーワードも省略できたり)、一体何が違うのかと混乱していた。
バージョンが上がって省略記法が用意されれば解りやすかったのだが、私のような素人にはちょっと取っ付きづらい部分だった。(最初から気が効きすぎていたというか)
また、上記TextBoxにはNameプロパティで名前を指定しているのだが、これもサンプルによってはx:nameと別のネームスペースを使っていたりして混乱した。
今回再勉強して分かったのは、この名前空間はXAML独自の名前空間らしいということ。WPFとして用意されていない要素、属性などはXAMLとして独自に拡張した機能をこの名前空間で提供しているらしい。
なのでnameプロパティにしてもWPFでもともと存在しているものについてはそれを使えばよいし、nameプロパティを持たないコントロールなどを参照したいときはx:nameで名前をつけてあげれば良い。


データバインドは必ずしも1対1でなくとも良いので、最初のサンプルに省略しない記法で書いたスライダーをもう一つ追加するとこんな感じになる。


  1. <Window x:Class="BindingSample.Window1"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window1" Height="300" Width="300">  
  5.     <StackPanel>  
  6.         <TextBox Name="myValue">0</TextBox>  
  7.         <Slider Maximum="100" Minimum="0" Value="{Binding ElementName=myValue, Path=Text}" />  
  8.         <Slider Maximum="100" Minimum="0">  
  9.             <Slider.Value>  
  10.                 <Binding ElementName="myValue" Path="Text" />  
  11.             </Slider.Value>  
  12.         </Slider>  
  13.   
  14.     </StackPanel>  
  15. </Window>  


image


どちらのスライダーを動かしても双方張り付いて全く同じに動く。


ちなみに、このnameプロパティの値はは大文字と小文字が区別され、間違ったりしてもエラーにならないので注意が必要だ。


Bindオブジェクト(要素)はModeプロパティでバインド方向を、
image
UpdateSourceTriggerプロパティでそのタイミングも指定できるようになっている。
image


ここで、こんどはXAMLではなくコードでバインドするとどうなるか試してみた。
Sliderをもう一つ追加し名前を”mySlider”とした。


  1. <Slider Maximum="100" Minimum="0" Value="{Binding ElementName=myValue, Path=Text}" />  
  2. <Slider Maximum="100" Minimum="0">  
  3.     <Slider.Value>  
  4.         <Binding ElementName="myValue" Path="Text" />  
  5.     </Slider.Value>  
  6. </Slider>  
  7. <Slider Name="mySlider" Maximum="100" Minimum="0" />  



  1. public Window1()  
  2. {  
  3.     InitializeComponent();  
  4.   
  5.     var bind = new Binding();  
  6.     bind.ElementName = "myValue";  
  7.     bind.Path = new PropertyPath("Text");  
  8.   
  9.     mySlider.SetBinding(Slider.ValueProperty, bind);  
  10. }  


XAMLの省略していない方の記述を見ながらそのとおりにコーディングしていけばよいので、それほど難しくはない。XAMLが入れ子で表現している部分はSetBindingメソッドを使ってあげれば良いようだ。
SliderのプロパティはSlider自身が持っているxxxPropertyスタティックプロパティが使える。
PropertyPathクラスのコンストラクターで指定している引数(型の定義はobjectになっている)も本来はTextBox.TextPropertyなのだと思うがXAMLと同様に”Text”でも動作してくれる。
PropertyPathクラスは以下のような定義になっているので、
[TypeConverterAttribute(typeof(PropertyPathConverter))]
public sealed class PropertyPath
ここで指定されたPropertyPathConverterが文字列からTextPropertyへの変換をしているのかな・・・。


単純に考えれば、文字列を取るコンストラクターを用意すればよいだけに思えるのだが・・・。
そうか、こいつはコンパイル時に変換してくれるという事なのかも。


<自分自身や親要素とのバインド>


  1. <TextBox Height"65"  
  2.          Text="{Binding RelativeSource={RelativeSource Mode=FindAncestorAncestorType=Window},   
  3.                         Path=Title}"   
  4.          Width="{Binding RelativeSource={RelativeSource Self}, Path=Height }" />  

バインドの対象を指定するのにはnameプロパティを参照する以外にも、このような指定もできる。


2行目では自分の親をたどってWindowコントロールを探し、そのタイトルプロパティとバインドしている。
はっきり言ってちょっと複雑・・・。
それほど使う機能でも無いのでこんなものだとメモしておけば良いか・・・。


4行目はWidthプロパティに対して自分自身のHeightプロパティをバインドしている。(結果的に正方形になる)


ココらへんは記法は面倒だが、当初に比べればだいぶインテリセンスが効いてくれるのですこしは楽になった。

◆Googleも殿様商売になってきたなぁ(いやーな感じ)

下の子がメールアドレスが欲しいというので登録してあげようと思ったのだが、いつも悩まされる認証確認の文字がいつもにも増して読めない。

image

いったい誰がこんな文字をまともに読めるのだろう。
都度パスワードをクリアしたくらいにして(仕方ないとはいえ)。

だんだん腹立たしく思えてきた。
しかもそのうち年齢制限がどうとかエラーになり始めた。

・・・・。

かなーり奮闘してやっと通ったと思ったらこんどは携帯で認証が必要なんだと。

image

以前はこんなのなかったのに・・・。
おれは携帯なんぞ持っていないっちゅーに。

たとえ持っていても教えたくはない!!

Google、いやーな感じぃ・・・・

驕れる者久しからず。

私が最近チェックした記事