2011年12月5日月曜日

リストボックスを引っ張って更新

スマートフォンでSNS系のアプリを使っていて
「更新ボタン」を押しているとなんとなく未来感がない。
Twitterの公式アプリ等はそうだが、上側に引っ張るとリストが更新される。

WindowsPhoneでもこれを行なってみようと思ったら

高橋忍氏のブログや
その書籍プログラミングWindowsPhoneでも紹介されています。

んじゃ書く必要ないじゃん。と思いましたけど少し書いてみます。


スタイルを変更
ListBoxの中身はScrollViewerで構成されているので、 ScrollViewerのVerticalCompressionというVisualStateGroupを利用して ListBoxの状態を把握しようってことですね。 上記のブログにはPageごとの設定とありますけど 私的にはアプリケーション内で指定したいのでApp.xmlにApplication.Resoucesとして定義しています。 この記述によって、アプリケーションのすべてのScrollViewerで圧縮した場合のイベントの発生が可能になります。 ※私には縦の必要がないのでVerticalのみです。
ListBoxから取り出してイベントを登録
上記ブログにある通り(そのままのコード)ListBoxからScrollViewerを取り出して 変更があったというイベントに登録します。
  1. private void ListBoxCompressionHandling(ListBox targetlistbox)  
  2. {  
  3.     VisualStateGroup vgroup = new VisualStateGroup();  
  4.   
  5.     // ListBox の初めに定義されている ScrollViewerを取り出す   
  6.     ScrollViewer ListboxScrollViewer = (ScrollViewer)VisualTreeHelper.GetChild(targetlistbox, 0);  
  7.   
  8.     // Visual State はコントロールテンプレートの常に最上位に定義されている   
  9.     FrameworkElement element = (FrameworkElement)VisualTreeHelper.GetChild(ListboxScrollViewer, 0);  
  10.     // Visual State を取り出しその中から 縦横Compression のVisualStateを取り出す   
  11.     foreach (VisualStateGroup group in VisualStateManager.GetVisualStateGroups(element))  
  12.         if (group.Name == "VerticalCompression") vgroup = group;  
  13.   
  14.     //縦横Compressionの状態が変わった時のイベントハンドラ   
  15.     vgroup.CurrentStateChanging += new EventHandler<VisualStateChangedEventArgs>(ScrollViewer_CurrentStateChanging);  
  16. }   
でイベントは
  1. void ScrollViewer_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)  
  2. {  
  3.     switch (e.NewState.Name)   
  4.     {   
  5.         case "CompressionTop":  
  6.             break;   
  7.         case "CompressionBottom":   
  8.             break;  
  9.         case "NoVerticalCompression":  
  10.             break;   
  11.         default:   
  12.             break;   
  13.     }   
  14. }  
で、この関数をLoadなどのイベントで呼び出すのですが Loadは何度も呼び出されるので、画面遷移が多いような画面だったら重複して登録してしまうので 一度だけ登録するようにしておくのが良いでしょう。
で何がしたいか?
これだと高橋忍氏のブログそのまま(アプリで登録した位の違い)です。 私的にはスクロールを「グッ」とした時にだけ、更新をしたいのです。 このままだと少しでも上にするだけでイベントが発生します。 んじゃ、圧縮イベントと圧縮が終わったイベントの時刻で処理してみよう!
アプローチ
  1. DateTime startTopTime = new DateTime(0);  
  2. DateTime startBottomTime = new DateTime(0);  
  3. DateTime endTime = new DateTime(0);  
  4. private void InitCompression()  
  5. {  
  6.     startTopTime = new DateTime(0);  
  7.     startBottomTime = new DateTime(0);  
  8.     endTime = new DateTime(0);  
  9. }  
  10. private Boolean IsCompressionTop() {  
  11.     if (!startTopTime.Equals(new DateTime(0)))  
  12.     {  
  13.         TimeSpan ts = endTime.Subtract(startTopTime);  
  14.         System.Diagnostics.Debug.WriteLine(ts.TotalSeconds);  
  15.         if (ts.TotalSeconds >= 0.9)  
  16.         {  
  17.             return true;  
  18.         }  
  19.     }  
  20.     return false;  
  21. }  
  22.   
  23. void ScrollViewer_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)  
  24. {  
  25.     switch (e.NewState.Name)   
  26.     {   
  27.         case "CompressionTop":  
  28.             startTopTime = DateTime.Now;  
  29.             break;   
  30.         case "CompressionBottom":   
  31.             startBottomTime = DateTime.Now;  
  32.             break;  
  33.         case "NoVerticalCompression":  
  34.             endTime = DateTime.Now;  
  35.             if (IsCompressionTop())  
  36.             {  
  37.                 MessageBox.Show("できたよー");  
  38.             }  
  39.   
  40.             InitCompression();  
  41.             break;   
  42.         default:   
  43.             break;   
  44.     }   
  45. }  
こうしてみると、確かにグッとした後にイベント発生ができるんだけど リストを離して戻った時の判定になってしまう。 できれば、グッとしている間にイベントを発生したい。 。。。もっといい方法があるような気がする。