在WPF中我需要有一组绘图对象的集合

我有一个WPF项目,它在一个面板中绘制了几个东西。 对于下一个版本,除了现有的东西之外,我还需要添加另一种类型的东西。 目前我有一个包含ItemsControl的网格,其中包含ItemsPanel和ItemsSource。 现有的ItemsSource看起来像这样:

           

大多数集合都是线条或多边形。 我定义了DataTemplates,以将绘图对象的属性绑定到后备对象。 BarrierLine对象的示例如下所示:

      

这一切都运作良好。 现在我需要添加一系列要绘制的东西以及现有的东西。 这个新东西有一系列线条以及平移和旋转值。 不幸的是,我需要绘制这些新东西的集合。 每个实例都有自己的转换和旋转以及行集合。 实际上,我现在拥有一系列线条的集合。 有没有办法嵌套CollectionContainers? 我应该尝试将一个集合添加到DataTemplate吗? 我不知道应该去哪个方向。

编辑:

好吧,我创建了一个概念validation程序,希望能满足彼得的要求。 我将在下面提供代码。 它由五个文件组成:LineArt.cs ObstacleArt.cs MainWindowResource.cs MainWindow.xaml.cs MainWindow.xaml

LineArt对象表示绘制单行所需的数据。 ObstacleArt对象表示线的集合以及这些线的平移和旋转。 我希望能够绘制一系列线条(没有任何转移或旋转)以及一系列障碍物,每个障碍物都有一系列线条。

我试图使用Peter的建议将CompositeCollection放在CollectionContainer的Collection属性中,该CollectionContainer位于CompositeCollection中。 您可以在靠近底部的xaml文件中看到它。

在Collection的Path属性中,当我放置“Obstacles [0] .Lines”时,它将绘制第一个障碍的线条,但是当我取出索引并说“Obstacles.Lines”时它不会绘制任何东西。 我需要的是能够绘制所有障碍物的所有线条。

除此之外,我还需要为每组线设置平移和旋转。

以下是文件,您应该能够编译并运行它。

