Entering WPF's development of Visio flowchart software

Entering WPF's development of Visio flowchart software

When you want to draw a flowchart, you will find that many software either requires secret keys or members. At this time, I wonder if I can make my own flowchart software?

最后更新 2/27/2022 3:04 PM
Alan.hsiang
预计阅读 16 分钟
分类
WPF
标签
.NET WPF Visio

When you want to draw a flowchart, you will find that many software either requires secret keys or members. At this time, I wonder if I can make my own flowchart software? This article uses a simple small example to briefly describe how to use WPF to make your own flowchart software. It is only for learning and sharing. Please correct if there are any shortcomings.

Knowledge points involved

This example is mainly developed using WPF technology, and the knowledge points involved are as follows:

  1. WPF drawing, related graphics technology for functions such as rectangles, straight lines, etc.
  2. Thumb control, which can be freely dragged by the user. The movable graphical controls used in the example all inherit the Thumb control.

Introduction to Thumb Control

The Thumb control is a control provided by WPF for user dragging. By default, the Thumb control is not in the toolbox and needs to be added manually, as shown below:

Toolbox--> General--> Right-click--> Select items, and then open the [Select Toolbox Dialog Box], as shown below:

On the Select Toolbox Item page, open WPF Component--> Select Thumb--> Click the OK button, as follows:

After the addition is successful, you can drag it from the toolbox to the page, as follows:

A brief introduction to the Thumb control is as follows:

namespace System.Windows.Controls.Primitives
{
    //
    // 摘要:
    //     表示可以由用户拖动的控件。
    [DefaultEvent("DragDelta")]
    [Localizability(LocalizationCategory.NeverLocalize)]
    public class Thumb : Control
    {
        //
        // 摘要:
        //     标识 System.Windows.Controls.Primitives.Thumb.DragStarted 路由事件。
        //
        // 返回结果:
        //     System.Windows.Controls.Primitives.Thumb.DragStarted 路由事件的标识符。
        public static readonly RoutedEvent DragStartedEvent;
        //
        // 摘要:
        //     标识 System.Windows.Controls.Primitives.Thumb.DragDelta 路由事件。
        //
        // 返回结果:
        //     System.Windows.Controls.Primitives.Thumb.DragDelta 路由事件的标识符。
        public static readonly RoutedEvent DragDeltaEvent;
        //
        // 摘要:
        //     标识 System.Windows.Controls.Primitives.Thumb.DragCompleted 路由事件。
        //
        // 返回结果:
        //     System.Windows.Controls.Primitives.Thumb.DragCompleted 路由事件的标识符。
        public static readonly RoutedEvent DragCompletedEvent;
        //
        // 摘要:
        //     标识 System.Windows.Controls.Primitives.Thumb.IsDragging 依赖属性。
        //
        // 返回结果:
        //     System.Windows.Controls.Primitives.Thumb.IsDragging 依赖项属性的标识符。
        public static readonly DependencyProperty IsDraggingProperty;

        //
        // 摘要:
        //     初始化 System.Windows.Controls.Primitives.Thumb 类的新实例。
        public Thumb();

        //
        // 摘要:
        //     获取是否 System.Windows.Controls.Primitives.Thumb 控件具有逻辑焦点和捕获鼠标并按下鼠标左键。
        //
        // 返回结果:
        //     true 如果 System.Windows.Controls.Primitives.Thumb 控件具有焦点且鼠标捕获; 否则为 false。 默认值为
        //     false。
        [Bindable(true)]
        [Browsable(false)]
        [Category("Appearance")]
        public bool IsDragging { get; protected set; }

        //
        // 摘要:
        //     发生时 System.Windows.Controls.Primitives.Thumb 控件接收逻辑焦点和鼠标捕获。
        [Category("Behavior")]
        public event DragStartedEventHandler DragStarted;
        //
        // 摘要:
        //     根据鼠标位置更改时出现了一个或多个次 System.Windows.Controls.Primitives.Thumb 控件具有逻辑焦点和鼠标捕获。
        [Category("Behavior")]
        public event DragDeltaEventHandler DragDelta;
        //
        // 摘要:
        //     发生时 System.Windows.Controls.Primitives.Thumb 控件失去鼠标捕获。
        [Category("Behavior")]
        public event DragCompletedEventHandler DragCompleted;

