• WPF:从WPF Diagram Designer Part 4学习分组、对齐、排序、序列化和常用功能


      在前面三篇文章中我们介绍了如何给图形设计器增加移动、选择、改变大小面板、缩略图、框线选择和工具箱连接等功能,本篇是这个图形设计器系列的最后一篇,将和大家一起来学习一下如何给图形设计器增加分组、对齐、排序、序列化等功能。

    WPF Diagram Designer - Part 4

    分组:Group, Ungroup

    由于WPF不允许一个对象作为多个其他元素的子对象存在,而当移动父对象时,模板也会Unload导致一些问题,所以在这个系列中对分组的实现方式是:当分组一组元素时,内部生成一个Group,这个Group内部其实也是一个DesignerItem对象,只是IsGroup=true而已,在分组时,内部的对象的ParentID都置为这个Group对象的Id

    public interface IGroupable
    {
    Guid ID {
    get; }
    Guid ParentID {
    get; set; }
    bool IsGroup { get; set; }
    }

    执行分组时的代码如下:

    private void Group_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    var items
    = from item in this.SelectionService.CurrentSelection.OfType<DesignerItem>()
    where item.ParentID == Guid.Empty
    select item;

    Rect rect
    = GetBoundingRectangle(items);

    DesignerItem groupItem
    = new DesignerItem();
    groupItem.IsGroup
    = true;
    groupItem.Width
    = rect.Width;
    groupItem.Height
    = rect.Height;
    Canvas.SetLeft(groupItem, rect.Left);
    Canvas.SetTop(groupItem, rect.Top);
    Canvas groupCanvas
    = new Canvas();
    groupItem.Content
    = groupCanvas;
    Canvas.SetZIndex(groupItem,
    this.Children.Count);
    this.Children.Add(groupItem);

    foreach (DesignerItem item in items)
    item.ParentID
    = groupItem.ID;

    this.SelectionService.SelectItem(groupItem);
    }

    当我们选择一个分组子对象时,设计器会选择这个分组以及分组的所有子对象

    internal void SelectItem(ISelectable item)
    {
    this.ClearSelection();
    this.AddToSelection(item);
    }

    internal void AddToSelection(ISelectable item)
    {
    if (item is IGroupable)
    {
    List
    <IGroupable> groupItems = GetGroupMembers(item as IGroupable);

    foreach (ISelectable groupItem in groupItems)
    {
    groupItem.IsSelected
    = true;
    CurrentSelection.Add(groupItem);
    }
    }
    else
    {
    item.IsSelected
    = true;
    CurrentSelection.Add(item);
    }
    }

    对齐:Align (Left, Right, Top, Bottom, Centered horizontal, Centered vertical)、Distribute (horizontal, vertical)

    private void AlignLeft_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    var selectedItems
    = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
    where item.ParentID == Guid.Empty
    select item;

    if (selectedItems.Count() > 1)
    {
    double left = Canvas.GetLeft(selectedItems.First());

    foreach (DesignerItem item in selectedItems)
    {
    double delta = left - Canvas.GetLeft(item);
    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
    {
    Canvas.SetLeft(di, Canvas.GetLeft(di)
    + delta);
    }
    }
    }
    }
    private void AlignHorizontalCenters_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    var selectedItems
    = from item in SelectionService.CurrentSelection.OfType<DesignerItem>()
    where item.ParentID == Guid.Empty
    select item;

    if (selectedItems.Count() > 1)
    {
    double center = Canvas.GetLeft(selectedItems.First()) + selectedItems.First().Width / 2;

    foreach (DesignerItem item in selectedItems)
    {
    double delta = center - (Canvas.GetLeft(item) + item.Width / 2);
    foreach (DesignerItem di in SelectionService.GetGroupMembers(item))
    {
    Canvas.SetLeft(di, Canvas.GetLeft(di)
    + delta);
    }
    }
    }
    }

    排序:Order (Bring forward, Bring to top, Send backward, Send to back)

    private void BringForward_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    List
    <UIElement> ordered = (from item in SelectionService.CurrentSelection
    orderby Canvas.GetZIndex(item
    as UIElement) descending
    select item
    as UIElement).ToList();

    int count = this.Children.Count;

    for (int i = 0; i < ordered.Count; i++)
    {
    int currentIndex = Canvas.GetZIndex(ordered[i]);
    int newIndex = Math.Min(count - 1 - i, currentIndex + 1);
    if (currentIndex != newIndex)
    {
    Canvas.SetZIndex(ordered[i], newIndex);
    IEnumerable
    <UIElement> it = this.Children.OfType<UIElement>().Where(item => Canvas.GetZIndex(item) == newIndex);

    foreach (UIElement elm in it)
    {
    if (elm != ordered[i])
    {
    Canvas.SetZIndex(elm, currentIndex);
    break;
    }
    }
    }
    }
    }

    序列化:Open, Save

    使用XML保存,代码如下:

    XElement serializedItems = new XElement("DesignerItems",
    from item
    in designerItems
    let contentXaml
    = XamlWriter.Save(((DesignerItem)item).Content)
    select
    new XElement("DesignerItem",
    new XElement("Left", Canvas.GetLeft(item)),
    new XElement("Top", Canvas.GetTop(item)),
    new XElement("Width", item.Width),
    new XElement("Height", item.Height),
    new XElement("ID", item.ID),
    new XElement("zIndex", Canvas.GetZIndex(item)),
    new XElement("IsGroup", item.IsGroup),
    new XElement("ParentID", item.ParentID),
    new XElement("Content", contentXaml)
    )
    );

    读取的时候需要建立Connection

    private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
    {
           ... 
    foreach (XElement connectionXML in connectionsXML)
    {
    Guid sourceID
    = new Guid(connectionXML.Element("SourceID").Value);
    Guid sinkID
    = new Guid(connectionXML.Element("SinkID").Value);

    String sourceConnectorName
    = connectionXML.Element("SourceConnectorName").Value;
    String sinkConnectorName
    = connectionXML.Element("SinkConnectorName").Value;

    Connector sourceConnector
    = GetConnector(sourceID, sourceConnectorName);
    Connector sinkConnector
    = GetConnector(sinkID, sinkConnectorName);

    Connection connection
    = new Connection(sourceConnector, sinkConnector);
    Canvas.SetZIndex(connection, Int32.Parse(connectionXML.Element(
    "zIndex").Value));
    this.Children.Add(connection);
    }
    }

    常用功能:Cut, Copy, Paste, Delete,Print

    private void Cut_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    CopyCurrentSelection();
    DeleteCurrentSelection();
    }
    private void Print_Executed(object sender, ExecutedRoutedEventArgs e)
    {
    SelectionService.ClearSelection();

    PrintDialog printDialog
    = new PrintDialog();

    if (true == printDialog.ShowDialog())
    {
    printDialog.PrintVisual(
    this, "WPF Diagram");
    }
    }

    欢迎转载,转载请注明:转载自周金根 [ http://zhoujg.cnblogs.com/ ]

  • 相关阅读:
    LR杂记-nmon+analyser监控linux系统资源
    accept函数
    android performClick使用
    #line 的作用是改变当前行数和文件名称
    C++常用排序法、随机数
    C语言运算符优先级及结合性
    如何高效把一字节的位对换, bit0和bit7,bit1和bit6,以此类推.
    NumPy
    Rational Rose、PowerDesign、Visio的一些比较
    vld,Bounds Checker,memwatch,mtrace,valgrind,debug_new几种内存泄露检测工具的比较,Valgrind Cheatsheet
  • 原文地址:https://www.cnblogs.com/zhoujg/p/1801663.html
Copyright © 2020-2023  润新知