dhq_boiler
@dhq_boiler

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

[WPF]PrismでWindowStartupLocation=ManualにしてもCenterOwnerになる

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

解決したいこと

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

2021-09-26.png

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

ブラシツールと消しゴムツールを実装したのですが、これらをツールウィンドウから選択すると、ブラシの太さウィンドウが出てくるようになっています。ここで、ブラシの太さや消しゴムの太さを調節できるようにしていますが、このウィンドウはいつもアプリの中央に出てきます。これが鬱陶しくて、せめてウィンドウの位置を保存できるようにしたいです。

ソースコードは下記のようになっています。

Thickness.xaml
<UserControl x:Class="boilersGraphics.Views.Thickness"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:converter="clr-namespace:boilersGraphics.Converters"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:boilersGraphics.Views"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:prism="http://prismlibrary.com/"
             d:DesignHeight="420"
             d:DesignWidth="400"
             prism:ViewModelLocator.AutoWireViewModel="True"
             mc:Ignorable="d">
    <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="Left" Value="{Binding Left.Value}" />
            <Setter Property="Top" Value="{Binding Top.Value}" />
            <Setter Property="prism:Dialog.WindowStartupLocation" Value="Manual" />
            <Setter Property="SizeToContent" Value="WidthAndHeight" />
        </Style>
    </prism:Dialog.WindowStyle>
    <UserControl.Resources>
        <converter:ThicknessToDoubleConverter x:Key="ThicknessToDouble" />
        <converter:QuadConverter x:Key="Quad" />
    </UserControl.Resources>
    <DockPanel>
        <DockPanel DockPanel.Dock="Top">
            <Label Content="{Binding ElementName=slider, Path=Value}" DockPanel.Dock="Right" />
            <Slider x:Name="slider"
                    IsSnapToTickEnabled="True"
                    Maximum="100"
                    Minimum="1"
                    TickFrequency="1"
                    TickPlacement="BottomRight"
                    Value="{Binding ViewModel.Value.Thickness.Value, Mode=TwoWay, Converter={StaticResource ThicknessToDouble}, UpdateSourceTrigger=PropertyChanged}" />
        </DockPanel>
        <DockPanel Width="400" Height="400">
            <Ellipse Width="{Binding ElementName=slider, Path=Value, Converter={StaticResource Quad}}"
                     Height="{Binding ElementName=slider, Path=Value, Converter={StaticResource Quad}}"
                     Fill="Black"
                     StrokeThickness="0" />
        </DockPanel>
    </DockPanel>
</UserControl>
ThicknessViewModel.cs
using Prism.Mvvm;
using Prism.Services.Dialogs;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;

namespace boilersGraphics.ViewModels
{
    public class ThicknessViewModel : BindableBase, IDialogAware
    {
        private CompositeDisposable disposables = new CompositeDisposable();

        public static double StaticLeft { get; set; }

        public static double StaticTop { get; set; }

        public ReactivePropertySlim<BrushViewModel> ViewModel { get; } = new ReactivePropertySlim<BrushViewModel>();

        public ReactivePropertySlim<double> Left { get; } = new ReactivePropertySlim<double>();
        public ReactivePropertySlim<double> Top { get; } = new ReactivePropertySlim<double>();

        public ThicknessViewModel()
        {
            Left.Subscribe(x =>
            {
                StaticLeft = x;
            })
            .AddTo(disposables);
            Top.Subscribe(x =>
            {
                StaticTop = x;
            })
            .AddTo(disposables);
        }

        public string Title => "ブラシの太さ";

        public event Action<IDialogResult> RequestClose;

        public bool CanCloseDialog()
        {
            return true;
        }

        public void OnDialogClosed()
        {
            ViewModel.Value.ThicknessDialogClose -= Value_ThicknessDialogClose;
        }

        public void OnDialogOpened(IDialogParameters parameters)
        {
            ViewModel.Value = parameters.GetValue<BrushViewModel>("ViewModel");
            ViewModel.Value.ThicknessDialogClose += Value_ThicknessDialogClose;
            Left.Value = StaticLeft;
            Top.Value = StaticTop;
        }

        private void Value_ThicknessDialogClose(object sender, EventArgs e)
        {
            IDialogResult result = new DialogResult(ButtonResult.OK);
            RequestClose.Invoke(result);
        }
    }
}

コードではスタティックプロパティStaticLeft, StaticTopにLeft、Topの値を保存しています。それをダイアログオープン時に読み込ませるようにしています。

描画中の筆跡からフォーカスが外れると、ブラシの太さウィンドウを非表示にするようになっています。筆跡にフォーカスが当たると再びブラシの太さウィンドウを表示します。

アプリの動作は下記のGifアニメーションのようになっています。
ブラシの太さウィンドウを画面隅に追いやっても、再びアプリ中央にでてきてしまいます。prism:Dialog.WindowStartupLocationをCenterOwnerではなくManualにしているにもかかわらずです。
一体どうしてこうなるのでしょうか。

center.gif

prism:Dialog.WindowStartupLocationをManualにする例がWeb上をサーフィンしても全く見当たらないので困っています。

該当するソースコード

boiler's Graphics
https://github.com/dhq-boiler/boiler-s-Graphics
コミット:9b40dce

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

0

1Answer

prism:Dialog.WindowStyle内のLeftやTopはあくまでWindowStartupLocationのパラメータなので、表示開始時の初期位置の意味しかないと思います。
なのでたぶんウィンドウを動かしても更新されません。

ユーザーコントロールの座標はスクリーン座標しか取れないので、それを元に親コントロールからの相対座標を計算するしかなさそうです。
最後の位置を覚えておく目的なら常に監視する必要はないので、IsVisibleChangedイベントで座標を保存すればいいのではないでしょうか。

0Like

Your answer might help someone💌