在WPF我需要有图形对象的集合的集合 [英] in WPF I need to have a collection of a collection of drawing objects

查看:287
本文介绍了在WPF我需要有图形对象的集合的集合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有画在面板上几件事情WPF项目。下一个版本,我需要添加其他类型的事情,除了现有的东西画。目前,我有一个包含其中包含一个ItemsPanel和ItemsSource的一个ItemsControl一个网格。现有的ItemsSource看起来是这样的:

 < ItemsControl.ItemsSource> 
< CompositeCollection>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径= DottedLines,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径= BarrierLines,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径= ProjectedLines,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径= ProjectedCranes,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径= CraneConfigs,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径=视线,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径= CraneCenters,
模式=单向}/>
< / CompositeCollection>
< /ItemsControl.ItemsSource>



大部分藏品是线或面的。我定义为图形对象的属性绑定到支持对象的DataTemplates。对于BarrierLine对象的一个​​例子是这样的:

 < D​​ataTemplate的数据类型={X:键入c:BarrierLineArt}> ; 
<线
X1 ={绑定路径= AX}
Y1 ={绑定路径= AY}
X2 ={绑定路径= BX}
Y2 ={绑定路径= BY}
行程={绑定路径= LineColor}
StrokeThickness ={绑定路径= ScaledWeight}
StrokeEndLineCap =圆
StrokeStartLineCap =圆>
< /线路>
< / DataTemplate中>

这一切运作良好。现在我需要添加的事物的集合,除了现有的东西画。这种新的东西有行的集合和平移和旋转值。不幸的是,我需要借助这些新事物的集合。每个实例有它自己的平移和旋转和线条的集合。实际上我现在有行的集合的集合。有什么办法巢CollectionContainers?我应该尝试集合添加到DataTemplate中?我很茫然,以哪个方向,我应该去



编辑:



好吧,我创建了验证的概念,一个程序,我希望能与彼得的需求满足。我公司将提供下面的代码。它由五个文件:
LineArt.cs
ObstacleArt.cs
MainWindowResource.cs
MainWindow.xaml.cs
MainWindow.xaml



LINEART对象表示必要制定单行的数据。该ObstacleArt对象代表行的集合和一个翻译,并为那些行的旋转。我希望能够画线的集合(没有翻译中或旋转)加障碍物集合各自具有行的集合



我试图使用彼得的建议,把CompositeCollection一个CollectionContainer这是一个CompositeCollection内的集合属性中。你可以看到它在靠近底部的XAML文件。



在收集,Path属性,当我把障碍[0] .Lines,它将绘制第一个障碍线,但是,当我拿出了指数,并说Obstacles.Lines它不会画什么。我需要的是画的一切障碍的所有行的能力。



除此之外,我将需要设置平移和旋转每一组线



下面是文件,你应该能够编译并运行此。



LINEART的.cs

 使用系统; 
使用System.Collections.Generic;
使用System.ComponentModel;
使用System.Linq的;
使用System.Text;使用System.Threading.Tasks
;使用System.Windows.Media
;

命名空间POC_WPF_nestedDrawingObjects
{
公共类LINEART:INotifyPropertyChanged的
{
私人的Int32 _id = int.MinValue;
私人双_A​​X = 0;
私人双_ay = 0;
私人双_bx = 0;
私人双_by = 0;
私人的SolidColorBrush _lineColor = Brushes.Black;
私人双_weight = 1;
私人双_scaledWeight = 1;

公共LINEART(
的Int32 ID,
双斧,
双唉,
双BX,由
双,
的SolidColorBrush lineColor)
{
_id = ID;
_AX = AX;
_ay = AY;
_bx = BX;
_by =通过;
_lineColor = lineColor;
_weight = 1;
_scaledWeight = _weight;
}

公众的Int32标识{{返回_id; }}
公共双斧
{
{返回_AX; }

{
_AX =价值;
SetPropertyChanged(AX);
}
}
公共双AY
{
{返回_ay; }

{
_ay =价值;
SetPropertyChanged(AY);
}
}
公共双BX
{
{返回_bx; }

{
_bx =价值;
SetPropertyChanged(BX);
}
}
公共一倍
{
{返回_by; }

{
_by =价值;
SetPropertyChanged(本经);
}
}
公众的SolidColorBrush LineColor
{
{返回_lineColor; }

{
_lineColor =价值;
SetPropertyChanged(LineColor);
}
}
公共双重量
{
{返回_weight; }

{
_weight =价值;
SetPropertyChanged(重量);
}
}
公共双ScaledWeight
{
{返回_scaledWeight; }

{
_scaledWeight =价值;
SetPropertyChanged(ScaledWeight);
}
}

#地区INotifyPropertyChanged的实施

公共事件PropertyChangedEventHandler的PropertyChanged;
私人无效SetPropertyChanged(字符串道具)
{
如果(的PropertyChanged!= NULL)
{
的PropertyChanged(
本,
新PropertyChangedEventArgs (支柱));
}
}

#endregion INotifyPropertyChanged的实施
}
}

