XAML のデザイン モード

WPF をはじめとする XAML 系のアプリを Visual Studio や Blend で開発するとき、
例えば「レイアウトの作業時はサンプルのデータを表示させたい」ということがあります。

このような場合に、DesignerProperties.GetIsInDesignMode メソッドを使うことが考えられます。
このメソッドは XAML の編集画面でアプリが実行されているときに戻り値が true となるため、
デザイン時と非デザイン時でアプリの挙動を切り替えることができます。
ただし、このメソッドでは引数に DependencyObject を受け付けるため、Model 層では使いづらいです。

そこで、デザイン時のみ有効となる d: を使います。
例えば Model 層のクラスに bool 型の IsForDesign プロパティを用意しておき、XAML 上で
<local:AppModel d:IsForDesign="True"/>
と設定することで、挙動を切り替えることができます。
また、d: は UI 要素自体にも指定することができ、デザイン時のみ配置することができます。

namespace ContentWpf
{
public class AppModel
{
public bool IsForDesign { get; set; }
}
}
view raw AppModel.cs hosted with ❤ by GitHub
<Window x:Class="ContentWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
xmlns:local="clr-namespace:ContentWpf"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:AppModel d:IsForDesign="True"/>
</Window.DataContext>
<Grid>
<d:TextBlock Text="{x:Static system:Environment.CurrentDirectory}"/>
</Grid>
</Window>
view raw MainWindow.xaml hosted with ❤ by GitHub

利用例: DQBG: DQ’S BATTLEGROUNDS

参照

カテゴリー: クライアント技術. タグ: , . 1 Comment »

XAML 系の Tips 集

WPF をはじめとする XAML 系技術についての Tips を集めたものです。

XAML の記述

その他

  • マウスなどのイベントを下の階層の UI 要素で発生させたい場合、IsHitTestVisible プロパティを False に設定します。
  • 同じプロパティに対して、データ バインディングとアニメーションの両方を設定してはいけません。
  • ClickOnce インストーラーは、そのままローカルのインストーラーとしても使えます。
    • .application または setup.exe をローカルで直接実行すればよいです。

次回: XAML のデザイン モード

参照

Surface Book を修理 (交換) した

2017年に購入した Surface Book を修理 (実際には Surface Book 3 に交換) しました。

正確には Surface Book with Performance Base (13.5in, i7, 8GB, 256GB) という機種で、電源が入らなくなってしまいました。
そこで、修理依頼から新デバイス受取りまでの記録を残しておきます。

修理を依頼する

Surface シリーズの修理依頼は、Microsoft アカウント | デバイス から行います。
このページには、自身のアカウントに登録されているデバイス (機器) の一覧が表示されます。

注意
・Microsoft アカウントを持っていない場合は、新たにアカウントを登録します。
・自身のデバイスとして登録されていない場合は、シリアル番号を入力してデバイスを登録します。

このデバイス一覧の中から修理対象のデバイスを選択し、[注文を開始する] を押します。

Device-Repair-1

Device-Repair-2

(以降の手順は、画面の案内に従えばよいため省略します。)

Surface シリーズの標準の保証期間 (サポート期間) は1年間です。
画面上では「修理」と表示されますが、保証期間外の場合は症状にかかわらず、一律料金で新品への交換になるようです。

注意
・途中でゆうパックの「集荷」「持ち込み」の選択肢がありますが、どちらを選んでもその後に影響しないと思います。ここで「集荷」を選びましたが、実際の集荷は自分で手配しました。

さて、修理依頼が完了すると、段取りが書かれた電子メールが送られてくるので、以降はそれに従えばよいです。
次はデバイスを梱包して送付します。

デバイスを着払ゆうパックで送付する

デバイスを梱包します。余っているダンボールを用意し、さらに緩衝材として、余っている紙を使えば十分です。
紙を丸めて、デバイスを包み込むように配置しましょう。
付属品は修理の対象になりません。本体のみを送付します。

集荷・持ち込みのどちらでもよいですが、今回はゆうびんマイページで集荷を依頼しました。
「ゆうパック」ではなく「着払ゆうパック」を指定します。

また、伝票の自動印字機能がないため、手書きでした。
やたら長い宛先を指定されましたが、返送時の伝票からすると、少し省略して次の宛先でよいはずです。

    〒143‑0001
    東京都大田区東海 1‑3‑6 プロロジスパーク東京大田 3F‑S5
    カンタムソリューションズ 気付 マイクロソフト サービス センター

