質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C#

    9054questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    826questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

  • MVVM

    106questions

    MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPFでの画像ズーム機能

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,008

pon-suke

score 10

前提・実現したいこと

はじめまして.

最近,C#を独学で勉強しています.特に,MVVMモデルを使ったWPFアプリケーション開発に興味があります.

ScrollViewerとImageを使って,簡単な画像ビューワをMVVMモデルに沿うようにトライしています.Google Mapのようにホイールを回すと,マウスで示した位置を維持したまま拡大縮小する機能を追加したいです.できるだけ,コードビハインドからコードは排除しようと考えて,ImageのBehaviorとして追加しようと考えています.

発生している問題・エラーメッセージ・質問

下記のように,Imageに対して,依存関係プロパティで拡大縮小機能を追加しました.

拡大・縮小処理はうまく動くのですが,スケーリングを行うとスクロール位置がくるってしまいます(スケール中心が原点になるからだと思います).拡大縮小処理を行う際に,"LayoutTransform"ではなく,"RenderTransform"を使うと.拡大縮小の挙動は思った通りに動くのですが,全体の座標系まで変わってしまい,ScrollViewerが正しく動かなくなりました.

そこで,拡大縮小処理の後に,マウスポインタの位置を維持するように,ScrollViewerの位置を制御しようと考えました.しかし,Imageのビヘイビアから,ScrollViewerの制御ができない(ScrollViewerのobjectが取得できない)ため困っています.どのようにするのが良いでしょうか?できるだけ,コードビハインドには書きたくないため,Behaviorか,ViewModelで処理ができないかと考えています.

また,現在は拡大縮小処理はBehaviorで実装していますが,本来はViewModelで行うべきなのでしょうか?表示の処理なので,Viewに書くべきなのかなと思っているのですが...

不足している情報などありましたら,お知らせいただければ幸いです.
お手数をお掛けしますが,どうぞよろしくお願いします.

該当のソースコード

MainWindow.xaml

<Window x:Class="ImageViewer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Behaviors="clr-namespace:ImageViewer.Views.Behaviors"
        Title="MainWindow" Height="512" Width="512">
    <Grid>
        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
            <Image Behaviors:WheelScale.IsEnabled="true" Stretch="None" Margin="2">
                <Image.Source>
                    <BitmapImage UriSource="https://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png"/>
                </Image.Source>
            </Image>
        </ScrollViewer> 
    </Grid>
</Window>

WheelScale.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace ImageViewer.Views.Behaviors
{
    public class WheelScale : DependencyObject
    {
        public static bool GetIsEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsEnabledProperty);
        }
        public static void SetIsEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(IsEnabledProperty, value);
        }

        public bool IsEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }

        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(WheelScale), new UIPropertyMetadata(false, IsEnabledChanged));

        static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var img = d as Image;
            if (img == null) return;

            if ((bool)e.NewValue)
            {
                img.Loaded += img_Loaded;
            }
            else img_Unloaded(img, new RoutedEventArgs());
        }

        // アタッチ
        static void img_Loaded(object sender, RoutedEventArgs e)
        {
            var img = sender as Image;
            if (img == null) return;
            img.Unloaded += img_Unloaded;
            // ホイールのイベントハンドラと関連付け
            img.PreviewMouseWheel += imgScaleChange;
        }

        // デタッチ
        static void img_Unloaded(object sender, RoutedEventArgs e)
        {
            var img = sender as Image;
            if (img == null) return;
            img.Loaded -= img_Loaded;
            img.Unloaded -= img_Unloaded;
        }

        // 実際のスケーリング処理
        static double total_scale = 1.0;
        static void imgScaleChange(object sender, MouseWheelEventArgs e)
        {
            var img = sender as Image;
            if (img != null)
            {
                double scale = (0 < e.Delta) ? 1.1 : (1.0 / 1.1);
                total_scale = total_scale * scale;
                // ScaleTransformでは,Centerの座標は反映されない.水平方向の移動が無視される?
                // 実際のスケール処理
                img.LayoutTransform = new ScaleTransform(total_scale, total_scale, e.GetPosition(img).X, e.GetPosition(img).Y);
                // スクロールが無効になるように
                e.Handled = true;
            }
        }
    }
}

補足情報(言語/FW/ツール等のバージョンなど)

VisualStudio2013

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

ユーザーコントロールを作るでいいんじゃないですかね。
そっちで、コードビハインドでバリバリ書けば。

標準のコントロールはピュアなものばかりですし。

Viewの仕事なので、Viewのコンポーネントでやるのが正しいんじゃないかな。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/04 11:46

    アドバイスありがとうございました.
    ユーザーコントロールを使って,試してみたいと思います!

    キャンセル

同じタグがついた質問を見る

  • C#

    9054questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    826questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

  • MVVM

    106questions

    MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。