縦横のレイアウト変更を自動化するビヘイビアー #wpdev_jp
Accent Color Cameraアプリを更新しました。カラーをリアルタイムに採取できるようになりました。
縦画面にも対応したのですが、そのときに得たTIPSです。
概要
アプリで縦横対応すると向きに合わせてデザインを変えたいですよね。それとカメラを処理しているとカメラの向き補正が必須だったりも。
hm。向きに合わせてVisualState切った方がよさそう。全部の向きでうまく表示されるマージンのとり方とかあるんだろうか #wpdev_jp
— しみみん (@KatsuYuzu) March 11, 2013
つぶやいていたらMSエバンジェリストの高橋さんが反応してくださって翌日あたりにはTIPSを記事にまで!ありがとうございました。
ここから取り込んで自分なりにブラッシュアップさせてみたお話です。
完成形
VisualStateManagerで各向きの状態を定義してビヘイビアーで指定することで、端末の向きが変わったときに自動で状態を遷移させてくれます。
OrientationChangedとLoaded
端末の向きがかわったときだけだと起動時の画面がちぐはぐになるのでLoadedに対応させておきます。
protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.Loaded += this.AssociatedObject_Loaded; this.AssociatedObject.OrientationChanged += this.AssociatedObject_OrientationChanged; } protected override void OnDetaching() { this.AssociatedObject.Loaded -= this.AssociatedObject_Loaded; this.AssociatedObject.OrientationChanged -= this.AssociatedObject_OrientationChanged; base.OnDetaching(); } void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { this.GoToState(this.AssociatedObject.Orientation); } void AssociatedObject_OrientationChanged(object sender, OrientationChangedEventArgs e) { this.GoToState(e.Orientation); }
CustomPropertyValueEditorAttribute
普通にPropertyを定義すると以下のようになりState名を入力しないといけません。
PropertyにCustomPropertyValueEditorAttributeをつけることでVisualStateManagerに定義されたStateから選択できるようになります。
#region PortraitUpState /// <summary> /// 縦長の向きの場合の状態名を取得または設定します。 /// </summary> [CustomPropertyValueEditor(CustomPropertyValueEditor.StateName)] public string PortraitUpState { get { return (string)GetValue(PortraitUpStateProperty); } set { SetValue(PortraitUpStateProperty, value); } } // Using a DependencyProperty as the backing store for PortraitUpState. This enables animation, styling, binding, etc... public static readonly DependencyProperty PortraitUpStateProperty = DependencyProperty.Register("PortraitUpState", typeof(string), typeof(OrientationStateBehavior), new PropertyMetadata(null)); #endregion
余談ですが、DependencyPropertyのこの長ったらしい定義、propdpまで入力してTabキー叩けばでてきますよ。コードスニペット便利!それとCustomPropertyValueEditorにはほかにもバインディング周りのサポートがありますので頭の片隅に。
CustomPropertyValueEditor 列挙 (System.Windows.Interactivity)
ビヘイビアー全体
少し長くなるのでgistにも投稿しました。
using Microsoft.Expression.Interactivity; using Microsoft.Phone.Controls; using System.Windows; using System.Windows.Interactivity; namespace KatsuYuzu.Interactivity { /// <summary> /// ページの向きを基に状態を切り替えます。 /// </summary> public sealed class OrientationStateBehavior : Behavior<PhoneApplicationPage> { #region PortraitUpState /// <summary> /// 縦長の向きの場合の状態名を取得または設定します。 /// </summary> [CustomPropertyValueEditor(CustomPropertyValueEditor.StateName)] public string PortraitUpState { get { return (string)GetValue(PortraitUpStateProperty); } set { SetValue(PortraitUpStateProperty, value); } } // Using a DependencyProperty as the backing store for PortraitUpState. This enables animation, styling, binding, etc... public static readonly DependencyProperty PortraitUpStateProperty = DependencyProperty.Register("PortraitUpState", typeof(string), typeof(OrientationStateBehavior), new PropertyMetadata(null)); #endregion #region LandscapeLeftState /// <summary> /// ページの上部を左に回転させた横向きの場合の状態名を取得または設定します。 /// </summary> [CustomPropertyValueEditor(CustomPropertyValueEditor.StateName)] public string LandscapeLeftState { get { return (string)GetValue(LandscapeLeftStateProperty); } set { SetValue(LandscapeLeftStateProperty, value); } } // Using a DependencyProperty as the backing store for LandscapeLeftState. This enables animation, styling, binding, etc... public static readonly DependencyProperty LandscapeLeftStateProperty = DependencyProperty.Register("LandscapeLeftState", typeof(string), typeof(OrientationStateBehavior), new PropertyMetadata(null)); #endregion #region LandscapeRightState /// <summary> /// ページの上部を右に回転させた横向きの場合の状態名を取得または設定します。 /// </summary> [CustomPropertyValueEditor(CustomPropertyValueEditor.StateName)] public string LandscapeRightState { get { return (string)GetValue(LandscapeRightStateProperty); } set { SetValue(LandscapeRightStateProperty, value); } } // Using a DependencyProperty as the backing store for LandscapeRightState. This enables animation, styling, binding, etc... public static readonly DependencyProperty LandscapeRightStateProperty = DependencyProperty.Register("LandscapeRightState", typeof(string), typeof(OrientationStateBehavior), new PropertyMetadata(null)); #endregion #region UseTransitions /// <summary> /// System.Windows.VisualTransition を使用して状態を切り替える場合は true、それ以外は false。 /// </summary> public bool UseTransitions { get { return (bool)GetValue(UseTransitionsProperty); } set { SetValue(UseTransitionsProperty, value); } } // Using a DependencyProperty as the backing store for UseTransitions. This enables animation, styling, binding, etc... public static readonly DependencyProperty UseTransitionsProperty = DependencyProperty.Register("UseTransitions", typeof(bool), typeof(OrientationStateBehavior), new PropertyMetadata(true)); #endregion protected override void OnAttached() { base.OnAttached(); this.AssociatedObject.Loaded += this.AssociatedObject_Loaded; this.AssociatedObject.OrientationChanged += this.AssociatedObject_OrientationChanged; } protected override void OnDetaching() { this.AssociatedObject.Loaded -= this.AssociatedObject_Loaded; this.AssociatedObject.OrientationChanged -= this.AssociatedObject_OrientationChanged; base.OnDetaching(); } void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { this.GoToState(this.AssociatedObject.Orientation); } void AssociatedObject_OrientationChanged(object sender, OrientationChangedEventArgs e) { this.GoToState(e.Orientation); } /// <summary> /// 状態を切り替えます。 /// </summary> /// <param name="orientation"></param> void GoToState(PageOrientation orientation) { string stateName; switch (orientation) { case PageOrientation.PortraitUp: stateName = this.PortraitUpState; break; case PageOrientation.LandscapeLeft: stateName = this.LandscapeLeftState; break; case PageOrientation.LandscapeRight: stateName = this.LandscapeRightState; break; default: stateName = string.Empty; break; } VisualStateUtilities.GoToState(this.AssociatedObject, stateName, this.UseTransitions); } } }