Files
Ayay/SHH.CameraDashboard/ViewModels/CameraImgProcViewModel.cs
2026-01-01 22:40:32 +08:00

369 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace SHH.CameraDashboard
{
public class CameraImgProcViewModel : INotifyPropertyChanged
{
private readonly WebApiCameraModel _sourceCamera;
private readonly CameraEditInfo _editInfo;
public event Action RequestClose;
public string Title => $"{_sourceCamera.Name} - 图像处理";
public string SourceResolutionText => $"{_sourceCamera.Width} x {_sourceCamera.Height}";
// --- 状态控制属性 ---
// 源是否是高清 (>= 1080P)
public bool IsSourceHighRes => _sourceCamera.Width >= 1920 || _sourceCamera.Height >= 1080;
// 是否允许勾选“允许放大” (UI 绑定 IsEnabled)
public bool CanCheckExpand => !IsSourceHighRes;
// 滑块是否可用 (只有开启了缩小 或 开启了放大,才允许拖动)
// ★★★ 修正:使用 AllowEnlarge ★★★
public bool IsSliderEnabled => AllowShrink || AllowEnlarge;
// 允许缩小
public bool AllowShrink
{
get => _editInfo.AllowShrink;
set
{
if (_editInfo.AllowShrink != value)
{
_editInfo.AllowShrink = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsSliderEnabled));
ValidateAndSyncSlider();
}
}
}
// ★★★ 修正:属性名为 AllowEnlarge对应 Model 中的 AllowEnlarge ★★★
public bool AllowEnlarge
{
get => _editInfo.AllowEnlarge;
set
{
if (_editInfo.AllowEnlarge != value)
{
_editInfo.AllowEnlarge = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsSliderEnabled));
// 开关变化时,重新校验当前尺寸是否合法
ValidateAndSyncSlider();
}
}
}
// 图像增强
public bool EnhanceImage
{
get => _editInfo.EnhanceImage;
set
{
if (_editInfo.EnhanceImage != value)
{
_editInfo.EnhanceImage = value;
OnPropertyChanged();
}
}
}
// --- 分辨率与缩放属性 ---
private int _targetWidth;
public int TargetWidth
{
get => _targetWidth;
set
{
if (SetProperty(ref _targetWidth, value))
{
if (IsRatioLocked && _sourceCamera.Width > 0)
{
_targetHeight = (int)((double)value / _sourceCamera.Width * _sourceCamera.Height);
OnPropertyChanged(nameof(TargetHeight));
}
ValidateAndSyncSlider();
}
}
}
private int _targetHeight;
public int TargetHeight
{
get => _targetHeight;
set
{
if (SetProperty(ref _targetHeight, value))
{
if (IsRatioLocked && _sourceCamera.Height > 0)
{
_targetWidth = (int)((double)value / _sourceCamera.Height * _sourceCamera.Width);
OnPropertyChanged(nameof(TargetWidth));
}
ValidateAndSyncSlider();
}
}
}
private bool _isRatioLocked = true;
public bool IsRatioLocked
{
get => _isRatioLocked;
set => SetProperty(ref _isRatioLocked, value);
}
private double _scalePercent = 100;
public double ScalePercent
{
get => _scalePercent;
set
{
if (SetProperty(ref _scalePercent, value))
{
UpdateResolutionByScale(value);
}
}
}
// [新增] 亮度调节属性
public int Brightness
{
get => _editInfo.Brightness;
set
{
if (_editInfo.Brightness != value)
{
_editInfo.Brightness = value;
OnPropertyChanged();
// 如果需要,可以在这里限制范围,例如 0-100
}
}
}
public ICommand SaveCommand { get; }
public ICommand CancelCommand { get; }
public ICommand ApplyPresetCommand { get; }
public CameraImgProcViewModel(WebApiCameraModel source, CameraEditInfo detail)
{
_sourceCamera = source;
// 此时 detail 可能是不完整的,先赋值防止空引用
_editInfo = detail ?? new CameraEditInfo { Id = source.Id };
SaveCommand = new RelayCommand(ExecuteSave);
CancelCommand = new RelayCommand(o => RequestClose?.Invoke());
ApplyPresetCommand = new RelayCommand(ExecutePreset);
InitializeState();
LoadDataAsync();
}
// 2. 实现加载逻辑
private async void LoadDataAsync()
{
// 调用 Repository
var remoteInfo = await ApiClient.Instance.Cameras.GetImageProcessingAsync(_sourceCamera.Id);
if (remoteInfo != null)
{
// --- A. 更新 ViewModel 的开关属性 ---
// 注意Setter 会自动触发 OnPropertyChanged 和 ValidateAndSyncSlider
AllowShrink = remoteInfo.AllowShrink;
AllowEnlarge = remoteInfo.AllowEnlarge; // 这里会自动更新 IsSliderEnabled
EnhanceImage = remoteInfo.EnhanceImage;
// --- B. 更新分辨率 (解析 "W x H") ---
if (!string.IsNullOrWhiteSpace(remoteInfo.TargetResolution))
{
var parts = remoteInfo.TargetResolution.Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h))
{
// 赋值 TargetWidth/Height 会自动触发 ValidateAndSyncSlider
// 从而更新 ScalePercent 和 滑块位置
TargetWidth = w;
TargetHeight = h;
}
}
}
}
private void InitializeState()
{
// 1. 强制规则:如果源分辨率 >= 1080P不允许放大
if (IsSourceHighRes)
{
_editInfo.AllowEnlarge = false; // ★★★ 修正引用 ★★★
}
// 2. 检查历史设置
// ★★★ 修正引用 ★★★
bool hasPreviousSettings = _editInfo.AllowShrink || _editInfo.AllowEnlarge;
if (hasPreviousSettings)
{
if (!string.IsNullOrWhiteSpace(_editInfo.TargetResolution))
{
var parts = _editInfo.TargetResolution.ToLower().Split('x');
if (parts.Length == 2 && int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h))
{
_targetWidth = w;
_targetHeight = h;
}
}
}
else
{
// 默认逻辑
if (_sourceCamera.Width > 1280)
{
_targetWidth = 1280;
_targetHeight = _sourceCamera.Width > 0
? (int)((double)1280 / _sourceCamera.Width * _sourceCamera.Height)
: 720;
}
else
{
_targetWidth = _sourceCamera.Width;
_targetHeight = _sourceCamera.Height;
}
}
ValidateAndSyncSlider();
}
private void UpdateResolutionByScale(double percent)
{
if (_sourceCamera.Width <= 0 || _sourceCamera.Height <= 0) return;
double w = _sourceCamera.Width * (percent / 100.0);
double h = _sourceCamera.Height * (percent / 100.0);
_targetWidth = (int)w;
_targetHeight = (int)h;
OnPropertyChanged(nameof(TargetWidth));
OnPropertyChanged(nameof(TargetHeight));
}
private void ValidateAndSyncSlider()
{
if (_sourceCamera.Width <= 0) return;
// ★★★ 修正引用:使用 AllowEnlarge ★★★
int maxWidth = AllowEnlarge ? 1920 : _sourceCamera.Width;
int maxHeight = AllowEnlarge ? 1080 : _sourceCamera.Height;
int minWidth = 160;
bool changed = false;
// 规则A如果未开启放大且当前 > 源 -> 强制回退到源
// ★★★ 修正引用 ★★★
if (!AllowEnlarge && (_targetWidth > _sourceCamera.Width))
{
_targetWidth = _sourceCamera.Width;
_targetHeight = _sourceCamera.Height;
changed = true;
}
if (!AllowShrink && (_targetWidth < _sourceCamera.Width))
{
_targetWidth = _sourceCamera.Width;
_targetHeight = _sourceCamera.Height;
changed = true;
}
if (_targetWidth > 1920) { _targetWidth = 1920; changed = true; }
if (_targetHeight > 1080) { _targetHeight = 1080; changed = true; }
if (_targetWidth < minWidth) { _targetWidth = minWidth; changed = true; }
if (changed)
{
OnPropertyChanged(nameof(TargetWidth));
OnPropertyChanged(nameof(TargetHeight));
}
double currentPercent = ((double)_targetWidth / _sourceCamera.Width) * 100.0;
if (Math.Abs(_scalePercent - currentPercent) > 0.1)
{
_scalePercent = currentPercent;
OnPropertyChanged(nameof(ScalePercent));
}
}
private void ExecutePreset(object parameter)
{
if (parameter is string preset)
{
var parts = preset.Split('x');
if (parts.Length != 2) return;
if (int.TryParse(parts[0], out int w) && int.TryParse(parts[1], out int h))
{
if (w > _sourceCamera.Width)
{
if (IsSourceHighRes)
{
MessageBox.Show("当前源分辨率已超过或等于 1080P不支持放大。", "提示");
return;
}
AllowEnlarge = true; // ★★★ 修正引用 ★★★
}
else if (w < _sourceCamera.Width)
{
AllowShrink = true;
}
TargetWidth = w;
TargetHeight = h;
IsRatioLocked = true;
}
}
}
private async void ExecuteSave(object obj)
{
// 1. 准备数据 (拼接分辨率字符串)
_editInfo.TargetResolution = $"{TargetWidth}x{TargetHeight}";
// 2. 调用专用的 Processing 接口
bool success = await ApiClient.Instance.Cameras.UpdateImageProcessingAsync(_editInfo); // <--- 换成这就行
if (success)
{
AppGlobal.RequestRefresh();
RequestClose?.Invoke();
}
else
{
MessageBox.Show("保存图像处理配置失败,请检查网络或后端日志。", "错误");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string name = null)
{
if (Equals(field, value)) return false;
field = value;
OnPropertyChanged(name);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}