        //
        // 摘要:
        //     取消的拖动操作 System.Windows.Controls.Primitives.Thumb。
        public void CancelDrag();
        //
        // 摘要:
        //     创建 System.Windows.Automation.Peers.AutomationPeer 为 System.Windows.Controls.Primitives.Thumb
        //     控件。
        //
        // 返回结果:
        //     一个 System.Windows.Automation.Peers.ThumbAutomationPeer 为 System.Windows.Controls.Primitives.Thumb
        //     控件。
        protected override AutomationPeer OnCreateAutomationPeer();
        //
        // 摘要:
        //     响应 System.Windows.Controls.Primitives.Thumb.IsDragging 属性值的更改。
        //
        // 参数:
        //   e:
        //     事件数据。
        protected virtual void OnDraggingChanged(DependencyPropertyChangedEventArgs e);
        //
        // 摘要:
        //     提供类处理 System.Windows.ContentElement.MouseLeftButtonDown 事件。
        //
        // 参数:
        //   e:
        //     事件数据。
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e);
        //
        // 摘要:
        //     提供类处理 System.Windows.ContentElement.MouseLeftButtonUp 事件。
        //
        // 参数:
        //   e:
        //     事件数据。
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e);
        //
        // 摘要:
        //     提供类处理 System.Windows.UIElement.MouseMove 事件。
        //
        // 参数:
        //   e:
        //     事件数据。
        protected override void OnMouseMove(MouseEventArgs e);
    }
}

Through the above summary introduction, it is found that the Thumb control provides three events, namely:

  • Drag start event: public event DragStartedEventHandler DragStarted;
  • Drag to perform an event: public event DragDeltaEventHandler DragDelta;
  • Drag completion event: public event DragCompleted EventHandler DragCompleted;

Thumb control example

First add a Thumb control on the window page, and then add three events respectively, as follows:

<Window
  x:Class="DemoVisio.MainWindow1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:DemoVisio"
  mc:Ignorable="d"
  Title="Thumb示例"
  Height="450"
  Width="800"
>
  <canvas>
    <Thumb
      x:Name="thumb"
      Canvas.Left="0"
      Canvas.Top="0"
      Height="100"
      Width="100"
      DragStarted="thumb_DragStarted"
      DragDelta="thumb_DragDelta"
      DragCompleted="thumb_DragCompleted"
    />
  </canvas>
</Window>

Then add code to three events, ending with the DragDelta event, as follows:

namespace DemoVisio
{
    /// <summary>
    /// MainWindow1.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow1 : Window
    {
        public MainWindow1()
        {
            InitializeComponent();
        }

        private void thumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            //开始拖动
        }

        /// <summary>
        /// 拖动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            Thumb myThumb = (Thumb)sender;
            double nTop = Canvas.GetTop(myThumb) + e.VerticalChange;
            double nLeft = Canvas.GetLeft(myThumb) + e.HorizontalChange;
            Canvas.SetTop(myThumb, nTop);
            Canvas.SetLeft(myThumb, nLeft);
        }

        private void thumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            //拖动完成
        }
    }
}

Note that in XAML, Thumb must be in the Canvas layout container, and the two properties [Canvas.Left="0" Canvas.Top="0"] must be set. If you do not set it, you cannot drag it when the value is NaN.

By default, the Thumb control is an ugly square, as follows:

Drag control base class

In this example, the flowchart requires multiple controls (such as circles, rectangles, lines, etc.), and each control cannot implement the three events [DragStarted, DragDelta, DragCompleted], so a base class ThumbControl needs to be defined to unify the implementation plan. The details are as follows:

namespace DemoVisio
{
    public class ThumbControl : Thumb
    {
        /// <summary>
        /// 是否可以输入
        /// </summary>
        public bool IsEnableInput { get { return (bool)GetValue(IsEnableInputProperty); } set {SetValue( IsEnableInputProperty, value); } }

