12 changed files with 2370 additions and 541 deletions
@ -0,0 +1,28 @@ |
|||
namespace AuroraDesk.Core.Entities; |
|||
|
|||
/// <summary>
|
|||
/// 节点附件圆显示模式
|
|||
/// </summary>
|
|||
public enum ConnectorAttachmentMode |
|||
{ |
|||
/// <summary>
|
|||
/// 不显示附件圆
|
|||
/// </summary>
|
|||
None, |
|||
|
|||
/// <summary>
|
|||
/// 仅显示左侧附件圆
|
|||
/// </summary>
|
|||
LeftOnly, |
|||
|
|||
/// <summary>
|
|||
/// 仅显示右侧附件圆
|
|||
/// </summary>
|
|||
RightOnly, |
|||
|
|||
/// <summary>
|
|||
/// 左右两侧都显示附件圆
|
|||
/// </summary>
|
|||
Both |
|||
} |
|||
|
|||
@ -0,0 +1,18 @@ |
|||
namespace AuroraDesk.Core.Entities; |
|||
|
|||
/// <summary>
|
|||
/// 附件圆定位模式(控制在节点边框内侧或外侧渲染)
|
|||
/// </summary>
|
|||
public enum ConnectorPlacementMode |
|||
{ |
|||
/// <summary>
|
|||
/// 在节点边框内侧对齐
|
|||
/// </summary>
|
|||
Inside, |
|||
|
|||
/// <summary>
|
|||
/// 在节点边框外侧对齐
|
|||
/// </summary>
|
|||
Outside |
|||
} |
|||
|
|||
@ -0,0 +1,61 @@ |
|||
using System; |
|||
using Avalonia; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace AuroraDesk.Presentation.Controls; |
|||
|
|||
/// <summary>
|
|||
/// 垂直均匀分布子元素的面板,用于渲染节点左右侧连接点。
|
|||
/// </summary>
|
|||
public class ConnectorColumnPanel : Panel |
|||
{ |
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
var maxWidth = 0d; |
|||
var totalHeight = 0d; |
|||
|
|||
foreach (var child in Children) |
|||
{ |
|||
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); |
|||
var desired = child.DesiredSize; |
|||
maxWidth = Math.Max(maxWidth, desired.Width); |
|||
totalHeight += desired.Height; |
|||
} |
|||
|
|||
var height = double.IsInfinity(availableSize.Height) ? totalHeight : availableSize.Height; |
|||
return new Size(maxWidth, height); |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
var count = Children.Count; |
|||
if (count == 0) |
|||
{ |
|||
return finalSize; |
|||
} |
|||
|
|||
var totalHeight = 0d; |
|||
foreach (var child in Children) |
|||
{ |
|||
totalHeight += child.DesiredSize.Height; |
|||
} |
|||
|
|||
var availableHeight = finalSize.Height; |
|||
var spacing = count > 0 |
|||
? Math.Max(0, (availableHeight - totalHeight) / (count + 1)) |
|||
: 0; |
|||
|
|||
var currentY = spacing; |
|||
foreach (var child in Children) |
|||
{ |
|||
var childHeight = child.DesiredSize.Height; |
|||
var childWidth = Math.Min(child.DesiredSize.Width, finalSize.Width); |
|||
var x = (finalSize.Width - childWidth) / 2; |
|||
child.Arrange(new Rect(x, currentY, childWidth, childHeight)); |
|||
currentY += childHeight + spacing; |
|||
} |
|||
|
|||
return finalSize; |
|||
} |
|||
} |
|||
|
|||
File diff suppressed because it is too large
@ -0,0 +1,128 @@ |
|||
# NodeCanvas 节点显示问题修复记录 |
|||
|
|||
## 问题描述 |
|||
|
|||
在节点编辑器页面中,`InitializeTestNode` 执行后节点已成功添加到服务,位置也设置成功,但画布上不显示节点。 |
|||
|
|||
## 问题现象 |
|||
|
|||
从日志可以看到: |
|||
- 节点已成功添加到 `Nodes` 集合(`ViewModel.Nodes.Count = 1`) |
|||
- 节点位置已正确设置(位置: (100, 100)) |
|||
- ContentPresenter 可见性为 `True` |
|||
- 但是 **Canvas 面板大小为 0x0**,导致节点不可见 |
|||
|
|||
``` |
|||
[节点更新] Canvas 面板大小: 0x0 |
|||
[节点更新] Canvas 子元素: ContentPresenter, 位置: (100, 100), 可见: True, 大小: 120x112 |
|||
``` |
|||
|
|||
## 根本原因 |
|||
|
|||
在 Avalonia 中,当使用 `ItemsControl` 和 `Canvas` 作为 `ItemsPanel` 时,存在以下问题: |
|||
|
|||
1. **Canvas 大小问题**:`ItemsControl` 使用的 `Canvas`(作为 `ItemsPanel`)不会自动计算大小,默认为 0x0,导致节点虽然位置正确但不可见。 |
|||
|
|||
2. **位置绑定缺失**:需要在 `ItemTemplate` 的根元素上绑定 `Canvas.Left` 和 `Canvas.Top` 属性,而不是仅依赖代码后台手动设置。 |
|||
|
|||
## 解决方案 |
|||
|
|||
### 1. 设置 Canvas 明确大小 |
|||
|
|||
在 `ItemsPanelTemplate` 的 `Canvas` 上设置明确的大小: |
|||
|
|||
```xml |
|||
<ItemsControl.ItemsPanel> |
|||
<ItemsPanelTemplate> |
|||
<Canvas ClipToBounds="False" |
|||
MinWidth="2000" |
|||
MinHeight="2000" |
|||
Width="2000" |
|||
Height="2000"/> |
|||
</ItemsPanelTemplate> |
|||
</ItemsControl.ItemsPanel> |
|||
``` |
|||
|
|||
**关键点**:Canvas 必须设置明确的大小,不能依赖自动计算。 |
|||
|
|||
### 2. 添加位置数据绑定 |
|||
|
|||
在 `ItemTemplate` 的根元素(`Border`)上绑定位置: |
|||
|
|||
```xml |
|||
<Border Background="White" |
|||
CornerRadius="6" |
|||
... |
|||
Canvas.Left="{Binding X}" |
|||
Canvas.Top="{Binding Y}"> |
|||
``` |
|||
|
|||
**关键点**:位置绑定应该在 `ItemTemplate` 的根元素上,数据绑定会自动处理位置更新。 |
|||
|
|||
### 3. ItemsControl 布局设置 |
|||
|
|||
为 `ItemsControl` 添加对齐属性: |
|||
|
|||
```xml |
|||
<ItemsControl x:Name="NodesItemsControl" |
|||
ItemsSource="{Binding Nodes}" |
|||
HorizontalAlignment="Stretch" |
|||
VerticalAlignment="Stretch"> |
|||
``` |
|||
|
|||
### 4. 代码后台位置更新(备用方案) |
|||
|
|||
保留代码后台的手动位置更新作为备用方案,确保在某些情况下位置也能正确设置。 |
|||
|
|||
## 技术要点 |
|||
|
|||
### Avalonia 与 WPF 的差异 |
|||
|
|||
在 Avalonia 中: |
|||
- `ItemsControl` 使用 `Canvas` 作为 `ItemsPanel` 时,Canvas **不会自动计算大小** |
|||
- 必须显式设置 Canvas 的 `Width` 和 `Height` |
|||
- 位置绑定应该在 `ItemTemplate` 的根元素上,而不是在容器上 |
|||
|
|||
### 最佳实践 |
|||
|
|||
1. **数据绑定优先**:使用 XAML 数据绑定处理位置更新,而不是代码后台手动设置 |
|||
2. **明确大小**:Canvas 必须设置明确的大小,建议与父容器保持一致 |
|||
3. **备用方案**:保留代码后台的位置更新作为备用,但主要依赖数据绑定 |
|||
|
|||
## 修改文件 |
|||
|
|||
- `AuroraDesk.Presentation/Views/Pages/NodeCanvasPageView.axaml` |
|||
- 在 `ItemsPanelTemplate` 的 `Canvas` 上设置大小 |
|||
- 在 `ItemTemplate` 的 `Border` 上添加 `Canvas.Left` 和 `Canvas.Top` 绑定 |
|||
- 为 `ItemsControl` 添加对齐属性 |
|||
|
|||
- `AuroraDesk.Presentation/Views/Pages/NodeCanvasPageView.axaml.cs` |
|||
- 保留位置更新代码作为备用方案 |
|||
- 添加调试信息帮助诊断问题 |
|||
|
|||
## 验证 |
|||
|
|||
修复后,节点应该能够正常显示在画布上: |
|||
- Canvas 面板大小不再是 0x0 |
|||
- 节点位置正确显示 |
|||
- 节点可以正常交互(拖拽、选择等) |
|||
|
|||
## 相关日志 |
|||
|
|||
修复前的关键日志: |
|||
``` |
|||
[节点更新] Canvas 面板大小: 0x0 |
|||
[节点更新] Canvas 子元素: ContentPresenter, 位置: (100, 100), 可见: True, 大小: 120x112 |
|||
``` |
|||
|
|||
修复后应该看到: |
|||
``` |
|||
[节点更新] Canvas 面板大小: 2000x2000 |
|||
[节点更新] Canvas 子元素: ContentPresenter, 位置: (100, 100), 可见: True, 大小: 120x112 |
|||
``` |
|||
|
|||
## 参考资料 |
|||
|
|||
- [Avalonia ItemsControl 文档](https://docs.avaloniaui.net/docs/guides/controls/itemscontrol) |
|||
- [Avalonia Canvas 布局](https://docs.avaloniaui.net/docs/guides/controls/canvas) |
|||
|
|||
Loading…
Reference in new issue