You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
8.9 KiB
C#

1 month ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
namespace KepGridEditor
{
public partial class FormTrend : XtraForm
{
public event EventHandler<TrendChangedEventArgs> TrendDataChanged;
private BindingList<TrendSource> _trends = new BindingList<TrendSource>();
private bool _isInternalUpdating = false;
public List<TrendSource> TrendParams => _trends.ToList();
public int FusionMode => cbMode.SelectedIndex;
private int _nameCounter = 1; // 初始为1
public FormTrend()
{
InitializeComponent();
SetupUIConfig();
InitData();
BindEvents();
gaugeKnob.Paint += GaugeKnob_Paint;
gaugeKnob.MouseDown += GaugeKnob_MouseDown;
gaugeKnob.MouseMove += GaugeKnob_MouseMove;
// 鼠标松开时,通知主界面最终结果
gaugeKnob.MouseUp += (s, e) => NotifyDataChanged(TrendOperateType.Update);
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
// 窗体完全显示后,手动触发一次 Update 通知
if (_trends.Count > 0)
{
NotifyDataChanged(TrendOperateType.Update);
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
NotifyDataChanged(TrendOperateType.Close);
}
private void SetupUIConfig()
{
cbAlgo.Properties.TextEditStyle = TextEditStyles.DisableTextEditor;
cbMode.Properties.TextEditStyle = TextEditStyles.DisableTextEditor;
cbAlgo.Properties.Items.Add("最小曲率法 (Thin Plate Spline)");
cbAlgo.SelectedIndex = 0;
cbMode.Properties.Items.AddRange(new[] { "加权融合", "极值融合" });
cbMode.SelectedIndex = 0;
arcScale.MinValue = 0;
arcScale.MaxValue = 360;
arcScale.StartAngle = -90;
arcScale.EndAngle = 270;
arcScale.MajorTickCount = 13;
arcScale.AppearanceTickmarkText.TextBrush = new DevExpress.XtraGauges.Core.Drawing.SolidBrushObject(Color.Black);
}
private void InitData()
{
listTrends.DataSource = _trends;
listTrends.DisplayMember = "DisplayText";
_trends.Add(new TrendSource { Name = $"趋势 {_nameCounter++}", Angle = 45, Ratio = 2.0 });
listTrends.SelectedIndex = 0;
}
private void BindEvents()
{
btnAdd.Click += (s, e) => {
var selected = listTrends.SelectedItem as TrendSource;
// 使用 _nameCounter++ 确保每次生成的数字都是唯一的,即使中间有删除
var newItem = new TrendSource
{
Name = $"趋势 {_nameCounter++}",
Angle = 0,
Ratio = 1.0,
Cx = selected?.Cx ?? 0,
Cy = selected?.Cy ?? 0
};
_trends.Add(newItem);
listTrends.SelectedIndex = _trends.Count - 1;
NotifyDataChanged(TrendOperateType.Update);
};
btnDel.Click += (s, e) =>
{
if (_trends.Count > 1)
{
var cur = listTrends.SelectedItem as TrendSource;
string nameToDelete = cur.Name; // 记录要删除的名字
_trends.RemoveAt(listTrends.SelectedIndex);
gaugeKnob.Invalidate();
NotifyDataChanged(TrendOperateType.Delete, nameToDelete); // 通知项删除
}
};
cbMode.SelectedIndexChanged += (s, e) => NotifyDataChanged(TrendOperateType.Update);
listTrends.SelectedIndexChanged += (s, e) => SyncUIFromModel();
trackRatio.ValueChanged += (s, e) =>
{
if (_isInternalUpdating || listTrends.SelectedItem == null) return;
var cur = listTrends.SelectedItem as TrendSource;
cur.Ratio = trackRatio.Value / 10.0;
lblRatioNum.Text = cur.Ratio.ToString("F1");
listTrends.Refresh();
};
// 滑块松开时通知
trackRatio.MouseUp += (s, e) => NotifyDataChanged(TrendOperateType.Update);
}
private void GaugeKnob_Paint(object sender, PaintEventArgs e)
{
if (listTrends.SelectedItem is TrendSource cur)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Point center = new Point(gaugeKnob.Width / 2, gaugeKnob.Height / 2);
double angleRad = (cur.Angle - 90) * Math.PI / 180.0;
int radius = (Math.Min(gaugeKnob.Width, gaugeKnob.Height) / 2) - 20;
Point endPoint = new Point((int)(center.X + radius * Math.Cos(angleRad)), (int)(center.Y + radius * Math.Sin(angleRad)));
using (Pen p = new Pen(Color.Blue, 6)) { p.EndCap = LineCap.ArrowAnchor; e.Graphics.DrawLine(p, center, endPoint); }
e.Graphics.FillEllipse(Brushes.DarkGray, center.X - 8, center.Y - 8, 16, 16);
}
}
private void GaugeKnob_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) UpdateAngle(e.Location); }
private void GaugeKnob_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) UpdateAngle(e.Location); }
private void UpdateAngle(Point pos)
{
if (listTrends.SelectedItem == null) return;
Point center = new Point(gaugeKnob.Width / 2, gaugeKnob.Height / 2);
double angleRad = Math.Atan2(pos.Y - center.Y, pos.X - center.X);
double angleDeg = angleRad * (180 / Math.PI) + 90;
if (angleDeg < 0) angleDeg += 360;
var cur = listTrends.SelectedItem as TrendSource;
cur.Angle = Math.Round(angleDeg);
lblAngleValue.Text = $"{cur.Angle}°";
listTrends.Refresh();
gaugeKnob.Invalidate();
}
private void SyncUIFromModel()
{
if (listTrends.SelectedItem is TrendSource cur)
{
_isInternalUpdating = true;
lblAngleValue.Text = $"{cur.Angle}°";
trackRatio.Value = (int)(cur.Ratio * 10);
lblRatioNum.Text = cur.Ratio.ToString("F1");
_isInternalUpdating = false;
gaugeKnob.Invalidate();
}
}
private void NotifyDataChanged(TrendOperateType type, string deletedName = "")
{
if (TrendDataChanged != null)
{
TrendDataChanged.Invoke(this, new TrendChangedEventArgs
{
ActiveTrend = listTrends.SelectedItem as TrendSource,
AllTrends = _trends.ToList(),
Mode = cbMode.SelectedIndex,
OperateType = type, // 告知主界面是更新、删除还是关闭
DeletedName = deletedName
});
}
}
private void updatePositionBtn_CheckedChanged(object sender, EventArgs e)
{
if (updatePositionBtn.Checked)
{
NotifyDataChanged(TrendOperateType.PickLocation);
}
}
public void ReleasePickButton()
{
if (updatePositionBtn.InvokeRequired)
{
updatePositionBtn.Invoke(new Action(() => updatePositionBtn.Checked = false));
}
else
{
updatePositionBtn.Checked = false;
}
NotifyDataChanged(TrendOperateType.Update);
}
}
public enum TrendOperateType
{
Update, // 更新(旋钮、滑块、添加)
Delete, // 删除
Close, // 界面关闭
PickLocation // 主界面进入拾取坐标模式
}
public class TrendChangedEventArgs : EventArgs
{
public TrendSource ActiveTrend { get; set; } // 当前被修改的项
public List<TrendSource> AllTrends { get; set; } // 全量列表
public int Mode { get; set; } // 融合模式
public TrendOperateType OperateType { get; set; } // 操作类型
public string DeletedName { get; set; } // 被删除项的名称方便OSG清理
}
public class TrendSource
{
public string Name { get; set; }
public double Angle { get; set; }
public double Ratio { get; set; }
public double Weight { get; set; } = 1.0;
public int Cx { get; set; } = 0;
public int Cy { get; set; } = 0;
public string DisplayText => $"{Name} : {Angle,3}° | 强度: {Ratio:F1} | w: {Weight:F2}";
}
}