实现ClashDetective结果保存到数据库功能,并在UI中展示碰撞检测历史记录

This commit is contained in:
tian 2026-01-08 12:50:00 +08:00
parent d473065025
commit 0de096aed0
5 changed files with 515 additions and 30 deletions

View File

@ -7,6 +7,7 @@
1. [ ] BUG虚拟车辆模型每次点击都重建
2. [x] BUG碰撞结果高亮应该显示clashdetective检测的结果
3. [x] 优化对clashdetective的检测结果进行向上合并找到集合对象。
4. [ ] (功能)碰撞检测结果保存数据库,列表展示
### [2025/12/25]

View File

@ -88,6 +88,11 @@ namespace NavisworksTransport
/// </summary>
public event EventHandler<CollisionDetectedEventArgs> CollisionDetected;
/// <summary>
/// ClashDetective结果保存到数据库事件
/// </summary>
public event EventHandler<ClashDetectiveResultSavedEventArgs> ClashDetectiveResultSaved;
private ClashDetectiveIntegration()
{
_currentCollisions = new List<ClashResult>();
@ -589,6 +594,39 @@ namespace NavisworksTransport
// 更新碰撞计数器
_clashDetectiveCollisionCount = clashResults.Count;
// 保存到数据库
try
{
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null)
{
var record = new ClashDetectiveResultRecord
{
TestName = _currentTestName,
PathName = pathName,
RouteId = routeId ?? "",
TestTime = DateTime.Now,
CollisionCount = clashResults.Count,
AnimationCollisionCount = _animationCollisionCount,
FrameRate = frameRate,
Duration = duration,
DetectionGap = detectionGap,
AnimatedObjectName = animatedObjectName,
CreatedAt = DateTime.Now
};
pathDatabase.SaveClashDetectiveResult(record);
LogManager.Info($"ClashDetective结果已保存到数据库");
// 触发结果保存事件通知UI刷新列表
ClashDetectiveResultSaved?.Invoke(this, new ClashDetectiveResultSavedEventArgs(pathName, _currentTestName, clashResults.Count));
}
}
catch (Exception dbEx)
{
LogManager.Error($"保存ClashDetective结果到数据库失败: {dbEx.Message}");
// 不影响主流程,继续执行
}
// 第四步:将分组添加到主测试
if (collisionGroup.Children.Count > 0)
{
@ -1666,4 +1704,21 @@ namespace NavisworksTransport
CollisionCount = results.Count;
}
}
/// <summary>
/// ClashDetective结果保存事件参数
/// </summary>
public class ClashDetectiveResultSavedEventArgs : EventArgs
{
public string PathName { get; private set; }
public string TestName { get; private set; }
public int CollisionCount { get; private set; }
public ClashDetectiveResultSavedEventArgs(string pathName, string testName, int collisionCount)
{
PathName = pathName;
TestName = testName;
CollisionCount = collisionCount;
}
}
}

View File

