@dhq_boiler

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

データバインディングが機能しない

Q&A

Closed

お世話になっております。

解決したいこと

C# + WPFでベクターグラフィックスドローイングツールを開発しています。

2021-06-20 (1).png

※下にソースコードへの案内を記載しております。よろしければそちらを参照ください。

このツールにミニマップというScrollViewerの内容を鳥瞰図のように俯瞰して全体図を把握しようという機能を実装しようとしているところです。

2021-07-02 (3).png

発生している問題・エラー

ZoomSliderのThumbを動かした時、拡大・縮小が適用され、それに伴ってミニマップ上のViewportも縮小・拡大したいのですが、下記のようにViewportWidth、ViewportHeightを代入してもViewportに変化が見られないのです。
何かバインディングの仕方が間違っているのでしょうか。
Visual Studio 2019のデバッグ時に出てくるXAMLバインドエラーのウィンドウには特にそれらしいエラーメッセージは出てませんでした。

MiniMap.cs
using boilersGraphics.Extensions;
using boilersGraphics.UserControls;
using boilersGraphics.ViewModels;
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.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

namespace boilersGraphics.Controls
{
    public class MiniMap : Control
    {
        private Thumb _zoomThumb;
        private Canvas _zoomCanvas;
        private Slider _zoomSlider;
        private ScaleTransform _scaleTransform;
        :
        #region ViewportWidth

        public static readonly DependencyProperty ViewportWidthProperty =
            DependencyProperty.Register("ViewportWidth", typeof(double), typeof(MiniMap));

        public double ViewportWidth
        {
            get { return (double)GetValue(ViewportWidthProperty); }
            set { SetValue(ViewportWidthProperty, value); }
        }

        #endregion

        #region ViewportHeight

        public static readonly DependencyProperty ViewportHeightProperty =
            DependencyProperty.Register("ViewportHeight", typeof(double), typeof(MiniMap));

        public double ViewportHeight
        {
            get { return (double)GetValue(ViewportHeightProperty); }
            set { SetValue(ViewportHeightProperty, value); }
        }

        #endregion
        :
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (this.ScrollViewer == null)
                return;

            _zoomThumb = Template.FindName("PART_ZoomThumb", this) as Thumb;
            if (_zoomThumb == null)
                throw new Exception("PART_ZoomThumb template is missing!");

            _zoomCanvas = Template.FindName("PART_ZoomCanvas", this) as Canvas;
            if (_zoomCanvas == null)
                throw new Exception("PART_ZoomCanvas template is missing!");

            _zoomSlider = Template.FindName("PART_ZoomSlider", this) as Slider;
            if (_zoomSlider == null)
                throw new Exception("PART_ZoomSlider template is missing!");

            _zoomThumb.DragDelta += new DragDeltaEventHandler(this.Thumb_DragDelta);
            _zoomSlider.ValueChanged += new RoutedPropertyChangedEventHandler<double>(this.ZoomSlider_ValueChanged);
            _scaleTransform = new ScaleTransform();
        }

