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 " );
    }
    }

     







     本文转自 jingen_zhou 51CTO博客,原文链接:http://blog.51cto.com/zhoujg/517448,如需转载请自行联系原作者


    相关文章
    |
    2月前
    |
    XML 开发框架 前端开发
    WPF学习之基础知识篇
    WPF(Windows Presentation Foundation)具有一个复杂且强大的架构,旨在提供丰富的用户界面、图形、动画和多媒体功能。
    92 9
    |
    7月前
    |
    前端开发 C#
    WPF学习小记
    WPF学习小记
    |
    7月前
    |
    存储 算法 Java
    从零开始学习 Java:简单易懂的入门指南之IO序列化、打印流、压缩流(三十三)
    从零开始学习 Java:简单易懂的入门指南之IO序列化、打印流、压缩流(三十三)
    |
    3月前
    |
    XML 存储 JSON
    Twaver-HTML5基础学习(19)数据容器(2)_数据序列化_XML、Json
    本文介绍了Twaver HTML5中的数据序列化,包括XML和JSON格式的序列化与反序列化方法。文章通过示例代码展示了如何将DataBox中的数据序列化为XML和JSON字符串,以及如何从这些字符串中反序列化数据,重建DataBox中的对象。此外,还提到了用户自定义属性的序列化注册方法。
    50 1
    |
    28天前
    |
    前端开发 C# Windows
    在WPF程序中实现PropertyGrid功能
    【11月更文挑战第15天】PropertyGrid 是一个用户界面组件,用于直观地查看和编辑对象属性。在 WPF 中可通过组合 Expander 和 DataGrid 实现基本功能,或使用第三方库 PropertyTools 获得更强大特性,包括属性验证和类型特定编辑器。
    |
    4月前
    |
    C# 机器学习/深度学习 搜索推荐
    WPF与机器学习的完美邂逅:手把手教你打造一个具有智能推荐功能的现代桌面应用——从理论到实践的全方位指南,让你的应用瞬间变得高大上且智能无比
    【8月更文挑战第31天】本文详细介绍如何在Windows Presentation Foundation(WPF)应用中集成机器学习功能,以开发具备智能化特性的桌面应用。通过使用Microsoft的ML.NET框架,本文演示了从安装NuGet包、准备数据集、训练推荐系统模型到最终将模型集成到WPF应用中的全过程。具体示例代码展示了如何基于用户行为数据训练模型,并实现实时推荐功能。这为WPF开发者提供了宝贵的实践指导。
    55 0
    |
    4月前
    |
    开发者 C# UED
    WPF与多媒体:解锁音频视频播放新姿势——从界面设计到代码实践,全方位教你如何在WPF应用中集成流畅的多媒体功能
    【8月更文挑战第31天】本文以随笔形式介绍了如何在WPF应用中集成音频和视频播放功能。通过使用MediaElement控件,开发者能轻松创建多媒体应用程序。文章详细展示了从创建WPF项目到设计UI及实现媒体控制逻辑的过程,并提供了完整的示例代码。此外,还介绍了如何添加进度条等额外功能以增强用户体验。希望本文能为WPF开发者提供实用的技术指导与灵感。
    175 0
    |
    4月前
    |
    存储 开发者 C#
    WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
    【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
    73 0
    |
    4月前
    |
    API C# Shell
    WPF与Windows Shell完美融合:深入解析文件系统操作技巧——从基本文件管理到高级Shell功能调用,全面掌握WPF中的文件处理艺术
    【8月更文挑战第31天】Windows Presentation Foundation (WPF) 是 .NET Framework 的关键组件,用于构建 Windows 桌面应用程序。WPF 提供了丰富的功能来创建美观且功能强大的用户界面。本文通过问题解答的形式,探讨了如何在 WPF 应用中集成 Windows Shell 功能,并通过具体示例代码展示了文件系统的操作方法,包括列出目录下的所有文件、创建和删除文件、移动和复制文件以及打开文件夹或文件等。
    96 0
    |
    4月前
    |
    C# Windows 监控
    WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
    【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
    125 0