Quantcast
Channel: かずきのBlog@hatena
Viewing all 1388 articles
Browse latest View live

UWPでカメラから動画撮影

$
0
0

UWPでカメラからの動画を録画する方法です。 CameraCaptureUIクラスを使用します。

VideoSettings.Formatで保存するタイプを指定してCapgureFileAsyncで録画とファイルへの保存を行います。

後は、FileSavePickerあたりで適当なファイルに出力してやればOKです。見た感じ録画した画像をインメモリにため込むことは出来ない気がします。何かごにょごにょやりたかったら、ファイルに保存されたものを読み込んで解析してねってことなのかな。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var camera = new CameraCaptureUI();
    camera.VideoSettings.Format = CameraCaptureUIVideoFormat.Mp4;
    var file = await camera.CaptureFileAsync(CameraCaptureUIMode.Video);

    var picker = new FileSavePicker();
    picker.FileTypeChoices.Add("mp4", new List<string> { ".mp4" });
    var dist = await picker.PickSaveFileAsync();
    if (dist != null)
    {
        file.CopyAndReplaceAsync(dist);
    }
}

最後に忘れがちなPackage.appxmanifestを開いて機能からWebカメラにチェックを入れます。

f:id:okazuki:20160314123531p:plain

実行すると以下のようなUIが表示されます。UIはカスタム出来ないと思うので、かっこいい動画録画用UIとか設計しないほうが無難でしょう。

f:id:okazuki:20160314123844p:plain

録画が終わると以下のようにプレビューしたりできます。至れり尽くせりですね。

f:id:okazuki:20160314124033p:plain


UWPで共有コントラクト 送信側

$
0
0

共有コントラクトってUWPでも言うんですかね?

Windows 8で追加されて、めでたく?Windows 10で廃止されたチャームにあった共有ですが、UWPではアプリから明示的に共有を表示してやる必要があります。それ以外はWindowsストアアプリの頃と変わっていません。(ストアアプリの頃も明示的に自分で共有のUI呼び出せたけど、特に理由がない限りチャームがあったのでする必要はなかった)

手順としては、DataTranferManagerをGetForCurrentViewで取得して、それのDataRequestedイベントを購読する感じです。このイベントは、共有されるときに呼び出されます。あとは、DataTranferManager.ShowShareUI()で共有用のUIを表示してやればOKです。コードだと以下のようになります。 (画面にボタンを1つだけおいてる前提)

using Windows.ApplicationModel.DataTransfer;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace App54
{
    publicsealedpartialclass MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        protectedoverridevoid OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
            // データの要求があったときのイベントを購読
            var dtm = DataTransferManager.GetForCurrentView();
            dtm.DataRequested += this.Dtm_DataRequested;
        }

        protectedoverridevoid OnNavigatedFrom(NavigationEventArgs e)
        {
            base.OnNavigatedFrom(e);
            // イベントの購読解除
            var dtm = DataTransferManager.GetForCurrentView();
            dtm.DataRequested -= this.Dtm_DataRequested;
        }

        privatevoid Button_Click(object sender, RoutedEventArgs e)
        {
            // 共有のためのUIを表示
            DataTransferManager.ShowShareUI();
        }

        privatevoid Dtm_DataRequested(DataTransferManager sender, DataRequestedEventArgs args)
        {
            // 共有するデータを設定する
            args.Request.Data.Properties.Title = "Hello";
            args.Request.Data.Properties.Description = "練習";
            args.Request.Data.SetText("Hello world");

            // 途中に非同期処理とかが入るときは以下のようにDeferralを使う// var d = args.Request.GetDeferral();// ここで非同期処理でデータをとってきたりする// d.Complete();
        }
    }
}

実行してボタンを押すと、以下のように共有のUIが表示されます。

f:id:okazuki:20160315125211p:plain

Aristeaに共有した場合。プログラムでセットしたHello worldが渡されてることがわかります。

f:id:okazuki:20160315125244p:plain

DataLakeを.NETから使う下準備

$
0
0

超ハマったのでメモを残しておきます。

まず、AzureADでアプリ作らないといけないんですよ、アプリ。基本的な手順は以下のページに従ってやればOKです。

azure.microsoft.com

ハマった所としては、このとき使うAzureADのディレクトリってAzureに最初から作られてるディレクトリじゃないといけないってところでした。新しくディレクトリ作ったりしてましたよ…。

アプリケーションを作ったら、クライアントIDとキーとテナントIDを取得しておきます。

以下のページにほぼしたがっていけばOKです。