・もとは「Quantium Solutions」と書かれていましたが、カタカナの会社名でよいと思います。
・「Ivy Tech Japan」は書かなくてもよいと思います。
・電話番号は指定されておらず、実際に記入しなくても OK でした。

新品を待つ

「Surface Book with Performance Base (13.5in, i7, 8GB, 256GB) の在庫はもうないから、
Surface Book 3 (15in, i7, 16GB, 256GB) を送りたいがよろしいか?」
という内容の連絡が電子メールで来たため、もちろん OK と返信しました。
新機種の代替品が送られるというのは初めての経験のため驚きました。

数日後に発送され、通常とは異なる簡素な箱が送られてきました。
中には本体のみが入っています。

SurfaceBook3-1-re-re

SurfaceBook3-2-re-re

ところで、Surface Book with Performance Base に付属していた電源アダプターは 102W ですが、
Surface Book 3 (15in) を通常購入して付属してくる電源アダプターは 127W のはずです。
これについて問い合わせたところ、Surface Book 3 は 102W で問題なく使用できるとの回答でした。
実際、65W でも問題なく使えています。

注意
・端子は、Mini DisplayPort だった部分が USB Power Delivery (USB PD) の Type-C に変わりました。
・解像度は、3000 x 2000 から 3240 x 2160 に変わりました。

修理 (交換) 費用は 71,854 円でした。
この価格で新型の機種が購入できると考えれば安いと思います。
なお、保証期間は更新されません。

Billing-Orders

Surface-App-1

参照

カテゴリー: 周辺機器. タグ: . Leave a Comment »

平衡二分探索木を優先度付きキューとして使う

.NET 6 では、優先度付きキューを表す PriorityQueue<TElement,TPriority> クラスが基本クラスライブラリ (BCL) に追加されました。
言い換えると、.NET 5 以前の BCL には優先度付きキューが存在しません。
そこで、.NET 5 以前の環境において、平衡二分探索木のクラスを利用して優先度付きキューに相当する処理を実現する方法を考えてみます。

優先度付きキューの要件

まず、優先度付きキュー (priority queue) に必要な機能を整理します:

  • Count: 現在の要素の数を取得する
  • Peek: 優先度が最も高い要素を取得する。削除はしない
  • Pop: 優先度が最も高い要素を取得し、同時に削除する
  • Push: 新たな要素を追加する

すなわち、初めから全ての要素がわかっている (静的データ) とは限らず、要素の動的な追加に対応します。
また、オプション機能として次のものが考えられます:

  • 一般的に、要素の重複を許す
  • 昇順・降順を指定できる
  • 要素に対して、優先度を表すキーを指定できる
  • 安定ソート (一般的な二分ヒープでは不可)

例えば、1組の生徒たち、2組の生徒たち、・・・のような順でデータを取得する要件に対応するには、優先度を表すキーを指定できる機能があると便利です。

平衡二分探索木に関連するクラス

ここでは、.NET 5 以前の BCL に存在する平衡二分探索木およびその周辺のクラスについてまとめます。
以下の3種類があり、「追加した要素が自動的にキーの順序でソートされる」「キーの重複は不許可」という点はこれらに共通です。
なお、SortedList<TKey,TValue> クラスは平衡二分探索木ではありません (以降の節では使いません)。

(1) SortedSet<T> クラス

  • 要素をキーとして使用する
  • 要素の動的な追加および削除の時間計算量は O(log n)
  • HashSet<T> が順序付きになったと考えてよい

(2) SortedDictionary<TKey,TValue> クラス

  • キーと値のペアを格納する
  • 要素の動的な追加および削除の時間計算量は O(log n)
  • Dictionary<TKey,TValue> が順序付きになったと考えてよい

(3) SortedList<TKey,TValue> クラス

  • キーと値のペアを格納する
  • 通常のリストのような構造であり、空間計算量は O(n)
    • 平衡二分探索木ではない
  • 要素の動的な追加および削除の時間計算量は O(n)
  • キーからインデックスの取得、インデックスから要素の取得の時間計算量は O(log n)
  • ソート済みの静的データで初期化するだけの場合や、末尾にくるデータを追加するだけであれば速い

実装

では、平衡二分探索木を利用して、優先度付きキューを実装していきます。
要件に合わせて以下の3種類を用意しました。