        /// <summary>
        /// 依赖属性
        /// </summary>
        public static readonly DependencyProperty IsEnableInputProperty = DependencyProperty.Register("IsEnableInput",typeof(bool),typeof(ThumbControl));

        public ThumbControl():base() {
            this.DragStarted += ThumbControl_DragStarted;
            this.DragDelta += ThumbControl_DragDelta;
            this.DragCompleted += ThumbControl_DragCompleted;
            this.MouseDoubleClick += ThumbControl_MouseDoubleClick;
            this.IsKeyboardFocusedChanged += ThumbControl_IsKeyboardFocusedChanged;
        }

        public void SetIsEnableInput(bool flag)
        {
            this.IsEnableInput = flag;
        }

        /// <summary>
        /// 是否具有键盘焦点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ThumbControl_IsKeyboardFocusedChanged(object sender, DependencyPropertyChangedEventArgs e)
        {

            this.IsEnableInput = this.IsKeyboardFocused;

        }

        /// <summary>
        /// 双击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ThumbControl_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            this.IsEnableInput = true;
        }

        private void ThumbControl_DragStarted(object sender, DragStartedEventArgs e)
        {
            //开始移动
        }

        private void ThumbControl_DragDelta(object sender, DragDeltaEventArgs e)
        {
            Thumb myThumb = (Thumb)sender;
            double nTop = Canvas.GetTop(myThumb) + e.VerticalChange;
            double nLeft = Canvas.GetLeft(myThumb) + e.HorizontalChange;
            Canvas.SetTop(myThumb, nTop);
            Canvas.SetLeft(myThumb, nLeft);
        }

        private void ThumbControl_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            //移动结束
        }
    }
}

Specific graphical controls

After encapsulating the base class, other controls can be used on this basis, showing different forms through ControlTemplate, as shown below:

  1. rectangular dice

In the flowchart, rectangular squares generally represent the process, and the implementation code is as follows:

<UserControl
  x:Class="DemoVisio.SquareControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:DemoVisio"
  mc:Ignorable="d"
  d:DesignHeight="450"
  d:DesignWidth="800"
>
  <canvas>
    <local:ThumbControl
      x:Name="s"
      BorderThickness="1"
      Canvas.Top="0"
      Canvas.Left="0"
      Width="100"
      Height="60"
      Background="AliceBlue"
    >
      <Thumb.Template>
        <ControlTemplate>
          <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"
          >
            <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>
          </Border>
        </ControlTemplate>
      </Thumb.Template>
    </local:ThumbControl>
  </canvas>
</UserControl>
  1. circular chart

In a flowchart, circles generally represent the beginning and end, as follows:

<UserControl
  x:Class="DemoVisio.CircleControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:DemoVisio"
  mc:Ignorable="d"
  d:DesignHeight="450"
  d:DesignWidth="800"
>
  <canvas>
    <local:ThumbControl
      x:Name="s"
      Canvas.Top="0"
      Canvas.Left="0"
      Width="60"
      Height="60"
      Background="AliceBlue"
      BorderThickness="1"
    >
      <local:ThumbControl.Template>
        <ControlTemplate>
          <Grid>
            <Border
              Width="{Binding Width}"
              Height="{Binding Height}"
              CornerRadius="30"
              BorderThickness="{Binding ElementName=s, Path=BorderThickness}"
              BorderBrush="Black"
              Background="{Binding ElementName=s, Path=Background}"
            >
              <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>
            </Border>
          </Grid>
        </ControlTemplate>
      </local:ThumbControl.Template>
    </local:ThumbControl>
  </canvas>
</UserControl>
  1. diamond diagram

In flow charts, diamonds generally represent choices and express boolean values in the program. As follows:

<UserControl
  x:Class="DemoVisio.RhombusControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:DemoVisio"
  mc:Ignorable="d"
  d:DesignHeight="450"
  d:DesignWidth="800"