azure.microsoft.com

ハマった所としては、RBACでリソースグループに対してアプリケーションにアクセス権をOwnerとして与えたのですが、それだと何故かアクセスできなくてData Lakeの画面からフォルダに対してアプリケーションにフルアクセス権限与えたら、フォルダのリストを取得できるようになりました。

とりあえずエラーが出ずに動いたコードを貼っておきます。上記サイトではTenantIdにcommonを使ってますが、私の環境ではエラーになったのでテナントIDはAzureADのアプリケーションから取得したものに差し替えました。

using Microsoft.Azure.Management.DataLake.Store;
using Microsoft.Azure.Management.DataLake.Store.Models;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Rest;
using System;

namespace ConsoleApplication8
{
    class Program
    {
        privatestaticstring ClientId { get; } = "AzureADで取得したクライアントID";
        privatestaticstring Key { get; } = "AzureADで取得したキー";
        privatestaticstring TenantId { get; } = "AzureADで取得したテナントID";
        privatestaticstring SubscriptionId { get; } = "自分のサブスクリプションID";

        staticvoid Main(string[] args)
        {
            // signin
            var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{TenantId}");
            var credential = new ClientCredential(ClientId, Key);
            var authResult = authContext.AcquireToken("https://management.core.windows.net/", credential);
            var tokenCredentials = new TokenCredentials(authResult.AccessToken);

            // init
            var adlsClient = new DataLakeStoreAccountManagementClient(tokenCredentials)
            {
                SubscriptionId = SubscriptionId
            };
            var adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(tokenCredentials)
            {
                SubscriptionId = SubscriptionId
            };
            adlsClient.Account.Create("リソースグループ名", "DataLakeのアカウント名(Data LakeのURLの最初の部分)", new DataLakeStoreAccount(location: "East US 2"));

            foreach(var fs in adlsFileSystemClient.FileSystem.ListFileStatus("/", "dlsampleadls").FileStatuses.FileStatus)
            {
                Console.WriteLine($"{fs.PathSuffix}");
            }
        }
    }
}

Data Lakeを.NETから使う「アップロード・ダウンロード」

$
0
0

下準備については1つ前のエントリを参照してください。

blog.okazuki.jp

このプログラムに続いて以下のコードを書くことでファイルのアップロードとダウンロードが出来ました。

// file uploadusing (var fs = new FileStream("ConsoleApplication8.exe", FileMode.Open, FileAccess.Read))
{
    adlsFileSystemClient.FileSystem.Create("/sample1/sample.exe", "Data Lakeのアカウント名", fs);
}

// file downloadusing (var fs = new FileStream("output.exe", FileMode.Create))
{
    var s = adlsFileSystemClient.FileSystem.Open("/sample1/sample.exe", "Data Lakeのアカウント名");
    s.CopyTo(fs);
}

UWPで電話に実機デプロイができなくなった

$
0
0

何がきっかけかわかりませんが以下のエラーが出て実機デプロイできなくなりました…。

2>エラー: DEP0001 : 予期しないエラー: Install failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CF9)

何だろう。

まとめ

他の人のVisual Studioからアプリの実機デプロイはできたので自分のマシンのせいかと思ったら、別のWindows 10 Mobileに対しては自分のマシンから実機デプロイできたので、思い切ってMADOSMAをリセットしました。そうすると、実機デプロイできなかった端末に対しても実機デプロイが出来るようになりました。

端末がどうもあやしかったみたいです。

Data Lake Analyticsに.NETからクエリを投げる

$
0
0

下準備については1つ前のエントリを参照してください。

blog.okazuki.jp

下準備で作ったプロジェクトに以下のアセンブリをNuGetから追加します(プレリリースで)

  • Microsoft.Azure.Management.DataLake.Analytics

そして下準備のプログラムに以下のような感じのプログラムを追加します。

// data upload
var ms = new MemoryStream();
var sw = new StreamWriter(ms);
sw.WriteLine("taro,12");
sw.WriteLine("jiro,14");
ms.Seek(0, SeekOrigin.Begin);
adlsFileSystemClient.FileSystem.Create("/sample1/data.csv", "Data Lakeストレージのアカウント名", ms, overwrite: true);

