如何在WPF中用矩形填充空白区域?

nxowjjhe  于 2022-11-18  发布在  其他
关注(0)|答案(2)|浏览(225)

我正在构建一个程序,以便在3D程序中查看和编辑橱柜/家具。
其中一个功能要求是让制作团队能够以2D方式查看橱柜的正面,并列出开口的大小以便于查看。我正在计算开口的大小,并添加一个该大小的矩形。目标是向显示开口大小的白色矩形添加文本,以便他们可以构建适合开口大小的项目。
我能够得到一个抽屉柜上的开口,见下图。

对于像下面这样的更复杂的问题,对我来说有点困难。

以下是棕色零件的属性:

public double X { get; set; }

public double Y { get; set; }

public double Width { get; set; }

public double Height { get; set; }

我的XAML很简单,只需将所有矩形添加到画布上,然后使用X,Y定位。
我的代码有点混乱,但它在下面。

//Filter list of parts to get the frame parts
int Counter = 0;
var frameParts = getFrameParts.Where(p => p.CatalogPartID == 1015 || p.CatalogPartID == 1016 || p.CatalogPartID == 3025).OrderBy(p => p.CatalogPartID).OrderBy(p => p.Y).ToList();
MoFacePart previousFrameRail = new MoFacePart();
MoFacePart previousMidFrameStile = new MoFacePart();

foreach (var part in frameParts)
{
        var totalParts = getFrameParts.Where(p => p.CatalogPartID == 1016).ToList().Count();
        // Adding Horizontal Spaces
        if (part.CatalogPartID == 1016)
        {
            var newOpening = new MoFacePart { Width = part.Width, Height = (previousFrameRail.Y - previousFrameRail.Height) - (130-(part.Y + part.Height)), X = ((80 - (double)SelectedViewerProduct.Width) / 2) + part.X, Y = (previousFrameRail.Y - previousFrameRail.Height), Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(255, 255, 255)) };
            if (Counter > 0 && Counter < (totalParts))
            {
                FaceParts.Add(newOpening);
            }
            Counter++;
        }

        var newPart = new MoFacePart { Width = part.Width, Height = part.Height, X = ((80 - (double)SelectedViewerProduct.Width) / 2) + part.X, Y = 130 - part.Y, Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(210, 180, 140)) };
        FaceParts.Add(newPart);
        if (part.CatalogPartID == 1016)
        {
            previousFrameRail = newPart;
        }
    
}

考虑到这一切,有没有更好的方法来找出所有的空白?
我相信我能找到一个解决办法做我正在做的,但我觉得有一个更好的方法,我错过了它。

euoag5mw

euoag5mw1#

这看起来很有趣,所以这里有一个答案。我只是在画布上放了一些矩形来存放我的源数据。有关详细信息,请参见代码中的注解。它可能需要一些调整,我只是用XAML中看到的矩形测试了它。
截图:

XAML语言

<Window x:Class="StackOverflow54985848.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Canvas x:Name="canvas" Margin="10">
        <Canvas.Resources>
            <Style TargetType="Rectangle">
                <Setter Property="Fill" Value="Tan" />
                <Setter Property="StrokeThickness" Value="1" />
                <Setter Property="Stroke" Value="Black" />
            </Style>
        </Canvas.Resources>
        <!-- Outside frame -->
        <Rectangle Canvas.Left="0" Canvas.Top="0" Width="10" Height="300" />
        <Rectangle Canvas.Left="300" Canvas.Top="0" Width="10" Height="300" />
        <Rectangle Canvas.Left="10" Canvas.Top="0" Width="290" Height="10" />
        <Rectangle Canvas.Left="10" Canvas.Top="290" Width="290" Height="10" />

        <!-- Insides -->
        <Rectangle Canvas.Left="10" Canvas.Top="75" Width="290" Height="10" />
        <Rectangle Canvas.Left="100" Canvas.Top="85" Width="10" Height="205" />
        <Rectangle Canvas.Left="10" Canvas.Top="175" Width="90" Height="10" />
    </Canvas>
</Window>