(1) 要素を重複させない場合

  • SortedSet<T> をほぼそのまま利用する
  • Min プロパティおよび Add, Remove メソッドを呼び出せばよい

(2) 要素の重複を許す場合 (一般的な優先度付きキュー)

  • SortedDictionary<T, int> を利用する
  • 要素ごとの個数を管理する

(3) 要素に対して優先度を表すキーを指定する場合 (重複を許可)

  • SortedDictionary<TKey, Queue<T>> を利用する
  • キーごとにキューで要素を管理する
  • したがって、安定ソートとなる

ソースコードは次の通りです。言語は C# です。
なお、降順を指定するには、前回の記事で作成した補助クラスで IComparer<T> を生成すればよいです。

using System;
using System.Collections.Generic;
using System.Linq;
namespace AlgorithmLab.DataTrees
{
// 要素が重複しない (すべての値の順序が異なる) 場合に利用できます。
public class DistinctPriorityQueue<T>
{
// 要素をそのままキーとして使用します。
SortedSet<T> ss;
public DistinctPriorityQueue(IComparer<T> comparer = null)
{
ss = new SortedSet<T>(comparer ?? Comparer<T>.Default);
}
public int Count => ss.Count;
public T Peek()
{
if (ss.Count == 0) throw new InvalidOperationException("The container is empty.");
return ss.Min;
}
public T Pop()
{
if (ss.Count == 0) throw new InvalidOperationException("The container is empty.");
var item = ss.Min;
ss.Remove(item);
return item;
}
public bool Push(T item) => ss.Add(item);
}
// 要素が重複する場合も利用できます (一般的な優先度付きキュー)。
public class BstPriorityQueue<T>
{
// 要素をそのままキーとして使用します。
SortedDictionary<T, int> sd;
public BstPriorityQueue(IComparer<T> comparer = null)
{
sd = new SortedDictionary<T, int>(comparer ?? Comparer<T>.Default);
}
public int Count { get; private set; }
public T Peek()
{
if (Count == 0) throw new InvalidOperationException("The container is empty.");
return sd.First().Key;
}
public T Pop()
{
if (Count == 0) throw new InvalidOperationException("The container is empty.");
Count--;
var (item, count) = sd.First();
if (count == 1) sd.Remove(item);
else sd[item] = count - 1;
return item;
}
public void Push(T item)
{
Count++;
sd.TryGetValue(item, out var count);
sd[item] = count + 1;
}
}
// 要素に対して優先度を表すキーを指定する場合に利用します。
public class KeyedPriorityQueue<T, TKey>
{
SortedDictionary<TKey, Queue<T>> sd;
Func<T, TKey> keySelector;
public KeyedPriorityQueue(Func<T, TKey> keySelector, IComparer<TKey> comparer = null)
{
this.keySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector));
sd = new SortedDictionary<TKey, Queue<T>>(comparer ?? Comparer<TKey>.Default);
}
public int Count { get; private set; }
public T Peek()
{
if (Count == 0) throw new InvalidOperationException("The container is empty.");
return sd.First().Value.Peek();
}
public T Pop()
{
if (Count == 0) throw new InvalidOperationException("The container is empty.");
Count--;
var (key, q) = sd.First();
if (q.Count == 1) sd.Remove(key);
return q.Dequeue();
}
public void Push(T item)
{
Count++;
var key = keySelector(item);
if (!sd.TryGetValue(key, out var q)) sd[key] = q = new Queue<T>();
q.Enqueue(item);
}
}
}

また、平衡二分探索木を利用する利点として、優先度の高いほうだけでなく、両側に対する優先度付きキューを実現できることが挙げられます。PopFirst, PopLast メソッドとして作成すればよいでしょう。

利用例

(1) DistinctPriorityQueue

(2) BstPriorityQueue

(3) KeyedPriorityQueue

前回: ソート用の比較関数の補助クラス

作成したサンプル

検証したバージョン

  • C# 8.0
  • .NET Standard 2.1

参照

ソート用の比較関数の補助クラス

.NET で配列やコレクションをソートするときに、ソートの条件を指定する方法がいくつかあります。
今回はそれらを一通り調べ、さらにソート条件の指定を補助するためのクラスを作成しました。

既存ライブラリのまとめ

まず、配列やコレクションをソートするために呼び出す主なメソッドを挙げてみます。

