[WPF] Xaml で親要素のプロパティをバインディングする

 Xaml ではコントロールのプロパティなどに別のコントロールのプロパティをバンディングできます。名前のあるコントロールのプロパティを参照する際はその名前を利用する事ができますが、単純に親のプロパティを引き継ぎたい場合もあります。親コントロールのプロパティを参照する方法です。
 

■ 概要


 RelativeSourceMode=FindAncestoAncestorType に参照したい親コントロールの型を設定します。また参照したい親コントロールまでに間に親コントロールの型のコントロールがある場合、AncestorLevel を指定します。
 次の一つ目の Label の文字色は Red、二つ目も Red、三つ目は LightGreen になります。
 

■ コード


<Grid Background="Red">
    <Label Content="sample" Background="Blue" Foreground="{Binding Background,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}}"/>
</Grid>

<Grid Background="LightGreen">
    <Grid Background="Red">
        <Label Content="sample" Background="Blue" Foreground="{Binding Background,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid,AncestorLevel=1}}"/>
    </Grid>
</Grid>

<Grid Background="LightGreen">
    <Grid Background="Red">
        <Label Content="sample" Background="Blue" Foreground="{Binding Background,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid,AncestorLevel=2}}"/>
    </Grid>
</Grid>

[WPF] 複数のコントロールのサイズを統一する

 Xaml ではコントロールのプロパティなどに別のコントロールのプロパティをバンディングできます。複数のコントロールのサイズなどを統一したい場合、この機能を利用すると一つのコントロールのサイズを変更するだけでミスなくすべてのコントロールのサイズを変える事ができ、メンテナンスのメリットがあることがあります。
 

■ 概要


 バインディングの設定で ElementName に参照したいコントロールの名前を設定します。
 次の例では、最初の背景色が RedGridHeight を変更することで他の二つの Grid も同様の高さに変更されます。
 

■ コード


<Grid Background="Red" x:Name="grid1" Height="20"/>
<Grid Background="Green" Height="{Binding Height, ElementName=grid1}"/>
<Grid Background="Blue" Height="{Binding Height, ElementName=grid1}"/>

[WPF] DatePicker のカレンダーを大きくする

 次のように DatePicker 自体を拡大する場合、カレンダーも同じように大きくなります。

<DatePicker>
    <DatePicker.LayoutTransform>
        <ScaleTransform ScaleX="2" ScaleY="2"/>
    </DatePicker.LayoutTransform>
</DatePicker>

 しかし逆に言うとこの場合は、本体自体も大きくしなければならず、カレンダーだけを大きくすることはできません。また、コントロールを大きく表示したい場合に、拡大をするのではなくフォントサイズを大きくする方法をとることのあるそうです。この場合、本体自体は大きくなりますがカレンダーは大きくなりません。例えば次のようなコードの場合がそれにあたります。

<DatePicker FontSize="20"/>

 

■ カレンダーだけを大きくする

 DatePickerCalendarStyle でカレンダーのスタイルと変更できます。ここで LayoutTransform を指定しカレンダーを大きくします。

<DatePicker>
    <DatePicker.CalendarStyle>
        <Style TargetType="Calendar">
            <Setter Property="LayoutTransform">
                <Setter.Value>
                    <ScaleTransform ScaleX="2" ScaleY="2"/>
                </Setter.Value>
            </Setter>
        </Style>
    </DatePicker.CalendarStyle>
</DatePicker>

[WPF] DataGrid の行の背景色を交互に変える

 かつて在りし日の Windows フォーム では、DataGridView が非常に人気があり、その中でも、行の背景を交互に変える事が特に人気でした。
 具体的には、奇数行の背景色だけを設定するプロパティがあり、それが活用されていたものと思います。
 Windows フォーム では AlternatingRowsDefaultCellStyleBackColor を指定することで実装できました。
 
 WPF ではもっと簡単に、DataGridAlternatingRowBackground というプロパティを持っています。
 ここにお好みの色を設定すれば OK です。
 

■ コード

コードは次のようになります。

<Window x:Class="WpfApp1.Window1"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="300" Width="300">
    <Grid>
        <DataGrid ItemsSource="{Binding}" AlternatingRowBackground="Azure"/>
    </Grid>
</Window>

[WPF] xaml でコントロールのアクセス修飾子を指定する

