RunningCSharp

MS系開発者による、雑多な記事。記事は所属企業とは関係のない、個人の見解です。

WPF:コントロールの脇にContextMenuを表示する

コントロールに沿う感じでコンテキストメニューを表示しようとしたところ、少しはまってしまった為メモ。

f:id:ys-soniclab:20160604152217p:plain

図のように、ボタンに沿う形でコンテキストメニューを出したかった。 コードは下記のとおり。

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button ContextMenuService.Placement="Left"
            HorizontalAlignment="Center" VerticalAlignment="Center" Content="test">
            <Button.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="test1" />
                    <MenuItem Header="test2" />
                    <MenuItem Header="test3" />
                </ContextMenu>
            </Button.ContextMenu>
        </Button>
    </Grid>
</Window>

上記のように、コンテキストメニューの表示元コントロールにContextMenuService.Placementプロパティを指定する。

C#:基底クラスのプロパティ情報をリフレクションで取得する拡張メソッド

    /// <summary>
    /// 基底クラス
    /// </summary>
    public class SuperClass
    {
        public SuperClass()
        {
            SuperProperty = "Super";
        }
        private string SuperProperty { get; set; }
    }

    /// <summary>
    /// 派生クラス(子クラス)
    /// </summary>
    public class Sub1Class : SuperClass
    {
    }

    /// <summary>
    /// 派生クラスの派生クラス(孫クラス)
    /// </summary>
    public class Sub2Class : Sub1Class
    {
    }

上記のようなクラス構成で「Sub2Class」より、SuperClassのSuperPropertyを取得したい場合、 privateなプロパティなので、リフレクションを用いて取得することになります。

しかし、孫クラスとなっているため単純にBaseTypeでクラス情報を取得することもできないため、 プロパティ名とBindingFlagsを渡すと継承ツリーからプロパティを取得してくる拡張メソッドを作成してみました。

    public static class MethodClass
    {
        /// <summary>
        /// プロパティ名とBindingFlagsより、型の継承ツリー内にあるプロパティ情報を検索し返却します。
        /// 最も派生クラスに近いクラスのプロパティが返却されます。
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name">プロパティ名</param>
        /// <param name="bindingAttr">BindingFlags</param>
        /// <returns>プロパティを表すオブジェクト</returns>
        public static PropertyInfo GetSuperClassPropertyInfo(this Type type, string name, BindingFlags bindingAttr)
        {
            var info = type.GetProperty(name, bindingAttr);
            if (info != null)
            {
                return info;
            }
            else if (type.BaseType != null)
            {
                return type.BaseType.GetSuperClassPropertyInfo(name, bindingAttr);
            }
            return null;
        }
    }

下記のような形で使用します。

    class Program
    {
        static void Main(string[] args)
        {
            Sub2Class sub2Class = new Sub2Class();
            var info = sub2Class.GetType().GetSuperClassPropertyInfo("SuperProperty", BindingFlags.Instance | BindingFlags.NonPublic);
            Console.WriteLine((string)info.GetValue(sub2Class));
            Console.ReadLine();
        }
    }

実行結果は、

Super

となります。

もう少しスマートな方法があると良いなとは思うのですが…

C#:unsafeでboolにintを入れてみる

C#界隈で非常に有名なお方が「unsafeとかリフレクションでboolにtrue, false以外の値が入れられる」といった旨を呟かれていたのを拝見しました。 「unsafeでならintは入るかも」と思い、恐縮ながら試してみました。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(IntToBool(0));
        Console.WriteLine(IntToBool(5));
        Console.ReadKey();
    }
    unsafe static bool IntToBool(int _int)
    {
        int* intptr = &_int;
        bool* boolptr = (bool*)intptr;
        return *boolptr;
    }
}

結果(実際はコンソールに表示されます)

False

True

少し試した程度ですが、0はFalse、0以外はTrueで扱われる模様です。

Microsoft Band:日本語表示でIBM拡張文字の一部が表示できない