そして、これらのメソッドのオーバーロードでソートの条件を指定する方法をまとめると、次のようになります。

  • IComparable<T>
    • 対象となる型 T が IComparable<T> インターフェイスを実装している場合、引数を指定しなくてもソート可能
    • ただし、これだけでは降順にできない
  • Comparison<T> または Func<T, T, int>
    • 引数 (x, y) に対し、次の値を返す比較関数
      • x が小さいならば、負の値
      • 等しいならば、0
      • x が大きいならば、正の値
  • IComparer<T>
  • TKey[]
    • 各要素に対応するキーの配列
    • キーの型が IComparable<TKey> インターフェイスを実装していることが必要
  • Func<T, TKey> によるキーの指定、および昇順・降順の指定
    • LINQ の Enumerable.OrderBy メソッドなど
    • 第2キー以降を指定するには Enumerable.ThenBy メソッドなど
    • キーの型が IComparable<TKey> インターフェイスを実装していることが必要

課題と解決策

普段これらを使っていると、次のような実感があります。

  • 対象となる型 T が IComparable<T> インターフェイスを実装しており、その昇順でソートしたい場合は、引数のないオーバーロードを呼び出すだけであり簡単
  • それ以外の少し複雑な条件の場合、LINQ のように、Func<T, TKey> によりキーを指定したいことが多い
    • しかし、このオーバーロードは用意されていないことが多い
  • IComparer<T> オブジェクトを引数に指定するオーバーロードであれば、だいたいどのソート関数でも備えている

そこで、「Func<T, TKey> によるキーの指定、および昇順・降順の指定」から IComparer<T> オブジェクトを生成するためのヘルパー クラスを作成しました。言語は C# です。

using System;
using System.Collections.Generic;
namespace AlgorithmLab.DataTrees
{
public static class ComparerHelper
{
public static IComparer<T> GetDefault<T>()
{
// カルチャに依存しない場合に高速化します。
if (typeof(T) == typeof(string)) return (IComparer<T>)StringComparer.Ordinal;
return Comparer<T>.Default;
}
public static IComparer<T> ToDescending<T>(this IComparer<T> c)
{
if (c == null) throw new ArgumentNullException(nameof(c));
return Comparer<T>.Create((x, y) => c.Compare(y, x));
}
}
// クラスに型引数を指定することで、Create メソッドを呼び出すときに型引数 <T, Tkey> の指定を省略できます。
public static class ComparerHelper<T>
{
public static IComparer<T> Create(bool descending = false)
{
var c = ComparerHelper.GetDefault<T>();
return descending ? c.ToDescending() : c;
}
public static IComparer<T> Create<TKey>(Func<T, TKey> keySelector, bool descending = false)
{
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
var c = ComparerHelper<TKey>.Create(descending);
return Comparer<T>.Create((x, y) => c.Compare(keySelector(x), keySelector(y)));
}
public static IComparer<T> Create<TKey1, TKey2>(Func<T, TKey1> keySelector1, bool descending1, Func<T, TKey2> keySelector2, bool descending2)
{
if (keySelector1 == null) throw new ArgumentNullException(nameof(keySelector1));
if (keySelector2 == null) throw new ArgumentNullException(nameof(keySelector2));
var c1 = ComparerHelper<TKey1>.Create(descending1);
var c2 = ComparerHelper<TKey2>.Create(descending2);
return Comparer<T>.Create((x, y) =>
{
var d = c1.Compare(keySelector1(x), keySelector1(y));
if (d != 0) return d;
return c2.Compare(keySelector2(x), keySelector2(y));
});
}
}
}
using System;
using AlgorithmLab.DataTrees;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var a = new[] { 3, 14, 1, 592, 65, 358 };
// 文字数の降順、値の昇順
// 結果: { 358, 592, 14, 65, 1, 3 }
Array.Sort(a, ComparerHelper<int>.Create(x => x.ToString().Length, true, x => x, false));
}
}
}
view raw Program.cs hosted with ❤ by GitHub

これにより、LINQ 以外の場面でソートするときも、LINQ とほぼ同様の記述方法でキーを指定できるようになりました。

次回: 平衡二分探索木を優先度付きキューとして使う

作成したサンプル

検証したバージョン

  • C# 6
  • .NET Standard 2.0

参照

カテゴリー: .NET Core, .NET Framework. タグ: . 1 Comment »
  • WordPress.com で次のようなサイトをデザイン
    始めてみよう