none
Rectangle.IntersectsWith RRS feed

  • 質問

  • 森と申します。

    Windowに表示したUIElementを矩形選択するためにRectangle.IntersectsWithを利用しているのですが、高DPI(150%)環境での動作が通常DPI(100%)の時と違い、Rect的には交差しているにも関わらず選択されない状況です。

    何か情報をお持ちでしたらお教え頂けないでしょうか。

    なお、現状の開発環境は以下の通りです。
     Visual Studio 2015 Pro
     .Net 4.6.2
     WPF
     Windows10 Pro 1703


    Shigehiro Mori

    2017年9月25日 6:27

回答

  • 解決しました。

    最初からソースを見ていて疑問に思っていたのですが、
     Point controlPoint = button.PointToScreen(new Point(0d, 0d));
    では
    PointToScreenによりDPIが考慮されているのですが、 Rect clientRect = new Rect(controlPoint.X, controlPoint.Y, button.ActualWidth, button.ActualHeight);
    における
    ActualWidthとActualHeightはDPIが考慮されていないように思えます。

    ここに別途取得したDPI値をかけてやれば問題なく動作するようです。

    以上、皆様ありがとうございました。


    Shigehiro Mori

    2017年9月26日 1:31

すべての返信

  • WPFでRectangleと言えばSystem.Windows.Shapes.Rectangleクラスを指すものと思いますが、このRectangleにはIntersectsWithがないようです。ひょっとして、System.Drawing.Rectangle構造体を利用されているのでしょうか。WPFは座標系をDouble型で扱うので、Int32型やせいぜいSingle型でしか扱えないSystem.Drawingは扱いがやや面倒だと思います。System.Windows.Rect構造体の使用をお勧めします。

    さて本題ですが、今提供されている情報の範囲ではなんともいえません。具体的なコードを提示いただけませんか。

    そもそもヒットテストであれば、System.Windows.Media.VisualTreeHelperのHitTestメソッドを利用できませんか?

    2017年9月25日 7:39
  • 早速のご返答ありがとうございます。

    Rectangle.IntersectsWithと書いてしまいましたがSystem.Windows.Rect.IntersectsWithが正解のようです。すみません。
    System.Windows.Media.VisualTreeHelperについて調べてみようと思います。

    現状テスト用で利用しているソースは以下の通りです。

    XAML:
    
    <Window x:Class="RectSelection.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:RectSelection"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525"
            PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
            PreviewMouseLeftButtonUp="Window_PreviewMouseLeftButtonUp"
            MouseMove="Window_MouseMove">
        <Grid>
            <Grid x:Name="MainGrid">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <ToggleButton Grid.Column="0" Grid.Row="0" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="1" Grid.Row="0" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="2" Grid.Row="0" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="3" Grid.Row="0" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="0" Grid.Row="1" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="1" Grid.Row="1" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="2" Grid.Row="1" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="3" Grid.Row="1" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="0" Grid.Row="2" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="1" Grid.Row="2" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="2" Grid.Row="2" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="3" Grid.Row="2" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="0" Grid.Row="3" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="1" Grid.Row="3" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="2" Grid.Row="3" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
                <ToggleButton Grid.Column="3" Grid.Row="3" Content="Button" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10"/>
            </Grid>
            <Rectangle Name="SelectionRect" HorizontalAlignment="Left" VerticalAlignment="Top"
                   StrokeDashArray="2,2" Stroke="LightGray" StrokeThickness="1" Visibility="Collapsed"/>
        </Grid>
    </Window>
    
    C#:
    
    using System;
    using System.Windows;
    using System.Windows.Controls.Primitives;
    using System.Windows.Input;
    
    namespace RectSelection
    {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private Point _DownedPoint = new Point();
    
            private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                _DownedPoint = Mouse.GetPosition(this);
                this.SelectionRect.Margin = new Thickness(_DownedPoint.X, _DownedPoint.Y, 0, 0);
                this.SelectionRect.Width = 0;
                this.SelectionRect.Height = 0;
                this.SelectionRect.Visibility = Visibility.Visible;
            }
    
            private void Window_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                Point selectionPoint = this.SelectionRect.PointToScreen(new Point(this.Left, this.Top));
                Rect selectionRect = new Rect(selectionPoint.X, selectionPoint.Y,
                    this.SelectionRect.ActualWidth, this.SelectionRect.ActualHeight);
                foreach (var child in LogicalTreeHelper.GetChildren(MainGrid))
                {
                    if (child is ToggleButton)
                    {
                        ToggleButton button = child as ToggleButton;
                        Point controlPoint = button.PointToScreen(new Point(this.Left, this.Top));
                        Rect clientRect = new Rect(controlPoint.X, controlPoint.Y, button.ActualWidth, button.ActualHeight);
                        if (selectionRect.IntersectsWith(clientRect))
                        {
                            button.IsChecked = true;
                        }
                        else
                        {
                            button.IsChecked = false;
                        }
                    }
                }
                this.SelectionRect.Width = 0;
                this.SelectionRect.Height = 0;
                this.SelectionRect.Visibility = Visibility.Hidden;
            }
    
            private void Window_MouseMove(object sender, MouseEventArgs e)
            {
                Point p = Mouse.GetPosition(this);
                if (!_DownedPoint.Equals(p))
                {
                    if (p.X - _DownedPoint.X >= 0 && p.Y - _DownedPoint.Y >= 0)
                    {
                        this.SelectionRect.Margin = new Thickness(_DownedPoint.X, _DownedPoint.Y, 0, 0);
                        this.SelectionRect.Width = p.X - _DownedPoint.X;
                        this.SelectionRect.Height = p.Y - _DownedPoint.Y;
                    }
                    else if (p.X - _DownedPoint.X < 0 && p.Y - _DownedPoint.Y >= 0)
                    {
                        this.SelectionRect.Margin = new Thickness(p.X, _DownedPoint.Y, 0, 0);
                        this.SelectionRect.Width = _DownedPoint.X - p.X;
                        this.SelectionRect.Height = p.Y - _DownedPoint.Y;
                    }
                    else if (p.X - _DownedPoint.X >= 0 && p.Y - _DownedPoint.Y < 0)
                    {
                        this.SelectionRect.Margin = new Thickness(_DownedPoint.X, p.Y, 0, 0);
                        this.SelectionRect.Width = p.X - _DownedPoint.X;
                        this.SelectionRect.Height = _DownedPoint.Y - p.Y;
                    }
                    else if (p.X - _DownedPoint.X < 0 && p.Y - _DownedPoint.Y < 0)
                    {
                        this.SelectionRect.Margin = new Thickness(p.X, p.Y, 0, 0);
                        this.SelectionRect.Width = Math.Abs(_DownedPoint.X - p.X);
                        this.SelectionRect.Height = Math.Abs(_DownedPoint.Y - p.Y);
                    }
                }
            }
        }
    }
    
    以上、よろしくお願い致します。



    Shigehiro Mori

    2017年9月25日 8:08
  • 気になる点があるとすれば、PointToScreen に渡している座標が this.Left, this.Top という点ですね。
    Window の Left/Top は Window の外枠の座標なので、それを PointToScreen で変換すると、Window.SelectionRect や button の左上から this.Left, this.Top 分ずらした位置をスクリーン座標に変換することになりますので、狙ったとおりのコードになっていないかと思われます。

    たとえば、Window のスクリーン座標が欲しいだけなら、(0, 0) を渡すべきなのでは?

    (100% でたまたまうまくいっているように見えるだけという予想)

    2017年9月25日 22:27
  • 返信ありがとうございます。

    ご指摘の点は、もともとnew Point(0d, 0d)としていたものを確認のためthis~に変更しておりました。
    なお、どちらの場合も同様の結果でした。

    ソースを確認せずに投稿してしまいました。すみません。

    以上、よろしくお願い致します。


    Shigehiro Mori

    2017年9月25日 23:40
  • 解決しました。

    最初からソースを見ていて疑問に思っていたのですが、
     Point controlPoint = button.PointToScreen(new Point(0d, 0d));
    では
    PointToScreenによりDPIが考慮されているのですが、 Rect clientRect = new Rect(controlPoint.X, controlPoint.Y, button.ActualWidth, button.ActualHeight);
    における
    ActualWidthとActualHeightはDPIが考慮されていないように思えます。

    ここに別途取得したDPI値をかけてやれば問題なく動作するようです。

    以上、皆様ありがとうございました。


    Shigehiro Mori

    2017年9月26日 1:31