@ -43,9 +43,13 @@ namespace NavisworksTransport
ExecuteNonQuery("PRAGMA journal_mode=WAL");
ExecuteNonQuery("PRAGMA foreign_keys=ON");
// 无论数据库是否为新数据库都调用CreateTables()
// 因为使用了CREATE TABLE IF NOT EXISTS语法所以是安全的
// 这样可以确保所有表都存在,避免在旧数据库中缺少新表的问题
CreateTables();
if (isNewDatabase)
{
CreateTables();
LogManager.Info($"创建新数据库: {_dbPath}");
}
else
@ -152,10 +156,30 @@ namespace NavisworksTransport
)
");
// 6. ClashDetective结果表
ExecuteNonQuery(@"
CREATE TABLE IF NOT EXISTS ClashDetectiveResults (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
TestName TEXT NOT NULL UNIQUE,
PathName TEXT NOT NULL,
RouteId TEXT NOT NULL,
TestTime DATETIME NOT NULL,
CollisionCount INTEGER NOT NULL,
AnimationCollisionCount INTEGER NOT NULL,
FrameRate INTEGER,
Duration REAL,
DetectionGap REAL,
AnimatedObjectName TEXT,
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
)
");
// 创建索引
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathpoints_route ON PathPoints(RouteId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_pathedges_route ON PathEdges(RouteId)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_path_name ON ClashDetectiveResults(PathName)");
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_clash_test_time ON ClashDetectiveResults(TestTime DESC)");
}
/// <summary>
@ -342,6 +366,183 @@ namespace NavisworksTransport
}
}
#region ClashDetective结果管理
/// <summary>
/// 保存ClashDetective碰撞检测结果
/// </summary>
public void SaveClashDetectiveResult(ClashDetectiveResultRecord record)
{
try
{
// 检查TestName是否已存在
var checkSql = "SELECT COUNT(*) FROM ClashDetectiveResults WHERE TestName = @testName";
using (var checkCmd = new SQLiteCommand(checkSql, _connection))
{
checkCmd.Parameters.AddWithValue("@testName", record.TestName);
var count = Convert.ToInt32(checkCmd.ExecuteScalar());
if (count > 0)
{
throw new InvalidOperationException($"测试名称 '{record.TestName}' 已存在,无法重复保存");
}
}
var sql = @"
INSERT INTO ClashDetectiveResults
(TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
FrameRate, Duration, DetectionGap, AnimatedObjectName, CreatedAt)
VALUES (@testName, @pathName, @routeId, @testTime, @collisionCount, @animationCollisionCount,
@frameRate, @duration, @detectionGap, @animatedObjectName, @createdAt)
";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@testName", record.TestName);
cmd.Parameters.AddWithValue("@pathName", record.PathName);
cmd.Parameters.AddWithValue("@routeId", record.RouteId);
cmd.Parameters.AddWithValue("@testTime", record.TestTime);
cmd.Parameters.AddWithValue("@collisionCount", record.CollisionCount);
cmd.Parameters.AddWithValue("@animationCollisionCount", record.AnimationCollisionCount);
cmd.Parameters.AddWithValue("@frameRate", record.FrameRate);
cmd.Parameters.AddWithValue("@duration", record.Duration);
cmd.Parameters.AddWithValue("@detectionGap", record.DetectionGap);
cmd.Parameters.AddWithValue("@animatedObjectName", record.AnimatedObjectName ?? "");
cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt);
cmd.ExecuteNonQuery();
}
LogManager.Info($"ClashDetective结果已保存: 测试={record.TestName}, 碰撞数={record.CollisionCount}");
}
catch (Exception ex)
{
LogManager.Error($"保存ClashDetective结果失败: {ex.Message}", ex);
throw;
}
}
/// <summary>
/// 获取路径的所有ClashDetective检测结果
/// </summary>
public List<ClashDetectiveResultRecord> GetClashDetectiveResultsByPath(string pathName)
{
var results = new List<ClashDetectiveResultRecord>();
try
{
var sql = @"
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
FrameRate, Duration, DetectionGap, AnimatedObjectName, CreatedAt
FROM ClashDetectiveResults
WHERE PathName = @pathName
ORDER BY TestTime DESC
";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@pathName", pathName);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
results.Add(new ClashDetectiveResultRecord
{
Id = Convert.ToInt32(reader["Id"]),
TestName = reader["TestName"].ToString(),
PathName = reader["PathName"].ToString(),
RouteId = reader["RouteId"]?.ToString(),
TestTime = Convert.ToDateTime(reader["TestTime"]),
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
FrameRate = reader["FrameRate"] != DBNull.Value ? Convert.ToInt32(reader["FrameRate"]) : 0,
Duration = reader["Duration"] != DBNull.Value ? Convert.ToDouble(reader["Duration"]) : 0.0,
DetectionGap = reader["DetectionGap"] != DBNull.Value ? Convert.ToDouble(reader["DetectionGap"]) : 0.0,
AnimatedObjectName = reader["AnimatedObjectName"]?.ToString(),
CreatedAt = Convert.ToDateTime(reader["CreatedAt"])
});
}
}
}
LogManager.Debug($"获取到 {results.Count} 条ClashDetective结果记录");
}
catch (Exception ex)
{
LogManager.Error($"获取ClashDetective结果失败: {ex.Message}", ex);
}
return results;
}
/// <summary>
/// 获取路径最新的ClashDetective检测结果
/// </summary>
public ClashDetectiveResultRecord GetLatestClashDetectiveResult(string pathName)
{
try
{
var sql = @"
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
FrameRate, Duration, DetectionGap, AnimatedObjectName, CreatedAt
FROM ClashDetectiveResults
WHERE PathName = @pathName
ORDER BY TestTime DESC
LIMIT 1
";
using (var cmd = new SQLiteCommand(sql, _connection))
{
cmd.Parameters.AddWithValue("@pathName", pathName);
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
return new ClashDetectiveResultRecord
{
Id = Convert.ToInt32(reader["Id"]),
TestName = reader["TestName"].ToString(),
PathName = reader["PathName"].ToString(),
RouteId = reader["RouteId"]?.ToString(),
TestTime = Convert.ToDateTime(reader["TestTime"]),
CollisionCount = Convert.ToInt32(reader["CollisionCount"]),
AnimationCollisionCount = Convert.ToInt32(reader["AnimationCollisionCount"]),
FrameRate = reader["FrameRate"] != DBNull.Value ? Convert.ToInt32(reader["FrameRate"]) : 0,
Duration = reader["Duration"] != DBNull.Value ? Convert.ToDouble(reader["Duration"]) : 0.0,
DetectionGap = reader["DetectionGap"] != DBNull.Value ? Convert.ToDouble(reader["DetectionGap"]) : 0.0,
AnimatedObjectName = reader["AnimatedObjectName"]?.ToString(),
CreatedAt = Convert.ToDateTime(reader["CreatedAt"])
};
}
}
}
}
catch (Exception ex)
{
LogManager.Error($"获取最新ClashDetective结果失败: {ex.Message}", ex);
}
return null;
}
/// <summary>
/// 删除ClashDetective检测结果
/// </summary>
public void DeleteClashDetectiveResult(int id)
{
try
{
ExecuteNonQuery("DELETE FROM ClashDetectiveResults WHERE Id=@id",
new { id = id });
LogManager.Info($"已删除ClashDetective结果记录: Id={id}");
}
catch (Exception ex)
{
LogManager.Error($"删除ClashDetective结果失败: {ex.Message}", ex);
throw;
}
}
#endregion
/// <summary>
/// 保存分析评分结果
/// </summary>
@ -836,9 +1037,37 @@ namespace NavisworksTransport
/// </summary>
public void Dispose()
{
_connection?.Close();
_connection?.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_connection?.Close();
_connection?.Dispose();
}
}
}
/// <summary>
/// ClashDetective碰撞检测结果记录
/// </summary>
public class ClashDetectiveResultRecord
{
public int Id { get; set; }
public string TestName { get; set; }
public string PathName { get; set; }
public string RouteId { get; set; }
public DateTime TestTime { get; set; }
public int CollisionCount { get; set; }
public int AnimationCollisionCount { get; set; }
public int FrameRate { get; set; }
public double Duration { get; set; }
public double DetectionGap { get; set; }
public string AnimatedObjectName { get; set; }
public DateTime CreatedAt { get; set; }
}
/// <summary>