ObstacleArt.cs

 使用系统; 
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.ComponentModel;
使用System.Linq的;
使用System.Text;使用System.Threading.Tasks
;

命名空间POC_WPF_nestedDrawingObjects
{
公共类ObstacleArt:INotifyPropertyChanged的
{
私人的Int32 _id = int.MinValue;
私人的ObservableCollection<&LINEART GT; _lines
=新的ObservableCollection<&LINEART GT;();
私人双_translateX = 0;
私人双_translateY = 0;
私人双_rotateAngle = 0;

公共ObstacleArt(
的Int32 ID,
双translateX,
双translateY,
双rotateAngle)
{
_id = ID;
_translateX = translateX;
_translateY = translateY;
_rotateAngle = rotateAngle;
}

公众的Int32标识
{
{返回_id; }
}
公共双TranslateX
{
{返回_translateX; }

{
_translateX =价值;
SetPropertyChanged(TranslateX);
}
}
公共双TranslateY
{
{返回_translateX; }

{
_translateX =价值;
SetPropertyChanged(TranslateX);
}
}
公共双RotateAngle
{
{返回_rotateAngle; }

{
_rotateAngle =价值;
SetPropertyChanged(RotateAngle);
}
}
公众的ObservableCollection<&LINEART GT;线
{
{返回_lines; }
}

公共无效NotifyLinesChanged()
{
SetPropertyChanged(线);
}

公共无效LinesAddPropertyChangedHandlers()
{
的foreach(在_lines LINEART线)
{
line.PropertyChanged + = line_PropertyChanged;
}
}

私人无效line_PropertyChanged(
对象发件人,
PropertyChangedEventArgs E)
{
SetPropertyChanged(e.PropertyName );
}

公共无效LinesDelPropertyChangedHandlers()
{
的foreach(在_lines LINEART线)
{
line.PropertyChanged - = line_PropertyChanged;
}
}

#地区INotifyPropertyChanged的实施

公共事件PropertyChangedEventHandler的PropertyChanged;
私人无效SetPropertyChanged(字符串道具)
{
如果(的PropertyChanged!= NULL)
{
的PropertyChanged(
本,
新PropertyChangedEventArgs (支柱));
}
}

#endregion INotifyPropertyChanged的实施
}
}

MainWindowResource.cs

 使用系统; 
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.ComponentModel;
使用System.Linq的;
使用System.Text;使用System.Threading.Tasks
;

命名空间POC_WPF_nestedDrawingObjects
{
公共类MainWindowResource:INotifyPropertyChanged的
{
私人的ObservableCollection<&LINEART GT; _lines
=新的ObservableCollection<&LINEART GT;();
私人的ObservableCollection< ObstacleArt> _obstacles
=新的ObservableCollection< ObstacleArt>();

公众的ObservableCollection<&LINEART GT;线
{
得到
{
返回_lines;
}
}

公众的ObservableCollection< ObstacleArt>障碍
{
得到
{
返回_obstacles;
}
}

公共无效NotifyLinesChanged()
{
SetPropertyChanged(线);
}

公共无效NotifyObstaclesChanged()
{
SetPropertyChanged(障碍);
}

公共无效LinesAddPropertyChangedHandlers()
{
的foreach(在_lines LINEART线)
{
line.PropertyChanged + = line_PropertyChanged;
}
}

公共无效LinesDelPropertyChangedHandlers()
{
的foreach(LINEART线_lines)
{
线。 PropertyChanged的 - = line_PropertyChanged;
}
}

私人无效line_PropertyChanged(
对象发件人,
PropertyChangedEventArgs E)
{
SetPropertyChanged(e.PropertyName );
}

#地区INotifyPropertyChanged的实施

公共事件PropertyChangedEventHandler的PropertyChanged;
私人无效SetPropertyChanged(字符串道具)
{
如果(的PropertyChanged!= NULL)
{
的PropertyChanged(
本,
新PropertyChangedEventArgs (支柱));
}
}

#endregion INotifyPropertyChanged的实施
}
}