// 実行するクエリ
var query = @"    @src =        EXTRACT            Name string,            Age int        FROM ""/sample1/data.csv""        USING Extractors.Csv();    OUTPUT @src        TO ""/sample1/output.csv""        USING Outputters.Csv();";
// init
var adlaClient = new DataLakeAnalyticsAccountManagementClient(tokenCredentials)
{
    SubscriptionId = SubscriptionId
};
var adlaJobClient = new DataLakeAnalyticsJobManagementClient(tokenCredentials)
{
    SubscriptionId = SubscriptionId
};
var adlaCatalogClient = new DataLakeAnalyticsCatalogManagementClient(tokenCredentials)
{
    SubscriptionId = SubscriptionId
};

// add account(これいるのかな?? Createは動かなかった…)
adlaClient.Account.AddDataLakeStoreAccount("datalake",
    "dlsample",
    "dlsampleadls",
    new AddDataLakeStoreParameters(new DataLakeStoreAccountInfoProperties()));

var jobId = Guid.NewGuid();
var properties = new USqlJobProperties(query);
var parameters = new JobInformation("copy", JobType.USql, properties);
var jobInfo = adlaJobClient.Job.Create(jobId, parameters, "Data Lake Analyticsのアカウント名");
while (jobInfo.State != JobState.Ended)
{
    Console.WriteLine($"Waiting... {jobInfo.State}");
    Task.Delay(1000).Wait();
    jobInfo = adlaJobClient.Job.Get(jobId, "Data Lake Analyticsのアカウント名");
}
var jobResult = jobInfo.Result.Value;
Console.WriteLine(jobResult);
Console.ReadKey();

UWPで共有コントラクト 受信側

$
0
0

送信側は簡単にできました。

blog.okazuki.jp

ということで、今度は受信側です。 受信側はPackage.appxmanifestでアプリが何を受信できるのか定義する必要があります。宣言タブで共有ターゲットを追加して、簡単な共有の説明と受け付けるデータ形式を入力します。この例ではファイルを受け取るようにしました。

f:id:okazuki:20160321152923p:plain

これで、配置をすると共有の選択肢に出てくるようになります。

f:id:okazuki:20160321153023p:plain

共有されたときの処理は、AppクラスのOnShareTargetActivatedメソッドをオーバーライドすることで記述できます。ここで、Window.Current.ContentにPageを突っ込むと、任意のページを表示することができます。

例えば、ShareTargetPageというページを作成して以下のようなXAMLを書きます。

<Page x:Class="App60.ShareTargetPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App60"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Image x:Name="Image" /></Grid></Page>

そして、以下のようなShareTargetActivatedEventArgsを受け取るメソッドを作ります。 ShareTargetActivatedEventArgsのShareOperationのDataからGetXXXメソッドで共有されたデータを取得することが出来ます。

ここでは、共有されたストレージアイテムが画像だという前提で表示するというコードを書いてます。(エラー処理とか省いてます)

using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;

namespace App60
{
    publicsealedpartialclass ShareTargetPage : Page
    {
        public ShareTargetPage()
        {
            this.InitializeComponent();
        }

        public async Task ActivateAsync(ShareTargetActivatedEventArgs args)
        {
            var item = (await args.ShareOperation.Data.GetStorageItemsAsync()).FirstOrDefault() as StorageFile;
            var bitmap = new BitmapImage();
            using (var s = await item.OpenReadAsync())
            {
                await bitmap.SetSourceAsync(s);
            }
            this.Image.Source = bitmap;

            Window.Current.Content = this;
            Window.Current.Activate();
        }
    }
}

そして、このメソッドをAppクラスのOnShareTargetActivatedから呼んでやります。

protectedoverride async void OnShareTargetActivated(ShareTargetActivatedEventArgs args)
{
    var page = new ShareTargetPage();
    await page.ActivateAsync(args);
}

こうすると、画像を共有すると以下のような感じに画像が表示されます。

f:id:okazuki:20160321154716p:plain

ここでは表示してるだけですが、本番コードではボタンなどを置いて、ボタンが押されたらツイッターにツイートするとか、アプリ固有の処理を行うといいと思います。

共有ターゲットのデバッグ方法

小ネタですが、共有ターゲットのように他から呼び出されるような処理をデバッグする方法があります。プロジェクトのプロパティのデバッグの開始操作に起動しないが、開始時にコードをデバッグにチェックを入れておきます。

f:id:okazuki:20160321154903p:plain

こうするとデバッグ実行を押してもVisual Studioがデバッグ状態になるだけでアプリは起動しません。その状態で、共有操作などをして、アプリをキックするとデバッグが開始されます。

UWP対応のNuGetパッケージ


ReactiveProperty v2.6.0をリリースしました

$
0
0

www.nuget.org

