當你想畫一個流程圖的時候,你會發現,很多軟件要么需要秘鑰,要么需要會員,這時我就在想,可不可自己制作一款流程圖軟件呢?本文以一個簡單的小例子,簡述如何利用WPF制作屬於自己的流程圖軟件,僅供學習分享使用,如有不足之處,還請指正。
涉及知識點
本示例主要通過WPF技術進行開發,涉及知識點如下:
- WPF繪圖,如矩形,直線等功能的相關圖形技術。
- Thumb控件,本控件可以由用戶自由拖動,示例中所用到的可移動的圖形控件,都繼承於Thumb控件。
Thumb控件簡介
Thumb控件是WPF提供的用於用戶拖動的控件。默認情況下,Thumb控件並不在工具箱中,需要手動添加,如下所示:
工具箱-->常規-->右鍵-->選擇項,然后打開【選擇工具箱對話框】,如下所示:
在選【擇工具箱項】頁面,打開WPF組件-->選擇Thumb-->點擊確定按鈕,如下所示:
添加成功后,即可從工具箱拖動到頁面,如下所示:
關於Thumb控件的簡介,如下所示:
1 namespace System.Windows.Controls.Primitives 2 { 3 // 4 // 摘要: 5 // 表示可以由用戶拖動的控件。 6 [DefaultEvent("DragDelta")] 7 [Localizability(LocalizationCategory.NeverLocalize)] 8 public class Thumb : Control 9 { 10 // 11 // 摘要: 12 // 標識 System.Windows.Controls.Primitives.Thumb.DragStarted 路由事件。 13 // 14 // 返回結果: 15 // System.Windows.Controls.Primitives.Thumb.DragStarted 路由事件的標識符。 16 public static readonly RoutedEvent DragStartedEvent; 17 // 18 // 摘要: 19 // 標識 System.Windows.Controls.Primitives.Thumb.DragDelta 路由事件。 20 // 21 // 返回結果: 22 // System.Windows.Controls.Primitives.Thumb.DragDelta 路由事件的標識符。 23 public static readonly RoutedEvent DragDeltaEvent; 24 // 25 // 摘要: 26 // 標識 System.Windows.Controls.Primitives.Thumb.DragCompleted 路由事件。 27 // 28 // 返回結果: 29 // System.Windows.Controls.Primitives.Thumb.DragCompleted 路由事件的標識符。 30 public static readonly RoutedEvent DragCompletedEvent; 31 // 32 // 摘要: 33 // 標識 System.Windows.Controls.Primitives.Thumb.IsDragging 依賴屬性。 34 // 35 // 返回結果: 36 // System.Windows.Controls.Primitives.Thumb.IsDragging 依賴項屬性的標識符。 37 public static readonly DependencyProperty IsDraggingProperty; 38 39 // 40 // 摘要: 41 // 初始化 System.Windows.Controls.Primitives.Thumb 類的新實例。 42 public Thumb(); 43 44 // 45 // 摘要: 46 // 獲取是否 System.Windows.Controls.Primitives.Thumb 控件具有邏輯焦點和捕獲鼠標並按下鼠標左鍵。 47 // 48 // 返回結果: 49 // true 如果 System.Windows.Controls.Primitives.Thumb 控件具有焦點且鼠標捕獲; 否則為 false。 默認值為 50 // false。 51 [Bindable(true)] 52 [Browsable(false)] 53 [Category("Appearance")] 54 public bool IsDragging { get; protected set; } 55 56 // 57 // 摘要: 58 // 發生時 System.Windows.Controls.Primitives.Thumb 控件接收邏輯焦點和鼠標捕獲。 59 [Category("Behavior")] 60 public event DragStartedEventHandler DragStarted; 61 // 62 // 摘要: 63 // 根據鼠標位置更改時出現了一個或多個次 System.Windows.Controls.Primitives.Thumb 控件具有邏輯焦點和鼠標捕獲。 64 [Category("Behavior")] 65 public event DragDeltaEventHandler DragDelta; 66 // 67 // 摘要: 68 // 發生時 System.Windows.Controls.Primitives.Thumb 控件失去鼠標捕獲。 69 [Category("Behavior")] 70 public event DragCompletedEventHandler DragCompleted; 71 72 // 73 // 摘要: 74 // 取消的拖動操作 System.Windows.Controls.Primitives.Thumb。 75 public void CancelDrag(); 76 // 77 // 摘要: 78 // 創建 System.Windows.Automation.Peers.AutomationPeer 為 System.Windows.Controls.Primitives.Thumb 79 // 控件。 80 // 81 // 返回結果: 82 // 一個 System.Windows.Automation.Peers.ThumbAutomationPeer 為 System.Windows.Controls.Primitives.Thumb 83 // 控件。 84 protected override AutomationPeer OnCreateAutomationPeer(); 85 // 86 // 摘要: 87 // 響應 System.Windows.Controls.Primitives.Thumb.IsDragging 屬性值的更改。 88 // 89 // 參數: 90 // e: 91 // 事件數據。 92 protected virtual void OnDraggingChanged(DependencyPropertyChangedEventArgs e); 93 // 94 // 摘要: 95 // 提供類處理 System.Windows.ContentElement.MouseLeftButtonDown 事件。 96 // 97 // 參數: 98 // e: 99 // 事件數據。 100 protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e); 101 // 102 // 摘要: 103 // 提供類處理 System.Windows.ContentElement.MouseLeftButtonUp 事件。 104 // 105 // 參數: 106 // e: 107 // 事件數據。 108 protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e); 109 // 110 // 摘要: 111 // 提供類處理 System.Windows.UIElement.MouseMove 事件。 112 // 113 // 參數: 114 // e: 115 // 事件數據。 116 protected override void OnMouseMove(MouseEventArgs e); 117 } 118 }
通過上述摘要簡介,發現Thumb控件提供了三個事件,分別是:
- 拖動開始事件:public event DragStartedEventHandler DragStarted;
- 拖動進行事件:public event DragDeltaEventHandler DragDelta;
- 拖動完成事件:public event DragCompletedEventHandler DragCompleted;
Thumb控件示例
首先在窗口頁面上添加一個Thumb控件,然后分別添加三個事件,如下所示:
1 <Window x:Class="DemoVisio.MainWindow1" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:DemoVisio" 7 mc:Ignorable="d" 8 Title="Thumb示例" Height="450" Width="800"> 9 <Canvas> 10 <Thumb x:Name="thumb" Canvas.Left="0" Canvas.Top="0" Height="100" Width="100" DragStarted="thumb_DragStarted" DragDelta="thumb_DragDelta" DragCompleted="thumb_DragCompleted"/> 11 12 </Canvas> 13 </Window>
然后在三個事件中添加代碼,終點是DragDelta事件,如下所示:
1 namespace DemoVisio 2 { 3 /// <summary> 4 /// MainWindow1.xaml 的交互邏輯 5 /// </summary> 6 public partial class MainWindow1 : Window 7 { 8 public MainWindow1() 9 { 10 InitializeComponent(); 11 } 12 13 private void thumb_DragStarted(object sender, DragStartedEventArgs e) 14 { 15 //開始拖動 16 } 17 18 /// <summary> 19 /// 拖動 20 /// </summary> 21 /// <param name="sender"></param> 22 /// <param name="e"></param> 23 private void thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) 24 { 25 Thumb myThumb = (Thumb)sender; 26 double nTop = Canvas.GetTop(myThumb) + e.VerticalChange; 27 double nLeft = Canvas.GetLeft(myThumb) + e.HorizontalChange; 28 Canvas.SetTop(myThumb, nTop); 29 Canvas.SetLeft(myThumb, nLeft); 30 } 31 32 private void thumb_DragCompleted(object sender, DragCompletedEventArgs e) 33 { 34 //拖動完成 35 } 36 } 37 }
注意,在XAML中,Thumb一定是在Canvas布局容器中,且一定要設置【Canvas.Left="0" Canvas.Top="0"】兩個屬性,如果不設置,在值為NaN,無法進行拖動。
默認情況下,Thumb控件就是一個丑丑的方塊,如下所示:
拖動控件基類
在本示例中,流程圖需要多種控件(如:圓形,矩形,線等),不能每一個控件都去實現那三個事件【DragStarted,DragDelta,DragCompleted】,所以需要定義一個基類ThumbControl,統一實現方案。具體如下所示:
1 namespace DemoVisio 2 { 3 public class ThumbControl : Thumb 4 { 5 /// <summary> 6 /// 是否可以輸入 7 /// </summary> 8 public bool IsEnableInput { get { return (bool)GetValue(IsEnableInputProperty); } set {SetValue( IsEnableInputProperty, value); } } 9 10 /// <summary> 11 /// 依賴屬性 12 /// </summary> 13 public static readonly DependencyProperty IsEnableInputProperty = DependencyProperty.Register("IsEnableInput",typeof(bool),typeof(ThumbControl)); 14 15 public ThumbControl():base() { 16 this.DragStarted += ThumbControl_DragStarted; 17 this.DragDelta += ThumbControl_DragDelta; 18 this.DragCompleted += ThumbControl_DragCompleted; 19 this.MouseDoubleClick += ThumbControl_MouseDoubleClick; 20 this.IsKeyboardFocusedChanged += ThumbControl_IsKeyboardFocusedChanged; 21 } 22 23 public void SetIsEnableInput(bool flag) 24 { 25 this.IsEnableInput = flag; 26 } 27 28 /// <summary> 29 /// 是否具有鍵盤焦點 30 /// </summary> 31 /// <param name="sender"></param> 32 /// <param name="e"></param> 33 private void ThumbControl_IsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e) 34 { 35 36 this.IsEnableInput = this.IsKeyboardFocused; 37 38 } 39 40 /// <summary> 41 /// 雙擊事件 42 /// </summary> 43 /// <param name="sender"></param> 44 /// <param name="e"></param> 45 private void ThumbControl_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) 46 { 47 this.IsEnableInput = true; 48 } 49 50 private void ThumbControl_DragStarted(object sender, DragStartedEventArgs e) 51 { 52 //開始移動 53 } 54 55 private void ThumbControl_DragDelta(object sender, DragDeltaEventArgs e) 56 { 57 Thumb myThumb = (Thumb)sender; 58 double nTop = Canvas.GetTop(myThumb) + e.VerticalChange; 59 double nLeft = Canvas.GetLeft(myThumb) + e.HorizontalChange; 60 Canvas.SetTop(myThumb, nTop); 61 Canvas.SetLeft(myThumb, nLeft); 62 } 63 64 private void ThumbControl_DragCompleted(object sender, DragCompletedEventArgs e) 65 { 66 //移動結束 67 } 68 } 69 }
具體圖形控件
封裝了基類以后,其他控件可以在此基礎上進行使用,通過ControlTemplate展現不同的形態,如下所示:
1. 矩形方塊
在流程圖中,矩形方塊一般表示過程,實現代碼如下所示:
1 <UserControl x:Class="DemoVisio.SquareControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:DemoVisio" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <Canvas> 10 <local:ThumbControl x:Name="s" BorderThickness="1" Canvas.Top ="0" Canvas.Left="0" Width="100" Height="60" Background="AliceBlue"> 11 <Thumb.Template> 12 <ControlTemplate> 13 <Border Width="{Binding ElementName=s, Path=Width}" Height="{Binding ElementName=s, Path=Height}" BorderBrush="Black" Background="{Binding ElementName=s, Path=Background}" BorderThickness="{Binding ElementName=s, Path=BorderThickness}" Padding="2"> 14 <TextBox Background="{Binding ElementName=s, Path=Background}" BorderThickness="0" VerticalAlignment="Center" IsEnabled="{Binding ElementName=s, Path=IsEnableInput}" MinLines="3" MaxLines="3" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"></TextBox> 15 </Border> 16 17 </ControlTemplate> 18 </Thumb.Template> 19 </local:ThumbControl> 20 </Canvas> 21 </UserControl>
2. 圓形圖
在流程圖中,圓形一般表示開始和結束,如下所示:
1 <UserControl x:Class="DemoVisio.CircleControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:DemoVisio" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <Canvas> 10 <local:ThumbControl x:Name="s" Canvas.Top ="0" Canvas.Left="0" Width="60" Height="60" Background="AliceBlue" BorderThickness="1" > 11 <local:ThumbControl.Template> 12 <ControlTemplate> 13 <Grid> 14 <Border Width="{Binding Width}" Height="{Binding Height}" CornerRadius="30" BorderThickness="{Binding ElementName=s, Path=BorderThickness}" BorderBrush="Black" Background="{Binding ElementName=s, Path=Background}"> 15 <TextBox VerticalAlignment="Center" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" MaxLines="2" MinLines="1" Width="{Binding Width}" Height="{Binding Height}" Background="{Binding ElementName=s, Path=Background}" BorderThickness="0" IsEnabled="{Binding ElementName=s, Path=IsEnableInput}"></TextBox> 16 </Border> 17 </Grid> 18 </ControlTemplate> 19 </local:ThumbControl.Template> 20 21 </local:ThumbControl> 22 </Canvas> 23 </UserControl>
3. 菱形圖
在流程圖中,菱形一般表示選擇,表達程序中的布爾值。如下所示:
1 <UserControl x:Class="DemoVisio.RhombusControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:DemoVisio" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <Canvas> 10 <local:ThumbControl x:Name="s" Canvas.Left="0" Canvas.Top="0" Width="80" Height="80" Background="AliceBlue" BorderThickness="1" > 11 <Thumb.Template> 12 <ControlTemplate> 13 <Border Width="{Binding ElementName=s, Path=Width}" Height="{Binding ElementName=s, Path=Height}" BorderBrush="Black" Background="{Binding ElementName=s, Path=Background}" BorderThickness="{Binding ElementName=s, Path=BorderThickness}" Padding="1"> 14 <TextBox Background="{Binding ElementName=s, Path=Background}" BorderThickness="0" VerticalAlignment="Center" IsEnabled="{Binding ElementName=s, Path=IsEnableInput}" Width="50" Height="50" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"> 15 <TextBox.RenderTransform> 16 <RotateTransform CenterX="25" CenterY="25" Angle="-45" ></RotateTransform> 17 </TextBox.RenderTransform> 18 </TextBox> 19 </Border> 20 21 </ControlTemplate> 22 23 </Thumb.Template> 24 <Thumb.RenderTransform> 25 26 <TransformGroup> 27 <RotateTransform CenterX="40" CenterY="40" Angle="45"></RotateTransform> 28 <ScaleTransform CenterX="40" CenterY="40" ScaleX="0.8"></ScaleTransform> 29 <TranslateTransform X="10" Y="15"></TranslateTransform> 30 </TransformGroup> 31 </Thumb.RenderTransform> 32 </local:ThumbControl> 33 </Canvas> 34 </UserControl>
4. 直線
在流程圖中,直線一般表示兩個過程之間的連接,如下所示:
1 <UserControl x:Class="DemoVisio.LineArrowControl" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:DemoVisio" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <Canvas> 10 <local:ThumbControl x:Name="s" Canvas.Left="0" Canvas.Top="0" Width="100" Height="100" Background="AliceBlue" IsEnableInput="False"> 11 <local:ThumbControl.Template> 12 <ControlTemplate> 13 <Grid> 14 15 <Line X1="0" Y1="0" X2="0" Y2="100" Stroke="Black" StrokeThickness="2" HorizontalAlignment="Center" > 16 17 </Line> 18 <Line X1="-5" Y1="90" X2="1" Y2="100" StrokeThickness="2" Stroke="Black" HorizontalAlignment="Center"></Line> 19 <Line X1="8" Y1="90" X2="3" Y2="100" StrokeThickness="2" Stroke="Black" HorizontalAlignment="Center"></Line> 20 <TextBox VerticalAlignment="Center" Height="30" HorizontalContentAlignment="Center" BorderThickness="0" VerticalContentAlignment="Center" MinLines="1" MaxLines="2" IsEnabled="{Binding ElementName=s, Path=IsEnableInput}" Opacity="0" Visibility="{Binding ElementName=s, Path=IsEnableInput}"></TextBox> 21 </Grid> 22 </ControlTemplate> 23 </local:ThumbControl.Template> 24 25 </local:ThumbControl> 26 </Canvas> 27 </UserControl>
主窗體
主窗體主要用於繪制流程圖,分為左右兩部分,左邊是控件列表,右邊是布局容器,如下所示:
1 <Window x:Class="DemoVisio.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:DemoVisio" 7 mc:Ignorable="d" 8 Title="流程圖" Height="800" Width="800" MouseDown="Window_MouseDown" Loaded="Window_Loaded"> 9 <Window.Resources> 10 <Style TargetType="TextBlock"> 11 <Setter Property="HorizontalAlignment" Value="Center"></Setter> 12 </Style> 13 </Window.Resources> 14 <Grid ShowGridLines="True"> 15 <Grid.ColumnDefinitions> 16 <ColumnDefinition Width="Auto" MinWidth="150"></ColumnDefinition> 17 <ColumnDefinition Width="*"></ColumnDefinition> 18 </Grid.ColumnDefinitions> 19 <StackPanel Grid.Column="0" x:Name="left" Orientation="Vertical"> 20 <Rectangle Width="80" Height="50" Fill="AliceBlue" Stroke="Black" Margin="5" x:Name="rectangle" MouseLeftButtonDown="rectangle_MouseLeftButtonDown"></Rectangle> 21 <TextBlock Text="矩形"></TextBlock> 22 <Rectangle Width="100" Height="100" Fill="AliceBlue" Stroke="Black" Margin="5" x:Name="rhombus" MouseLeftButtonDown="rhombus_MouseLeftButtonDown"> 23 <Rectangle.RenderTransform> 24 <TransformGroup> 25 <RotateTransform CenterX="50" CenterY="50" Angle="45"></RotateTransform> 26 <ScaleTransform CenterX="50" CenterY="50" ScaleX="0.5" ScaleY="0.7"></ScaleTransform> 27 </TransformGroup> 28 </Rectangle.RenderTransform> 29 </Rectangle> 30 <TextBlock Text="菱形" Margin="0,5"></TextBlock> 31 <Line X1="0" Y1="0" X2="0" Y2="80" Stroke="Black" StrokeThickness="2" HorizontalAlignment="Center" Margin="5" x:Name="line" MouseLeftButtonDown="line_MouseLeftButtonDown" ></Line> 32 <TextBlock Text="直線"></TextBlock> 33 <Ellipse Width="60" Height="60" Fill="AliceBlue" Stroke="Black" Margin="5" x:Name="circle" MouseLeftButtonDown="circle_MouseLeftButtonDown"></Ellipse> 34 <TextBlock Text="圓形"></TextBlock> 35 </StackPanel> 36 <Canvas Grid.Column="1" x:Name="right" > 37 38 39 </Canvas> 40 </Grid> 41 </Window>
當點擊左邊基礎控件時,在右邊容器中,生成新的控件。如下所示:
1 namespace DemoVisio 2 { 3 /// <summary> 4 /// MainWindow.xaml 的交互邏輯 5 /// </summary> 6 public partial class MainWindow : Window 7 { 8 /// <summary> 9 /// 控件列表 10 /// </summary> 11 private List<Control> rightControls = new List<Control>(); 12 13 public MainWindow() 14 { 15 InitializeComponent(); 16 } 17 18 private void Window_MouseDown(object sender, MouseButtonEventArgs e) 19 { 20 //this.Focus(); 21 //this.one.SetIsEnableInput(false); 22 foreach (var control in this.right.Children) { 23 var thumb = control as ThumbControl; 24 if (thumb != null) 25 { 26 thumb.SetIsEnableInput(false); 27 } 28 } 29 } 30 31 private void Window_Loaded(object sender, RoutedEventArgs e) 32 { 33 var width = this.right.ActualWidth; 34 var height = this.right.ActualHeight; 35 36 int x = 0; 37 int y = 0; 38 //橫線 39 while (y<height) { 40 Line line = new Line(); 41 line.X1 = x; 42 line.Y1 = y; 43 line.X2 = width; 44 line.Y2 = y; 45 line.Stroke = Brushes.LightGray; 46 line.StrokeThickness = 1; 47 this.right.Children.Add(line); 48 y = y + 10; 49 } 50 //重新初始化值 51 x = 0; 52 y = 0; 53 //豎線 54 while (x < width) { 55 Line line = new Line(); 56 line.X1 = x; 57 line.Y1 = y; 58 line.X2 = x; 59 line.Y2 = height; 60 line.Stroke = Brushes.LightGray; 61 line.StrokeThickness = 1; 62 this.right.Children.Add(line); 63 x = x + 10; 64 } 65 } 66 67 private void rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 68 { 69 SquareControl squareControl = new SquareControl(); 70 this.right.Children.Add(squareControl); 71 } 72 73 private void rhombus_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 74 { 75 RhombusControl rhombusControl = new RhombusControl(); 76 this.right.Children.Add(rhombusControl); 77 } 78 79 private void line_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 80 { 81 LineArrowControl lineArrowControl = new LineArrowControl(); 82 this.right.Children.Add(lineArrowControl); 83 } 84 85 private void circle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 86 { 87 CircleControl circleControl = new CircleControl(); 88 this.right.Children.Add(circleControl); 89 } 90 } 91 }
示例截圖
示例基礎截圖如下所示:
備注
以上示例只是基礎,功能並不完善,旨在拋磚引玉,共同學習,一起進步,希望可以對大家有所啟發。
賀新郎·九日
少年自負凌雲筆。到而今、春華落盡,滿懷蕭瑟。