[英]How to create a custom MultiSelector / ItemsControl in WPF / C#
I'm trying to create a diagramming application in C# / WPF.我正在尝试在 C#/WPF 中创建一个图表应用程序。 What I going for is somewhat similar to Microsoft Visio although I'm not trying to clone it.
我想要的有点类似于 Microsoft Visio,尽管我不想克隆它。 I kind of wrote this question as I was coding and just put all the issues I had into it in case someone will find it useful.
我在编码时写了这个问题,只是把我遇到的所有问题都放在里面,以防有人觉得它有用。 Maybe I've been thinking too hard but I feel like I could throw up on my keyboard and produce better code, so feel free to give any suggestions on every detail you catch (grammar excluded :-))
也许我一直在想太多,但我觉得我可以扔在键盘上并编写出更好的代码,所以请随时对您发现的每个细节提出任何建议(不包括语法:-))
In short:简而言之:
Why are all the items positioned at (0,0)?为什么所有项目都位于(0,0)?
public class Diagram : MultiSelector
{
public Diagram()
{
this.CanSelectMultipleItems = true;
// The canvas supports absolute positioning
FrameworkElementFactory panel = new FrameworkElementFactory(typeof(Canvas));
this.ItemsPanel = new ItemsPanelTemplate(panel);
// Tells the container where to position the items
this.ItemContainerStyle = new Style();
this.ItemContainerStyle.Setters.Add(new Setter(Canvas.LeftProperty, new Binding("X")));
this.ItemContainerStyle.Setters.Add(new Setter(Canvas.TopProperty, new Binding("Y")));
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
FrameworkElement contentitem = element as FrameworkElement;
Binding leftBinding = new Binding("X");
Binding topBinding = new Binding("Y");
contentitem.SetBinding(Canvas.LeftProperty, leftBinding);
contentitem.SetBinding(Canvas.TopProperty, topBinding);
base.PrepareContainerForItemOverride(element, item);
}
public class DiagramItem : ContentControl
{
private Point _location;
public DiagramItem()
{
}
static DiagramItem()
{
}
public Point Location
{
get { return _location; }
set
{
_location = value;
}
}
public double X
{
get { return _location.X; }
set
{
_location.X = value;
}
}
public double Y
{
get { return _location.Y; }
set
{
_location.Y = value;
}
}
}
//...
Ok, so the idea is that the Diagram : ItemsControl places its item on a Canvas panel at the position defined in the Item DiagramItem.Location.好的,所以这个想法是Diagram : ItemsControl将它的项目放置在 Canvas 面板上的 Item DiagramItem.Location 中定义的位置。 IOW when I change the X property in a DiagramItem the Diagram moves the item on the x-axis.
IOW 当我更改 DiagramItem 中的 X 属性时,图表会在 x 轴上移动该项目。
Note: MultiSelector is derived from ItemsControl and Selector and is only used here because I need the displayed item to be selectable.注意: MultiSelector 派生自 ItemsControl 和 Selector,仅在此处使用,因为我需要显示的项目是可选的。
Please note that I'd prefer not to use xaml if possible.请注意,如果可能,我不想使用 xaml。
In long:长:
A Diagram instance as seen by the user has these requirements:用户看到的 Diagram 实例具有以下要求:
I basically have two and possibly three classes relevant to this question.我基本上有两个甚至可能三个与这个问题相关的课程。
The Diagram.ItemsPanel aka the visual panel which displays the items should be a panel which supports absolute positioning, like the Canvas . Diagram.ItemsPanel 又名显示项目的可视面板应该是一个支持绝对定位的面板,如Canvas 。
How should I implement a class derived from MultiSelector and what resources can you point at which are relevant to this question?我应该如何实现从 MultiSelector 派生的类,以及您可以指出哪些与此问题相关的资源?
What does one have to consider when implementing a custom MultiSelector / ItemsControl?实现自定义 MultiSelector / ItemsControl 时必须考虑什么?
Resources:资源:
I've found very few resources relevant to my issue, but then again I'm not sure what I'm supposed to be looking for.我发现与我的问题相关的资源很少,但我又不确定我应该寻找什么。 I've read the source code for ListBox and ListBoxItem using Reflector but didn't find it very useful.
我已经使用 Reflector 阅读了 ListBox 和 ListBoxItem 的源代码,但没有发现它非常有用。
Other resources:其他资源:
OK, apparently this can easily be achieved by using bindings and the property framework.好的,显然这可以通过使用绑定和属性框架轻松实现。
public class Diagram : MultiSelector
{
public Diagram()
{
this.CanSelectMultipleItems = true;
// The canvas supports absolute positioning
FrameworkElementFactory panel = new FrameworkElementFactory(typeof(Canvas));
this.ItemsPanel = new ItemsPanelTemplate(panel);
// Tells the container where to position the items
this.ItemContainerStyle = new Style();
this.ItemContainerStyle.Setters.Add(new Setter(Canvas.LeftProperty, new Binding("X")));
this.ItemContainerStyle.Setters.Add(new Setter(Canvas.TopProperty, new Binding("Y")));
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
FrameworkElement contentitem = element as FrameworkElement;
Binding leftBinding = new Binding("XProperty");
leftBinding.Source = contentitem;
Binding topBinding = new Binding("YProperty");
topBinding.Source = contentitem;
contentitem.SetBinding(Canvas.LeftProperty, leftBinding);
contentitem.SetBinding(Canvas.TopProperty, topBinding);
base.PrepareContainerForItemOverride(element, item);
}
public class DiagramItem : ContentControl
{
public static readonly DependencyProperty XProperty;
public static readonly DependencyProperty YProperty;
public static readonly RoutedEvent SelectedEvent;
public static readonly RoutedEvent UnselectedEvent;
public static readonly DependencyProperty IsSelectedProperty;
public DiagramItem()
{
}
static DiagramItem()
{
XProperty = DependencyProperty.Register("XProperty", typeof(Double), typeof(DiagramItem));
YProperty = DependencyProperty.Register("YProperty", typeof(Double), typeof(DiagramItem));
SelectedEvent = MultiSelector.SelectedEvent.AddOwner(typeof(DiagramItem));
UnselectedEvent = MultiSelector.SelectedEvent.AddOwner(typeof(DiagramItem));
IsSelectedProperty = MultiSelector.IsSelectedProperty.AddOwner(typeof(DiagramItem));
}
public Double X
{
get
{
return (Double)this.GetValue(XProperty);
}
set
{
this.SetValue(XProperty, value);
}
}
public Double Y
{
get
{
return (Double)this.GetValue(YProperty);
}
set
{
this.SetValue(YProperty, value);
}
}
public Point Location
{
get
{
return new Point(X, Y);
}
set
{
this.X = value.X;
this.Y = value.Y;
}
}
}
The magic is in the proper usage of Bindings, the key was to add the contentitem as Source
.神奇之处在于正确使用 Bindings,关键是将 contentitem 添加为
Source
。 The next step is obviously to handle the selection of items, but that's another question on its own.下一步显然是处理项目的选择,但这本身就是另一个问题。
If it is any help, I wrote a code project article based on my graphing and diagramming custom control called NetworkView:如果有帮助,我根据我的图形和图表自定义控件 NetworkView 写了一篇代码项目文章:
http://www.codeproject.com/Articles/182683/NetworkView-A-WPF-custom-control-for-visualizing-a http://www.codeproject.com/Articles/182683/NetworkView-A-WPF-custom-control-for-visualizing-a
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.