View File

@ -77,6 +77,56 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public Guid InstanceGuid { get; }
}
/// <summary>
/// ClashDetective碰撞检测结果视图模型
/// </summary>
public class ClashDetectiveResultViewModel
{
private readonly Action _refreshCallback;
public ClashDetectiveResultViewModel(ClashDetectiveResultRecord record, Action refreshCallback)
{
Record = record ?? throw new ArgumentNullException(nameof(record));
_refreshCallback = refreshCallback ?? throw new ArgumentNullException(nameof(refreshCallback));
ViewCommand = new RelayCommand(() => ExecuteView());
ReportCommand = new RelayCommand(() => ExecuteReport());
DeleteCommand = new RelayCommand(() => ExecuteDelete());
}
public ClashDetectiveResultRecord Record { get; }
public string TestTimeDisplay => Record.TestTime.ToString("HH:mm:ss");
public string CollisionCountDisplay => $"{Record.CollisionCount}个";
public ICommand ViewCommand { get; }
public ICommand ReportCommand { get; }
public ICommand DeleteCommand { get; }
private void ExecuteView()
{
// 高亮显示该次检测的碰撞对象
var clashIntegration = ClashDetectiveIntegration.Instance;
clashIntegration?.HighlightClashDetectiveResults(Record.TestName);
}
private void ExecuteReport()
{
// 打开该次检测的详细报告
var command = GenerateCollisionReportCommand.CreateComprehensive(autoHighlight: false);
command.ExecuteAsync();
}
private void ExecuteDelete()
{
// 删除该次检测记录
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase != null)
{
pathDatabase.DeleteClashDetectiveResult(Record.Id);
// 通过回调刷新列表
_refreshCallback?.Invoke();
}
}
}
/// <summary>
/// 动画控制视图模型
/// 专门处理路径动画播放和碰撞检测功能
@ -351,6 +401,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
OnPropertyChanged(nameof(MovementSpeed));
OnPropertyChanged(nameof(PathLength));
UpdateCanGenerateAnimation();
// 路径改变时刷新ClashDetective结果列表
RefreshClashDetectiveResultsList();
}
}
}
@ -562,6 +615,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#endregion
#region ClashDetective结果管理
private ObservableCollection<ClashDetectiveResultViewModel> _clashDetectiveResults = new ObservableCollection<ClashDetectiveResultViewModel>();
public ObservableCollection<ClashDetectiveResultViewModel> ClashDetectiveResults => _clashDetectiveResults;
public ICommand RefreshClashDetectiveResultsCommand { get; private set; }
#endregion
#region
/// <summary>
@ -706,6 +769,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 订阅碰撞检测事件
_clashIntegration.CollisionDetected += OnCollisionDetected;
_clashIntegration.ClashDetectiveResultSaved += OnClashDetectiveResultSaved;
// 订阅文档状态事件
DocumentStateManager.Instance.DocumentInvalidated += OnDocumentInvalidated;
@ -786,6 +850,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ClearCollisionHighlightsCommand = new RelayCommand(ExecuteClearCollisionHighlights, () => HasCollisionResults);
HighlightClashDetectiveResultsCommand = new RelayCommand(ExecuteHighlightClashDetectiveResults, () => HasClashDetectiveResults);
ClearClashDetectiveHighlightsCommand = new RelayCommand(ExecuteClearClashDetectiveHighlights, () => HasClashDetectiveResults);
RefreshClashDetectiveResultsCommand = new RelayCommand(ExecuteRefreshClashDetectiveResults);
PlayForwardCommand = new RelayCommand(ExecutePlayForward, CanExecuteMediaCommands);
PlayReverseCommand = new RelayCommand(ExecutePlayReverse, CanExecuteMediaCommands);
StepForwardCommand = new RelayCommand(ExecuteStepForward, CanExecuteMediaCommands);
@ -1408,6 +1473,23 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
/// <summary>
/// ClashDetective结果保存事件处理
/// </summary>
private void OnClashDetectiveResultSaved(object sender, ClashDetectiveResultSavedEventArgs e)
{
try
{
// 刷新ClashDetective结果列表
RefreshClashDetectiveResultsList();
LogManager.Info($"ClashDetective结果已保存列表已刷新: 路径={e.PathName}, 测试={e.TestName}, 碰撞数={e.CollisionCount}");
}
catch (Exception ex)
{
LogManager.Error($"处理ClashDetective结果保存事件失败: {ex.Message}", ex);
}
}
/// <summary>
/// 保存碰撞报告到数据库
/// </summary>
@ -1648,6 +1730,56 @@ namespace NavisworksTransport.UI.WPF.ViewModels
UpdateMainStatus("已清除ClashDetective高亮");
}
/// <summary>
/// 刷新ClashDetective结果列表
/// </summary>
private void RefreshClashDetectiveResultsList()
{
try
{
var pathName = CurrentPathRoute?.Name;
if (string.IsNullOrEmpty(pathName))
{
_clashDetectiveResults.Clear();
HasClashDetectiveResults = false;
return;
}
var pathDatabase = PathPlanningManager.Instance?.GetPathDatabase();
if (pathDatabase == null)
{
_clashDetectiveResults.Clear();
HasClashDetectiveResults = false;
return;
}
var records = pathDatabase.GetClashDetectiveResultsByPath(pathName);
_clashDetectiveResults.Clear();
foreach (var record in records)
{
_clashDetectiveResults.Add(new ClashDetectiveResultViewModel(record, RefreshClashDetectiveResultsList));
}
// 更新HasClashDetectiveResults属性
HasClashDetectiveResults = _clashDetectiveResults.Count > 0;
UpdateMainStatus($"已刷新ClashDetective结果列表{_clashDetectiveResults.Count}条记录");
LogManager.Info($"ClashDetective结果列表已刷新{_clashDetectiveResults.Count}条记录");
}
catch (Exception ex)
{
LogManager.Error($"刷新ClashDetective结果列表失败: {ex.Message}");
_clashDetectiveResults.Clear();
HasClashDetectiveResults = false;
}
}
private void ExecuteRefreshClashDetectiveResults()
{
RefreshClashDetectiveResultsList();
}
#endregion
/// <summary>
@ -2598,6 +2730,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
try
{
_clashIntegration.CollisionDetected -= OnCollisionDetected;
_clashIntegration.ClashDetectiveResultSaved -= OnClashDetectiveResultSaved;
LogManager.Debug("碰撞检测事件订阅取消完成");
}
catch (Exception eventEx)