さくっとリリースしました。

  • ReactivePropertyで使うデフォルトのスケジューラをReactivePropertySchedulerにしました

何もしないと、UIDispatcherSchedulerを使い、ReactivePropertyScheduler.SetDefaultでスケジューラーを明示したときにはそちらを使うようにしました。主にテスト時にスケジューラーを差し替えたり、コンソールアプリケーションのようにSynchronizationContextが無い環境での利用を想定しています。

  • UWPを正式にターゲットに追加しました(今までしてなかったのかよ)

Windows store appで良かったので特に対応していませんでしたが、UWP版のBehaviorであるMicrosoft.Xaml.Behaviors.Uwp.Managedを参照するようにしました。 EventToReactiveCommandやEventToReactivePropertyをUWPで使っている人はBehaviorSDKからMicrosoft.Xaml.Behaviors.Uwp.Managedに変更が必要になります。

後、それに伴って何故かアセットからドラッグアンドドロップでEventToReactiveXXXXXが追加できなくなりました…。Blendのせいだと思うのでちょっと様子見です。(手書きすれば波線が出るけど使えます)

UWPでキーボードのキーが押されているかどうか判定する

$
0
0

AltキーやCtrlキーやShiftキーなんかが押されてるかというときに使うと思います。

CoreWindow.GetKeyStateメソッドを使います。

