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

时间:2019-03-04 14:59:26

标签: c# wpf xaml 2d

我正在构建一个程序,以在3D程序中查看和编辑橱柜/家具。

一个功能要求是生产团队能够以2D方式查看橱柜的正面,并列出列出的开口尺寸以便于查看。 我正在弄清楚开口的大小,并添加一个该大小的矩形。目标是将文本添加到显示开口大小的白色矩形中,以便他们可以构建适合其内部的项目。

我能够在抽屉柜上找到开口,见下文。

enter image description here

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

enter image description here

以下是棕色部分的属性:

公共双X {组; }

公共双Y {组; }

public double Width {get;组; }

public double Height {get;组; }

我的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;
        }

}

鉴于所有这些,是否有更好的方法找出所有空白?

我很抱歉插入作为图像插入的代码。我无法正确设置格式以允许发布。

我有信心我可以找到一种解决方案,但是我觉得有更好的方法了,我很想念它。

谢谢!

2 个答案:

答案 0 :(得分:1)

这看起来很有趣,所以这里是一个答案。我只是将矩形放在画布上作为源数据。有关详细信息,请参见代码中的注释。可能需要进行一些调整,我只对您在XAML中看到的矩形进行了测试。

屏幕截图:

enter image description here

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);
            }
        }
    }
}

答案 1 :(得分:0)

与其尝试找出您的空白空间,不如假设您在耕作方块内部的任何地方都是空白空间,然后将Parant容器的Background设置为白色。假设您有一个包含所有块的父项。如果没有,您应该考虑一下。

在wpf中,您还可以结合使用宝石学来查看本文,这可能会有所帮助: http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx