2012/02/06

Bandicam + Aviutl + x264 = …

音が途中で途切れること多し。

音声が壊れたら mp4box で h264 を抽出して、それに別途 WAV 出力した音声を neroAacEnc でエンコードした音声ファイルをくっつけると直るけど、めんどくさい。

具体的には以下のような感じに……。

neroAacEnc –br 192000 –lc –if audio.wav –of audio.aac
mp4box –raw 1 broken.mp4
mp4box –fps 30 –add broken_track1.h264 –add audio.aac –new fixed.mp4

ちなみに、mp4box –raw 2 broken.mp4 と入力すれば AAC ファイルが抽出できるのだけど、15 分の動画に対して 2 分くらいのファイルになってた。

AviUtl からのソース出力のところで何か問題が起こってそうだ。

2012/02/05

Logicool G300

ロジクールの有線マウス。 2000 円なり。

マイクロソフトの安物ワイヤレスマウスとはポインタの動きの滑らかさが全然違った。

断線してお亡くなりになったマイクロソフトの SideWinder X8 さんよりも好みかもしれない。

ボタン多いし。

2012/02/03

ListView のソート

DisplayMemberBinding を使って表示させる方法なら前回の方法で大丈夫だったけど、CellTemplate の中でバインディングを指定する場合は何を参照して良いやらわからないことになるということが発覚。

まず思い浮かぶのが、俺版の GridViewColumnHeader を作るという方法。

public class SortableGridViewColumnHeader : GridViewColumnHeader
{
    static SortableGridViewColumnHeader()
    {
        DefaultStyleKeyProperty.OverrideMetadata(
            typeof(SortableGridViewColumnHeader),
            new FrameworkPropertyMetadata(typeof(GridViewColumnHeader)));
    }

    public SortableGridViewColumnHeader()
    {
        DefaultStyleKey = typeof(GridViewColumnHeader);
    }

    public static readonly DependencyProperty SortPathProperty =
        DependencyProperty.Register("SortPath", typeof(string), typeof(SortableGridViewColumnHeader));
    public string SortPath
    {
        get
        {
            return GetValue(SortPathProperty) as string;
        }
        set
        {
            SetValue(SortPathProperty, value);
        }
    }
}

これを使えば問題解決!と思ったのだけど、カラムのリサイズ時の(境界付近に持って行ったときの)カーソルが変わらなくなるという、よく分からない現象に遭遇したので没に。スタイル関連の問題なのだろうと思って、上のコードでもちょっといじってあるけど変化なかった。

で、俺版の x:Name みたいなのは作れないのかーと調べてたらそれっぽい機能があったので使ってみた。添付プロパティとかいう機能らしい。正確には DockPanel.Dock みたいなプロパティを実装するための仕組みらしい。

public class Sortable
{
    public static readonly DependencyProperty SortPathProperty =
        DependencyProperty.RegisterAttached("SortPath", typeof(string), typeof(Sortable));
    public static void SetSortPath(GridViewColumnHeader element, string value)
    {
        element.SetValue(SortPathProperty, value);
    }
    public static string GetSortPath(GridViewColumnHeader element)
    {
        return (string)element.GetValue(SortPathProperty);
    }
}

こうやっておいて、XAML には、

<GridViewColumn Width="120" CellTemplate="{StaticResource NameColumnTemplate}">
    <GridViewColumnHeader Content="なまえ" ore:Sortable.SortPath="Name" />
</GridViewColumn>
<GridViewColumn Width="80" CellTemplate="{StaticResource GenderColumnTemplate}">
    <GridViewColumnHeader Content="せいべつ" ore:Sortable.SortPath="Gender" />
</GridViewColumn>
<GridViewColumn Width="80" CellTemplate="{StaticResource RaceColumnTemplate}">
    <GridViewColumnHeader Content="しゅぞく" ore:Sortable.SortPath="Race" />
</GridViewColumn>

こんな感じに記述すれば、あとはクリック時のコードビハインドで、

var path = header.GetValue(Sortable.SortPathProperty) as string;

これで取得完了。動作も正常。

やったね!!!

プロパティ名でソート

バインディングで指定したパスを使ってソートキーを指定したかった。

ListView のカラムをクリックしたときのイベントは ListView.GridViewColumnHeader.Click らしい。

private void listView_Click(object sender, RoutedEventArgs e)
{
    var header = e.OriginalSource as GridViewColumnHeader;
    var binding = header.Column.DisplayMemberBinding as Binding;

    if (currentOrder == SortingOrder.Ascending)
    {
        currentOrder = SortingOrder.Descending;
    }
    else
    {
        currentOrder = SortingOrder.Ascending;
    }

    SortByProperty(binding.Path.Path, currentOrder);
}

コードビハインドに直書きしてるけど、MVVM スタイルでやりたいならビューモデルに binding.Path.Path を送信すればよさそう。

ソート部分はリフレクションを使って、

private void SortByProperty(string name, SortingOrder order)
{
    var sorted = Players.OrderBy(x => x.GetType().GetProperty(name).GetValue(x, null)).ToList();

    if (order == SortingOrder.Descending)
    {
        sorted.Reverse();
    }

    listView.ItemsSource = sorted;
}

みたいな感じでうまくいった。

ListView にある本来のソート機能を使うべきなんだろうけど、魔改造(TreeListView とか)してあると中途半端にソートされて上手くいかないので、データソースを直接ソートするしかないという。

2012/02/01

Dependency Property

はまった。

public class Tesuto : TextBox
{
    static Tesuto()
    {
        TextProperty = DependencyProperty.Register("Text", typeof(object), typeof(Tesuto));
    }

    public new static readonly DependencyProperty TextProperty;
    public new object Text
    {
        get { return GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
}

とやって、デザイナに貼り付けてやると VS2010 がクラッシュして強制終了する。

どうやら Dependency Property は、基底クラスで登録されたものは引き継がれるらしく、デフォルト値は変更できても登録解除とか型の変更とかはできないらしい。

Visual Studio さんも、こういうコードを書く輩がいるとは露とも思わず、Text プロパティが string 型だと思い込みながら新しく定義した object 型の Text プロパティを読み込みに行って失敗してるらしい。

Dependency Property の登録解除に関しては無理矢理やる方法が StackOverflow に載ってたけど、無理矢理過ぎるので流石に使えなさそう。MS さんが何とかしてくれないかなあ。

ちなみに上のコード、デザイナでクラッシュさえ起こさなければ普通にコンパイルが通り、意図通りに動作したりする。余計にもどかしいのであった。