如何在 WPF 中用矩形填充空 space?
How can I fill empty space with rectangles in WPF?
我正在构建一个程序以在 3D 程序中查看和编辑 Cabinets/furniture。
一个功能要求是让制作团队能够以二维方式查看橱柜的表面,并列出开口的尺寸以便于查看。
我正在计算开口的大小并添加一个该大小的矩形。目标是向显示开口大小的白色矩形添加文本,以便他们可以构建适合开口大小的项目。
我找到了抽屉柜的开口,见下文。
对于像下面这样更复杂的,对我来说有点困难。
以下是棕色部分的属性:
public 双 X { 得到;放; }
public 双 Y { 得到;放; }
public 双宽度 { get;放; }
public双高{get;放; }
我的XAML很简单,就是把所有的矩形加到一个canvas上,用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;
}
}
考虑到所有这些,是否有更好的方法来找出所有的空白区域?
对于以图像形式插入的代码,我深表歉意。我无法正确格式化它以允许我 post.
我相信我可以找到一个解决方案来做我正在做的事情,但我觉得有更好的方法,但我错过了。
谢谢!
与其试图弄清楚你的空 space 在哪里,不如假设你的构造方块内的任何地方都是空的 space 并将 Parant 容器的 Background
设置为白色.假设你有一个 parent 包含你所有的块..如果没有你应该考虑它..
在 wpf 中,您还可以结合 gemoetrys 查看这篇文章,它可能会有很大帮助:
http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx
这看起来很有趣,所以这是一个答案。我只是将矩形放在 canvas 上作为我的源数据。有关详细信息,请参阅代码中的注释。它可能需要一些调整,我只用你在 XAML.
中看到的矩形对其进行了测试
截图:
XAML
<Window x:Class="Whosebug54985848.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 Whosebug54985848
{
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);
}
}
}
}
我正在构建一个程序以在 3D 程序中查看和编辑 Cabinets/furniture。
一个功能要求是让制作团队能够以二维方式查看橱柜的表面,并列出开口的尺寸以便于查看。 我正在计算开口的大小并添加一个该大小的矩形。目标是向显示开口大小的白色矩形添加文本,以便他们可以构建适合开口大小的项目。
我找到了抽屉柜的开口,见下文。
对于像下面这样更复杂的,对我来说有点困难。
以下是棕色部分的属性:
public 双 X { 得到;放; }
public 双 Y { 得到;放; }
public 双宽度 { get;放; }
public双高{get;放; }
我的XAML很简单,就是把所有的矩形加到一个canvas上,用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;
}
}
考虑到所有这些,是否有更好的方法来找出所有的空白区域?
对于以图像形式插入的代码,我深表歉意。我无法正确格式化它以允许我 post.
我相信我可以找到一个解决方案来做我正在做的事情,但我觉得有更好的方法,但我错过了。
谢谢!
与其试图弄清楚你的空 space 在哪里,不如假设你的构造方块内的任何地方都是空的 space 并将 Parant 容器的 Background
设置为白色.假设你有一个 parent 包含你所有的块..如果没有你应该考虑它..
在 wpf 中,您还可以结合 gemoetrys 查看这篇文章,它可能会有很大帮助: http://www.blackwasp.co.uk/WPFCombinedGeometry.aspx
这看起来很有趣,所以这是一个答案。我只是将矩形放在 canvas 上作为我的源数据。有关详细信息,请参阅代码中的注释。它可能需要一些调整,我只用你在 XAML.
中看到的矩形对其进行了测试截图:
XAML
<Window x:Class="Whosebug54985848.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 Whosebug54985848
{
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);
}
}
}
}