MainWindow.xaml.cs

 使用系统; 
使用System.Collections.Generic;
使用System.Drawing中;
使用System.Linq的;
使用System.Text;使用System.Threading.Tasks
;使用System.Windows
;使用System.Windows.Controls的
;
使用System.Windows.Data;使用System.Windows.Documents
;
使用System.Windows.Input;使用System.Windows.Media
;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;使用System.Windows.Shapes
;

命名空间POC_WPF_nestedDrawingObjects
{
///<总结> $ B $为MainWindow.xaml
///< b ///交互逻辑; /总结>
公共部分类主窗口:窗口
{
私人MainWindowResource _mainWindowResource = NULL;
私人的SolidColorBrush _brush
=新的SolidColorBrush(Color.FromArgb(255,0,0,0));

公共主窗口()
{
LINEART线= NULL;
ObstacleArt障碍= NULL;

的InitializeComponent();

应用程序= Application.Current;
_mainWindowResource
=(MainWindowResource)this.Resources [MainWindowResource];

//初始化四行,就会形成一个矩形
_mainWindowResource.LinesDelPropertyChangedHandlers();
线=新LINEART(1,10,10,30,10,_brush);
_mainWindowResource.Lines.Add(线);
线=新LINEART(2,30,10,30,20,_brush);
_mainWindowResource.Lines.Add(线);
线=新LINEART(2,30,20,10,20,_brush);
_mainWindowResource.Lines.Add(线);
线=新LINEART(2,10,20,10,10,_brush);
_mainWindowResource.Lines.Add(线);
_mainWindowResource.LinesAddPropertyChangedHandlers();
_mainWindowResource.NotifyLinesChanged();

//初始化制成的四行的障碍。
//线条形成一个20×20平方左右0,0。
//障碍物应trastlated至50,50
//和不转动
障碍物=新ObstacleArt(1,50,50,0);
obstacle.LinesDelPropertyChangedHandlers();
线=新LINEART(1,-10,10,10,10,_brush);
obstacle.Lines.Add(线);
线=新LINEART(2,10,10,10,-10,_brush);
obstacle.Lines.Add(线);
线=新LINEART(3,10,-10,-10,-10,_brush);
obstacle.Lines.Add(线);
线=新LINEART(4,-10,-10,-10,10,_brush);
obstacle.Lines.Add(线);
obstacle.LinesAddPropertyChangedHandlers();
_mainWindowResource.Obstacles.Add(障碍);

//初始化制成的四行的障碍。
//线条形成一个20×20平方左右0,0。
//障碍物应trastlated至100,100
//并旋转至45度角
障碍物=新ObstacleArt(1,100,100,45);
obstacle.LinesDelPropertyChangedHandlers();
线=新LINEART(1,-10,10,10,10,_brush);
obstacle.Lines.Add(线);
线=新LINEART(2,10,10,10,-10,_brush);
obstacle.Lines.Add(线);
线=新LINEART(3,10,-10,-10,-10,_brush);
obstacle.Lines.Add(线);
线=新LINEART(4,-10,-10,-10,10,_brush);
obstacle.Lines.Add(线);
obstacle.LinesAddPropertyChangedHandlers();
_mainWindowResource.Obstacles.Add(障碍);

_mainWindowResource.NotifyObstaclesChanged();
_mainWindowResource.NotifyLinesChanged();
的foreach(ObstacleArt obstacleArt在_mainWindowResource.Obstacles)
{
obstacleArt.NotifyLinesChanged();
}
}
}
}

< STRONG> MainWindow.xaml

 <窗​​口x:类=POC_WPF_nestedDrawingObjects.MainWindow