日本語表示に対応したMicrosoft Band2ですが、人名などで使われるはしご高(髙)が表示できませんでした。 幾つかの文字で試してみたところ、IBM拡張文字の中で「CP932に定義されているが、JIS X 0212JIS X 0213にない文字」の一部が表示できない模様です。

Wikipediaの「Microsoftコードページ932」(https://ja.wikipedia.org/wiki/Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932)より、 「∑ 仼 僴 凬 坙 峵 悅 愠 敎 昻 櫢 淲 淸 皂 蠇 赶 閒 靑 髙 﨎 晴 益 礼 靖 精 羽 﨣 逸 﨧 﨨 﨩 飯 飼 館 鶴」がCP932にしかないIBM拡張文字(=JIS X〜に登録されていない文字)との事ですので、 全て表示を試みた結果、「∑」だけは表示する事ができ、他は表示できませんでした。

その他の文字(=JIS X~に登録されている文字)について私が試した限りでは、NEC特殊文字IBM拡張文字ともに正しく表示できる模様でした。

Microsoft Band:Web Tileを作ってみる

Microsoft Bandで、このブログの更新を読み込むタイルを作成してみます。

https://developer.microsoftband.com/

上記サイトで、「Web Tile」のGet Startedボタンを押下します。(次の遷移先でも、GetStarted押下。)

f:id:ys-soniclab:20160507233751p:plain

上記ページで、今回は更新を読み込むタイルを作るので「Feed tile」を選択しNextを押下。

f:id:ys-soniclab:20160508204456p:plain ここでrssのurlを入力します。なお、はてなブログでは http://running-cs.hatenablog.com/rssのように、トップのurlにrssを付けたものを渡します。

入力後、Nextを押下。

f:id:ys-soniclab:20160508205355p:plain

この画面で画面に表示する項目をドラッグドロップで決定します。 今回はブログの新規投稿タイトルと内容を表示したいので、画像の矢印の通りドラッグドロップします。 なお、ドロップ後の文字列は日本語が化けてしまいますが、Bandに表示される際はきちんと化けずに表示されます。

f:id:ys-soniclab:20160508210129p:plain

そしてNext押下です。

f:id:ys-soniclab:20160508210442p:plain

ここはSkip押下です。

f:id:ys-soniclab:20160508210628p:plain

ここでタイトルの表示色やタイル名、説明、作者、任意でアイコンなどを設定できます。

今回は画像がないので、アイコンは設定せずに進みます。

f:id:ys-soniclab:20160508211047p:plain

こんな感じに入力後、Nextを押下。

f:id:ys-soniclab:20160508211135p:plain

この画面で「Submit my tile」を押下するとこのタイルを公開できるようなのですが、流石に技術ブログの更新をBandで受けたい人も少ないと思いますので、公開はしませんでした。

「Download my tile」を押下し、「Microsoft Health」が入ったモバイルにメールなどで添付ファイルとして送り、「Microsoft Health」でそのファイルを開けばタイルが利用可能となります。

---追記---

f:id:ys-soniclab:20160508223340j:plain

この記事がアップされたことの通知が、Bandに表示されました。

Microsoft Band:1カ月半ほど使い込んだ所感

3月下旬にMicrosoft Bandを購入しました。 Blogのタイトル通りですが、一応私はフルマラソンなど走るので(自己ベストで5時間10分程と遅いのですが)、そのお供にと考えまして。

以下に使用体験の結果や個人的な感想などを記載させていただきます。

とりあえず付けているだけで毎日歩いた歩数と一日の消費カロリーなどが出るのも便利ですし、睡眠の質なども計れ、スマートフォン通知の日本語対応もできております。 (なお通知は日本語表示OKですが、Bandのメニュー表示やMicrosoft Healthは日本語表示できない模様です。)

ランニング、ロードバイク等のライディングやエクササイズについて細かい情報が表示されます。 (ランニングならタイムやコースや高低差、ペース、心拍数の推移も図れますし、更にランニング中も歩数計がちゃんと動いています。)

とっても便利ですが、唯一困ってしまったのが「ランニングなどGPSを使った計測が4時間前後行うと、電池が切れてしまう」ことです。 先日某フルマラソンで使うために、レース直前まで電源OFFにして試しましたが、3時間47分で電池切れとなってしまいました。 いや、もちろん私が4時間切るタイムでゴールしていればよかったのですが…

GPS Power Saver」なる機能を別の30kmレースで試してみましたが、レース終了時には32キロほど走った結果となり、2キロほど距離の誤差が出ていました。 走行場所は都内で、一緒に走った方が別のエクササイズバンド(G社製)で計測した際は正しく距離が出たコースでした。 私個人が利用した結果としては、「GPS Power Saver」はちょっと心配な感じでした。

海外のサイトなどで調べた結果、BlueTooth接続を切っておくと電池持つような記載があったため、そちらも今度試してみます。

現段階では、ハーフマラソンなど2~3時間程度の競技ではまさに最適なフィットネストラッカーと認識しております。 フルマラソンの際は、サブ4が出来ない間はリチウム電池とコードをリュックに詰めて走る所存です。

開発者的には、Microsoft Bandの一番の魅力は開発用SDKや簡単なタイルの作成、CloudAPIなんかが用意されていることですよね。 https://developer.microsoftband.com/

上記の機能を少し触りつつ、今後記事にできたらと考えております。

WPF:コマンド実行中は非活性となり、連続実行を抑止するボタン

わざわざIsEnabledプロパティをバインドしてコマンドにasync/awaitを書いて、連続クリックをロックで止めて…と書く画面に実装するのが面倒だと思い、作ってみました。

    public class AsyncButton : Button
    {
        /// <summary>
        /// Commandに値を入れられないようスコープを変更
        /// </summary>
        private new ICommand Command
        {
            get { return base.Command; }
            set { base.Command = value; }
        }

        /// <summary>
        /// 非同期実行用コマンド
        /// </summary>
        public ICommand AsyncCommand
        {
            get { return (ICommand)GetValue(AsyncCommandProperty); }
            set { SetValue(AsyncCommandProperty, value); }
        }

        /// <summary>
        /// キー連打による連続実行を止めるためのロックオブジェクト
        /// </summary>
        private object lockObject { get; set; }

        // Using a DependencyProperty as the backing store for AsyncCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AsyncCommandProperty =
            DependencyProperty.Register("AsyncCommand", typeof(ICommand), typeof(AsyncButton), new PropertyMetadata(null,OnAsyncCommandChanged));

        private static void OnAsyncCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AsyncButton btn = d as AsyncButton;
            //クリックイベントでEnterキー押下時などもハンドル可能なので、こちらを利用
            btn.Click += Btn_Click;
        }

        private static void Btn_Click(object sender, RoutedEventArgs e)
        {
            AsyncButton btn = sender as AsyncButton;
            ExecAsyncCommand(btn, btn.AsyncCommand, btn.CommandParameter);
        }

        private static async void ExecAsyncCommand(AsyncButton btn, ICommand command , object parameter)
        {
            if (btn.lockObject == null)
            {
                btn.lockObject = new object();
            }
            else
            {
                return;
            }
            await Task.Run(() =>
            {
                lock (btn.lockObject)
                {
                    btn.Dispatcher.Invoke(() => btn.IsEnabled = false);
                    command.Execute(parameter);
                    btn.Dispatcher.Invoke(() => btn.IsEnabled = true);
                }
                btn.lockObject = null;
            });
        }

使うときは、こんな感じで。

View

<local:AsyncButton AsyncCommand="{Binding MyCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Test" />

ViewModel

    public class MyViewModel
    {
        private RelayCommand _myCommand = null;
        public RelayCommand MyCommand
        {
            get
            {
                return _myCommand ?? (_myCommand = new RelayCommand(() =>
                {
                    //重い処理
                    Thread.Sleep(10000);
                }));
            }
        }
    }