编码:

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace StackOverflow54985848
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // Get the rectangles from the canvas
            var rects = canvas.Children
                .Cast<Rectangle>()
                .Select(r => new Rect(
                    (double)r.GetValue(Canvas.LeftProperty),
                    (double)r.GetValue(Canvas.TopProperty),
                    r.Width, r.Height))
                    .ToArray();

            // Determine the bounds of the rects
            var minX = rects.Min(r => r.Left);
            var maxX = rects.Max(r => r.Right);
            var minY = rects.Min(r => r.Top);
            var maxY = rects.Max(r => r.Bottom);
            var bounds = new Rect(minX, minY, maxX - minX, maxY - minY);

            // openSpace initially is the entire area
            List<Rect> openSpace = new List<Rect>() { bounds };

            // Remove r from all rects in openSpace
            foreach (var r in rects)
            {
                List<Rect> openSpaceToRemove = new List<Rect>();
                List<Rect> openSpaceToAdd = new List<Rect>();

                foreach (var os in openSpace)
                {
                    if (!os.IntersectsWith(r))
                        continue;
                    var r2 = os;
                    r2.Intersect(r); // result stored in r2, it is the area that isn't open anymore

                    // We will be removing os since it intersects
                    openSpaceToRemove.Add(os);

                    // Remove r2 from os
                    //
                    // Probably a better way to do this...
                    // We have the area that ISNT open (r2) but we want the area that IS open still
                    // Create 4 rects that cover the area OUTSIDE of r2 (to the left, right, above, below)
                    // The intersection of those rects and os is our still open space (subset of os)

                    // Create a rect that is everything to the left of r2 and intersect it with os
                    var rr = new Rect(bounds.Left, bounds.Top, r2.Left, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);

                    // Repeat with everything to the right
                    rr = new Rect(r2.Right, bounds.Top, bounds.Right - r2.Right, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);

                    // Repeat with everything above the top
                    rr = new Rect(bounds.Left, r2.Top - bounds.Height, bounds.Width, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);

                    // Repeat with everything below the bottom
                    rr = new Rect(bounds.Left, r2.Bottom, bounds.Width, bounds.Height);
                    rr.Intersect(os); // intersection is stored in rr
                    if (rr.Width > 0 && rr.Height > 0)
                        openSpaceToAdd.Add(rr);
                }

                // Remove rects we don't want
                foreach (var os in openSpaceToRemove)
                    openSpace.Remove(os);
                // Add rects we do want
                openSpace.AddRange(openSpaceToAdd);
            }

            // Merge openSpace entries
            for (int i = 0; i < openSpace.Count; i++)
            {
                // Get an openSpace rect
                var r = openSpace[i];
                // Loop through the rects that come after it
                for (int j = i + 1; j < openSpace.Count; j++)
                {
                    // Get the next rect
                    var c = openSpace[j];
                    // If c or r contains each other then expand r to contain both and remove c
                    if (r.Contains(c) || c.Contains(r))
                    {
                        r.Union(c);
                        openSpace[i] = r;
                        openSpace.RemoveAt(j);
                        // start over since r changed and we removed openSpace at index j
                        // set j = i so when the loop counter increments, j will equal i + 1
                        j = i;
                    }
                }
            }
            // Remove duplicates?
            openSpace = openSpace.Distinct().ToList();

            // Now that our openspace has been determined, add it to the canvas
            foreach (var r in openSpace)
            {
                var rr = new Rectangle()
                {
                    Width = r.Width,
                    Height = r.Height,
                    Fill = Brushes.Beige,
                    Stroke = Brushes.Red,
                    StrokeThickness = 1.0
                };
                rr.SetValue(Canvas.LeftProperty, r.Left);
                rr.SetValue(Canvas.TopProperty, r.Top);
                canvas.Children.Add(rr);

                // Grid to hold the textblock (more control over width/height)
                var grid = new Grid()
                {
                    Width = r.Width,
                    Height = r.Height,
                };
                grid.SetValue(Canvas.LeftProperty, r.Left);
                grid.SetValue(Canvas.TopProperty, r.Top);
                TextBlock tb = new TextBlock()
                {
                    Text = $"Width: {rr.Width} Height: {rr.Height}",
                    Foreground = Brushes.Red,
                    VerticalAlignment = VerticalAlignment.Center,
                    HorizontalAlignment = HorizontalAlignment.Center,
                    TextWrapping = TextWrapping.Wrap
                };
                grid.Children.Add(tb);
                canvas.Children.Add(grid);
            }
        }
    }
}
js4nwp54

js4nwp542#

而不是试图找出你的空白空间在哪里,只是假设你的结构广场内的每一个地方都是空白空间,并将Parant容器的Background设置为白色。假设你有一个包含所有块的父容器。如果没有,你应该考虑它。
在wpf中,你也可以合并gemoetrys,看看这篇文章,它可能会有很大的帮助:http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx

相关问题