>
  <canvas>
    <local:ThumbControl
      x:Name="s"
      Canvas.Left="0"
      Canvas.Top="0"
      Width="80"
      Height="80"
      Background="AliceBlue"
      BorderThickness="1"
    >
      <Thumb.Template>
        <ControlTemplate>
          <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"
          >
            <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"
            >
              <TextBox.RenderTransform>
                <RotateTransform
                  CenterX="25"
                  CenterY="25"
                  Angle="-45"
                ></RotateTransform>
              </TextBox.RenderTransform>
            </TextBox>
          </Border>
        </ControlTemplate>
      </Thumb.Template>
      <Thumb.RenderTransform>
        <TransformGroup>
          <RotateTransform
            CenterX="40"
            CenterY="40"
            Angle="45"
          ></RotateTransform>
          <ScaleTransform
            CenterX="40"
            CenterY="40"
            ScaleX="0.8"
          ></ScaleTransform>
          <TranslateTransform X="10" Y="15"></TranslateTransform>
        </TransformGroup>
      </Thumb.RenderTransform>
    </local:ThumbControl>
  </canvas>
</UserControl>
  1. straight line

In a flowchart, a straight line generally represents the connection between two processes, as follows:

<UserControl
  x:Class="DemoVisio.LineArrowControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:local="clr-namespace:DemoVisio"
  mc:Ignorable="d"
  d:DesignHeight="450"
  d:DesignWidth="800"
>
  <canvas>
    <local:ThumbControl
      x:Name="s"
      Canvas.Left="0"
      Canvas.Top="0"
      Width="100"
      Height="100"
      Background="AliceBlue"
      IsEnableInput="False"
    >
      <local:ThumbControl.Template>
        <ControlTemplate>
          <Grid>
            <Line
              X1="0"
              Y1="0"
              X2="0"
              Y2="100"
              Stroke="Black"
              StrokeThickness="2"
              HorizontalAlignment="Center"
            >
            </Line>
            <Line
              X1="-5"
              Y1="90"
              X2="1"
              Y2="100"
              StrokeThickness="2"
              Stroke="Black"
              HorizontalAlignment="Center"
            ></Line>
            <Line
              X1="8"
              Y1="90"
              X2="3"
              Y2="100"
              StrokeThickness="2"
              Stroke="Black"
              HorizontalAlignment="Center"
            ></Line>
            <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>
          </Grid>
        </ControlTemplate>
      </local:ThumbControl.Template>
    </local:ThumbControl>
  </canvas>
</UserControl>

main form

The main form is mainly used to draw flow charts. It is divided into two parts: the control list on the left and the layout container on the right, as shown below:

<Window
  x:Class="DemoVisio.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:DemoVisio"
  mc:Ignorable="d"
  Title="流程图"
  Height="800"
  Width="800"
  MouseDown="Window_MouseDown"
  Loaded="Window_Loaded"
>
  <Window.Resources>
    <style TargetType="TextBlock">
      <Setter Property="HorizontalAlignment" Value="Center"></Setter>
    </style>
  </Window.Resources>
  <Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" MinWidth="150"></ColumnDefinition>
      <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0" x:Name="left" Orientation="Vertical">
      <Rectangle
        Width="80"
        Height="50"
        Fill="AliceBlue"
        Stroke="Black"
        Margin="5"
        x:Name="rectangle"
        MouseLeftButtonDown="rectangle_MouseLeftButtonDown"
      ></Rectangle>
      <TextBlock Text="矩形"></TextBlock>
      <Rectangle
        Width="100"
        Height="100"
        Fill="AliceBlue"
        Stroke="Black"
        Margin="5"
        x:Name="rhombus"
        MouseLeftButtonDown="rhombus_MouseLeftButtonDown"
      >
        <Rectangle.RenderTransform>
          <TransformGroup>
            <RotateTransform
              CenterX="50"
              CenterY="50"
              Angle="45"
            ></RotateTransform>
            <ScaleTransform
              CenterX="50"
              CenterY="50"
              ScaleX="0.5"
              ScaleY="0.7"
            ></ScaleTransform>
          </TransformGroup>
        </Rectangle.RenderTransform>
      </Rectangle>
      <TextBlock Text="菱形" Margin="0,5"></TextBlock>
      <Line
        X1="0"
        Y1="0"
        X2="0"
        Y2="80"
        Stroke="Black"
        StrokeThickness="2"
        HorizontalAlignment="Center"
        Margin="5"
        x:Name="line"
        MouseLeftButtonDown="line_MouseLeftButtonDown"
      ></Line>
      <TextBlock Text="直线"></TextBlock>
      <Ellipse
        Width="60"
        Height="60"
        Fill="AliceBlue"
        Stroke="Black"
        Margin="5"
        x:Name="circle"
        MouseLeftButtonDown="circle_MouseLeftButtonDown"
      ></Ellipse>
      <TextBlock Text="圆形"></TextBlock>
    </StackPanel>
    <canvas Grid.Column="1" x:Name="right"> </canvas>
  </Grid>