WPF で xaml により UI を定義した場合、各コントロールのアクセス修飾子は規定で internal になります。
これを変更する場合、 x:FieldModifier を使用します。

■ 例

次の例は、 label をいう名前の Label のアクセス修飾子を protected にしています。

<Label x:Name="label" x:FieldModifier="protected"/>

[WPF] ファイルダイアログを使う

WPF ではファイルダイアログは
「開く」ダイアログは Microsoft.Win32 名前空間の OpenFileDialog クラスを使用します。
「名前を付けて保存」ダイアログは Microsoft.Win32 名前空間の SaveFileDialog クラスを使用します。

使い方は次のようになります。 (サンプルは C# コードです)
いずれの場合でも、using に using Microsoft.Win32; を追加してください。

■ OpenFileDialog / SaveFileDialog

注意

WPF ではファイルダイアログは Microsoft.Win32 名前空間のクラスを使用します。
しかし OpenFileDialogSaveFileDialog は同名のクラスが System.Windows.Forms アセンブリ内の System.Windows.Forms 名前空間にも存在します。
誤って System.Windows.Forms 名前空間のクラスを使ってしまうことがあるようです。
ご注意ください。

[WPF] ボタン押下時の処理中に画面操作を受け付けなくする

C# には async/await という素晴らしい仕組みがあり、時間のかかる処理を行う際にもアプリそのものは固まらないようにすることが簡単にできます。
ここでアプリが固まるというのは、いわゆるタイトルバーに「応答なし」と出るヤツです。

■ async/await を使用して「応答なし」にならないボタン

例えば次のように書けば 5 秒かかる処理をボタン押下で行っても「応答なし」にはなりません。
ここでは xaml に、x:Name=”button” というボタンがあると思ってください。

private async void button_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Start!");
    await Task.Delay(5000);
    MessageBox.Show("End!!");
}

これで、「応答なし」にならずに、5 秒間の処理ができます。
しかし、「応答なし」にならない代わりに、処理中でもボタンを何度も押せてしまいます。

人力でクリックできる速度より早く完了する処理であれば問題はないでしょう。
しかし、時間がかかる処理を行う場合は、連打されると都合が悪い場合もあるでしょう。
ボタン連打ばかりでなく、他のボタンを押されるなどでいろいろ整合性が取れなくなってしまう場合などもあるでしょう。

今回は、豪快に処理中は Window 内の UI 全体を使用不可にしてみましょう。

■ 基本方針

既に、ボタン押下時の処理は(コードビハインドに)実装されているものとします。
それも何か所も存在するし、今後も増えて行くとします。
これをなるべく省力で変更する方法として、Func を受け取り、Func の実行前に UI をロックし、Func の実行後に UI のロックを解除するメソッドを作りこれを使用するようにします。

■ 処理中に UI をロックするコード

まず、LockUI メソッドを次のように作ります。今回は、マウスカーソルもグルグルするようにしてみました。
次に button_Click2 メソッドを作り、button_Click メソッドの内容を移動します。
最後に、button_Click の中で、LockUI を button_Click2 を引数に渡し実行します。

private async Task LockUI(Func<Task> act)
{
    var topElm = ((UIElement)VisualTreeHelper.GetChild(this, 0));
    var oldEnabled = topElm.IsEnabled;
    var oldCursor = this.Cursor;
    try
    {
        this.Cursor = Cursors.Wait;
        topElm.IsEnabled = false;
        await act();
    }
    finally
    {
        topElm.IsEnabled = oldEnabled;
        this.Cursor = oldCursor;
    }
}

private async void button_Click(object sender, RoutedEventArgs e)
{
    await LockUI(async ()=> { await button_Click2(); });
}

private async Task button_Click2()
{
    MessageBox.Show("Start!");
    await Task.Delay(5000);
    MessageBox.Show("End!!");
}

これで、処理中に UI がロックされるボタンができました。

[WPF] リソースの画像から Bitmap オブジェクトを生成する

WPF ではプロジェクトに追加したリソースへの参照は Uri により行えます。
例えば、ビューの Image コントロールへリソースの画像を表示する場合、次のように Source プロパティに画像リソースの Uri を設定します。

<Image x:Name="image" Source="Resources/Sample.bmp"/>

