Bruce's Blocks






4.94/5 (10投票s)
WPF 块状编辑器,
背景
我为铣削、切割、钻孔、EDM、增材制造等编写复杂的 CNC 机床控制软件。这些程序运行在主机 PC 上,并与多轴运动控制器和其他工业设备通信。它们提供触摸屏操作员界面、机器控制逻辑、网络通信、诊断以及许多其他功能。通常会包含一个彩色文本编辑器用于编辑 G 代码。
G 代码是 CNC 编程的神秘语言。这是一个“点钻循环”的例子
G83 Z1. F.001 R.03 Q.04 P0
G 代码程序通常通过以下三种方式之一创建
- 从 CAD 模型生成
- 通过专门的对话式界面生成
- 在文本编辑器中编辑
CAD 生成高度自动化,但需要存在 CAD 模型。对话式界面非常特定于应用程序,创建和维护耗时。在文本编辑器中编写 G 代码容易出错,需要专业知识。
我创建了一个块式 G 代码编辑器,旨在解决这些方法的不足之处。联系我讨论许可该块式 G 代码编辑器以用于您的 CNC 应用程序。
Bruce's Blocks
作为 G 代码编辑器开发过程的一部分,我创建了一个更简单的 WPF 应用程序,作为新技术(如拖放重新排序、多级分组以及块视图和文本视图之间的同步)的测试平台。这就是那个应用程序——以开源项目的形式呈现!
主要目标是优化带有嵌套分组级别的块式文本编辑器。我还想创造一种干净现代的设计美学,消除传统的 WPF 窗口标题栏。
特点
- 将文本文件作为图形块的集合进行操作
- 标准编辑器 Ctrl 命令
- 多选块剪切、复制、删除和分组
- Font Awesome Free v5 图标
- 浅色和深色主题
- 简单 .NET 4.6.1 项目,无依赖项
块管理器
BlockManager
类负责管理支持的块类型的集合。它是块工厂,提供块和文本之间的双向转换(读取和写入文件和 string
缓冲区)。通过 IBlockManager
接口将块管理器暴露给应用程序的其余部分,以便在启动时注入自定义版本。
public interface IBlockManager
{
/// <summary>The set of block types supported by this manager.</summary>
IBlock[] BlockTypes { get; }
/// <summary>Creates an array of new blocks that match the lines.</summary>
IBlock[] NewBlocks(string buffer);
/// <summary>Produces a string buffer from an array of blocks.</summary>
string ToString(IBlock[] blocks);
/// <summary>Creates an array of new blocks from a text file.</summary>
bool ReadFile(string path, out IBlock[] blocks);
/// <summary>Writes a text file from an array of blocks.</summary>
bool WriteFile(string path, IBlock[] blocks);
}
有用于块类型和块组类型的 IBlock
和 IBlockGroup
接口。每种块类型都管理自己的 ListBox
项目,用于块视图和文本视图,使其能够自由决定如何最好地显示自己。每种块类型都能够从文本行创建新块。
public interface IBlock
{
/// <summary>Parent block group, or null for top-level blocks.</summary>
IBlockGroup BlockGroup { get; set; }
/// <summary>Each block maintains its own block and code listbox items.</summary>
ListBoxItem BlockItem { get; }
ListBoxItem CodeItem { get; }
/// <summary>Creates a new block from a line, or null if no match.</summary>
IBlock NewBlock(string line);
}
BlockControl
类提供了块视图的默认布局,包括图像和三行文本。
<Grid Height="64" Width="128">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="64" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Name="image" Grid.Column="0"
Source="Images/blockPalette.png" VerticalAlignment="Center" />
<StackPanel Grid.Column="1" Margin="2,0,0,0" VerticalAlignment="Center">
<TextBlock Name="tb1" Text="Palette" />
<TextBlock Name="tb2" Text="for" />
<TextBlock Name="tb3" Text="colors" />
</StackPanel>
</Grid>
拖放重新排序
DragPanel
类重写左鼠标按钮事件以实现拖放重新排序功能,并重写 MeasureOverride()
和 ArrangeOverride()
以实现其布局策略。支持不同大小的块类型。MeasureOverride()
设置统一的行高和列宽以容纳最大的项目。
foreach (UIElement child in Children)
{
child.Measure(infSize);
itemWidth = Math.Max(itemWidth, child.DesiredSize.Width);
rowHeight = Math.Max(rowHeight, child.DesiredSize.Height);
}
每个项目通过一组附加属性维护其列表顺序和布局位置。当用户开始拖动时,选定的项目会添加一个阴影效果——使其看起来像从其他项目上方升起。
多级分组
BlockList
用户控件包含块项目 ListBox
。它还包括一个水平分割器和一个可以按需填充第二个 BlockList
的下部区域。这种安排使得可以显示任意数量的分组级别。每个 BlockList
以链表形式保存对其父块和子块 BlockList
对象的引用。
MainWindow
类背后有大量精心设计的簿记逻辑,负责实现块式编辑器(添加、删除、分组、取消分组、剪切、复制、粘贴等)。嵌套组通过大量的递归方法调用得到支持!
private void InsertCode(IBlockGroup group, ref int index)
{
foreach (IBlock block in group.Blocks)
{
if (block is IBlockGroup)
InsertCode((IBlockGroup)block, ref index);
else
listCode.Items.Insert(index, block.CodeItem);
}
}
BlockManager
以纯文本格式将块集合保存到文件和剪贴板。
private void WriteLines(StreamWriter sw, IBlock[] blocks, ref int indent)
{
foreach (IBlock block in blocks)
{
for (int i = 0; i < indent; i++) sw.Write(" ");
sw.WriteLine(block.ToString().Trim());
if (block is IBlockGroup)
{
++indent;
IBlockGroup group = (IBlockGroup)block;
WriteLines(sw, group.Blocks, ref indent);
--indent;
for (int i = 0; i < indent; i++) sw.Write(" ");
sw.WriteLine(BlockGroup.EndMarker);
}
}
}
组由开始和结束标记分隔,组内容缩进以便阅读。
自定义窗口样式
我想实现一个简单的自定义主窗口,而无需传统的标题栏。
这是我窗口样式的相关部分。
<Style x:Key="MainWindowStyle" TargetType="Window">
<Setter Property="WindowStyle" Value="None" />
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="0" GlassFrameThickness="1" ResizeBorderThickness="4" />
</Setter.Value>
</Setter>
</Style>
一个不可见的 Button
覆盖了自定义标题栏区域,以支持窗口拖动和双击最大化/还原。工具栏图标是 Font Awesome Free(第 5 版)。最小化和关闭按钮执行显而易见的命令。唯一值得注意的技巧在于最大化
/还原
功能。在最大化之前设置NoResize
可以防止最大化窗口稍微偏离屏幕。
private void ToggleMaximized()
{
if (WindowState == WindowState.Maximized)
{
WindowState = WindowState.Normal;
ResizeMode = ResizeMode.CanResize;
}
else
{
if (WindowState != WindowState.Normal)
WindowState = WindowState.Normal;
ResizeMode = ResizeMode.NoResize;
WindowState = WindowState.Maximized;
}
}
浅色和深色主题
App
类在其资源字典中保存全局样式和颜色。App
可以通过简单地清空字典并用请求的配色方案替换其内容来在浅色和深色主题之间切换。请记住在 XAML 中指定动态资源引用。
关注点
- 一个健壮的 WPF 拖放重新排序
ListBox
- 多级块分组方案
- 块视图和文本视图之间的同步
- 支持标准编辑器功能集
- 一个简单的 WPF 自定义主窗口实现
- Font Awesome Free v5 集成
- 简单的浅色和深色主题支持
- 一个干净、美观且响应迅速的 WPF UI 设计示例
希望您喜欢玩这个应用程序并探索代码。如果您发现错误,请告知我!
历史
- 2019 年 2 月 23 日:初始版本
- 2019 年 3 月 10 日:GitHub 存储库已更新,提供了新的、改进的
DragListBox
类,具有RowMajor
、ColumnMajor
等布局选项,以及许多其他改进。