</Window>

When you click on the base control on the left, a new control is generated in the container on the right. As follows:

namespace DemoVisio
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// 控件列表
        /// </summary>
        private List<Control> rightControls = new List<Control>();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //this.Focus();
            //this.one.SetIsEnableInput(false);
            foreach (var control in this.right.Children) {
                var thumb = control as ThumbControl;
                if (thumb != null)
                {
                    thumb.SetIsEnableInput(false);
                }
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            var width = this.right.ActualWidth;
            var height = this.right.ActualHeight;

            int x = 0;
            int y = 0;
            //横线
            while (y<height) {
                Line line = new Line();
                line.X1 = x;
                line.Y1 = y;
                line.X2 = width;
                line.Y2 = y;
                line.Stroke = Brushes.LightGray;
                line.StrokeThickness = 1;
                this.right.Children.Add(line);
                y = y + 10;
            }
            //重新初始化值
            x = 0;
            y = 0;
            //竖线
            while (x < width) {
                Line line = new Line();
                line.X1 = x;
                line.Y1 = y;
                line.X2 = x;
                line.Y2 = height;
                line.Stroke = Brushes.LightGray;
                line.StrokeThickness = 1;
                this.right.Children.Add(line);
                x = x + 10;
            }
        }

        private void rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            SquareControl squareControl = new SquareControl();
            this.right.Children.Add(squareControl);
        }

        private void rhombus_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            RhombusControl rhombusControl = new RhombusControl();
            this.right.Children.Add(rhombusControl);
        }

        private void line_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            LineArrowControl lineArrowControl = new LineArrowControl();
            this.right.Children.Add(lineArrowControl);
        }

        private void circle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            CircleControl circleControl = new CircleControl();
            this.right.Children.Add(circleControl);
        }
    }
}

example screenshot

The basic screenshot of the example is as follows:

remarks

The above examples are only the basics and the functions are not perfect. They are designed to attract people, learn together, and make progress together. I hope they can inspire everyone.

贺新郎·九日

【作者】刘克庄 【朝代】宋
湛湛长空黑。更那堪、斜风细雨,乱愁如织。
老眼平生空四海,赖有高楼百尺。
看浩荡、千崖秋色。
白发书生神州泪,尽凄凉、不向牛山滴。
追往事,去无迹。

少年自负凌云笔。到而今、春华落尽,满怀萧瑟。
常恨世人新意少,爱说南朝狂客。
把破帽、年年拈出。
若对黄花孤负酒,怕黄花、也笑人岑寂。
鸿北去,日西匿。
Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 9/13/2025

Migration from WPF to Avalonia series: Why I have to migrate WPF programs to Avalonia

In the past few years, our host computer software has been mainly developed using WPF and WinForm. These technologies are really easy to use on the Windows platform, and they have also accompanied us through the stage of small-scale trial production to today's large-scale delivery. However, with the development of business and changes in customer needs, the single Windows technology stack has gradually become a hurdle that we must overcome.

继续阅读
同分类 / 同标签 1/26/2025

WPF internationalizes with custom XML files

This article describes in detail the methods of using custom XML files to achieve internationalization in WPF programs, including installing the necessary NuGet package, dynamically obtaining language lists, dynamically switching languages, using translation strings in code and xaml interfaces, etc. It also provides source code links to help developers easily internationalize WPF applications.

继续阅读