的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/presentation
的xmlns:X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
的xmlns :C =CLR的命名空间:POC_WPF_nestedDrawingObjects
标题=主窗口HEIGHT =350WIDTH =525>
< Window.Resources>
< C:MainWindowResource X:键=MainWindowResource/>
<数据类型的DataTemplate ={X:键入c:LINEART}>
<线
X1 ={绑定路径= AX}
Y1 ={绑定路径= AY}
X2 ={绑定路径= BX}
Y2 ={绑定路径= BY}
行程={绑定路径= LineColor}
StrokeThickness ={绑定路径= ScaledWeight}
StrokeEndLineCap =圆
StrokeStartLineCap =圆>
< /线路>
< / DataTemplate中>
<风格X:键=ContentCanvasStyle的TargetType =画布>
< setter属性=RenderTransformOriginVALUE =0,0/>
< /样式和GT;
< /Window.Resources>
<网格和GT;
<&ItemsControl的GT;
< ItemsControl.ItemsPanel>
< ItemsPanelTemplate>
<帆布X:NAME =ContentCanvas
风格={StaticResource的ContentCanvasStyle}>
< /帆布>
< / ItemsPanelTemplate>
< /ItemsControl.ItemsPanel>
< ItemsControl.ItemsSource>
< CompositeCollection>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径=行,
模式=单向}/>
< CollectionContainer>
< CollectionContainer.Collection>
< CompositeCollection>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径=障碍[0] .Lines,
模式=单向}/ >
< / CompositeCollection>
< /CollectionContainer.Collection>
< / CollectionContainer>
< / CompositeCollection>
< /ItemsControl.ItemsSource>
< / ItemsControl的>
< /网格和GT;
< /窗GT;


解决方案

感谢您对改进的代码示例。从这一点,在我看来,你会了解你的目标完全走错了路。



这是,你想有一个 ItemsControl的对象渲染所有的线。但是,这不是你的数据模型是如何组织的。您的数据模型有两种完全不同类型的对象: LINEART 对象和 ObstacleArt 对象,其中包含 LINEART 的对象。



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



下面是你的XAML的一个新版本,显示我的意思:

 <窗​​口x: CLASS =POC_WPF_nestedDrawingObjects.MainWindow
的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/presentation
的xmlns:X =htt​​p://schemas.microsoft.com / WinFX的/ 2006 / XAML
的xmlns:C =CLR的命名空间:POC_WPF_nestedDrawingObjects
标题=主窗口HEIGHT =350WIDTH =525>

< Window.Resources>
< C:MainWindowResource X:键=MainWindowResource/>
<风格X:键=ContentCanvasStyle的TargetType =画布>
< setter属性=RenderTransformOriginVALUE =0,0/>
< /样式和GT;
<数据类型的DataTemplate ={X:键入c:LINEART}>
<线
X1 ={绑定路径= AX}
Y1 ={绑定路径= AY}
X2 ={绑定路径= BX}
Y2 ={绑定路径= BY}
行程={绑定路径= LineColor}
StrokeThickness ={绑定路径= ScaledWeight}
StrokeEndLineCap =圆
StrokeStartLineCap =圆>
< /线路>
< / DataTemplate中>
<数据类型的DataTemplate ={X:键入c:ObstacleArt}>
<的ItemsControl的ItemsSource ={结合线条,模式=单向}>
< ItemsControl.ItemsPanel>
< ItemsPanelTemplate>
<帆布X:NAME =ContentCanvas
风格={StaticResource的ContentCanvasStyle}>
< /帆布>
< / ItemsPanelTemplate>
< /ItemsControl.ItemsPanel>
< ItemsControl.RenderTransform>
<&的TransformGroup GT;
< RotateTransform角={结合RotateAngle}/>
< TranslateTransform X ={结合TranslateX}Y ={结合TranslateY}/>
< /&的TransformGroup GT;
< /ItemsControl.RenderTransform>
< / ItemsControl的>
< / DataTemplate中>
< /Window.Resources>
<网格和GT;
<&ItemsControl的GT;
< ItemsControl.ItemsPanel>
< ItemsPanelTemplate>
<帆布X:NAME =ContentCanvas
风格={StaticResource的ContentCanvasStyle}>
< /帆布>
< / ItemsPanelTemplate>
< /ItemsControl.ItemsPanel>
< ItemsControl.ItemsSource>
< CompositeCollection>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径=行,
模式=单向}/>
< CollectionContainer
系列={结合
来源= {StaticResource的MainWindowResource}
路径=障碍,
模式=单向}/>
< / CompositeCollection>
< /ItemsControl.ItemsSource>
< / ItemsControl的>
< /网格和GT;
< /窗GT;



这里的关键是第二个的DataTemplate ,与 ObstacleArt 的目标类型。这使得主的ItemsControl 来显示各个 ObstacleArt 复合集合中的元素。通过第二数据模板,它嵌套这样做一个全新的的ItemsControl 中的每个 ObstacleArt 对象,其中的的ItemsControl 处理所有渲染为 ObstacleArt 的对象。请注意,由于实际的项目的在嵌套的的ItemsControl 对象本身 LINEART 的项目,这缠指回的DataTemplate LINEART 键入



