I found that the application life cycle of Windows Phone applications is not so simple as I expected. Here is a pseudo code to fulfill the requirements. See Execution Model for Windows Phone (http://msdn.microsoft.com/en-us/library/ff769557%28v=vs.92%29.aspx) for details.
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// Launching to Running state.
// If application persistent state is available, it should be restored.
// However, it should not be done here to avoid consuming too much time
// on launching.
}
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (e.IsApplicationInstancePreserved)
{
// Activated from Dormant state.
}
else
{
// Activated from Tombstoned state.
// Restore the application transient state which was saved
// during the last call of Application_Deactivated() method.
var state = PhoneApplicationService.Current.State;
RestoreApplicationTransientState(state);
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
// Deactivated to Dormant state.
// Save the application transient state which may be used later
// by Application_Activated() method in case this application is
// relaunched from Tombstoned state.
var state = PhoneApplicationService.Current.State;
SaveApplicationTransientState(state);
// Save the application persistent state to isolated storage
// because this application may be terminated without being
// activated, and in such a case, Application_Closing() is
// not called.
SaveApplicationPersistentState();
}
private void Application_Closing(object sender, ClosingEventArgs e)
{
// Save the application persistent state to isolated storage
// to reuse it later on next invocation.
SaveApplicationPersistentState();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// If this page instance is new.
//
// The variable _isNewPageInstance here is an instance variable
// and initialized to true in this page's constructor.
if (_isNewPageInstance)
{
if (0 < State.Count)
{
// Activated from Tombstoned state.
// Use the page state data to build up this page.
// The data was saved during the last call of
// OnNavigatedFrom() method which was executed
// when this application entered Dormant state.
RestorePageState(State);
}
else
{
// Normal forward page navigation. Build up this page
// from scratch.
InitializePageState(State);
}
_isNewPageInstance = false;
}
else
{
// Activated from Dormant state, or
// normal page backward navigation.
// There is nothing to do because the page state remains
// intact in memory.
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.Back)
{
// Normal backward page navigation.
// No need to save the page state because this page
// instance is about to be discarded.
}
else
{
// Save the page state to reuse it later when this
// application is activated from Tombstoned state.
SavePageState(State);
}
}
Windows Phoneアプリケーションのライフサイクル
Windows Phone アプリケーションのライフサイクルは、期待していたほど単純なものではなかった。要求事項を満たすための擬似コードは下記の通り。詳細は Execution Model for Windows Phone (http://msdn.microsoft.com/en-us/library/ff769557%28v=vs.92%29.aspx) を参照のこと。
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// Launching to Running state.
// もしもアプリケーションの永続状態が利用可能であれば、復元する。
// ただし、起動時に多くの時間を消費しないように、復元はここでは
// おこなわない。
}
private void Application_Activated(object sender, ActivatedEventArgs e)
{
if (e.IsApplicationInstancePreserved)
{
// Activated from Dormant state.
}
else
{
// Activated from Tombstoned state.
// 最後の Application_Deactivated() メソッドの呼び出しの際に
// 保存されたアプリケーションの一時状態を復元する。
var state = PhoneApplicationService.Current.State;
RestoreApplicationTransientState(state);
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
// Deactivated to Dormant state.
// このアプリケーションが Tombstoned 状態から再起動された場合に
// Application_Activated() メソッドで使用することになる、アプリ
// ケーションの一時状態を保存する。
var state = PhoneApplicationService.Current.State;
SaveApplicationTransientState(state);
// Activate されることなくアプリケーションが終了させられることも
// ありうるので(そのケースでは Application_Closing() は呼ばれない)、
// アプリケーションの永続状態を isolated storage に保存しておく。
SaveApplicationPersistentState();
}
private void Application_Closing(object sender, ClosingEventArgs e)
{
// 次回の起動時に再利用できるよう、アプリケーションの永続状態を
// isolated storage に保存する。
SaveApplicationPersistentState();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// このページインスタンスが新しい場合
//
// 変数 _isNewPageInstance はインスタンス変数で、このページの
// コンストラクタで true に初期化される。
if (_isNewPageInstance)
{
if (0 < State.Count)
{
// Activated from Tombstoned state.
// ページの状態データを使ってこのページを構築する。
// データは、このアプリケーションが Dormant state に
// 移行するときに実行された OnNavigatedFrom() メソッド
// の呼び出し中に保存されたもの。
RestorePageState(State);
}
else
{
// 通常の前方ページ遷移。このページを一から構築する。
InitializePageState(State);
}
_isNewPageInstance = false;
}
else
{
// Activated from Dormant state, もしくは通常の後方ページ遷移。
// ページの状態はメモリ上にそのまま残っているので、ここでは
// 特にすることはない。
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
if (e.NavigationMode == NavigationMode.Back)
{
// 通常の後方ページ遷移
// このページインスタンスは破棄されようとしているところなので、
// ページの状態を保存する必要はない。
}
else
{
// 後でアプリケーションが Tombstoned 状態から復帰したときに
// 再利用するため、ページの状態を保存しておく。
SavePageState(State);
}
}
2011年7月31日日曜日
2011年7月29日金曜日
Binding ActualHeight and ActualWidth (Silverlight)
As reported to Microsoft Connect:
and as described in the API document:
ActualHeight and ActualWidth are not suitable for data binding. Therefore, as a workaround, Microsoft suggests that SizeChanged event be used if we want to do something when actual size changes. However, all we want are just data-binding-suitable ActualHeight and ActualWidth, and here I show an implementation for that purpose.
The class, SizeChange, whose implementation I show later, defines 3 attached properties named "IsEnabled", "ActualHeight" and "ActualWidth", respectively. If SizeChange.IsEnabled="True" is given as a property to a FrameworkElement, actual height and actual width of the FrameworkElement can be obtained through SizeChange.ActualHeight and SizeChange.ActualWidth.
Before diving into the implementation, let's see a usage example to understand what we want to realize.
Usage Example:
And the implementation is here:
ActualHeight と ActualWidth をバインドする (Silverlight)
Microsoft Connect に報告されているように、
また、API ドキュメントに記述されているように、
ActualHeight と ActualWidth はデータバインディングに適していない。そのため、回避策として、実際のサイズが変更されたときに何かをしたい場合は SizeChanged イベントを使うようにと、マイクロソフトは言っている。しかし、私たちが欲しいのは、単にデータバインディングに適した ActualHeight と ActualWidth であり、そしてそのための実装をここにあげる。
後で実装を見ることになる SizeChange クラスは、IsEnabled、ActualHeight、ActualWidth という三つのアタッチトプロパティ (attached property) を定義している。SizeChange.IsEnabled="True" がプロパティとして FrameworkElement に与えられると、その FrameworkElement の実際の高さと実際の幅を、SizeChange.ActualHeight と SizeChange.ActualWidth を通じて得ることができるようになる。
実装を見る前に、何を実現しようとしているかを理解するために、使用例を見よう。
使用例:
そして実装は下記のとおり。
Silverlight Grid/StackPanel/Canvas ActualHeight and AcutalWidth is 0
http://connect.microsoft.com/VisualStudio/feedback/details/522231/silverlight-grid-stackpanel-canvas-actualheight-and-acutalwidth-is-0
and as described in the API document:
FrameworkElement.ActualHeight Property
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.actualheight%28v=VS.95%29.aspx
FrameworkElement.ActualWidth Property
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.actualwidth%28v=VS.95%29.aspx
ActualHeight and ActualWidth are not suitable for data binding. Therefore, as a workaround, Microsoft suggests that SizeChanged event be used if we want to do something when actual size changes. However, all we want are just data-binding-suitable ActualHeight and ActualWidth, and here I show an implementation for that purpose.
The class, SizeChange, whose implementation I show later, defines 3 attached properties named "IsEnabled", "ActualHeight" and "ActualWidth", respectively. If SizeChange.IsEnabled="True" is given as a property to a FrameworkElement, actual height and actual width of the FrameworkElement can be obtained through SizeChange.ActualHeight and SizeChange.ActualWidth.
Before diving into the implementation, let's see a usage example to understand what we want to realize.
Usage Example:
<...
// At an appropriate place, define a namespace prefix for// the namespace under whicn SizeChange class is contained.//// The definition below assumes that SizeChange class exists// under the namespace "MyApp.UI" and specifies "local_ui"// as a prefix for the namespace.xmlns:local_ui="clr-namespace:MyApp.UI"
>
// Give a SizeChange.IsEnabled="True" to a FrameworkElement.// By doing this, actual height and actual width of this// element can be bound through SizeChange.ActualHeight and// SizeChange.ActualWidth.<Grid local_ui:SizeChange.IsEnabled="True" x:Name="grid1">
...
</Grid>
// Bind the data-binding-suitable attached property.//// Here, Text property of the TextBlock instance is bound// to SizeChange.ActualHeight attached-property of the Grid// instance named "grid1".//// As a result, The TextBlock shows the actual height of// the Grid instance, and the shown value is refreshed// whenever the actual size of the Grid instance is changed.//// Note that "Path=(local_ui:SizeChange.ActualHeight)"// is used instead of "Path=ActualHeight".<TextBlock Text="{Binding ElementName=grid1,
Path=(local_ui:SizeChange.ActualHeight)}"/>
And the implementation is here:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace MyApp.UI
{
// Declare SizeChange class as a sub class of DependencyObject// because we need to register attached properties.public class SizeChange : DependencyObject
{
#region Attached property "IsEnabled"
// The name of IsEnabled property.public const string IsEnabledPropertyName = "IsEnabled";
// Register an attached property named "IsEnabled".// Note that OnIsEnabledChanged method is called when// the value of IsEnabled property is changed.public static readonly DependencyProperty IsEnabledProperty
= DependencyProperty.RegisterAttached(
IsEnabledPropertyName,
typeof(bool),
typeof(SizeChange),
new PropertyMetadata(false, OnIsEnabledChanged));
// Getter of IsEnabled property. The name of this method// should not be changed because the dependency system// uses it.public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
// Setter of IsEnabled property. The name of this method// should not be changed because the dependency system// uses it.public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
#endregion
#region Attached property "ActualHeight"
// The name of ActualHeight property.public const string ActualHeightPropertyName = "ActualHeight";
// Register an attached property named "ActualHeight".// The value of this property is updated When SizeChanged// event is raised.public static readonly DependencyProperty ActualHeightProperty
= DependencyProperty.RegisterAttached(
ActualHeightPropertyName,
typeof(double),
typeof(SizeChange),
null);
// Getter of ActualHeight property. The name of this method// should not be changed because the dependency system// uses it.public static double GetActualHeight(DependencyObject obj)
{
return (double)obj.GetValue(ActualHeightProperty);
}
// Setter of ActualHeight property. The name of this method// should not be changed because the dependency system// uses it.public static void SetActualHeight(DependencyObject obj, double value)
{
obj.SetValue(ActualHeightProperty, value);
}
#endregion
#region Attached property "ActualWidth"
// The name of ActualWidth property.public const string ActualWidthPropertyName = "ActualWidth";
// Register an attached property named "ActualWidth".// The value of this property is updated When SizeChanged// event is raised.public static readonly DependencyProperty ActualWidthProperty
= DependencyProperty.RegisterAttached(
ActualWidthPropertyName,
typeof(double),
typeof(SizeChange),
null);
// Getter of ActualWidth property. The name of this method// should not be changed because the dependency system// uses it.public static double GetActualWidth(DependencyObject obj)
{
return (double)obj.GetValue(ActualWidthProperty);
}
// Setter of ActualWidth property. The name of this method// should not be changed because the dependency system// uses it.public static void SetActualWidth(DependencyObject obj, double value)
{
obj.SetValue(ActualWidthProperty, value);
}
#endregion
// This method is called when the value of IsEnabled property// is changed. If the new value is true, an event handler is// added to SizeChanged event of the target element.private static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
// The given object must be a FrameworkElement instance,// because we add an event handler to SizeChanged event// of it.var element = obj as FrameworkElement;
if (element == null)
{
// The given object is not an instance of FrameworkElement,// meaning SizeChanged event is not available. So, nothing// can be done for the object.return;
}
// If IsEnabled=Trueif (args.NewValue != null && (bool)args.NewValue == true)
{
// Attach to the element.
Attach(element);
}
else
{
// Detach from the element.
Detach(element);
}
}
private static void Attach(FrameworkElement element)
{
// Add an event handler to SizeChanged event of the element// to take action when actual size of the element changes.element.SizeChanged += HandleSizeChanged;
}
private static void Detach(FrameworkElement element)
{
// Remove the event handler from the element.element.SizeChanged -= HandleSizeChanged;
}
// An event handler invoked when SizeChanged event is raised.private static void HandleSizeChanged(object sender, SizeChangedEventArgs args)
{
var element = sender as FrameworkElement;
if (element == null)
{
return;
}
// Get the new actual height and width.var width = args.NewSize.Width;
var height = args.NewSize.Height;
// Update values of SizeChange.ActualHeight and// SizeChange.ActualWidth.SetActualWidth(element, width);
SetActualHeight(element, height);
}
}
}
ActualHeight と ActualWidth をバインドする (Silverlight)
Microsoft Connect に報告されているように、
Silverlight Grid/StackPanel/Canvas ActualHeight and AcutalWidth is 0
http://connect.microsoft.com/VisualStudio/feedback/details/522231/silverlight-grid-stackpanel-canvas-actualheight-and-acutalwidth-is-0
また、API ドキュメントに記述されているように、
FrameworkElement.ActualHeight プロパティ
http://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.actualheight%28v=VS.95%29.aspx
FrameworkElement.ActualWidth プロパティ
http://msdn.microsoft.com/ja-jp/library/system.windows.frameworkelement.actualwidth%28v=VS.95%29.aspx
ActualHeight と ActualWidth はデータバインディングに適していない。そのため、回避策として、実際のサイズが変更されたときに何かをしたい場合は SizeChanged イベントを使うようにと、マイクロソフトは言っている。しかし、私たちが欲しいのは、単にデータバインディングに適した ActualHeight と ActualWidth であり、そしてそのための実装をここにあげる。
後で実装を見ることになる SizeChange クラスは、IsEnabled、ActualHeight、ActualWidth という三つのアタッチトプロパティ (attached property) を定義している。SizeChange.IsEnabled="True" がプロパティとして FrameworkElement に与えられると、その FrameworkElement の実際の高さと実際の幅を、SizeChange.ActualHeight と SizeChange.ActualWidth を通じて得ることができるようになる。
実装を見る前に、何を実現しようとしているかを理解するために、使用例を見よう。
使用例:
<...
// 適切な場所で、SizeChange クラスが含まれる名前空間用の// 名前空間プレフィックスを定義する。//// 下記の定義では、SizeChange クラスが名前空間 MyApp.UI// にあるものと仮定し、その名前空間用のプレフィックスと// して local_ui を指定している。xmlns:local_ui="clr-namespace:MyApp.UI"
>
// SizeChange.IsEnabled="True" を FrameworkElement に// 与える。これをすることにより、このエレメントの// 実際の高さと幅を、SizeChange.ActualHeight と// SizeChange.ActualWidth でバインドできるようになる。<Grid local_ui:SizeChange.IsEnabled="True" x:Name="grid1">
...
</Grid>
// データバインディングに適したアタッチトプロパティを// バインドする。//// ここでは、TextBlock インスタンスの Text プロパティを、// grid1 という名前の Grid インスタンスのアタッチト// プロパティ SizeChange.ActualHeight にバインドしている。//// 結果として、TextBlock は Grid インスタンスの実際の// 高さを表示し、Grid インスタンスの実際のサイズが変更// されるたびに表示される値が更新される。//// "Path=ActualHeight" ではなく// "Path=(local_ui:SizeChange.ActualHeight)" が使用されている// ことに注意。<TextBlock Text="{Binding ElementName=grid1,
Path=(local_ui:SizeChange.ActualHeight)}"/>
そして実装は下記のとおり。
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace MyApp.UI
{
// アタッチトプロパティを登録する必要があるので、SizeChange// クラスを DependencyObject のサブクラスとして宣言する。public class SizeChange : DependencyObject
{
#region Attached property "IsEnabled"
// IsEnabled プロパティの名前public const string IsEnabledPropertyName = "IsEnabled";
// IsEnabled という名前のアタッチトプロパティを登録する。// IsEnabled プロパティの値が変更されると OnIsEnabledChanged// メソッドが呼ばれることに注意。public static readonly DependencyProperty IsEnabledProperty
= DependencyProperty.RegisterAttached(
IsEnabledPropertyName,
typeof(bool),
typeof(SizeChange),
new PropertyMetadata(false, OnIsEnabledChanged));
// IsEnabled プロパティのゲッター。依存関係システムが// 使用するので、このメソッドの名前は変更するべきではない。public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
// IsEnabled プロパティのセッター。依存関係システムが// 使用するので、このメソッドの名前は変更するべきではない。public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
#endregion
#region Attached property "ActualHeight"
// ActualHeight プロパティの名前public const string ActualHeightPropertyName = "ActualHeight";
// ActualHeight という名前のアタッチトプロパティを登録する。// このプロパティの値は、SizeChanged イベントが起こったときに// 変更される。public static readonly DependencyProperty ActualHeightProperty
= DependencyProperty.RegisterAttached(
ActualHeightPropertyName,
typeof(double),
typeof(SizeChange),
null);
// ActualHeight プロパティのゲッター。依存関係システムが// 使用するので、このメソッドの名前は変更するべきではない。public static double GetActualHeight(DependencyObject obj)
{
return (double)obj.GetValue(ActualHeightProperty);
}
// ActualHeight プロパティのセッター。依存関係システムが// 使用するので、このメソッドの名前は変更するべきではない。public static void SetActualHeight(DependencyObject obj, double value)
{
obj.SetValue(ActualHeightProperty, value);
}
#endregion
#region Attached property "ActualWidth"
// ActualWidth プロパティの名前public const string ActualWidthPropertyName = "ActualWidth";
// ActualWidth という名前のアタッチトプロパティを登録する。// このプロパティの値は、SizeChanged イベントが起こったときに// 変更される。public static readonly DependencyProperty ActualWidthProperty
= DependencyProperty.RegisterAttached(
ActualWidthPropertyName,
typeof(double),
typeof(SizeChange),
null);
// ActualWidth プロパティのゲッター。依存関係システムが// 使用するので、このメソッドの名前は変更するべきではない。public static double GetActualWidth(DependencyObject obj)
{
return (double)obj.GetValue(ActualWidthProperty);
}
// ActualWidth プロパティのセッター。依存関係システムが// 使用するので、このメソッドの名前は変更するべきではない。public static void SetActualWidth(DependencyObject obj, double value)
{
obj.SetValue(ActualWidthProperty, value);
}
#endregion
// このメソッドは、IsEnabled プロパティの値が変更されたときに// 呼び出される。新しい値が true の場合、対象のエレメントの// SizeChanged イベントにイベントハンドラーが追加される。private static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
// SizeChanged イベントにイベントハンドラーを追加するので、// エレメントは FrameworkElement のインスタンスでなければ// ならない。var element = obj as FrameworkElement;
if (element == null)
{
// 与えられたオブジェクトは FrameworkElement のインス// タンスではないので、SizeChanged イベントが無い。// そのため、当オブジェクトに対してできることがない。return;
}
// IsEnabled=True の場合if (args.NewValue != null && (bool)args.NewValue == true)
{
// エレメントにアタッチするAttach(element);
}
else
{
// エレメントからデタッチする。Detach(element);
}
}
private static void Attach(FrameworkElement element)
{
// エレメントの実際のサイズが変更されたときに処理を// おこなえるよう、エレメントの SizeChanged イベントに// イベントハンドラーを追加する。element.SizeChanged += HandleSizeChanged;
}
private static void Detach(FrameworkElement element)
{
// エレメントからイベントハンドラーを取り除く。element.SizeChanged -= HandleSizeChanged;
}
// SizeChanged イベントが起こった時に実行されるイベントハンドラーprivate static void HandleSizeChanged(object sender, SizeChangedEventArgs args)
{
var element = sender as FrameworkElement;
if (element == null)
{
return;
}
// 実際の高さと幅を取得する。var width = args.NewSize.Width;
var height = args.NewSize.Height;
// SizeChange.ActualHeight と SizeChange.ActualWidth の// 値を更新する。SetActualWidth(element, width);
SetActualHeight(element, height);
}
}
}
2011年7月26日火曜日
LongListSelectorが空表示になる問題の回避方法 (Windows Phone)
// 2011/8/21:
// 以前の回避方法だとまだ問題が発生することが分かったので、内容修正。
Windows Phone 用の Silverlight Toolkit (http://silverlight.codeplex.com/) に含まれる LongListSelector は、LongListSelector のインスタンスが含まれるページ (PhoneApplicationPage) から他のページに遷移した後にまた戻ってきたときに、内容が空になってしまうという不具合がある。この問題は、LongListSelector の ScrollTo() を使って強制的に再描画をトリガーすることで回避できる。
参考: WP7 LongListSelector? Be aware of its problems
http://mikaelkoskinen.net/post/wp7-longlist-selector-problems-and-fixing-them.aspx
ScrollTo() が不適当な場合は、ItemsSource の再設定でも再描画をかけることができる。ただし、他のページから戻ってきたとき、LongListSelector インスタンスのロードが終わる前に ItemsSource を設定すると、(既に ItemsSource が設定済みの場合) LongListSelector.Balance() メソッドが NullPointerException を投げてしまう。そのため、ItemsSource を再設定する場合は、LongListSelector の Loaded イベントのハンドラーでおこなう必要がある。
参考: LongListSelector NullReferenceException in Balance()
http://silverlight.codeplex.com/workitem/8376
// 2011/8/21:
// それでもまだLongListSelector.Balance()が問題を起こすようなので、
// LongListSelector.ItemsSourceの再設定は避けたほうがよさそう。
最終的には、対象となる LongListSelector インスタンスの Loaded イベントのハンドラーとして、次のようなメソッドを指定することになる。
// A "Loaded" event handler for LongListSelector.
private void RedrawLongListSelector(object sender, RoutedEventArgs e)
{
// LongListSelector has a bug where the content of it may
// become empty when we navigate back to a page which contains
// the LongListSelector instance.
//
// See http://mikaelkoskinen.net/post/wp7-longlist-selector-problems-and-fixing-them.aspx
//
// Therefore, we need to redraw it.
//
// The reason that redrawing must be written here in an event handler
// for Loaded is that LongListSelector throws a NullPointerException
// if ItemsSource is changed before it gets loaded (if ItemsSource
// has already been set).
//
// See http://silverlight.codeplex.com/workitem/8376
//
// 2011/8/21:
// Because LongListSelector still throws an exception, it seems better
// to avoid re-assigning data to LongListSelector.ItemsSource.
LongListSelector longListSelector = sender as LongListSelector;
if (longListSelector == null)
{
// This should not happen.
return;
}
if (longListSelector.Visibility == Visibility.Collapsed)
{
}
if (selectedItem != null)
{
return;
}
if (items != null && items.Count != 0)
{
return;
}
/*
// Get the current ItemsSource.
}
// 以前の回避方法だとまだ問題が発生することが分かったので、内容修正。
Windows Phone 用の Silverlight Toolkit (http://silverlight.codeplex.com/) に含まれる LongListSelector は、LongListSelector のインスタンスが含まれるページ (PhoneApplicationPage) から他のページに遷移した後にまた戻ってきたときに、内容が空になってしまうという不具合がある。この問題は、LongListSelector の ScrollTo() を使って強制的に再描画をトリガーすることで回避できる。
参考: WP7 LongListSelector? Be aware of its problems
http://mikaelkoskinen.net/post/wp7-longlist-selector-problems-and-fixing-them.aspx
ScrollTo() が不適当な場合は、ItemsSource の再設定でも再描画をかけることができる。ただし、他のページから戻ってきたとき、LongListSelector インスタンスのロードが終わる前に ItemsSource を設定すると、(既に ItemsSource が設定済みの場合) LongListSelector.Balance() メソッドが NullPointerException を投げてしまう。そのため、ItemsSource を再設定する場合は、LongListSelector の Loaded イベントのハンドラーでおこなう必要がある。
参考: LongListSelector NullReferenceException in Balance()
http://silverlight.codeplex.com/workitem/8376
// 2011/8/21:
// それでもまだLongListSelector.Balance()が問題を起こすようなので、
// LongListSelector.ItemsSourceの再設定は避けたほうがよさそう。
最終的には、対象となる LongListSelector インスタンスの Loaded イベントのハンドラーとして、次のようなメソッドを指定することになる。
// A "Loaded" event handler for LongListSelector.
private void RedrawLongListSelector(object sender, RoutedEventArgs e)
{
// LongListSelector has a bug where the content of it may
// become empty when we navigate back to a page which contains
// the LongListSelector instance.
//
// See http://mikaelkoskinen.net/post/wp7-longlist-selector-problems-and-fixing-them.aspx
//
// Therefore, we need to redraw it.
//
// The reason that redrawing must be written here in an event handler
// for Loaded is that LongListSelector throws a NullPointerException
// if ItemsSource is changed before it gets loaded (if ItemsSource
// has already been set).
//
// See http://silverlight.codeplex.com/workitem/8376
//
// 2011/8/21:
// Because LongListSelector still throws an exception, it seems better
// to avoid re-assigning data to LongListSelector.ItemsSource.
LongListSelector longListSelector = sender as LongListSelector;
if (longListSelector == null)
{
// This should not happen.
return;
}
if (longListSelector.Visibility == Visibility.Collapsed)
{
// The LongListSelector instance is hidden.
// No need to redraw it now.
return; }
// Get the current selected item.
var selectedItem = longListSelector.SelectedItem;if (selectedItem != null)
{
// Redraw the LongListSelector by scrolling to the item.
longListSelector.ScrollTo(selectedItem);return;
}
// Get items in the current view. An empty list may be returned
// if scrolling is happening too quickly. In addition, since the new
// Silverlight Toolkit for Windows Phone (2011 August), this
// method has been marked as obsolete and always returns an
// empty list, the document says.
var items = longListSelector.GetItemsInView();if (items != null && items.Count != 0)
{
// Redraw the LongListSelector by scrolling to the item.
longListSelector.ScrollingTo(items.ElementAt(0));return;
}
// No item in the current view. Nothing can be done.
return; // 2011/8/21:
// Removed the following code because it seems re-assigning
// data to LongListSelector.ItemsSource causes a problem
// in some contexts.
/*
// Get the current ItemsSource.
var itemsSource = longListSelector.ItemsSource;
if (itemsSource == null)
{
// No content. No need to redraw.
return;
}
// Set dummy data to force the LongListSelector to redraw itself.
longListSelector.ItemsSource = new ObservableCollection<object>();
// Assign the ItemsSource again.
longListSelector.ItemsSource = itemsSource;
*/ }
登録:
投稿 (Atom)