コードから設定する場合は、Uri から BitmapImage オブジェクトを生成し Source プロパティに設定します。

image.Source = new BitmapImage(new Uri("Resources/Sample.bmp", UriKind.Relative));

通常ならばこのどちらかで事足ります。
しかし、一部サードパティ製コンポーネントなどで、System.Drawing.Image のオブジェクトが必要になる場合があるようです。

■ アセンブリ参照の追加

System.Drawing 名前空間は標準では使えませんので、アセンブリの参照を追加します。
追加するアセンブリ「System.Drawing」

■ ストリームの取得

Application.GetResourceStream メソッドを使用して、Uri からリソース画像データのストリームを取得します。

var stream = Application.GetResourceStream(new Uri("Resources/Sample.bmp", UriKind.Relative)).Stream;

■ Bitmap オブジェクトの生成

Stream から Bitmap オブジェクトを生成します。

using (var stream = Application.GetResourceStream(new Uri("Resources/Sample.bmp", UriKind.Relative)).Stream)
{
    var bmp = new System.Drawing.Bitmap(stream);
}

これで、リソースの画像から Bitmap オブジェクトを生成することができました。

[WPF] DataGrid 上にワンクリックでチェックの変更を反映する CheckBox を置く

通常の DataGridCheckBoxColumn では、チェックを ON にしたあと、行を移動するとデータが反映されます。
これを 1 回クリックした瞬間に他の項目に影響を与えてほしいという場合、 DataGridCheckBoxColumn ではなく通常の CheckBox を使用することで要求に応えられる場合があります。
DataGrid 上に通常のコントロールを配置する場合は、DataGridTemplateColumn を使用します。
その際、UpdateSourceTrigger には PropertyChanged をセットします。

例えば次のようなコードになります。

■ DataGridTemplateColumn を使用し通常の CheckBox を配置する

<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" >
    <DataGrid.Columns>

        <!-- 普通の CheckBoxColumn -->
        <DataGridCheckBoxColumn Binding="{Binding Checked1}"/>

        <!-- チェックの変更を即座に反映する CheckBox -->
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding Checked1,UpdateSourceTrigger=PropertyChanged}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>

通常の DataGridCheckBoxColumn と通常の CheckBox でどちらも見た目は変わりません。
しかし、 DataGridCheckBoxColumn の場合は、クリックでチェックを変更した後、行を移動しないと CheckBox のチェックが変化しません。
DataGridTemplateColumn を使用し CheckBox を使用した場合は、クリックした瞬間に DataGridCheckBoxColumn のチェックも変化します。

[WPF] DataGrid 上にワンクリックでチェックの ON/OFF ができる CheckBox を置く

DataGrid 上にチェックボックスを置きたい場合、通常は DataGridCheckBoxColumn を使用します。
しかし、日本はグリッドの操作性に厳しい国です。
その日本の高い要求に応えるには、通常を越える必要があります。

例えば、グリッド上の全行にチェックボックスを置いて、チェックボックスによってデータを複数選択する場合の操作性は代表的なものでしょう。

通常の DataGridCheckBoxColumn では、チェックを ON にするために 2 回のクリックが必要です。
これを 1 回のクリックでチェックが ON になるようにしてほしいという場合、 DataGridCheckBoxColumn ではなく通常の CheckBox を使用することで要求に応えられる場合があります。
DataGrid 上に通常のコントロールを配置する場合は、DataGridTemplateColumn を使用します。

例えば次のようなコードになります。

■ DataGridTemplateColumn を使用し通常の CheckBox を配置する

<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False">
    <DataGrid.Columns>

        <!-- 普通の CheckBoxColumn -->
        <DataGridCheckBoxColumn Binding="{Binding Checked1}"/>
        
        <!-- TemplateColumn の中に普通の CheckBox -->
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding Checked2}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>

</DataGrid>

通常の DataGridCheckBoxColumn と通常の CheckBox でどちらも見た目は変わりません。
しかし、 DataGridCheckBoxColumn の場合は、1クリック目で行が選択され、2クリック目でコントロールの値を変更(チェックを変更)にすることができます。
DataGridTemplateColumn を使用し CheckBox を使用した場合は、1クリック目で行の選択とチェックの変更をすることができます。

これで日本の高い要求に応えることができるかもしれません。