var state = Window.Current.CoreWindow.GetKeyState(VirtualKey.Shift);
if ((state & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down)
{
    // 押されてるっぽい
}

UWPでキーボードショートカット

$
0
0

こんな感じのビヘイビアを定義しておきます。

using Microsoft.Xaml.Interactivity;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;

namespace App65
{
    publicclass KeyEventTriggerBehavior : DependencyObject, IBehavior
    {
        public DependencyObject AssociatedObject { get; private set; }

        public ActionCollection Actions
        {
            get
            {
                if (GetValue(ActionsProperty) == null)
                {
                    this.Actions = new ActionCollection();
                }
                return (ActionCollection)GetValue(ActionsProperty);
            }
            set { SetValue(ActionsProperty, value); }
        }

        publicstaticreadonly DependencyProperty ActionsProperty =
            DependencyProperty.Register(
                nameof(Actions), 
                typeof(ActionCollection), 
                typeof(KeyEventTriggerBehavior), 
                new PropertyMetadata(null));

        publicbool ShiftKey
        {
            get { return (bool)GetValue(ShiftKeyProperty); }
            set { SetValue(ShiftKeyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShiftKey.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ShiftKeyProperty =
            DependencyProperty.Register("ShiftKey", typeof(bool), typeof(KeyEventTriggerBehavior), new PropertyMetadata(false));

        publicbool CtrlKey
        {
            get { return (bool)GetValue(CtrlKeyProperty); }
            set { SetValue(CtrlKeyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CtrlKey.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty CtrlKeyProperty =
            DependencyProperty.Register("CtrlKey", typeof(bool), typeof(KeyEventTriggerBehavior), new PropertyMetadata(false));

        public VirtualKey Key
        {
            get { return (VirtualKey)GetValue(KeyProperty); }
            set { SetValue(KeyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Key.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty KeyProperty =
            DependencyProperty.Register("Key", typeof(VirtualKey), typeof(KeyEventTriggerBehavior), new PropertyMetadata(VirtualKey.None));

        publicvoid Attach(DependencyObject associatedObject)
        {
            this.AssociatedObject = associatedObject;
            this.Register();
        }

        publicvoid Detach()
        {
            this.Unregister();
            this.AssociatedObject = null;
        }

        privatevoid Register()
        {
            var fe = this.AssociatedObject as FrameworkElement;
            if (fe == null) { return; }
            fe.Unloaded += this.Fe_Unloaded;
            Window.Current.CoreWindow.KeyDown += this.CoreWindow_KeyDown;
        }

        privatevoid Unregister()
        {
            var fe = this.AssociatedObject as FrameworkElement;
            if (fe == null) { return; }
            fe.Unloaded -= this.Fe_Unloaded;
            Window.Current.CoreWindow.KeyDown -= this.CoreWindow_KeyDown;
        }

        privatevoid CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args)
        {
            if (this.ShiftKey && (Window.Current.CoreWindow.GetKeyState(VirtualKey.Shift) & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
            {
                return;
            }

            if (this.CtrlKey && (Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
            {
                return;
            }

            if (this.Key == args.VirtualKey)
            {
                Interaction.ExecuteActions(this, this.Actions, args);
                args.Handled = true;
            }
        }

        privatevoid Fe_Unloaded(object sender, RoutedEventArgs e)
        {
            this.Unregister();
        }
    }
}

こういう風に使います。

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:local="using:App65"      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:Core="using:Microsoft.Xaml.Interactions.Core"      x:Name="page"      x:Class="App65.MainPage"      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

        <Interactivity:Interaction.Behaviors>
            <local:KeyEventTriggerBehavior Key="A"
                                           ShiftKey="True">
                <local:KeyEventTriggerBehavior.Actions>
                    <Core:CallMethodAction TargetObject="{Binding ElementName=page}"
                                           MethodName="Alert" />
                </local:KeyEventTriggerBehavior.Actions>
            </local:KeyEventTriggerBehavior>
            <local:KeyEventTriggerBehavior Key="Enter"
                                           CtrlKey="True">
                <local:KeyEventTriggerBehavior.Actions>
                    <Core:CallMethodAction TargetObject="{Binding ElementName=page}"
                                           MethodName="Tweet" />
                </local:KeyEventTriggerBehavior.Actions>
            </local:KeyEventTriggerBehavior>
        </Interactivity:Interaction.Behaviors>
        <Button Content="Click" />
    </Grid>
</Page>

課題としては、TextBoxみたいにキーイベントをかっさらってしまう奴にフォーカスがあるとショートカットが効かないという点でしょうか…Preview系イベントが恋しい。

UWPでキーボードショートカット その2

$
0
0

その1では、CoreWindowのKeyDownを使ってました。 DispatcherのAccelaratorKeyActivatedイベントも使える奴みたいですね。 こっちでやるとTextBoxがフォーカス持っててもEnterとかのイベントが拾えたのでこっちのほうがいいかも。

こんな感じのBehaviorになりました。

using Microsoft.Xaml.Interactivity;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;

namespace App65
{
    publicclass KeyEventTriggerBehavior : DependencyObject, IBehavior
    {
        public DependencyObject AssociatedObject { get; private set; }

        public ActionCollection Actions
        {
            get
            {
                if (GetValue(ActionsProperty) == null)
                {
                    this.Actions = new ActionCollection();
                }
                return (ActionCollection)GetValue(ActionsProperty);
            }
            set { SetValue(ActionsProperty, value); }
        }

        publicstaticreadonly DependencyProperty ActionsProperty =
            DependencyProperty.Register(
                nameof(Actions), 
                typeof(ActionCollection), 
                typeof(KeyEventTriggerBehavior), 
                new PropertyMetadata(null));

        publicbool ShiftKey
        {
            get { return (bool)GetValue(ShiftKeyProperty); }
            set { SetValue(ShiftKeyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShiftKey.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty ShiftKeyProperty =
            DependencyProperty.Register("ShiftKey", typeof(bool), typeof(KeyEventTriggerBehavior), new PropertyMetadata(false));

        publicbool CtrlKey
        {
            get { return (bool)GetValue(CtrlKeyProperty); }
            set { SetValue(CtrlKeyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CtrlKey.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty CtrlKeyProperty =
            DependencyProperty.Register("CtrlKey", typeof(bool), typeof(KeyEventTriggerBehavior), new PropertyMetadata(false));

        public VirtualKey Key
        {
            get { return (VirtualKey)GetValue(KeyProperty); }
            set { SetValue(KeyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Key.  This enables animation, styling, binding, etc...publicstaticreadonly DependencyProperty KeyProperty =
            DependencyProperty.Register("Key", typeof(VirtualKey), typeof(KeyEventTriggerBehavior), new PropertyMetadata(VirtualKey.None));

        publicvoid Attach(DependencyObject associatedObject)
        {
            this.AssociatedObject = associatedObject;
            this.Register();
        }

        publicvoid Detach()
        {
            this.Unregister();
            this.AssociatedObject = null;
        }

        privatevoid Register()
        {
            var fe = this.AssociatedObject as FrameworkElement;
            if (fe == null) { return; }
            fe.Unloaded += this.Fe_Unloaded;
            this.Dispatcher.AcceleratorKeyActivated += this.Dispatcher_AccelaratorKeyActivated;
        }

        privatevoid Unregister()
        {
            var fe = this.AssociatedObject as FrameworkElement;
            if (fe == null) { return; }
            fe.Unloaded -= this.Fe_Unloaded;
            this.Dispatcher.AcceleratorKeyActivated -= this.Dispatcher_AccelaratorKeyActivated;
        }

        privatevoid Dispatcher_AccelaratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs args)
        {
            if (args.KeyStatus.RepeatCount != 1)
            {
                return;
            }
            if (args.EventType != CoreAcceleratorKeyEventType.KeyDown)
            {
                return;
            }

            if (this.ShiftKey && (Window.Current.CoreWindow.GetKeyState(VirtualKey.Shift) & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
            {
                return;
            }

            if (this.CtrlKey && (Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) & CoreVirtualKeyStates.Down) != CoreVirtualKeyStates.Down)
            {
                return;
            }

            if (this.Key == args.VirtualKey)
            {
                Interaction.ExecuteActions(this, this.Actions, args);
                args.Handled = true;
            }
        }

        privatevoid Fe_Unloaded(object sender, RoutedEventArgs e)
        {
            this.Unregister();
        }
    }
}

使い方は前回と同じ。

Taskを返すメソッドをそのままreturnして失敗した

$
0
0

Task HogehogeAsync()みたいなメソッドがあるとします。 そのメソッドをTask FugafugaAsync()で呼ぶときに、単発で呼ぶだけならそのままreturnしてしまいますよね。

async FugafugaAsync()
{
    return HogehogeAsync();
}

こんなノリで以下のようなメソッドでやらかしてしまいました。

async FugafugaAsync()
{
    IsProcessing = true;
    try
    {
        return HogehogeAsync();
    }
    finally
    {
        this.IsProcessing = false;
    }
}

これはawaitしないと一瞬でメソッドを抜けてしまうのでIsProcessingが一瞬でtrue→falseになってしまいます。 暫く悩みました。

UWPのListView/GridViewでSelectedItemsをViewModelに渡したい

$
0
0

SelectedItemsってDependencyPropertyじゃないっぽいのでバインド出来ないんですよね。ということで、ベタな方法ですがView → ViewModelの1方向でいいなら以下のような方法が使えます。

まず、ViewModelとListViewに表示するアイテムのクラスを作ります。

using System;
using System.Collections.Generic;
using System.Linq;
using Windows.UI.Popups;

namespace App66
{
    publicclass MainPageViewModel
    {
        public IEnumerable<Person> SelectedItems { get; set; } = Enumerable.Empty<Person>();

        public async void Dump()
        {
            var message = $"{string.Join(", ", this.SelectedItems.Select(x => x.Name).ToArray())} selected.";
            var dlg = new MessageDialog(message);
            await dlg.ShowAsync();
        }
    }

    publicclass Person
    {
        publicstring Name { get; set; }

        publicoverridestring ToString()
        {
            returnthis.Name;
        }
    }
}

ViewModelのSelectedItemsに選択された項目を突っ込むようにします。 やり方は、ListViewのSelectionChangedで突っ込むだけ。

<Pagex:Class="App66.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App66"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:System="using:System"mc:Ignorable="d"><Page.DataContext><local:MainPageViewModel /></Page.DataContext><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><Button Content="Dump"HorizontalAlignment="Stretch"Click="{x:Bind ViewModel.Dump}" /><ListView x:Name="ListView"Grid.Row="1"SelectionMode="Multiple"SelectionChanged="ListView_SelectionChanged"><local:Person Name="Tanaka" /><local:Person Name="Kimura" /><local:Person Name="Ohta" /><local:Person Name="Homuhomu" /></ListView></Grid></Page>
using System.Linq;
using Windows.UI.Xaml.Controls;

// 空白ページのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 を参照してくださいnamespace App66
{
    /// <summary>/// それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。/// </summary>publicsealedpartialclass MainPage : Page
    {
        public MainPageViewModel ViewModel => this.DataContext as MainPageViewModel;

        public MainPage()
        {
            this.InitializeComponent();
        }

        privatevoid ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            this.ViewModel.SelectedItems = this.ListView.SelectedItems.Cast<Person>();
        }
    }
}

多用するならビヘイビア化してもいいかもですね。

UWPでListView/GridViewのItemのIsSelectedプロパティをバインディングする

$
0
0

WPFだとStyleにBinding書けばいけるんですけど、UWPだと出来ない!由々しき事態。ということでUWPだとどうやるかというとListViewとかを拡張してごにょごにょする必要があります。

stackoverflow.com

こんな感じでListViewを拡張します。ListViewItemが作られるときにさくっと手を入れてバインドを作る感じです。

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;

namespace App66
{
    publicclass ListViewEx : ListView
    {
        protectedoverridevoid PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            var binding = new Binding();
            binding.Path = new PropertyPath("IsSelected");
            binding.Mode = BindingMode.TwoWay;
            binding.Source = item;
            ((ListViewItem)element).SetBinding(ListViewItem.IsSelectedProperty, binding);
        }
    }
}

あとは、IsSelectedを持ったクラスのコレクションとItemsSourceをBindingすればOK。

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Windows.UI.Popups;

namespace App66
{
    publicclass MainPageViewModel
    {
        public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();

        publicvoid AddPerson()
        {
            this.People.Add(new Person { Name = Guid.NewGuid().ToString() });
        }

        public async void Dump()
        {
            var message = $"{string.Join(", ", this.People.Where(x => x.IsSelected).Select(x => x.Name).ToArray())} selected.";
            var dlg = new MessageDialog(message);
            await dlg.ShowAsync();
        }
    }

    publicclass Person : INotifyPropertyChanged
    {
        publicevent PropertyChangedEventHandler PropertyChanged;

        publicstring Name { get; set; }

        privatestaticreadonly PropertyChangedEventArgs IsSelectedPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsSelected));

        privatebool isSelected;

        publicbool IsSelected
        {
            get { returnthis.isSelected; }
            set
            {
                if (this.isSelected == value) { return; }
                this.isSelected = value;
                this.PropertyChanged?.Invoke(this, IsSelectedPropertyChangedEventArgs);
            }
        }


        publicoverridestring ToString()
        {
            returnthis.Name;
        }
    }
}
<Page x:Class="App66.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App66"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:System="using:System"mc:Ignorable="d"><Page.DataContext><local:MainPageViewModel /></Page.DataContext><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition /></Grid.RowDefinitions><StackPanel Orientation="Horizontal"><Button Content="Add"Click="{x:Bind ViewModel.AddPerson}" /><Button Content="Dump"Click="{x:Bind ViewModel.Dump}" /></StackPanel><local:ListViewEx x:Name="ListView"Grid.Row="1"SelectionMode="Multiple"ItemsSource="{x:Bind ViewModel.People}"></local:ListViewEx></Grid></Page>

なかなかにメンドイ。


UWPでSHIFT_JISを使いたい

$
0
0

Encoding.GetEncodingを呼ぶ前に以下のおまじないを追加しておくことで使うことが出来るようになります。

System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

Visual StudioのUWPデザイナで謎のエラー

$
0
0

たぶんこれと一緒のエラーが出てました。

[UWP]Xaml designer failed to load on VS2015 Pro Update 2

System.TypeLoadException
Method 'GetStylePropertyTargetType' in type 'Microsoft.VisualStudio.DesignTools.WindowsXamlDesigner.Metadata.WindowsUIXamlPlatformMetadata' from assembly 'Microsoft.VisualStudio.DesignTools.UniversalXamlDesigner, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' does not have an implementation.

症状としては、UWPのツールが正常にインストールされない状態でした。 修復もだめ、VSの再インストールもダメなので、出ちゃったらOSからクリーンにしないといけないかもです。

私はOSから入れなおして復旧しました。

HoloLensのエミュレータでUWPが動いた!

$
0
0

当然っちゃ当然ですが動きました。

HoloLensのエミュレータをインストールすると、以下のように実行の並びにHoloLens Emulatorが追加されます。

f:id:okazuki:20160402082726p:plain

これで実行するとい以下のようにエミュレータが起動して、その中でアプリが動きます。

f:id:okazuki:20160402082952p:plain

ドラッグで視線みたいなカーソル移動で右クリックでタップみたいな雰囲気みたいです。試しにボタンを押してみました。

f:id:okazuki:20160402083104p:plain

Button_ClickにTextBlock書き換える処理書いたプログラムなんですが当然っちゃ当然ですが動きますね!早く実物で試したい。(高いし北米だけなので買えない)

UWPで要素をソフトウェアキーボードに追従させる

$
0
0

雪猫さんのところでこんな感じのことをやってました。

[UWP] 要素をソフトウェアキーボードに追従させる | 雪猫ノート

こういう感じでUIに閉じた操作ならBehaviorのほうがいいかな?と思ったのでどんな感じなのかというのを。

using Microsoft.Xaml.Interactivity;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;

namespace App2
{
    publicclass InputPaneBehavior : DependencyObject, IBehavior
    {
        public DependencyObject AssociatedObject { get; private set; }

        private FrameworkElement AssociatedElement => this.AssociatedObject as FrameworkElement;

        publicvoid Attach(DependencyObject associatedObject)
        {
            this.AssociatedObject = associatedObject;
            InputPane.GetForCurrentView().Showing += this.InputPaneBehavior_Showing;
            InputPane.GetForCurrentView().Hiding += this.InputPaneBehavior_Hiding;
        }

        publicvoid Detach()
        {
            InputPane.GetForCurrentView().Showing -= this.InputPaneBehavior_Showing;
            InputPane.GetForCurrentView().Hiding -= this.InputPaneBehavior_Hiding;
        }

        privatevoid InputPaneBehavior_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
        {
            this.AssociatedElement.Margin = new Thickness(0, 0, 0, sender.OccludedRect.Height);
        }

        privatevoid InputPaneBehavior_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
        {
            this.AssociatedElement.Margin = new Thickness(0, 0, 0, sender.OccludedRect.Height);
        }

    }
}

こんな感じのBehaviorを用意しておいて

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App2"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:Core="using:Microsoft.Xaml.Interactions.Core"x:Class="App2.MainPage"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinition /><RowDefinition Height="Auto" /></Grid.RowDefinitions><TextBox /><Grid Grid.Row="1"><Interactivity:Interaction.Behaviors><local:InputPaneBehavior /></Interactivity:Interaction.Behaviors><Button Content="OK" /></Grid></Grid></Page>

こういう感じで使います。

因みに、CommandBarでいい気がするのですが、今のところCommandBarの上のボタンってソフトウェアキーボードが出てると押せなかったり押せたりと動作が不安定なんですよね…。

UWPのサンプルのPullToRefresh(引っ張って更新)からRefreshableListViewを持ってくる方法

$
0
0

@mntoneさんがRefreshableListViewがあることをツイッターで呟いてたので見てみました。実装は置いといて、使うためには自分のプロジェクトに持ってこないといけないですね。ということでもっていこうと思います。

UWPのサンプルのリポジトリをクローンします。まず、ソースがないと始まりません。

github.com

そして、PullToRefreshのプロジェクトのフォルダをエクスプローラで開きます。

次に移動先のプロジェクトをVisual Studioで開いてお行きます。

クラスの本体は「RefreshableListView.cs」なので、まずこいつをコピペします。エクスプローラからVisual Studioにドラッグアンドドロップしましょう。次にStylesフォルダをドラッグアンドドロップします。

f:id:okazuki:20160403154520p:plain

Stylesフォルダの下のStyles.xamlをプロジェクトに取り込むために、ソリューションエクスプローラの「すべてのファイルを表示」ボタンを押します。

f:id:okazuki:20160403154644p:plain

そして、Stylesの下にあるStyles.xamlをプロジェクトに含めるを選択してプロジェクトに追加します。

f:id:okazuki:20160403154759p:plain

そして、App.xamlにStyles.xamlを取り込むように書きます。

<Application x:Class="App4.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App4"RequestedTheme="Light"><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="/Styles/Styles.xaml" /></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources></Application>

あとは、以下のように画面に置くことで使用できます。

<Page x:Class="App4.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="using:App4"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:RefreshableListView="using:RefreshableListView"mc:Ignorable="d"><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><RefreshableListView:RefreshableListView ItemsSource="{x:Bind Source}"RefreshRequested="RefreshableListView_RefreshRequested"AutoRefresh="False"><RefreshableListView:RefreshableListView.RefreshIndicatorContent><Grid Height="100"><SymbolIcon Symbol="Accept"VerticalAlignment="Center"HorizontalAlignment="Center"/></Grid></RefreshableListView:RefreshableListView.RefreshIndicatorContent><RefreshableListView:RefreshableListView.ItemTemplate><DataTemplate><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition /></Grid.ColumnDefinitions><Rectangle Width="100"Height="100"Fill="Red"Margin="5" /><TextBlock Grid.Column="1"Text="{Binding}"Style="{StaticResource BodyTextBlockStyle}" /></Grid></DataTemplate></RefreshableListView:RefreshableListView.ItemTemplate></RefreshableListView:RefreshableListView></Grid></Page>

RefreshIndicatorContentで引っ張ったときに表示されるコンテンツを指定できます。また、RefreshRequestedイベントでリフレッシュ時の処理を指定できます。

あと、AutoRefreshがTrueのとき(デフォルト)の挙動はちょっとあやしいっぽいのでFalseにして使うのがよさそうです。

なんとなく

このRefreshableListViewの実装だと仮想化効かない気がするんだけど…。

Viewing all 1388 articles
Browse latest View live