以上作品没有在所有代码隐藏任何更改。这就是说,它是我的看法,你会过得更好让您的类继承的DependencyObject ,然后进行绑定属性依赖属性。 WPF不支持 INotifyPropertyChanged的当然,但你必须有很多明确的财产通知代码将走开,如果你正在使用的依赖属性的范例。


I have a WPF project that draws several things in a panel. For the next release I need to add another type of thing to draw in addition to the existing things. Currently I have a grid that contains an ItemsControl which contains an ItemsPanel and an ItemsSource. The existing ItemsSource looks something like this:

                    <ItemsControl.ItemsSource>
                    <CompositeCollection>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=DottedLines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=BarrierLines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=ProjectedLines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=ProjectedCranes,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=CraneConfigs,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=Sightlines,
                                    Mode=OneWay}"/>
                        <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=CraneCenters,
                                    Mode=OneWay}"/>
                    </CompositeCollection>
                </ItemsControl.ItemsSource>

Most of the collections are of Lines or Polygons. I have DataTemplates defined to bind the properties of the drawing objects to a backing object. An example for the BarrierLine object looks like this:

        <DataTemplate DataType="{x:Type c:BarrierLineArt}">
        <Line
            X1="{Binding Path=AX}"
            Y1="{Binding Path=AY}"
            X2="{Binding Path=BX}"
            Y2="{Binding Path=BY}"
            Stroke="{Binding Path=LineColor}"
            StrokeThickness="{Binding Path=ScaledWeight}"
            StrokeEndLineCap="Round"
            StrokeStartLineCap="Round">
        </Line>
    </DataTemplate>

This all works well. Now I need to add a collection of things to draw in addition to the existing things. This new thing has a collection of lines and a translation and rotation value. Unfortunately, I need to draw a collection of these new things. Each instance has it own translation and rotation and collection of lines. In effect I now have a collection of a collection of lines. Is there any way to nest CollectionContainers? Should I try to add a collection to the DataTemplate? I'm at a loss as to which direction I should go.

EDIT:

Ok, I created a proof-of-concept program that I hope meets with Peter's requirements. I will provide the code below. It consists of five files: LineArt.cs ObstacleArt.cs MainWindowResource.cs MainWindow.xaml.cs MainWindow.xaml

The LineArt object represents the data necessary to draw a single line. The ObstacleArt object represents a collection of lines and a translation and a rotation for those lines. I wish to be able to draw a collection of lines (with no traslation or rotation) plus a collection of obstacles each of which has a collection of lines.

I attempted to use Peter's suggestion to put a CompositeCollection inside the Collection property of a CollectionContainer which is within a CompositeCollection. You can see it in the xaml file near the bottom.

In the Path property of the Collection, when I put "Obstacles[0].Lines" it will draw the lines for the first obstacle, But when I take out the index and say "Obstacles.Lines" it will not draw anything. What I need is the ability to draw all the lines of all the obstacles.

In addition to that, I will need to set the translation and rotation for each set of lines.

Here are the files, you should be able to compile and run this.

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<LineArt> _lines
            = new ObservableCollection<LineArt>();
        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<LineArt> 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<LineArt> _lines
            = new ObservableCollection<LineArt>();
        private ObservableCollection<ObstacleArt> _obstacles
            = new ObservableCollection<ObstacleArt>();

        public ObservableCollection<LineArt> Lines
        {
            get
            {
                return _lines;
            }
        }

        public ObservableCollection<ObstacleArt> 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
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    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

<Window x:Class="POC_WPF_nestedDrawingObjects.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:POC_WPF_nestedDrawingObjects"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <c:MainWindowResource x:Key="MainWindowResource"/>
        <DataTemplate DataType="{x:Type c:LineArt}">
            <Line
                X1="{Binding Path=AX}"
                Y1="{Binding Path=AY}"
                X2="{Binding Path=BX}"
                Y2="{Binding Path=BY}"
                Stroke="{Binding Path=LineColor}"
                StrokeThickness="{Binding Path=ScaledWeight}"
                StrokeEndLineCap="Round"
                StrokeStartLineCap="Round">
            </Line>
        </DataTemplate>
        <Style x:Key="ContentCanvasStyle" TargetType="Canvas">
            <Setter Property="RenderTransformOrigin" Value="0,0"/>
        </Style>
    </Window.Resources>
    <Grid>
        <ItemsControl>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas x:Name="ContentCanvas"
                        Style="{StaticResource ContentCanvasStyle}">
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemsSource>
                <CompositeCollection>
                    <CollectionContainer
                        Collection="{Binding
                            Source={StaticResource MainWindowResource},
                            Path=Lines,
                            Mode=OneWay}"/>
                    <CollectionContainer>
                        <CollectionContainer.Collection>
                        <CompositeCollection>
                            <CollectionContainer
                                Collection="{Binding
                                    Source={StaticResource MainWindowResource},
                                    Path=Obstacles[0].Lines,
                                    Mode=OneWay}"/>
                            </CompositeCollection>
                        </CollectionContainer.Collection>
                    </CollectionContainer>
                </CompositeCollection>
            </ItemsControl.ItemsSource>
        </ItemsControl>
    </Grid>