        private void ZoomSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double scale = e.NewValue / e.OldValue;
            ViewportWidth = ViewportWidth * scale; //機能しない
            ViewportHeight = ViewportHeight * scale; //機能しない
        }
        :
    }
}
MiniMap.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:control="clr-namespace:boilersGraphics.Controls">
    <Style TargetType="{x:Type control:MiniMap}">

        :

        <Setter Property="SnapsToDevicePixels" Value="true" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type control:MiniMap}">
                    <Border
                        Background="#EEE"
                        BorderBrush="DimGray"
                        BorderThickness="1"
                        CornerRadius="1">
                        <Expander Background="Transparent" IsExpanded="True">
                            <Border
                                Height="180"
                                Padding="0"
                                BorderBrush="DimGray"
                                BorderThickness="0,1,0,0">
                                <Grid>
                                    <Canvas Name="PART_ZoomCanvas"
                                            Width="{Binding MiniMapWidth, RelativeSource={RelativeSource TemplatedParent}}"
                                            Height="{Binding MiniMapHeight, RelativeSource={RelativeSource TemplatedParent}}"
                                            Background="{StaticResource AlphaBrush}">
                                        <Rectangle Canvas.Left="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CanvasLeft}"
                                                   Canvas.Top="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CanvasTop}"
                                                   Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CanvasWidth}"
                                                   Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CanvasHeight}">
                                            <Rectangle.Fill>
                                                <VisualBrush Visual="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ScrollViewer.Content}" />
                                            </Rectangle.Fill>
                                        </Rectangle>
                                        <Thumb Name="PART_ZoomThumb"
                                               Cursor="SizeAll"
                                               Canvas.Left="{Binding ViewportLeft, RelativeSource={RelativeSource TemplatedParent}}"
                                               Canvas.Top="{Binding ViewportTop, RelativeSource={RelativeSource TemplatedParent}}"
                                               Width="{Binding ViewportWidth, RelativeSource={RelativeSource TemplatedParent}}"
                                               Height="{Binding ViewportHeight, RelativeSource={RelativeSource TemplatedParent}}">
                                            <Thumb.Style>
                                                <Style TargetType="Thumb">
                                                    <Setter Property="Template">
                                                        <Setter.Value>
                                                            <ControlTemplate TargetType="Thumb">
                                                                <Rectangle
                                                                    Fill="Transparent"
                                                                    Stroke="Red"
                                                                    StrokeThickness="1" />
                                                            </ControlTemplate>
                                                        </Setter.Value>
                                                    </Setter>
                                                </Style>
                                            </Thumb.Style>
                                        </Thumb>
                                    </Canvas>
                                </Grid>
                            </Border>
                            <Expander.Header>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <Slider Name="PART_ZoomSlider"
                                            MinWidth="104"
                                            MinHeight="21"
                                            Margin="0"
                                            HorizontalAlignment="Center"
                                            VerticalAlignment="Center"
                                            IsMoveToPointEnabled="False"
                                            IsSnapToTickEnabled="True"
                                            LargeChange="25"
                                            Maximum="500"
                                            Minimum="25"
                                            SmallChange="25"
                                            Ticks="25,50,75,100,125,150,200,300,400,500"
                                            Value="100" />

                                    <TextBlock Grid.Column="1"
                                               Margin="0,0,14,0"
                                               HorizontalAlignment="Right"
                                               VerticalAlignment="Center"
                                               Text="{Binding ElementName=PART_ZoomSlider, Path=Value}" />
                                    <TextBlock Grid.Column="1"
                                               Margin="1,0,2,0"
                                               HorizontalAlignment="Right"
                                               VerticalAlignment="Center"
                                               Text="%" />
                                </Grid>
                            </Expander.Header>
                        </Expander>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

ソースコード

boiler's Graphics
https://github.com/dhq-boiler/boiler-s-Graphics

gitリポジトリ
https://github.com/dhq-boiler/boiler-s-Graphics.git

ブランチ:develop

コミット:b00e6f0

何か私の見落とし、致命的な勘違いなど気づいたところがあれば、回答していただけると助かります。よろしくお願いいたします。

1 likes

1Answer

自己解決しました。

MiniMapViewModelクラスを新たに作成し、MiniMapクラスに関連付けました。
MiniMapViewModelクラスでは、ReactivePropertyでプロパティを作成したところ
データバインディングが動作するようになりました。

MiniMapViewModel.cs
using boilersGraphics.Controls;
using Prism.Mvvm;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;

namespace boilersGraphics.ViewModels
{
    public class MiniMapViewModel : BindableBase
    {
        private CompositeDisposable disposables = new CompositeDisposable();
        public ReactiveProperty<double> CanvasLeft { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> CanvasTop { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> CanvasWidth { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> CanvasHeight { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> MiniMapWidth { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> MiniMapHeight { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> ViewportLeft { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> ViewportTop { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> ViewportWidth { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> ViewportHeight { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> Scale { get; set; } = new ReactiveProperty<double>();
        public ReactiveProperty<double> Ratio { get; set; } = new ReactiveProperty<double>();

        private MiniMap parent;

        public MiniMapViewModel(MiniMap parent)
        {
            this.parent = parent;
            Scale.Value = 1.0;

            this.ViewportLeft
                .Subscribe(x =>
                {
                    if (x < 0)
                        ViewportLeft.Value = 0;
                    else if (x + ViewportWidth.Value > MiniMapWidth.Value)
                        ViewportLeft.Value = MiniMapWidth.Value - ViewportWidth.Value;
                    parent.ScrollViewer?.ScrollToHorizontalOffset(ViewportLeft.Value / Ratio.Value);
                })
                .AddTo(disposables);
            this.ViewportTop
                .Subscribe(x =>
                {
                    if (x < 0)
                        ViewportTop.Value = 0;
                    else if (x + ViewportHeight.Value > MiniMapHeight.Value)
                        ViewportTop.Value = MiniMapHeight.Value - ViewportHeight.Value;
                    parent.ScrollViewer?.ScrollToVerticalOffset(ViewportTop.Value / Ratio.Value);
                })
                .AddTo(disposables);
        }
    }
}

0Like

Your answer might help someone💌