简体   繁体   English

如何在 WPF/C# 中创建自定义 MultiSelector/ItemsControl

[英]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)?

Code:代码:

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 实例具有以下要求:

  1. Has multiple DiagramItems.有多个DiagramItems。
  2. User can select multiple DiagramItems.用户可以选择多个 DiagramItems。
  3. DiagramItems can be resized, rotated and dragged anywhere on the Diagram. DiagramItems 可以调整大小、旋转和拖动到图表上的任何地方。
  4. Possible to navigate between DiagramItems using the keyboard.可以使用键盘在 DiagramItems 之间导航。

I basically have two and possibly three classes relevant to this question.我基本上有两个甚至可能三个与这个问题相关的课程。

  • Diagram extends System.Windows.Controls.Primitives.图表扩展了 System.Windows.Controls.Primitives。 MultiSelector : Selector : ItemsControl选择器:选择器:ItemsControl
  • DiagramItem extends ContentControl or some other Control DiagramItem扩展了ContentControl或其他一些控件

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.

相关问题 如何将自定义用户控件上的 ItemsControl 子项绑定到二维数组 WPF C#? - How to bind children of ItemsControl on a custom user control to a 2D array WPF C#? C#WPF ItemsControl项目移动动画 - C# WPF ItemsControl item moving animation ItemsControl儿童活动WPF C# - ItemsControl Children Events WPF C# C#数据绑定嵌套ItemsControl WPF - C# data binding nested ItemsControl wpf 在 C#/WPF 中将字典绑定到 ItemsControl - Bind Dictionary to ItemsControl in C#/WPF 如何创建自定义路由事件? WPF c# - How to create a Custom Routed Event ? WPF c# 如何在后面的代码中使用WPF C#创建带有图像的自定义MessageBox? - How to create, in code behind, a Custom MessageBox with Images in WPF C#? 如何访问ItemsControl-> ItemTemplate-> DataTemplate-> StackPanel中的按钮? WPF的C# - How to access the button inside ItemsControl->ItemTemplate->DataTemplate->StackPanel? wpf c# 打开窗口时,如何将数据模板化的文本框聚焦在窗口中 I​​temsControl 的第一个元素中? (C#, WPF) - How to focus a datatemplated TextBox in the first element of an ItemsControl in a Window, when the Window is opened? (C#, WPF) C#WPF MVVM-如何从ItemsControl / DataTemplate访问特定项目? - C# WPF MVVM - How can I access a specific item from ItemsControl/DataTemplate?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM