把动画播放控制改成媒体控制按钮,增加了步进、快放,支持反向播放
This commit is contained in:
parent
9024eb2672
commit
89c98f1556
@ -133,7 +133,8 @@
|
||||
"Read(//c/Users/Tellme/apps/OpenSource/AStar-master/Roy-T.AStar/**)",
|
||||
"Read(//c/Users/Tellme/apps/OpenSource/AStar-master/**)",
|
||||
"Bash(\"./compile.bat\")",
|
||||
"Bash(xcopy:*)"
|
||||
"Bash(xcopy:*)",
|
||||
"Read(//c/ProgramData/Autodesk/Navisworks Manage 2026/NavisworksTransport/logs/**)"
|
||||
],
|
||||
"deny": [],
|
||||
"additionalDirectories": [
|
||||
|
||||
@ -201,6 +201,9 @@
|
||||
<Compile Include="src\UI\WPF\Views\PathAnalysisDialog.xaml.cs">
|
||||
<DependentUpon>PathAnalysisDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="src\UI\WPF\Views\MediaControlBar.xaml.cs">
|
||||
<DependentUpon>MediaControlBar.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
||||
<!-- UI - WPF ViewModels -->
|
||||
<Compile Include="src\UI\WPF\ViewModels\ViewModelBase.cs" />
|
||||
@ -305,11 +308,19 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="src\UI\WPF\Views\MediaControlBar.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<!-- Shared Resource Dictionary -->
|
||||
<Page Include="src\UI\WPF\Resources\NavisworksStyles.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="src\UI\WPF\Resources\MediaControlIcons.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@ -365,4 +365,17 @@
|
||||
│ 3. 改造播放控制为步进式 │
|
||||
│ 4. 实现帧导航方法 │
|
||||
│ 5. 更新UI添加步进控制 │
|
||||
│ 6. 测试各种播放模式
|
||||
│ 6. 测试各种播放模式
|
||||
|
||||
|
||||
|
||||
🎮 媒体控制功能:
|
||||
|
||||
- 快退10帧 (Shift+←) - 快速后退功能
|
||||
- 上一帧 (←) - 精确单帧后退
|
||||
- 反向播放 (R键) - 连续反向播放动画
|
||||
- 暂停 (Space) - 居中暂停控制
|
||||
- 正向播放 - 标准前进播放
|
||||
- 下一帧 (→) - 精确单帧前进
|
||||
- 快进10帧 (Shift+→) - 快速前进功能
|
||||
- 停止 (Esc) - 停止并重置动画
|
||||
@ -66,6 +66,11 @@ namespace NavisworksTransport.Core.Animation
|
||||
private double _movementSpeed = 1.0; // 运动速度(默认1米/秒)
|
||||
private double _detectionGap = 0.05; // 检测间隙(默认0.05米)
|
||||
|
||||
// === 双向播放和步进控制 ===
|
||||
private int _playbackDirection = 1; // 播放方向:1=正向,-1=反向
|
||||
private double _playbackSpeed = 1.0; // 播放速度倍率
|
||||
private bool _isStepMode = false; // 是否处于步进模式
|
||||
|
||||
// === 性能监控 ===
|
||||
private int _fpsFrameCount = 0;
|
||||
private DateTime _fpsCounterStart = DateTime.Now;
|
||||
@ -316,7 +321,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
LogManager.Info($"潜在碰撞对象数: {potentialColliders.Count}");
|
||||
|
||||
// 预计算每一帧
|
||||
for (int i = 0; i <= totalFrames; i++)
|
||||
for (int i = 0; i < totalFrames; i++)
|
||||
{
|
||||
double progress = (double)i / totalFrames;
|
||||
var framePosition = InterpolatePosition(progress);
|
||||
@ -640,6 +645,14 @@ namespace NavisworksTransport.Core.Animation
|
||||
SetState(AnimationState.Stopped);
|
||||
_pausedProgress = 0.0; // 重置暂停进度
|
||||
_currentFrameIndex = 0; // 重置帧索引
|
||||
|
||||
// 将物体移回起点位置
|
||||
if (_pathPoints != null && _pathPoints.Count > 0 && _animatedObject != null)
|
||||
{
|
||||
UpdateObjectPosition(_pathPoints[0]);
|
||||
LogManager.Info($"物体已移回起点位置: ({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2})");
|
||||
}
|
||||
|
||||
LogManager.Info("动画已停止");
|
||||
|
||||
// 动画停止时不创建碰撞测试汇总,由动画完成事件统一处理
|
||||
@ -1315,6 +1328,245 @@ namespace NavisworksTransport.Core.Animation
|
||||
/// </summary>
|
||||
public TimeLinerIntegrationManager TimeLinerManager => _timeLinerManager;
|
||||
|
||||
#region 帧控制功能
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前帧索引
|
||||
/// </summary>
|
||||
public int CurrentFrame => _currentFrameIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 获取总帧数
|
||||
/// </summary>
|
||||
public int TotalFrames => _animationFrames?.Count ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取播放方向(1=正向,-1=反向)
|
||||
/// </summary>
|
||||
public int PlaybackDirection => _playbackDirection;
|
||||
|
||||
/// <summary>
|
||||
/// 获取播放速度倍率
|
||||
/// </summary>
|
||||
public double PlaybackSpeed => _playbackSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否正在反向播放
|
||||
/// </summary>
|
||||
public bool IsPlayingReverse => _playbackDirection == -1;
|
||||
|
||||
/// <summary>
|
||||
/// 设置播放方向
|
||||
/// </summary>
|
||||
/// <param name="reverse">true=反向播放,false=正向播放</param>
|
||||
public void SetPlaybackDirection(bool reverse)
|
||||
{
|
||||
_playbackDirection = reverse ? -1 : 1;
|
||||
LogManager.Info($"播放方向设置为: {(reverse ? "反向" : "正向")}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置播放速度
|
||||
/// </summary>
|
||||
/// <param name="speed">播放速度倍率,负值表示反向</param>
|
||||
public void SetPlaybackSpeed(double speed)
|
||||
{
|
||||
_playbackSpeed = Math.Abs(speed);
|
||||
_playbackDirection = speed >= 0 ? 1 : -1;
|
||||
|
||||
// 更新帧间隔
|
||||
_frameInterval = 1000.0 / (_animationFrameRate * _playbackSpeed);
|
||||
LogManager.Info($"播放速度设置为: {speed}x,方向: {(speed >= 0 ? "正向" : "反向")}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跳转到指定帧
|
||||
/// </summary>
|
||||
/// <param name="frameIndex">目标帧索引</param>
|
||||
public void SeekToFrame(int frameIndex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_animationFrames == null || _animationFrames.Count == 0)
|
||||
{
|
||||
LogManager.Warning("无动画帧数据,无法跳转");
|
||||
return;
|
||||
}
|
||||
|
||||
// 限制帧索引范围
|
||||
frameIndex = Math.Max(0, Math.Min(frameIndex, _animationFrames.Count - 1));
|
||||
|
||||
if (frameIndex == _currentFrameIndex)
|
||||
{
|
||||
LogManager.Debug($"已处于目标帧 {frameIndex},无需跳转");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentFrameIndex = frameIndex;
|
||||
|
||||
// 更新对象位置
|
||||
var frameData = _animationFrames[_currentFrameIndex];
|
||||
UpdateObjectPosition(frameData.Position);
|
||||
|
||||
// 更新碰撞高亮
|
||||
UpdateCollisionHighlightFromFrame();
|
||||
|
||||
// 计算并触发进度事件
|
||||
double progress = TotalFrames > 1 ? (double)_currentFrameIndex / (TotalFrames - 1) * 100 : 0;
|
||||
ProgressChanged?.Invoke(this, progress);
|
||||
|
||||
LogManager.Info($"跳转到帧 {frameIndex}/{TotalFrames - 1} (进度: {progress:F1}%)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"跳转到帧失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跳转到指定进度
|
||||
/// </summary>
|
||||
/// <param name="progress">进度值(0.0-1.0)</param>
|
||||
public void SeekToProgress(double progress)
|
||||
{
|
||||
try
|
||||
{
|
||||
progress = Math.Max(0.0, Math.Min(1.0, progress));
|
||||
int targetFrame = TotalFrames > 1 ? (int)(progress * (TotalFrames - 1)) : 0;
|
||||
SeekToFrame(targetFrame);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"跳转到进度失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单帧前进
|
||||
/// </summary>
|
||||
public void StepForward()
|
||||
{
|
||||
StepForward(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 前进指定帧数
|
||||
/// </summary>
|
||||
/// <param name="frames">帧数</param>
|
||||
public void StepForward(int frames)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (frames <= 0) return;
|
||||
|
||||
int targetFrame = _currentFrameIndex + frames;
|
||||
targetFrame = Math.Min(targetFrame, TotalFrames - 1);
|
||||
|
||||
SeekToFrame(targetFrame);
|
||||
LogManager.Debug($"前进 {frames} 帧,当前帧: {_currentFrameIndex}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"前进步进失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单帧后退
|
||||
/// </summary>
|
||||
public void StepBackward()
|
||||
{
|
||||
StepBackward(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后退指定帧数
|
||||
/// </summary>
|
||||
/// <param name="frames">帧数</param>
|
||||
public void StepBackward(int frames)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (frames <= 0) return;
|
||||
|
||||
int targetFrame = _currentFrameIndex - frames;
|
||||
targetFrame = Math.Max(targetFrame, 0);
|
||||
|
||||
SeekToFrame(targetFrame);
|
||||
LogManager.Debug($"后退 {frames} 帧,当前帧: {_currentFrameIndex}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"后退步进失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快进10帧
|
||||
/// </summary>
|
||||
public void FastForward()
|
||||
{
|
||||
StepForward(10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快退10帧
|
||||
/// </summary>
|
||||
public void FastBackward()
|
||||
{
|
||||
StepBackward(10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始正向播放
|
||||
/// </summary>
|
||||
public void PlayForward()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetPlaybackDirection(false);
|
||||
if (_currentState == AnimationState.Paused)
|
||||
{
|
||||
ResumeAnimation(); // 从暂停位置继续
|
||||
}
|
||||
else if (_currentState != AnimationState.Playing)
|
||||
{
|
||||
StartAnimation(); // 只有非播放和非暂停状态才从头开始
|
||||
}
|
||||
LogManager.Info("开始正向播放");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"正向播放失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始反向播放
|
||||
/// </summary>
|
||||
public void PlayReverse()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetPlaybackDirection(true);
|
||||
if (_currentState == AnimationState.Paused)
|
||||
{
|
||||
ResumeAnimation(); // 从暂停位置继续
|
||||
}
|
||||
else if (_currentState != AnimationState.Playing)
|
||||
{
|
||||
StartAnimation(); // 只有非播放和非暂停状态才从头开始
|
||||
}
|
||||
LogManager.Info("开始反向播放");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"反向播放失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 清理对象引用
|
||||
/// </summary>
|
||||
@ -1713,18 +1965,38 @@ namespace NavisworksTransport.Core.Animation
|
||||
return; // 还没到下一帧时间,跳过本次Idle
|
||||
}
|
||||
|
||||
// 计算动画进度
|
||||
var totalElapsed = (now - _animationStartTime).TotalSeconds;
|
||||
var progress = Math.Min(totalElapsed / _animationDuration, 1.0);
|
||||
|
||||
// 根据进度计算当前帧索引
|
||||
int targetFrameIndex = (int)(progress * (_animationFrames.Count - 1));
|
||||
targetFrameIndex = Math.Min(targetFrameIndex, _animationFrames.Count - 1);
|
||||
// 计算下一帧索引(考虑播放方向和速度)
|
||||
int nextFrameIndex = _currentFrameIndex;
|
||||
|
||||
// 如果帧索引发生变化,更新位置和碰撞高亮
|
||||
if (targetFrameIndex != _currentFrameIndex || _currentFrameIndex == 0)
|
||||
if (_playbackDirection == 1) // 正向播放
|
||||
{
|
||||
_currentFrameIndex = targetFrameIndex;
|
||||
nextFrameIndex = _currentFrameIndex + 1;
|
||||
if (nextFrameIndex >= _animationFrames.Count)
|
||||
{
|
||||
// 正向播放到达终点
|
||||
nextFrameIndex = _animationFrames.Count - 1;
|
||||
NavisApplication.Idle -= OnApplicationIdle;
|
||||
FinishAnimation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // 反向播放
|
||||
{
|
||||
nextFrameIndex = _currentFrameIndex - 1;
|
||||
if (nextFrameIndex < 0)
|
||||
{
|
||||
// 反向播放到达起点
|
||||
nextFrameIndex = 0;
|
||||
NavisApplication.Idle -= OnApplicationIdle;
|
||||
FinishAnimation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新帧索引和位置
|
||||
if (nextFrameIndex != _currentFrameIndex)
|
||||
{
|
||||
_currentFrameIndex = nextFrameIndex;
|
||||
|
||||
// 使用预计算的帧位置
|
||||
if (_currentFrameIndex < _animationFrames.Count)
|
||||
@ -1735,34 +2007,29 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 更新碰撞高亮(基于预计算结果)
|
||||
UpdateCollisionHighlightFromFrame();
|
||||
|
||||
LogManager.Debug($"帧 {_currentFrameIndex}/{_animationFrames.Count-1}, 进度: {progress*100:F1}%, 位置: ({frameData.Position.X:F2},{frameData.Position.Y:F2},{frameData.Position.Z:F2})");
|
||||
// 计算当前进度(基于帧索引)
|
||||
double progress = _animationFrames.Count > 1 ? (double)_currentFrameIndex / (_animationFrames.Count - 1) : 0;
|
||||
|
||||
LogManager.Debug($"帧 {_currentFrameIndex}/{_animationFrames.Count-1}, 进度: {progress*100:F1}%, 方向: {(_playbackDirection == 1 ? "正向" : "反向")}, 位置: ({frameData.Position.X:F2},{frameData.Position.Y:F2},{frameData.Position.Z:F2})");
|
||||
|
||||
// 触发进度事件
|
||||
var progressPercent = progress * 100;
|
||||
ProgressChanged?.Invoke(this, progressPercent);
|
||||
|
||||
// 同步进度到 TimeLiner(如果可用)
|
||||
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
|
||||
{
|
||||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 触发进度事件
|
||||
var progressPercent = progress * 100;
|
||||
ProgressChanged?.Invoke(this, progressPercent);
|
||||
|
||||
// 同步进度到 TimeLiner(如果可用)
|
||||
if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId))
|
||||
{
|
||||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
|
||||
}
|
||||
|
||||
// 记录帧时间
|
||||
_lastFrameTime = now;
|
||||
_animationFrameCount++;
|
||||
|
||||
// 性能监控
|
||||
UpdateFPSCounter();
|
||||
|
||||
// 检查动画完成
|
||||
if (progress >= 1.0)
|
||||
{
|
||||
// 先注销事件,避免重复调用
|
||||
NavisApplication.Idle -= OnApplicationIdle;
|
||||
FinishAnimation();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
240
src/UI/WPF/Resources/MediaControlIcons.xaml
Normal file
240
src/UI/WPF/Resources/MediaControlIcons.xaml
Normal file
@ -0,0 +1,240 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- 媒体控制图标定义 -->
|
||||
|
||||
<!-- 快退10帧图标 << -->
|
||||
<Geometry x:Key="FastBackwardIcon">
|
||||
M11,6L2,12L11,18V6M20,6L11,12L20,18V6Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 上一帧图标 <| -->
|
||||
<Geometry x:Key="StepBackwardIcon">
|
||||
M6,6V18H8V6H6M9.5,12L18,6V18L9.5,12Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 反向播放图标 ◀ -->
|
||||
<Geometry x:Key="ReversePlayIcon">
|
||||
M8,6V18L19,12L8,6Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 暂停图标 || -->
|
||||
<Geometry x:Key="PauseIcon">
|
||||
M14,18H18V6H14M6,18H10V6H6V18Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 正向播放图标 ▶ -->
|
||||
<Geometry x:Key="PlayIcon">
|
||||
M8,6V18L19,12L8,6Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 下一帧图标 |> -->
|
||||
<Geometry x:Key="StepForwardIcon">
|
||||
M16,18H18V6H16M6,18L14.5,12L6,6V18Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 快进10帧图标 >> -->
|
||||
<Geometry x:Key="FastForwardIcon">
|
||||
M13,6V18L22,12M4,18L12.5,12L4,6V18Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 停止图标 ■ -->
|
||||
<Geometry x:Key="StopIcon">
|
||||
M18,18H6V6H18V18Z
|
||||
</Geometry>
|
||||
|
||||
<!-- 媒体控制按钮基础样式 -->
|
||||
<Style x:Key="MediaControlButtonStyle" TargetType="Button">
|
||||
<Setter Property="Width" Value="32"/>
|
||||
<Setter Property="Height" Value="32"/>
|
||||
<Setter Property="Margin" Value="2"/>
|
||||
<Setter Property="Background" Value="#FF404040"/>
|
||||
<Setter Property="BorderBrush" Value="#FF606060"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="3">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#FF5A5A5A"/>
|
||||
<Setter TargetName="border" Property="BorderBrush" Value="#FF4472C4"/>
|
||||
<Setter Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="1.05" ScaleY="1.05"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#FF2B579A"/>
|
||||
<Setter Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="0.95" ScaleY="0.95"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="border" Property="Background" Value="#FF2A2A2A"/>
|
||||
<Setter TargetName="border" Property="BorderBrush" Value="#FF3A3A3A"/>
|
||||
<Setter Property="Foreground" Value="#FF666666"/>
|
||||
<Setter Property="Opacity" Value="0.6"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 停止按钮样式(红色主题) -->
|
||||
<Style x:Key="StopButtonStyle" TargetType="Button" BasedOn="{StaticResource MediaControlButtonStyle}">
|
||||
<Setter Property="Background" Value="#FF8B0000"/>
|
||||
<Setter Property="BorderBrush" Value="#FFAA0000"/>
|
||||
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="border"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="3">
|
||||
<ContentPresenter HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#FFAA0000"/>
|
||||
<Setter TargetName="border" Property="BorderBrush" Value="#FFCC0000"/>
|
||||
<Setter Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="1.05" ScaleY="1.05"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter TargetName="border" Property="Background" Value="#FF660000"/>
|
||||
<Setter Property="RenderTransform">
|
||||
<Setter.Value>
|
||||
<ScaleTransform ScaleX="0.95" ScaleY="0.95"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="border" Property="Background" Value="#FF2A2A2A"/>
|
||||
<Setter TargetName="border" Property="BorderBrush" Value="#FF3A3A3A"/>
|
||||
<Setter Property="Foreground" Value="#FF666666"/>
|
||||
<Setter Property="Opacity" Value="0.6"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 播放状态指示样式 -->
|
||||
<Style x:Key="ActivePlayButtonStyle" TargetType="Button" BasedOn="{StaticResource MediaControlButtonStyle}">
|
||||
<Setter Property="Background" Value="#FF4472C4"/>
|
||||
<Setter Property="BorderBrush" Value="#FF5A82D4"/>
|
||||
</Style>
|
||||
|
||||
<!-- 滑块按钮样式(必须在TimelineSliderStyle之前定义) -->
|
||||
<Style x:Key="SliderButtonStyle" TargetType="RepeatButton">
|
||||
<Setter Property="SnapsToDevicePixels" Value="true"/>
|
||||
<Setter Property="OverridesDefaultStyle" Value="true"/>
|
||||
<Setter Property="IsTabStop" Value="false"/>
|
||||
<Setter Property="Focusable" Value="false"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="RepeatButton">
|
||||
<Border BorderThickness="1"
|
||||
BorderBrush="#FF606060"
|
||||
Background="#FF404040"
|
||||
Height="6"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 滑块拇指样式(必须在TimelineSliderStyle之前定义) -->
|
||||
<Style x:Key="SliderThumbStyle" TargetType="Thumb">
|
||||
<Setter Property="SnapsToDevicePixels" Value="true"/>
|
||||
<Setter Property="OverridesDefaultStyle" Value="true"/>
|
||||
<Setter Property="Height" Value="14"/>
|
||||
<Setter Property="Width" Value="14"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Thumb">
|
||||
<Ellipse x:Name="Ellipse"
|
||||
Fill="#FF4472C4"
|
||||
Stroke="#FF2B579A"
|
||||
StrokeThickness="1"/>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="Ellipse" Property="Fill" Value="#FF5A82D4"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsDragging" Value="True">
|
||||
<Setter TargetName="Ellipse" Property="Fill" Value="#FF2B579A"/>
|
||||
<Setter TargetName="Ellipse" Property="StrokeThickness" Value="2"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 时间轴滑块样式 -->
|
||||
<Style x:Key="TimelineSliderStyle" TargetType="Slider">
|
||||
<Setter Property="Height" Value="20"/>
|
||||
<Setter Property="Background" Value="#FF404040"/>
|
||||
<Setter Property="Foreground" Value="#FF4472C4"/>
|
||||
<Setter Property="BorderBrush" Value="#FF606060"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Slider">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Track Grid.Row="1" x:Name="PART_Track">
|
||||
<Track.DecreaseRepeatButton>
|
||||
<RepeatButton Style="{StaticResource SliderButtonStyle}"
|
||||
Command="Slider.DecreaseLarge"/>
|
||||
</Track.DecreaseRepeatButton>
|
||||
<Track.Thumb>
|
||||
<Thumb Style="{StaticResource SliderThumbStyle}"/>
|
||||
</Track.Thumb>
|
||||
<Track.IncreaseRepeatButton>
|
||||
<RepeatButton Style="{StaticResource SliderButtonStyle}"
|
||||
Command="Slider.IncreaseLarge"/>
|
||||
</Track.IncreaseRepeatButton>
|
||||
</Track>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 时间显示文本样式 -->
|
||||
<Style x:Key="TimeDisplayStyle" TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="FontFamily" Value="Consolas, Courier New"/>
|
||||
<Setter Property="FontSize" Value="12"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
<Setter Property="Margin" Value="8,0"/>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@ -77,12 +77,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private ObservableCollection<int> _availableFrameRates;
|
||||
private int _selectedFrameRate = 30;
|
||||
private double _animationDuration = 10.0;
|
||||
private double _currentAnimationTime = 0.0;
|
||||
private bool _canStartAnimation = true;
|
||||
private bool _canPauseAnimation = false;
|
||||
private bool _canStopAnimation = false;
|
||||
private double _animationProgress = 0.0;
|
||||
private string _startAnimationButtonText = "开始动画";
|
||||
|
||||
// 碰撞检测相关字段
|
||||
private bool _hasCollisionResults = false;
|
||||
@ -142,15 +139,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 当前动画时间
|
||||
/// </summary>
|
||||
public double CurrentAnimationTime
|
||||
{
|
||||
get => _currentAnimationTime;
|
||||
set => SetProperty(ref _currentAnimationTime, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否可以开始动画
|
||||
/// </summary>
|
||||
@ -179,25 +167,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 动画进度
|
||||
/// </summary>
|
||||
public double AnimationProgress
|
||||
{
|
||||
get => _animationProgress;
|
||||
set => SetProperty(ref _animationProgress, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始动画按钮文本
|
||||
/// </summary>
|
||||
public string StartAnimationButtonText
|
||||
{
|
||||
get => _startAnimationButtonText;
|
||||
set => SetProperty(ref _startAnimationButtonText, value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否有碰撞检测结果
|
||||
/// </summary>
|
||||
@ -359,11 +328,80 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
set => SetProperty(ref _canGenerateAnimation, value);
|
||||
}
|
||||
|
||||
#region 媒体控制属性
|
||||
|
||||
/// <summary>
|
||||
/// 时间轴位置(0-总帧数)
|
||||
/// </summary>
|
||||
public double TimelinePosition
|
||||
{
|
||||
get => _pathAnimationManager?.CurrentFrame ?? 0;
|
||||
set
|
||||
{
|
||||
if (_pathAnimationManager != null)
|
||||
{
|
||||
_pathAnimationManager.SeekToFrame((int)Math.Round(value));
|
||||
}
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间轴持续时间(总帧数)
|
||||
/// </summary>
|
||||
public double TimelineDuration => _pathAnimationManager?.TotalFrames - 1 ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 当前时间显示(格式化)
|
||||
/// </summary>
|
||||
public string CurrentTimeDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_pathAnimationManager == null) return "00:00";
|
||||
|
||||
var currentSeconds = (_pathAnimationManager.CurrentFrame / (double)(_pathAnimationManager.TotalFrames - 1)) * _animationDuration;
|
||||
return FormatTime(currentSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 总时长显示(格式化)
|
||||
/// </summary>
|
||||
public string TotalTimeDisplay => FormatTime(_animationDuration);
|
||||
|
||||
/// <summary>
|
||||
/// 帧信息显示
|
||||
/// </summary>
|
||||
public string FrameInfoDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_pathAnimationManager == null) return "0/0";
|
||||
return $"{_pathAnimationManager.CurrentFrame + 1}/{_pathAnimationManager.TotalFrames}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在正向播放
|
||||
/// </summary>
|
||||
public bool IsPlayingForward => _pathAnimationManager?.PlaybackDirection == 1 &&
|
||||
_pathAnimationManager?.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Playing;
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在反向播放
|
||||
/// </summary>
|
||||
public bool IsPlayingReverse => _pathAnimationManager?.PlaybackDirection == -1 &&
|
||||
_pathAnimationManager?.CurrentState == NavisworksTransport.Core.Animation.AnimationState.Playing;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 命令
|
||||
|
||||
// 原有命令
|
||||
public ICommand StartAnimationCommand { get; private set; }
|
||||
public ICommand PauseAnimationCommand { get; private set; }
|
||||
public ICommand StopAnimationCommand { get; private set; }
|
||||
@ -372,6 +410,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ICommand ClearAnimatedObjectCommand { get; private set; }
|
||||
public ICommand GenerateAnimationCommand { get; private set; }
|
||||
|
||||
// 媒体控制命令
|
||||
public ICommand PlayForwardCommand { get; private set; }
|
||||
public ICommand PlayReverseCommand { get; private set; }
|
||||
public ICommand StepForwardCommand { get; private set; }
|
||||
public ICommand StepBackwardCommand { get; private set; }
|
||||
public ICommand FastForwardCommand { get; private set; }
|
||||
public ICommand FastBackwardCommand { get; private set; }
|
||||
public ICommand SeekToStartCommand { get; private set; }
|
||||
public ICommand SeekToEndCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
@ -484,13 +532,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
// 设置默认动画参数
|
||||
AnimationDuration = 10.0;
|
||||
CurrentAnimationTime = 0.0;
|
||||
|
||||
// 设置初始状态 - 修改: 默认状态应该是未激活,等待有效动画
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = false;
|
||||
StartAnimationButtonText = "开始动画";
|
||||
AnimationProgress = 0;
|
||||
|
||||
// 初始化碰撞检测状态
|
||||
HasCollisionResults = false;
|
||||
@ -517,6 +562,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// </summary>
|
||||
private void InitializeCommands()
|
||||
{
|
||||
// 原有命令
|
||||
StartAnimationCommand = new RelayCommand(async () => await ExecuteStartAnimationAsync(), () => CanStartAnimation);
|
||||
PauseAnimationCommand = new RelayCommand(async () => await ExecutePauseAnimationAsync(), () => CanPauseAnimation);
|
||||
StopAnimationCommand = new RelayCommand(async () => await ExecuteStopAnimationAsync(), () => CanStopAnimation);
|
||||
@ -525,6 +571,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
ClearAnimatedObjectCommand = new RelayCommand(ExecuteClearAnimatedObject, () => HasSelectedAnimatedObject);
|
||||
GenerateAnimationCommand = new RelayCommand(ExecuteGenerateAnimation, () => CanGenerateAnimation);
|
||||
|
||||
// 媒体控制命令
|
||||
PlayForwardCommand = new RelayCommand(ExecutePlayForward, CanExecuteMediaCommands);
|
||||
PlayReverseCommand = new RelayCommand(ExecutePlayReverse, CanExecuteMediaCommands);
|
||||
StepForwardCommand = new RelayCommand(ExecuteStepForward, CanExecuteMediaCommands);
|
||||
StepBackwardCommand = new RelayCommand(ExecuteStepBackward, CanExecuteMediaCommands);
|
||||
FastForwardCommand = new RelayCommand(ExecuteFastForward, CanExecuteMediaCommands);
|
||||
FastBackwardCommand = new RelayCommand(ExecuteFastBackward, CanExecuteMediaCommands);
|
||||
SeekToStartCommand = new RelayCommand(ExecuteSeekToStart, CanExecuteMediaCommands);
|
||||
SeekToEndCommand = new RelayCommand(ExecuteSeekToEnd, CanExecuteMediaCommands);
|
||||
|
||||
LogManager.Info("动画控制命令初始化完成");
|
||||
}
|
||||
|
||||
@ -592,11 +648,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
LogManager.Info("已清理UI层碰撞检测缓存");
|
||||
|
||||
// 首先重置进度(开始全新动画时)
|
||||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
AnimationProgress = 0;
|
||||
CurrentAnimationTime = 0.0;
|
||||
});
|
||||
|
||||
_pathAnimationManager.StartAnimation();
|
||||
|
||||
@ -652,8 +703,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
await _uiStateManager.ExecuteUIUpdateAsync(() =>
|
||||
{
|
||||
UpdateMainStatus("动画已停止");
|
||||
AnimationProgress = 0;
|
||||
CurrentAnimationTime = 0.0;
|
||||
|
||||
CanStartAnimation = true;
|
||||
CanPauseAnimation = false;
|
||||
@ -738,6 +787,181 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
#region 媒体控制命令实现
|
||||
|
||||
/// <summary>
|
||||
/// 检查媒体控制命令是否可执行
|
||||
/// </summary>
|
||||
private bool CanExecuteMediaCommands()
|
||||
{
|
||||
return _pathAnimationManager != null &&
|
||||
_pathAnimationManager.CurrentState != NavisworksTransport.Core.Animation.AnimationState.Idle &&
|
||||
_pathAnimationManager.TotalFrames > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行正向播放命令
|
||||
/// </summary>
|
||||
private void ExecutePlayForward()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.PlayForward();
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Info("执行正向播放命令");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"正向播放命令执行失败: {ex.Message}");
|
||||
UpdateMainStatus("正向播放失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行反向播放命令
|
||||
/// </summary>
|
||||
private void ExecutePlayReverse()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.PlayReverse();
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Info("执行反向播放命令");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"反向播放命令执行失败: {ex.Message}");
|
||||
UpdateMainStatus("反向播放失败");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行单帧前进命令
|
||||
/// </summary>
|
||||
private void ExecuteStepForward()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.StepForward();
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Debug("执行单帧前进命令");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"单帧前进命令执行失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行单帧后退命令
|
||||
/// </summary>
|
||||
private void ExecuteStepBackward()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.StepBackward();
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Debug("执行单帧后退命令");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"单帧后退命令执行失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行快进10帧命令
|
||||
/// </summary>
|
||||
private void ExecuteFastForward()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.FastForward();
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Debug("执行快进10帧命令");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"快进命令执行失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行快退10帧命令
|
||||
/// </summary>
|
||||
private void ExecuteFastBackward()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.FastBackward();
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Debug("执行快退10帧命令");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"快退命令执行失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行跳转到开头命令
|
||||
/// </summary>
|
||||
private void ExecuteSeekToStart()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathAnimationManager?.SeekToFrame(0);
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Info("跳转到动画开头");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"跳转到开头失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行跳转到结尾命令
|
||||
/// </summary>
|
||||
private void ExecuteSeekToEnd()
|
||||
{
|
||||
try
|
||||
{
|
||||
int lastFrame = (_pathAnimationManager?.TotalFrames ?? 1) - 1;
|
||||
_pathAnimationManager?.SeekToFrame(lastFrame);
|
||||
UpdateMediaControlProperties();
|
||||
LogManager.Info("跳转到动画结尾");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"跳转到结尾失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新媒体控制相关属性
|
||||
/// </summary>
|
||||
private void UpdateMediaControlProperties()
|
||||
{
|
||||
OnPropertyChanged(nameof(TimelinePosition));
|
||||
OnPropertyChanged(nameof(TimelineDuration));
|
||||
OnPropertyChanged(nameof(CurrentTimeDisplay));
|
||||
OnPropertyChanged(nameof(FrameInfoDisplay));
|
||||
OnPropertyChanged(nameof(IsPlayingForward));
|
||||
OnPropertyChanged(nameof(IsPlayingReverse));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化时间显示
|
||||
/// </summary>
|
||||
private string FormatTime(double seconds)
|
||||
{
|
||||
var timeSpan = TimeSpan.FromSeconds(Math.Max(0, seconds));
|
||||
return $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region 辅助方法
|
||||
@ -794,9 +1018,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 直接使用Dispatcher.BeginInvoke进行UI更新,避免UIStateManager的5秒超时导致积压
|
||||
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
// 直接使用传入的百分比数值
|
||||
AnimationProgress = progressPercent;
|
||||
CurrentAnimationTime = (AnimationProgress / 100.0) * AnimationDuration;
|
||||
// 更新时间轴相关属性
|
||||
OnPropertyChanged(nameof(TimelinePosition));
|
||||
OnPropertyChanged(nameof(CurrentTimeDisplay));
|
||||
OnPropertyChanged(nameof(FrameInfoDisplay));
|
||||
}));
|
||||
}
|
||||
|
||||
@ -815,35 +1040,35 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
CanStartAnimation = false;
|
||||
CanPauseAnimation = true;
|
||||
CanStopAnimation = true;
|
||||
StartAnimationButtonText = "开始动画"; // 播放中时按钮显示为灰色的"开始动画"
|
||||
UpdateMainStatus("动画播放中");
|
||||
UpdateMediaControlProperties(); // 更新媒体控制属性
|
||||
break;
|
||||
case NavisworksTransport.Core.Animation.AnimationState.Paused:
|
||||
CanStartAnimation = true; // 暂停状态下可以继续
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = true;
|
||||
StartAnimationButtonText = "继续播放"; // 暂停状态下显示"继续播放"
|
||||
UpdateMainStatus("动画已暂停");
|
||||
UpdateMediaControlProperties(); // 更新媒体控制属性
|
||||
break;
|
||||
case NavisworksTransport.Core.Animation.AnimationState.Finished:
|
||||
CanStartAnimation = true;
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = false;
|
||||
StartAnimationButtonText = "开始动画"; // 完成后恢复"开始动画"
|
||||
UpdateMainStatus("动画已完成");
|
||||
// 动画完成后更新碰撞检测状态
|
||||
UpdateCollisionStatusAfterAnimation();
|
||||
UpdateMediaControlProperties(); // 更新媒体控制属性
|
||||
break;
|
||||
case NavisworksTransport.Core.Animation.AnimationState.Stopped:
|
||||
CanStartAnimation = true;
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = false;
|
||||
StartAnimationButtonText = "开始动画"; // 停止后恢复"开始动画"
|
||||
UpdateMainStatus("动画已停止");
|
||||
UpdateMediaControlProperties(); // 更新媒体控制属性
|
||||
break;
|
||||
default:
|
||||
StartAnimationButtonText = "开始动画";
|
||||
UpdateMainStatus($"动画状态: {state}");
|
||||
UpdateMediaControlProperties(); // 更新媒体控制属性
|
||||
break;
|
||||
}
|
||||
}));
|
||||
@ -987,11 +1212,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
// 清理选择状态
|
||||
SelectedAnimatedObject = null;
|
||||
|
||||
// 重置动画进度和状态
|
||||
AnimationProgress = 0;
|
||||
CurrentAnimationTime = 0.0;
|
||||
|
||||
|
||||
// 清理碰撞检测结果
|
||||
HasCollisionResults = false;
|
||||
UpdateMainStatus("碰撞检测重置");
|
||||
@ -1269,16 +1490,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
CanStartAnimation = hasValidAnimation && isAnimationReady;
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = true;
|
||||
StartAnimationButtonText = "继续播放";
|
||||
UpdateMainStatus("动画已暂停");
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// 停止或其他状态:需要同时满足有效动画条件和动画已生成条件
|
||||
CanStartAnimation = hasValidAnimation && isAnimationReady;
|
||||
CanPauseAnimation = false;
|
||||
CanStopAnimation = false;
|
||||
StartAnimationButtonText = "开始动画";
|
||||
|
||||
// 更新状态文本
|
||||
if (!hasValidPath && !hasValidAnimatedObject)
|
||||
@ -1698,11 +1917,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
SelectedAnimatedObjectName = string.Empty;
|
||||
CurrentPathRoute = null;
|
||||
|
||||
// 重置动画参数
|
||||
AnimationProgress = 0;
|
||||
CurrentAnimationTime = 0;
|
||||
StartAnimationButtonText = "开始";
|
||||
|
||||
// 禁用按钮
|
||||
CanStartAnimation = false;
|
||||
CanPauseAnimation = false;
|
||||
|
||||
@ -15,6 +15,7 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:converters="clr-namespace:NavisworksTransport.UI.WPF.Converters"
|
||||
xmlns:views="clr-namespace:NavisworksTransport.UI.WPF.Views"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="700" d:DesignWidth="480">
|
||||
|
||||
@ -254,57 +255,14 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 区域4: 播放控制 -->
|
||||
<!-- 区域4: 专业级媒体播放控制 -->
|
||||
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,0,0,15" Padding="12">
|
||||
<StackPanel>
|
||||
<Label Content="播放控制" Style="{StaticResource SectionHeaderStyle}"/>
|
||||
|
||||
<!-- 播放按钮组 -->
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,15">
|
||||
<Button Content="{Binding StartAnimationButtonText}"
|
||||
Command="{Binding StartAnimationCommand}"
|
||||
IsEnabled="{Binding CanStartAnimation}"
|
||||
Style="{StaticResource ActionButtonStyle}"/>
|
||||
<Button Content="暂停动画"
|
||||
Command="{Binding PauseAnimationCommand}"
|
||||
IsEnabled="{Binding CanPauseAnimation}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"/>
|
||||
<Button Content="停止动画"
|
||||
Command="{Binding StopAnimationCommand}"
|
||||
IsEnabled="{Binding CanStopAnimation}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"/>
|
||||
</StackPanel>
|
||||
<!-- 专业级媒体控制条 -->
|
||||
<views:MediaControlBar DataContext="{Binding}" Margin="0,10,0,10"/>
|
||||
|
||||
|
||||
<!-- 进度条 -->
|
||||
<Grid Margin="0,5,0,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Column="0" Content="播放进度:" Width="80" VerticalAlignment="Center"/>
|
||||
<ProgressBar Grid.Column="1"
|
||||
Value="{Binding AnimationProgress}"
|
||||
Style="{StaticResource ProgressBarStyle}"
|
||||
Width="300"
|
||||
Maximum="100"
|
||||
Margin="5,0,10,0"/>
|
||||
<TextBlock Grid.Column="2"
|
||||
Text="{Binding AnimationProgress, StringFormat={}{0:F1}%}"
|
||||
Width="80"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="#FF4472C4"
|
||||
Margin="10,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 时间信息 -->
|
||||
<TextBlock Text="{Binding CurrentAnimationTime, StringFormat='当前时间: {0:F1}s'}"
|
||||
Style="{StaticResource StatusTextStyle}"
|
||||
Margin="0,5,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
211
src/UI/WPF/Views/MediaControlBar.xaml
Normal file
211
src/UI/WPF/Views/MediaControlBar.xaml
Normal file
@ -0,0 +1,211 @@
|
||||
<UserControl x:Class="NavisworksTransport.UI.WPF.Views.MediaControlBar"
|
||||
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"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="60" d:DesignWidth="600">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/NavisworksTransportPlugin;component/src/UI/WPF/Resources/MediaControlIcons.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
<!-- 媒体控制条主容器 -->
|
||||
<Border Background="#FF2D2D30"
|
||||
BorderBrush="#FF3F3F46"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
MouseEnter="OnMouseEnter"
|
||||
GotFocus="OnGotFocus">
|
||||
<Grid Margin="10,8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 播放控制按钮行 -->
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,8">
|
||||
|
||||
<!-- 快退10帧 << -->
|
||||
<Button x:Name="FastBackwardButton"
|
||||
Command="{Binding FastBackwardCommand}"
|
||||
Style="{StaticResource MediaControlButtonStyle}"
|
||||
ToolTip="快退10帧 (Shift+←)">
|
||||
<Path Data="{StaticResource FastBackwardIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="14" Height="14"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
<!-- 上一帧 <| -->
|
||||
<Button x:Name="StepBackwardButton"
|
||||
Command="{Binding StepBackwardCommand}"
|
||||
Style="{StaticResource MediaControlButtonStyle}"
|
||||
ToolTip="上一帧 (←)">
|
||||
<Path Data="{StaticResource StepBackwardIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="12" Height="12"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
<!-- 反向播放 ◀ -->
|
||||
<Button x:Name="ReversePlayButton"
|
||||
Command="{Binding PlayReverseCommand}"
|
||||
ToolTip="反向播放">
|
||||
<Button.Style>
|
||||
<Style BasedOn="{StaticResource MediaControlButtonStyle}" TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsPlayingReverse}" Value="True">
|
||||
<Setter Property="Background" Value="#FF4472C4"/>
|
||||
<Setter Property="BorderBrush" Value="#FF5A82D4"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
<Path Data="{StaticResource ReversePlayIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="12" Height="12"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Path.RenderTransform>
|
||||
<ScaleTransform ScaleX="-1" ScaleY="1"/>
|
||||
</Path.RenderTransform>
|
||||
</Path>
|
||||
</Button>
|
||||
|
||||
<!-- 暂停 || (居中位置) -->
|
||||
<Button x:Name="PauseButton"
|
||||
Command="{Binding PauseAnimationCommand}"
|
||||
IsEnabled="{Binding CanPauseAnimation}"
|
||||
Style="{StaticResource MediaControlButtonStyle}"
|
||||
ToolTip="暂停 (Space)">
|
||||
<Path Data="{StaticResource PauseIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="12" Height="12"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
<!-- 正向播放 ▶ -->
|
||||
<Button x:Name="PlayButton"
|
||||
Command="{Binding PlayForwardCommand}"
|
||||
ToolTip="播放">
|
||||
<Button.Style>
|
||||
<Style BasedOn="{StaticResource MediaControlButtonStyle}" TargetType="Button">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsPlayingForward}" Value="True">
|
||||
<Setter Property="Background" Value="#FF4472C4"/>
|
||||
<Setter Property="BorderBrush" Value="#FF5A82D4"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Button.Style>
|
||||
<Path Data="{StaticResource PlayIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="12" Height="12"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
<!-- 下一帧 |> -->
|
||||
<Button x:Name="StepForwardButton"
|
||||
Command="{Binding StepForwardCommand}"
|
||||
Style="{StaticResource MediaControlButtonStyle}"
|
||||
ToolTip="下一帧 (→)">
|
||||
<Path Data="{StaticResource StepForwardIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="12" Height="12"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
<!-- 快进10帧 >> -->
|
||||
<Button x:Name="FastForwardButton"
|
||||
Command="{Binding FastForwardCommand}"
|
||||
Style="{StaticResource MediaControlButtonStyle}"
|
||||
ToolTip="快进10帧 (Shift+→)">
|
||||
<Path Data="{StaticResource FastForwardIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="14" Height="14"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
<!-- 停止 ■ (最右侧) -->
|
||||
<Button x:Name="StopButton"
|
||||
Command="{Binding StopAnimationCommand}"
|
||||
IsEnabled="{Binding CanStopAnimation}"
|
||||
Style="{StaticResource StopButtonStyle}"
|
||||
ToolTip="停止"
|
||||
Margin="12,2,2,2">
|
||||
<Path Data="{StaticResource StopIcon}"
|
||||
Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
|
||||
Width="12" Height="12"
|
||||
Stretch="Uniform"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<!-- 时间轴和时间显示行 -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧信息区 -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,0,10,0">
|
||||
<TextBlock Text="{Binding CurrentTimeDisplay}"
|
||||
Style="{StaticResource TimeDisplayStyle}"
|
||||
MinWidth="50"
|
||||
TextAlignment="Right"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 中央时间轴区(居中) -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
<Slider x:Name="TimelineSlider"
|
||||
Value="{Binding TimelinePosition, Mode=TwoWay}"
|
||||
Minimum="0"
|
||||
Maximum="{Binding TimelineDuration}"
|
||||
Style="{StaticResource TimelineSliderStyle}"
|
||||
Width="300"
|
||||
TickFrequency="1"
|
||||
IsSnapToTickEnabled="True"
|
||||
ToolTip="点击或拖动跳转到指定位置"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 右侧信息区 -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Left" Margin="10,0,0,0">
|
||||
<TextBlock Text="{Binding TotalTimeDisplay}"
|
||||
Style="{StaticResource TimeDisplayStyle}"
|
||||
MinWidth="50"
|
||||
Margin="0,0,15,0"/>
|
||||
<TextBlock Text="{Binding FrameInfoDisplay}"
|
||||
Style="{StaticResource TimeDisplayStyle}"
|
||||
Foreground="#FF999999"
|
||||
FontSize="10"
|
||||
MinWidth="80"
|
||||
TextAlignment="Left"/>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
153
src/UI/WPF/Views/MediaControlBar.xaml.cs
Normal file
153
src/UI/WPF/Views/MediaControlBar.xaml.cs
Normal file
@ -0,0 +1,153 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// MediaControlBar.xaml 的交互逻辑
|
||||
/// 专业级媒体播放器风格的动画控制条
|
||||
/// </summary>
|
||||
public partial class MediaControlBar : UserControl
|
||||
{
|
||||
public MediaControlBar()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// 启用键盘焦点以支持快捷键
|
||||
this.Focusable = true;
|
||||
this.KeyDown += OnKeyDown;
|
||||
|
||||
LogManager.Debug("MediaControlBar 初始化完成");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 键盘快捷键处理
|
||||
/// </summary>
|
||||
private void OnKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var viewModel = this.DataContext as NavisworksTransport.UI.WPF.ViewModels.AnimationControlViewModel;
|
||||
if (viewModel == null) return;
|
||||
|
||||
// 处理键盘快捷键
|
||||
switch (e.Key)
|
||||
{
|
||||
case Key.Space:
|
||||
// 空格键:播放/暂停切换
|
||||
if (viewModel.CanPauseAnimation)
|
||||
{
|
||||
viewModel.PauseAnimationCommand?.Execute(null);
|
||||
}
|
||||
else if (viewModel.CanStartAnimation)
|
||||
{
|
||||
viewModel.StartAnimationCommand?.Execute(null);
|
||||
}
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
||||
case Key.Left:
|
||||
// 左箭头:单帧后退 或 Shift+左箭头:快退10帧
|
||||
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
|
||||
{
|
||||
viewModel.FastBackwardCommand?.Execute(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel.StepBackwardCommand?.Execute(null);
|
||||
}
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
||||
case Key.Right:
|
||||
// 右箭头:单帧前进 或 Shift+右箭头:快进10帧
|
||||
if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
|
||||
{
|
||||
viewModel.FastForwardCommand?.Execute(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel.StepForwardCommand?.Execute(null);
|
||||
}
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
||||
case Key.R:
|
||||
// R键:切换播放方向
|
||||
if (viewModel.IsPlayingReverse)
|
||||
{
|
||||
viewModel.PlayForwardCommand?.Execute(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewModel.PlayReverseCommand?.Execute(null);
|
||||
}
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
||||
case Key.Home:
|
||||
// Home键:跳转到开头
|
||||
viewModel.SeekToStartCommand?.Execute(null);
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
||||
case Key.End:
|
||||
// End键:跳转到结尾
|
||||
viewModel.SeekToEndCommand?.Execute(null);
|
||||
e.Handled = true;
|
||||
break;
|
||||
|
||||
case Key.Escape:
|
||||
// Esc键:停止播放
|
||||
viewModel.StopAnimationCommand?.Execute(null);
|
||||
e.Handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.Handled)
|
||||
{
|
||||
LogManager.Debug($"处理键盘快捷键: {e.Key} (修饰键: {Keyboard.Modifiers})");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogManager.Error($"处理键盘快捷键时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控件获得焦点时自动聚焦到时间轴滑块,以便支持键盘操作
|
||||
/// </summary>
|
||||
private void OnGotFocus(object sender, System.Windows.RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 将焦点设置到时间轴滑块,以便键盘操作
|
||||
TimelineSlider?.Focus();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogManager.Debug($"设置焦点时出现异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 鼠标进入控制条时获取焦点,以便支持键盘快捷键
|
||||
/// </summary>
|
||||
private void OnMouseEnter(object sender, MouseEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.IsFocused)
|
||||
{
|
||||
this.Focus();
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
LogManager.Debug($"鼠标进入时设置焦点异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user