【WPF学習中】XAMLでCanvasのサイズを親要素のサイズに合わせる

C# コンピュータ
C#
WPFで扱うコントロールの多くは幅や高さを指定しない場合(デフォルト)親要素のクライアント領域一杯に広がりますが、Canvasの場合、幅や高さのデフォルトは0ですので基本的に数値を指定してあげる必要があるようです。
以前作成したプログラムでは、親要素のサイズ変更に連動して自動的にリサイズしてくれるよう、親要素のリサイズイベントを拾ってCanvasの幅と高さを変更するようにコーディングしました。
【WPF学習中】ドラッグアンドドロップで画像を表示する。20220304
フォームにエクスプローラーなどから画像ファイルをドラッグアンドドロップすると画像が表示されるサンプルプログラムです。 プロジェクトの作成 PowerShellで実行。要dotnet.exe mkdir プロジェクト名 cd プロジェクト名 ...

よくよく考えると、XAML内で親要素のWidthとHeightプロパティをバインディングすることが出来ればC#に頼ることなく機能を実現することが出来ます。
【WPF学習中】XAML内のプロパティの値をバインディングする
XAMLだけでスライダーの動きに合わせて円が拡大縮小するサンプルプログラム作成してみました。 プロジェクトの作成 PowerShellで実行。要dotnet.exe mkdir En cd En dotnet new wpf code . ...

プロジェクトの作成

PowerShellで実行。要dotnet.exe

mkdir プロジェクト名
cd プロジェクト名
dotnet new wpf
dotnet add package Microsoft.Xaml.Behaviors.Wpf
dotnet add package ReactiveProperty.WPF
code .

ソースコード

ファイル名:MainWindow.xaml

<Window x:Class="DDImageView2.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:DDImageView2"
        xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
        xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closed">
            <interactivity:EventToReactiveCommand Command="{Binding WindowClosedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <StackPanel>
            <Canvas
                Width="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel}}"
                Height="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel}}"
                Background="Gray"
                AllowDrop="True">          
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="DragOver">
                        <interactivity:EventToReactiveCommand Command="{Binding DragOverCommand}" />
                    </i:EventTrigger>
                    <i:EventTrigger EventName="Drop">
                        <interactivity:EventToReactiveCommand Command="{Binding DropCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <Image
                    Stretch="None"
                    Source="{Binding PictureView.Value}">
                </Image>
            </Canvas>
        </StackPanel>
    </Grid>
</Window>

ファイル名:MainWindowViewModel.cs

using System.Diagnostics;
using System;
using System.ComponentModel;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;
using System.Threading.Tasks;

namespace DDImageView2
{
    public class MainWindowViewModel : INotifyPropertyChanged, IDisposable
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string name) =>
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        private CompositeDisposable Disposable { get; } = new ();

        public ReactiveCommand<EventArgs> WindowClosedCommand { get; }
        public ReactiveProperty<BitmapImage> PictureView { get; private set; }
        public ReactiveCommand<DragEventArgs> DragOverCommand { get; }
        public ReactiveCommand<DragEventArgs> DropCommand { get; }
        public MainWindowViewModel()
        {
            PropertyChanged += (o, e) => {};
            
            WindowClosedCommand = new ReactiveCommand<EventArgs>()
                .WithSubscribe(e =>this.Dispose()).AddTo(Disposable);
            
            PictureView = new ReactiveProperty<BitmapImage>().AddTo(Disposable);

            DragOverCommand = new ReactiveCommand<DragEventArgs>()
                .WithSubscribe(e=>
                {
                    if (e.Data.GetDataPresent(DataFormats.FileDrop))
                    {
                        e.Effects = DragDropEffects.Copy;
                        e.Handled = true;
                    }
                    else
                    {
                        e.Effects = DragDropEffects.None;
                        e.Handled = false;
                    }
                }).AddTo(Disposable);
            
            var LoadPicture = new Func<string, BitmapImage> ((path)=>
            {
                BitmapImage bi = new();

                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                using(var ms = new MemoryStream())
                {
                    fs.CopyTo(ms);
                    ms.Seek(0, SeekOrigin.Begin);
                    bi.BeginInit();
                    bi.CacheOption = BitmapCacheOption.OnLoad;
                    bi.StreamSource = ms;
                    bi.EndInit();
                    bi.Freeze();
                }

                return bi;                
            });

            DropCommand = new ReactiveCommand<DragEventArgs>()
                .WithSubscribe(async e=>
                {
                    var files = (string[])e.Data.GetData(DataFormats.FileDrop);

                    var bi = await Task.Run(() => LoadPicture(files[0]));

                    PictureView.Value = bi;
                }).AddTo(Disposable);
        }
        public void Dispose()
        {
            Debug.WriteLine("Dispose()");
            Disposable.Dispose();
        }
    }
}

実行

dotnet run

説明

XAMLの主な変更点は、CanvasのWidthプロパティを”{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType=StackPanel}}”に変更しています。
FindAncestorを指定すると親要素を検索します。AncestorTypeで検索する要素のタイプ、今回はStackPanelを指定しています。
また、実行時の幅を取得するためにバインドするプロパティにActualWidthを指定してみました。
Heightも同様ににしています。
これで、親要素であるStackPanelのサイズ変更と連動してCanvasのサイズも変更されます。このサンプルの場合StackPanelの幅と高さを指定していないので、Windowサイズの変更に伴いStackPanelのサイズも変更されますので、おのずとWindowのサイズ変更でCanvasサイズも変更となる仕掛けとなっています。
動作は以前の記事と同様ですが、C#のコードが少しコンパクトになりました。
【WPF学習中】ドラッグアンドドロップで画像を表示する。20220304
フォームにエクスプローラーなどから画像ファイルをドラッグアンドドロップすると画像が表示されるサンプルプログラムです。 プロジェクトの作成 PowerShellで実行。要dotnet.exe mkdir プロジェクト名 cd プロジェクト名 ...

スポンサーリンク

コメント