LineArt.cs

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Media; namespace POC_WPF_nestedDrawingObjects { public class LineArt : INotifyPropertyChanged { private Int32 _id = int.MinValue; private double _ax = 0; private double _ay = 0; private double _bx = 0; private double _by = 0; private SolidColorBrush _lineColor = Brushes.Black; private double _weight = 1; private double _scaledWeight = 1; public LineArt( Int32 id, double ax, double ay, double bx, double by, SolidColorBrush lineColor) { _id = id; _ax = ax; _ay = ay; _bx = bx; _by = by; _lineColor = lineColor; _weight = 1; _scaledWeight = _weight; } public Int32 Id { get { return _id; } } public double AX { get { return _ax; } set { _ax = value; SetPropertyChanged("AX"); } } public double AY { get { return _ay; } set { _ay = value; SetPropertyChanged("AY"); } } public double BX { get { return _bx; } set { _bx = value; SetPropertyChanged("BX"); } } public double BY { get { return _by; } set { _by = value; SetPropertyChanged("BY"); } } public SolidColorBrush LineColor { get { return _lineColor; } set { _lineColor = value; SetPropertyChanged("LineColor"); } } public double Weight { get { return _weight; } set { _weight = value; SetPropertyChanged("Weight"); } } public double ScaledWeight { get { return _scaledWeight; } set { _scaledWeight = value; SetPropertyChanged("ScaledWeight"); } } #region INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void SetPropertyChanged(string prop) { if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs(prop)); } } #endregion INotifyPropertyChanged implementation } } 

ObstacleArt.cs

 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace POC_WPF_nestedDrawingObjects { public class ObstacleArt : INotifyPropertyChanged { private Int32 _id = int.MinValue; private ObservableCollection _lines = new ObservableCollection(); private double _translateX = 0; private double _translateY = 0; private double _rotateAngle = 0; public ObstacleArt( Int32 id, double translateX, double translateY, double rotateAngle) { _id = id; _translateX = translateX; _translateY = translateY; _rotateAngle = rotateAngle; } public Int32 Id { get { return _id; } } public double TranslateX { get { return _translateX; } set { _translateX = value; SetPropertyChanged("TranslateX"); } } public double TranslateY { get { return _translateX; } set { _translateX = value; SetPropertyChanged("TranslateX"); } } public double RotateAngle { get { return _rotateAngle; } set { _rotateAngle = value; SetPropertyChanged("RotateAngle"); } } public ObservableCollection Lines { get { return _lines; } } public void NotifyLinesChanged() { SetPropertyChanged("Lines"); } public void LinesAddPropertyChangedHandlers() { foreach (LineArt line in _lines) { line.PropertyChanged += line_PropertyChanged; } } private void line_PropertyChanged( object sender, PropertyChangedEventArgs e) { SetPropertyChanged(e.PropertyName); } public void LinesDelPropertyChangedHandlers() { foreach (LineArt line in _lines) { line.PropertyChanged -= line_PropertyChanged; } } #region INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void SetPropertyChanged(string prop) { if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs(prop)); } } #endregion INotifyPropertyChanged implementation } } 

MainWindowResource.cs

 using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace POC_WPF_nestedDrawingObjects { public class MainWindowResource : INotifyPropertyChanged { private ObservableCollection _lines = new ObservableCollection(); private ObservableCollection _obstacles = new ObservableCollection(); public ObservableCollection Lines { get { return _lines; } } public ObservableCollection Obstacles { get { return _obstacles; } } public void NotifyLinesChanged() { SetPropertyChanged("Lines"); } public void NotifyObstaclesChanged() { SetPropertyChanged("Obstacles"); } public void LinesAddPropertyChangedHandlers() { foreach (LineArt line in _lines) { line.PropertyChanged += line_PropertyChanged; } } public void LinesDelPropertyChangedHandlers() { foreach (LineArt line in _lines) { line.PropertyChanged -= line_PropertyChanged; } } private void line_PropertyChanged( object sender, PropertyChangedEventArgs e) { SetPropertyChanged(e.PropertyName); } #region INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void SetPropertyChanged(string prop) { if (PropertyChanged != null) { PropertyChanged( this, new PropertyChangedEventArgs(prop)); } } #endregion INotifyPropertyChanged implementation } } 

MainWindow.xaml.cs

 using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace POC_WPF_nestedDrawingObjects { ///  /// Interaction logic for MainWindow.xaml ///  public partial class MainWindow : Window { private MainWindowResource _mainWindowResource = null; private SolidColorBrush _brush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)); public MainWindow() { LineArt line = null; ObstacleArt obstacle = null; InitializeComponent(); Application app = Application.Current; _mainWindowResource = (MainWindowResource)this.Resources["MainWindowResource"]; // initialize four lines, they will form a rectangle _mainWindowResource.LinesDelPropertyChangedHandlers(); line = new LineArt(1, 10, 10, 30, 10, _brush); _mainWindowResource.Lines.Add(line); line = new LineArt(2, 30, 10, 30, 20, _brush); _mainWindowResource.Lines.Add(line); line = new LineArt(2, 30, 20, 10, 20, _brush); _mainWindowResource.Lines.Add(line); line = new LineArt(2, 10, 20, 10, 10, _brush); _mainWindowResource.Lines.Add(line); _mainWindowResource.LinesAddPropertyChangedHandlers(); _mainWindowResource.NotifyLinesChanged(); // initialize an obstacle made of four lines. // the lines form a 20x20 square around 0,0. // the obstacle should be trastlated to 50,50 // and not rotated obstacle = new ObstacleArt(1, 50, 50, 0); obstacle.LinesDelPropertyChangedHandlers(); line = new LineArt(1, -10, 10, 10, 10, _brush); obstacle.Lines.Add(line); line = new LineArt(2, 10, 10, 10, -10, _brush); obstacle.Lines.Add(line); line = new LineArt(3, 10, -10, -10, -10, _brush); obstacle.Lines.Add(line); line = new LineArt(4, -10, -10, -10, 10, _brush); obstacle.Lines.Add(line); obstacle.LinesAddPropertyChangedHandlers(); _mainWindowResource.Obstacles.Add(obstacle); // initialize an obstacle made of four lines. // the lines form a 20x20 square around 0,0. // the obstacle should be trastlated to 100,100 // and rotated to a 45 degree angle obstacle = new ObstacleArt(1, 100, 100, 45); obstacle.LinesDelPropertyChangedHandlers(); line = new LineArt(1, -10, 10, 10, 10, _brush); obstacle.Lines.Add(line); line = new LineArt(2, 10, 10, 10, -10, _brush); obstacle.Lines.Add(line); line = new LineArt(3, 10, -10, -10, -10, _brush); obstacle.Lines.Add(line); line = new LineArt(4, -10, -10, -10, 10, _brush); obstacle.Lines.Add(line); obstacle.LinesAddPropertyChangedHandlers(); _mainWindowResource.Obstacles.Add(obstacle); _mainWindowResource.NotifyObstaclesChanged(); _mainWindowResource.NotifyLinesChanged(); foreach(ObstacleArt obstacleArt in _mainWindowResource.Obstacles) { obstacleArt.NotifyLinesChanged(); } } } } 

MainWindow.xaml

                                   

感谢您提供改进的代码示例。 从那以后,在我看来,你完全以错误的方式实现目标。

也就是说,您尝试使用单个ItemsControl对象渲染所有行。 但这不是您的数据模型的组织方式。 您的数据模型有两种完全不同的对象: LineArt对象和包含LineArt对象的ObstacleArt对象。

鉴于此,在我看来,更合适的方法是简单地组成MainWindowResource.LinesMainWindowResource.Obstacles集合,然后使用数据模板将这些集合适当地显示在一起。

这是您的XAML的新版本,它显示了我的意思:

                                           

这里的关键是第二个DataTemplate ,其目标类型为ObstacleArt 。 这允许主ItemsControl在复合集合中显示各个ObstacleArt元素。 通过第二个数据模板,它通过在每个ObstacleArt对象中嵌套一个全新的ItemsControl来实现,其中ItemsControl处理ObstacleArt对象的所有渲染。 请注意,由于嵌套的ItemsControl对象中的实际项目本身就是LineArt项目,因此最终将引用返回到LineArt类型的DataTemplate

以上工作对您的代码隐藏完全没有任何改变。 也就是说,我认为你最好让你的类inheritanceDependencyObject ,然后创建可绑定属性依赖属性。 当然,WPF确实支持INotifyPropertyChanged ,但是如果你使用了依赖属性范例,那么你有很多明确的属性通知代码就会消失。