XAMLで始めるWPF入門:カスタムコントロールで作るスクロールバー付きImageコントロール

コンピュータ

Imageコントロールで画像サイズが大きい場合、スクロールバー自動有効化されるカスタムコントロールです。
ScrollViewerとImageを組み合わせれば比較的簡単に実装出来るのでカスタムコントロールにしてみました。

ソースコード

ファイル名:Themes\Generic.xaml
・カスタムコントロールのコントロールテンプレート(構成するコントロール定義)

<ResourceDictionary    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ScrollableImageSample">

    <Style TargetType="{x:Type local:ScrollableImage}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ScrollableImage}">
                    <ScrollViewer HorizontalScrollBarVisibility="Auto"
                                  VerticalScrollBarVisibility="Auto"
                                  CanContentScroll="False"
                                  Focusable="False"
                                  SnapsToDevicePixels="True">
                        <Image Source="{TemplateBinding Source}"
                               Stretch="None"
                               HorizontalAlignment="Left"
                               VerticalAlignment="Top"
                               SnapsToDevicePixels="True"/>
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

ファイル名:Controls\ScrollableImage.cs
・カスタムコントロールの振る舞いの定義

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ScrollableImageSample;

public class ScrollableImage : Control
{
    // コンストラクタ
    static ScrollableImage()
    {
        // Generic.xaml の既定テンプレートをこの型に自動適用
        DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollableImage),
            new FrameworkPropertyMetadata(typeof(ScrollableImage)));
    }

    /*
        依存関係プロパティでSource(ImageSource)を生やす。
    */
    public static readonly DependencyProperty SourceProperty =
        DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(ScrollableImage),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsMeasure));
            // ↑AffectsMeasure...Source 変更で必要サイズが変わるため、レイアウト再計測を要求(スクロール有無の再判定にも必要)。

    public ImageSource? Source
    {
        get => (ImageSource?)GetValue(SourceProperty);
        set => SetValue(SourceProperty, value);
    }
}

ファイル名:MainWindow.xaml
・カスタムコントロールを使う側のコード

<Window x:Class="ScrollableImageSample.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:ScrollableImageSample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:ScrollableImage Source="H:\csharp\wpf\ScrollableImageSample\memo.jpg" />
    </Grid>
</Window>

実行イメージ


自動的にスクロールバーが表示される。

感想

カスタムコントロールのコントロールテンプレートは Generic.xaml に定義(内容は ScrollViewer+Image)。
csコード側でテンプレート部品を触らない場合は、必要な依存プロパティだけ TemplateBinding で接続すればOK。

コメント