UWP GridView/ListViewのスクロール位置を復元したい
探すとたくさんあるスクロール位置の復元ですが
- ViewModel:スクロール位置の保持
- View:スクロール位置の復元/監視
と役割分担が明確になっているサンプルがなかったのでそれっぽく作りました。
がっちゃんこできるBehaviorで実装
以下、
1. スクロール位置復元できるBehavior
2. 使ってみよう
3. サンプルsln
1. スクロール位置復元できるBehavior
public class ScrollOffsetControlBehavior : DependencyObject, IBehavior { public int RestoreScrollOffset { get { return (int)GetValue(RestoreScrollOffsetProperty); } set { SetValue(RestoreScrollOffsetProperty, value); } } // Using a DependencyProperty as the backing store for RestoreScrollOffset. This enables animation, styling, binding, etc... public static readonly DependencyProperty RestoreScrollOffsetProperty = DependencyProperty.Register("RestoreScrollOffset", typeof(int), typeof(ScrollOffsetControlBehavior), new PropertyMetadata(0)); int CurrentItemViewIndex { get { var listviewbase = AssociatedObject as ListViewBase; var item = listviewbase.ItemsPanelRoot as ItemsStackPanel; return item.FirstVisibleIndex > 0 ? item.FirstVisibleIndex : 0; } } public DependencyObject AssociatedObject { get; set; } private ScrollViewer sv; public void Attach(DependencyObject associatedObject) { if ((associatedObject != AssociatedObject) && !Windows.ApplicationModel.DesignMode.DesignModeEnabled) { AssociatedObject = associatedObject; var listviewbase = AssociatedObject as ListViewBase; if (listviewbase != null) { listviewbase.DataContextChanged += Itemscontrol_DataContextChanged; listviewbase.Loaded += Listviewbase_Loaded; } } } private void Listviewbase_Loaded(object sender, RoutedEventArgs e) { var listviewbase = sender as ListViewBase; AutomationPeer ap = ListViewAutomationPeer.CreatePeerForElement(listviewbase); ScrollViewerAutomationPeer avap = (ScrollViewerAutomationPeer)ap.GetPattern(PatternInterface.Scroll); sv = (ScrollViewer)avap.Owner; sv.ViewChanged += sv_ViewChanged; } private void Itemscontrol_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args) { if (args.NewValue == null) return; var listviewbase = AssociatedObject as ListViewBase; listviewbase.ScrollIntoView(listviewbase.Items.ElementAt(RestoreScrollOffset), ScrollIntoViewAlignment.Leading); } private void sv_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) { if (e.IsIntermediate) return; RestoreScrollOffset = CurrentItemViewIndex; } public void Detach() { var listviewbase = AssociatedObject as ListViewBase; listviewbase.Loaded -= Listviewbase_Loaded; sv.ViewChanged -= sv_ViewChanged; sv = null; } }
2. 使ってみよう
MainPage.xaml
<Page x:Class="App1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Interactions="using:Microsoft.Xaml.Interactions.Core" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*" /> <ColumnDefinition Width="3*" /> </Grid.ColumnDefinitions> <ListView x:Name="MasterListView" ItemsSource="{x:Bind ViewModel.DetailViewModels}"> <ListView.ItemTemplate> <DataTemplate x:DataType="local:DetailViewModel"> <TextBlock Text="{x:Bind Title}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> <ContentPresenter x:Name="DetailContent" Grid.Column="1" Content="{x:Bind Path=MasterListView.SelectedItem, Mode=OneWay}"> <ContentPresenter.ContentTemplate> <DataTemplate x:Name="ListTemplate" x:DataType="local:DetailViewModel"> <ListView ItemsSource="{x:Bind Items}"> <Interactivity:Interaction.Behaviors> <local:ScrollOffsetControlBehavior RestoreScrollOffset="{x:Bind ReadOffset, Mode=TwoWay}" /> </Interactivity:Interaction.Behaviors> </ListView> </DataTemplate> </ContentPresenter.ContentTemplate> </ContentPresenter> </Grid> </Page>
MainPage.xaml.cs
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Input; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; // 空白ページのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 を参照してください namespace App1 { /// <summary> /// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。 /// </summary> public sealed partial class MainPage : Page { MainPageViewModel ViewModel => new MainPageViewModel(); public MainPage() { this.InitializeComponent(); } } public class MainPageViewModel { public List<DetailViewModel> DetailViewModels { get; set; } public MainPageViewModel() { DetailViewModels = Enumerable.Range(1, 10).Select(i => new DetailViewModel(i)).ToList(); } } public class DetailViewModel { public int ReadOffset { get; set; } public string Title { get; set; } public List<string> Items { get; set; } public DetailViewModel(int num) { Title = num.ToString(); Items = Enumerable.Range(1, 100).Select(i => $"{num} : {i}").ToList(); } } }
3. サンプルsln
onedrive.live.com
UWP エクスプローラからのドラッグ&ドロップ
channel9.msdn.com
ユーザーは待っていたドラッグアンドドロップ
UWP App ファイルエクスプローラからのドラックアンドドロップアンドオープン https://t.co/dNrOyZhmEm
— くコ:彡@衰退期 (@ideaki19) 2015, 5月 3
したサンプル
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" AllowDrop="True" DragOver="Grid_DragOver"Drop="Grid_Drop"> <TextBlock x:Name="AATextBlock" FontFamily="MS PGothic" Text="Drag & Drop Text File" VerticalAlignment="Center" HorizontalAlignment="Center"/> <FontIcon/>
private void Grid_DragOver(object sender, DragEventArgs e) { e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Copy; e.Handled = true; } private async void Grid_Drop(object sender, DragEventArgs e) { var d = e.GetDeferral(); var files = await e.DataView.GetStorageItemsAsync(); var file = files.First(); this.AATextBlock.Text = await FileIO.ReadTextAsync(file as IStorageFile); d.Complete(); }
はてなAPI ブックマークコメントのパーマリンクを取得する
はてなブックマークエントリー情報取得APIのレスポンスから作成することができます。
http://b.hatena.ne.jp/{user}/{timestamp}/#bookmark-{eid}
使い道として、
はてなスターカウントAPIにブコメのリンクを指定して人気コメントの抽出とか。
#region 宣伝
はてなびゅぅゎぁ | Windows Phone Apps+Games Store (United States)
#endoregion
C# 動的なキーのJSONをデシリアライズする
C# LINQ 複数のDictionaryをまとめる
備忘録、、、
dics.SelectMany(d => d).ToDictionary(x => x.Key, x => x.Value);
WinRT コードビハインド書かずにWebViewにプログレスバーつける
WebViewがコンテンツをロード中にプログレスバーを出しておくとユーザに親切だったりします。
ただこれ作るのにコードビハインド書かなくちゃいけなくて、めんどうだなあと思ってました。
実はXAMLだけで簡単に書けるみたいです。
<Border> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="WebViewVisualStateGroup"> <VisualState x:Name="contentloading"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="progressBar" Storyboard.TargetProperty="(ProgressBar.IsIndeterminate)"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <x:Boolean>True</x:Boolean> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <FadeInThemeAnimation TargetName="border" /> </Storyboard> </VisualState> <VisualState x:Name="contentloaded"> <Storyboard> <FadeOutThemeAnimation TargetName="border" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid> <Grid.RowDefinitions> <RowDefinition Height="20" /> <RowDefinition Height="1*" /> </Grid.RowDefinitions> <WebView x:Name="WebViewObj" Grid.RowSpan="2" Source="{Binding SelectFeedItem.Uri}"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="NavigationCompleted"> <Core:GoToStateAction StateName="contentloaded" /> </Core:EventTriggerBehavior> <Core:EventTriggerBehavior EventName="NavigationStarting"> <Core:GoToStateAction StateName="contentloading" /> </Core:EventTriggerBehavior> <Core:EventTriggerBehavior EventName="NavigationFailed"> <Core:GoToStateAction StateName="contentloaded" /> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </WebView> <Border x:Name="border" Background="#B2F0F8FF"> <ProgressBar x:Name="progressBar" /> </Border> </Grid> </Border>
参考になるエントリ
Blend for VS2013 RCにXAMLでのBehavior復活!!(ただしWindows 8.1のみのもよう) - かずきのBlog@hatena
MVVM開発をさらに加速させる ノンコーディングUI開発 - SSSSLIDE