View File

@ -399,22 +399,105 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
IsEnabled="{Binding HasClashDetectiveResults}"
Style="{StaticResource SecondaryButtonStyle}"/>
</StackPanel>
<!-- 提示信息 -->
<TextBlock Text="注:碰撞检测将在动画播放过程中自动进行,结果会在动画完成后显示"
Style="{StaticResource StatusTextStyle}"
FontStyle="Italic"
Opacity="0.7"
Margin="0,10,0,0"/>
<!-- ClashDetective结果列表 -->
<StackPanel Margin="0,15,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="碰撞检测历史" Style="{StaticResource SectionHeaderStyle}"/>
<Button Grid.Column="1"
Content="刷新"
Command="{Binding RefreshClashDetectiveResultsCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Height="22"
Padding="8,2"
Margin="0,0,0,0"/>
</Grid>
<TextBlock Text="当前路径暂无碰撞检测结果"
Style="{StaticResource StatusTextStyle}"
Foreground="#FF777777"
Visibility="{Binding HasClashDetectiveResults, Converter={StaticResource BoolToVisConverter}, ConverterParameter=Inverse}"
Margin="0,5,0,0"/>
<ListView ItemsSource="{Binding ClashDetectiveResults}"
Margin="0,5,0,0"
Visibility="{Binding HasClashDetectiveResults, Converter={StaticResource BoolToVisConverter}}"
MinHeight="120"
MaxHeight="200"
BorderBrush="#FFE2E8F0"
BorderThickness="1">
<ListView.View>
<GridView>
<GridViewColumn Header="测试名称" Width="140">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Record.TestName}"
FontWeight="SemiBold"
TextTrimming="CharacterEllipsis"
ToolTip="{Binding Record.TestName}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="时间" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding TestTimeDisplay}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="碰撞数" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding CollisionCountDisplay}"
HorizontalAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="操作" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="查看"
Command="{Binding ViewCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Height="20"
Padding="6,0"
Margin="0,0,4,0"/>
<Button Content="报告"
Command="{Binding ReportCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Height="20"
Padding="6,0"
Margin="0,0,4,0"/>
<Button Content="删除"
Command="{Binding DeleteCommand}"
Style="{StaticResource SecondaryButtonStyle}"
Height="20"
Padding="6,0"
Background="#FFFFE6E6"
Foreground="#FF8B0000"
Margin="0"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</StackPanel>
</Border>
<!-- 区域6: 动画预览和操作指南 -->
<!-- 区域6: 当前路径信息 -->
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,0" Padding="12">
<StackPanel>
<Label Content="动画预览与操作指南" Style="{StaticResource SectionHeaderStyle}"/>
<Label Content="当前路径信息" Style="{StaticResource SectionHeaderStyle}"/>
<!-- 当前路径信息 -->
<Grid Margin="0,5,0,10">
@ -433,22 +516,6 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
Text="{Binding CurrentPathRoute.Points.Count, StringFormat='路径点数: {0}个', TargetNullValue='路径点数: 0个'}"
Style="{StaticResource StatusTextStyle}"/>
</Grid>
<!-- 操作提示 -->
<StackPanel Margin="0,5,0,0">
<TextBlock Text="• 确保已在路径编辑页签中选择了有效的路径"
Style="{StaticResource StatusTextStyle}"
Margin="0,2"/>
<TextBlock Text="• 动画将沿着当前选中的路径创建"
Style="{StaticResource StatusTextStyle}"
Margin="0,2"/>
<TextBlock Text="• 碰撞检测会在路径上模拟运输过程中的冲突检测"
Style="{StaticResource StatusTextStyle}"
Margin="0,2"/>
<TextBlock Text="• 碰撞检测后可在ClashDetective插件中查看碰撞结果"
Style="{StaticResource StatusTextStyle}"
Margin="0,2"/>
</StackPanel>
</StackPanel>
</Border>