在 WPF 上分层绘制矩形 Canvas

Drawing hierarchically Rectangle on WPF Canvas

我正在尝试将 rectangles 放在下一张图片中

蓝色箭头显示 child 中的 parent-element

我得到了一个名为 Box 的 class,它有一个 Box 的父属性。 我将所有创建的 Boxes 推入 ObservableCollection 以用于 XAML 代码中的绑定。

这是盒子 class:

public class Box
{
    public string Content { get; set; } //Content in the Box
    public double X { get; set; }       //For Canvas.Left propertie
    public double Y { get; set; }       //For Canvas.Right propertie
    public double Width { get; set; }
    public double Height { get; set; }
    public Box Parent { get; set; }
}

但是现在我没有像图片中那样在 canvas 上绘制矩形的正确方法。我想创建一个具有不同列数量的网格,但我不确定这是否可行。

此致。

如果您想在 Canvas 中绘制矩形只是为了布局目的,利用 WPF 的面板会容易得多。要使用 ObservableCollection 的完整绑定,需要大量编码。所以下面是一个简单的例子。

盒子Class

public class Box
{
  public int Id { get; private set; }
  public int ParentId { get; private set; }
  public string Content { get; private set; }

  public Box(string content, int id, int parentId)
  {
    this.Id = id;
    this.ParentId = parentId;
    this.Content = content;
  }
}

BoxPanel Class 继承自 StackPanel

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

public class BoxPanel : StackPanel
{
  public int Id { get; private set; }

  private readonly Border topPanel;
  private readonly StackPanel bottomPanel;

  public BoxPanel()
  {
    topPanel = new Border();
    bottomPanel = new StackPanel { Orientation = Orientation.Horizontal };

    this.Children.Add(topPanel);
    this.Children.Add(bottomPanel);
  }

  public BoxPanel(Box box)
    : this()
  {
    Id = box.Id;

    topPanel.Child = new TextBlock
    {
      Text = box.Content,
      HorizontalAlignment = HorizontalAlignment.Center,
      VerticalAlignment = VerticalAlignment.Center,
      Padding = new Thickness(14),
      Foreground = Brushes.White,
    };
    topPanel.Background = (Id % 2 == 0) ? Brushes.Gray : Brushes.DarkGray;
    topPanel.BorderBrush = Brushes.Black;
    topPanel.BorderThickness = new Thickness(1);
  }

  protected override void OnInitialized(EventArgs e)
  {
    base.OnInitialized(e);

    this.Loaded += (_, __) => AdjustBottomPanel();
    this.LayoutUpdated += (_, __) => AdjustBottomPanel();
  }

  public IReadOnlyCollection<BoxPanel> ChildrenPanel
  {
    get { return bottomPanel.Children.Cast<BoxPanel>().ToArray(); }
  }

  public void AddChildPanel(BoxPanel child)
  {
    bottomPanel.Children.Add(child);
    AdjustBottomPanel();
  }

  private void AdjustBottomPanel()
  {
    if (!ChildrenPanel.Any())
      return;

    var childWidth = Math.Max((Double.IsNaN(this.Width) ? 0 : this.Width), this.ActualWidth)
      / ChildrenPanel.Count;

    foreach (var child in ChildrenPanel)
      child.Width = childWidth;
  }
}
主窗口

XAML

<Window x:Class="WpfBoxPanel.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Width="500" Height="240">
  <Grid x:Name="LayoutRoot"/>
</Window>

以及 MainWindow 的代码隐藏

using System.Collections.Generic;
using System.Linq;
using System.Windows;

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

    boxPanelRoot = new BoxPanel();
    LayoutRoot.Children.Add(boxPanelRoot);

    this.Loaded += (_, __) => PopulateBoxPanel();
  }

  private readonly IList<Box> Boxes = new List<Box>
  {
    new Box("1st", 1, 0),
    new Box("2nd 1", 2, 1),
    new Box("2nd 2", 3, 1),
    new Box("3rd 1", 4, 2),
    new Box("3rd 2", 5, 2),
    new Box("3rd 3", 6, 3),
    new Box("4th 1", 7, 4),
    new Box("4th 2", 8, 5),
    new Box("4th 3", 9, 5),
    new Box("4th 4", 10, 6),
    new Box("4th 5", 11, 6),
  };

  private readonly BoxPanel boxPanelRoot;

  private void PopulateBoxPanel()
  {
    foreach (var box in Boxes)
    {
      var existingPanels = boxPanelRoot.GetDescendants() // See VisualTreeHelperExtensions.GetDescendants method of WinRT Xaml Toolkit
        .OfType<BoxPanel>()
        .ToArray();

      if (existingPanels.Any(x => x.Id == box.Id))
        continue;

      var parent = existingPanels.FirstOrDefault(x => x.Id == box.ParentId);
      if (parent == null)
        parent = boxPanelRoot;

      parent.AddChildPanel(new BoxPanel(box));
    }
  }
}