</Window>

解决方案

Thank your for the improved code example. From that, it appears to me that you are going about your goal the wrong way entirely.

That is, you are trying to have a single ItemsControl object render all of your lines. But that's not how your data model is organized. Your data model has two completely different kinds of objects: LineArt objects, and the ObstacleArt object that contains LineArt objects.

Given that, it seems to me that a more appropriate approach would be to simply compose the MainWindowResource.Lines and MainWindowResource.Obstacles collections, and then use data templates to appropriately display these collections together.

Here is a new version of your XAML that shows what I mean:

<Window x:Class="POC_WPF_nestedDrawingObjects.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:POC_WPF_nestedDrawingObjects"
        Title="MainWindow" Height="350" Width="525">

  <Window.Resources>
    <c:MainWindowResource x:Key="MainWindowResource"/>
    <Style x:Key="ContentCanvasStyle" TargetType="Canvas">
      <Setter Property="RenderTransformOrigin" Value="0,0"/>
    </Style>
    <DataTemplate DataType="{x:Type c:LineArt}">
      <Line
                X1="{Binding Path=AX}"
                Y1="{Binding Path=AY}"
                X2="{Binding Path=BX}"
                Y2="{Binding Path=BY}"
                Stroke="{Binding Path=LineColor}"
                StrokeThickness="{Binding Path=ScaledWeight}"
                StrokeEndLineCap="Round"
                StrokeStartLineCap="Round">
      </Line>
    </DataTemplate>
    <DataTemplate DataType="{x:Type c:ObstacleArt}">
      <ItemsControl ItemsSource="{Binding Lines, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
            <Canvas x:Name="ContentCanvas"
                        Style="{StaticResource ContentCanvasStyle}">
            </Canvas>
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.RenderTransform>
          <TransformGroup>
            <RotateTransform Angle="{Binding RotateAngle}"/>
            <TranslateTransform X="{Binding TranslateX}" Y="{Binding TranslateY}"/>
          </TransformGroup>
        </ItemsControl.RenderTransform>
      </ItemsControl>
    </DataTemplate>
  </Window.Resources>
  <Grid>
    <ItemsControl>
      <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
          <Canvas x:Name="ContentCanvas"
                  Style="{StaticResource ContentCanvasStyle}">
          </Canvas>
        </ItemsPanelTemplate>
      </ItemsControl.ItemsPanel>
      <ItemsControl.ItemsSource>
        <CompositeCollection>
          <CollectionContainer
              Collection="{Binding
                  Source={StaticResource MainWindowResource},
                  Path=Lines,
                  Mode=OneWay}"/>
          <CollectionContainer
              Collection="{Binding
                  Source={StaticResource MainWindowResource},
                  Path=Obstacles,
                  Mode=OneWay}"/>
        </CompositeCollection>
      </ItemsControl.ItemsSource>
    </ItemsControl>
  </Grid>
</Window>

The key here is the second DataTemplate, with a target type of ObstacleArt. This allows the main ItemsControl to display the individual ObstacleArt elements in the composite collection. Via the second data template, it does so by nesting an entirely new ItemsControl within for each ObstacleArt object, where that ItemsControl handles all of the rendering for the ObstacleArt object. Note that since the actual items in that nested ItemsControl object are themselves LineArt items, this winds up referring back to the DataTemplate for the LineArt type.

The above works without any changes at all to your code-behind. That said, it is my opinion that you would be better off making your classes inherit DependencyObject, and then make the bindable properties dependency properties. WPF does support INotifyPropertyChanged of course, but you have a lot of explicit property notification code in there that would just go away if you were using the dependency-property paradigm.

这篇关于在WPF我需要有图形对象的集合的集合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