読者です 読者をやめる 読者になる 読者になる

ideaki's blog

WinRT C#/XAML の開発について

UWP SwipeableSplitViewを作ってみた

SwipeableSplitViewぐぐるとよさそうなのがでてくるけどもいかんせん転用しづらいため勉強のため車輪してみた。

作ってみた後にこっちのほうがはるかに使い道が限られているなと思った。(DisplayModeがCompactOverlayじゃないとまともに動かない)
興味あるかた向けにすぐ動くソースを置いておきます。

https://1drv.ms/u/s!AhaSq7vQuLAcidkSnYZ7c4VzJq17ZQ

その他所感

  • Styleはほとんどいじらず済んだ(2か所コメントアウト、2か所にManipulatonModeを追加、PaneClipRectangleTransform.TranslateXにBindingを追加)
  • Manipulation周りは正直もうさわりたくない
  • Style内のTemplateで双方向Bindingはできる

UWP UISettingsを使用する場合の注意点

投稿時点(2017/01/08)の投稿者のWindows 10 SDKの最新バージョンは14393になります。
この記事はUWPデスクトップアプリ向けです。

UISettingsを使用することで、アクセントカラーの取得や変更通知の受け取りができます。

// using Windows.UI.ViewManagement;
// sender is UISettings
new UISettings().ColorValuesChanged += (sender, e) => Debug.WriteLine(sender.GetColorValue(UIColorType.Accent));

使用する場合はいくつかの注意点があります。

  1. ColorValuesChanged 通知が2度続けてやってくる(500ms前後の間あり)
  2. ColorValuesChanged 通知直後にGetColorValue()すると値が正しくない(1~2秒待機する必要がある)
  3. リリースビルドではUISettingsをAppクラス外でインスタンス化するとコードの実行がスキップされる(?!)

3.の対処は簡単ですAppコンストラクタでUISettingsをインスタンス化してどこかに保持しておけばいいです。
 デバッグビルドではどこでUISettingsをインスタンス化しても問題ない、なぜなのか...
1.も2.もRxを使えばたいしたことないのですが、なぜかリリースビルドでは下記のコードがエラーとなってしまいます。
 #Rxの達人でもなければ今回の対処のために初めて実用したレベルなのであきらめ...

Observable.FromEventPattern(UISettings, "ColorValuesChanged")
    .Throttle(TimeSpan.FromMilliseconds(2000))
    .ObserveOn(SynchronizationContext.Current)
    .Subscribe( _ =>
    {
        // hoge
    });
// ↓リリースビルド実行結果↓
// 例外がスローされました: 'System.InvalidOperationException' (System.Reactive.Linq.dll の中)
// 型 'System.InvalidOperationException' の例外が System.Reactive.Linq.dll で発生しましたが、ユーザー コード内ではハンドルされませんでした
// 追加情報:Could not find event 'ColorValuesChanged' on object of type 'Windows.UI.ViewManagement.UISettings'.

手っ取り早くアクセントカラーの変更監視と取得を行うにはUISettingsしかありません。
できれば使いたくない代物ですが頑張って使うしかないです。

Flyoutをかっこよくカスタマイズしてみる UWP/XAML

この記事はWindows 10 Mobile / Windows Phone Advent Calendar 2016 - Adventar 20日目の記事です。

続きを読む

UWP ライブラリを追加したらまずリリースビルドをしましょう

タイトルがすべてです。


※ 以下、2016/05/09時点のお話しです。

たまのリリースビルドで EntityFramework.SqliteのDbContextからエラーが発生

原因はWinRTXamlToolkit.UWPをプロジェクトが参照していたから。
アンインストールすることでエラーは出なくなりました。
もともとコンバータを少し使っていただけなので原因の究明はしない...

UWP EntityFramework SQLite DBファイルをデフォルト以外に保存する

ドキュメントによるとデフォルトは下記

ApplicationData.Current.LocalFolder.Path

デフォルト以外に保存する場合は

public class FooBarContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }
    public DbSet<Bar> Bars { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connectionString = System.IO.Path.Combine($"Data Source={Windows.Storage.ApplicationData.Current.RoamingFolder.Path}", "FooBar.db");
        optionsBuilder.UseSqlite(connectionString);
    }
}

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
ユーザーは待っていたドラッグアンドドロップ

したサンプル

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" AllowDrop="True" DragOver="Grid_DragOver"Drop="Grid_Drop">
    <TextBlock x:Name="AATextBlock" FontFamily="MS PGothic" Text="Drag &amp; 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();
}