layui监听select下拉框获取数据通过doT.js加载页面多选框

本文介绍如何使用layui框架实现表单下拉框的选择事件监听,并根据选择结果动态加载和渲染多选框选项。通过AJAX请求获取数据并利用doT模板引擎更新DOM结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

form表单中的下拉框

<select  class="layui-input" name="base_id" datatype="*" lay-filter="base_id">
     <option value="">请选择</option>
    <#list baseList as base>
     <option value="${base.id!''}" disabled="disabled" >${base.base_name!''}</option>
         <#list base.base_list as ba>
                <option value="${ba.id!''}">&nbsp;&nbsp;&nbsp;&nbsp;${ba.base_name!''}</option>
         </#list> 
    </#list> 
</select>

>

layui通过lay-filter=”base_id”检测选中事件

layui.use(['form','util'], function(){
    var form = layui.form;

    /*监听select框*/
    form.on('select(base_id)', function(data){
         var base_id = data.value;
        //remove掉原来的html
         $('#reSpec').remove();
        if(base_id==""){
            parent.layer.alert("请选择联系人");
        }else{
            selectspec(base_id);
        }
    });

    /*数据新增方法增加checkbox*/
    function selectspec(base_id) {
        $.ajax({
            type : "post",
            url : "${ctx}/origin/goods/select_spec",
            data : {base_id,base_id}, // 你的formid
            error : function(request) {
                parent.layer.alert("网络超时");
            },
            success : function(data) {
                 if (data.length != 0) {
                    var evaluation = doT.template($('#adTemp').html());//组装数据
                    $("#spec").append(evaluation(data));//添加到id="spec"中
                     form.render();//重新渲染,应为动态加载所以需要重新渲染
                } 
            }
        });
    };
});

在页面html中添加上述方法查到的数据(下面的是页面html)

>
    <div id="spec" >
        <div class="layui-form-item" id="reSpec">
            <label class="layui-form-label">请选择规格:</label>
            <div class="layui-input-block" >
            <#list spec as sp>
                <input type="checkbox" name="spec"  lay-skin="primary" <#if goods.spec?contains("${sp}")> checked="checked" </#if> title="${sp}" value="${sp}">
            </#list>
            </div>
        </div>
    </div>
        <script id="adTemp" type="text/x-dot-template">
        <div class="layui-form-item" id="reSpec">
            <label class="layui-form-label">请选择规格:</label>
            <div class="layui-input-block" >
            {{~it:val:index}}
                <input type="checkbox" name="spec" lay-skin="primary"  title="{{=val}}" value="{{=val}}"/>
            {{~}}
            </div>
        </div>
    **上述html代码中id为spec的div为页面加载时匹配目前选中的多选框,当触发下base_id属性对应的属性框时会调用查询方法先清除原来数据,在加载新查找的数据。根据id为adTemp的script调用doT.js方法加载新查询的数组,当查询到的数据为对象时取值换做{{==val.name}}判断为空时显示空字符串格式:{{==val.name?val.name:""}}**
using System; using System.Collections.Generic; using System.Data; using System.Drawing; using System.Linq; using System.Windows.Forms; using OxyPlot; using OxyPlot.Series; using OxyPlot.WindowsForms; using OxyPlot.Axes; using MathNet.Numerics; using MathNet.Numerics.Interpolation; using Microsoft.VisualBasic.FileIO; using PdfSharp.Pdf; using PdfSharp.Drawing; namespace TestPro { public partial class Form1 : Form { private List<double> depthData = new List<double>(); private List<double> gammaData = new List<double>(); private PlotModel plotModel; private PlotView plotView; private Panel plotPanel; private ComboBox interpolationComboBox; private Button exportPdfButton; private Label statusLabel; private double scaleRatio = 200; // 默认比例尺1:200 private double minVisibleDepth = 0; private double maxVisibleDepth = 100; private const double VisibleDepthRange = 100; // 初始可见深度范围 private double pixelsPerMeter = 20; // 增加默认每米像素数 private double visibleDepthRange = 100; // 可见深度范围(米) private double minDepth = 0; private double maxDepth = 0; private VScrollBar depthScrollBar; // 垂直滚动条 private double totalDepthRange = 0; // 总深度范围 private bool isScrollBarDragging = false; // 标记是否正在拖动滚动条 private double dpi = 96; // 默认DPI值 public Form1() { InitializeComponent(); InitializeCustomComponents(); LoadData(); SetupPlotModel(); SetupScrollBar(); } private void InitializeCustomComponents() { this.Text = "自然伽马测井曲线可视化工具"; this.Size = new Size(1200, 800); this.StartPosition = FormStartPosition.CenterScreen; // 获取系统DPI using (Graphics g = this.CreateGraphics()) { dpi = g.DpiY; } // 创建主布局面板 var mainPanel = new Panel { Dock = DockStyle.Fill }; this.Controls.Add(mainPanel); // 创建顶部控制面板 var controlPanel = new Panel { Dock = DockStyle.Top, Height = 50, BackColor = Color.LightGray }; mainPanel.Controls.Add(controlPanel); // 创建插值方法选择下拉框 interpolationComboBox = new ComboBox { Location = new Point(20, 15), Width = 150, DropDownStyle = ComboBoxStyle.DropDownList }; interpolationComboBox.Items.AddRange(new object[] { "线性插值", "三次样条", "PCHIP", "Akima" }); interpolationComboBox.SelectedIndex = 2; // 默认选择PCHIP interpolationComboBox.SelectedIndexChanged += InterpolationComboBox_SelectedIndexChanged; controlPanel.Controls.Add(interpolationComboBox); // 添加比例尺选择 var scaleLabel = new Label { Text = "比例尺:", Location = new Point(190, 18), AutoSize = true }; controlPanel.Controls.Add(scaleLabel); var scaleComboBox = new ComboBox { Location = new Point(240, 15), Width = 80, DropDownStyle = ComboBoxStyle.DropDownList }; scaleComboBox.Items.AddRange(new object[] { "1:100", "1:200", "1:500" }); scaleComboBox.SelectedIndex = 1; // 默认1:200 // 初始计算像素/米 - 使用优化的公式 pixelsPerMeter = CalculatePixelsPerMeter(200, dpi); scaleComboBox.SelectedIndexChanged += (s, e) => { string selectedScale = scaleComboBox.SelectedItem.ToString(); switch (selectedScale) { case "1:100": scaleRatio = 100; pixelsPerMeter = CalculatePixelsPerMeter(100, dpi); break; case "1:200": scaleRatio = 200; pixelsPerMeter = CalculatePixelsPerMeter(200, dpi); break; case "1:500": scaleRatio = 500; pixelsPerMeter = CalculatePixelsPerMeter(500, dpi); break; } // 重新计算可见深度范围 CalculateVisibleDepthRange(); // 更新图表 UpdatePlot(); // 更新滚动条 UpdateScrollBar(); }; controlPanel.Controls.Add(scaleComboBox); // 比例因子调整滑块 - 修改为仅影响深度范围 var scaleFactorTrackBar = new TrackBar { Location = new Point(520, 15), Width = 150, Minimum = 50, Maximum = 200, Value = 100, TickFrequency = 10 }; scaleFactorTrackBar.ValueChanged += (s, e) => { double factor = scaleFactorTrackBar.Value / 100.0; double basePixels = CalculatePixelsPerMeter(scaleRatio, dpi); pixelsPerMeter = basePixels * factor; // 仅更新可见范围和图表 CalculateVisibleDepthRange(); UpdatePlot(); UpdateScrollBar(); }; controlPanel.Controls.Add(scaleFactorTrackBar); // 创建PDF导出按钮 exportPdfButton = new Button { Text = "导出PDF", Location = new Point(690, 15), Size = new Size(80, 25) }; exportPdfButton.Click += ExportPdfButton_Click; controlPanel.Controls.Add(exportPdfButton); // 创建状态标签 statusLabel = new Label { Dock = DockStyle.Bottom, Height = 20, Text = "就绪", TextAlign = ContentAlignment.MiddleLeft, BorderStyle = BorderStyle.FixedSingle }; mainPanel.Controls.Add(statusLabel); // 创建绘图面板容器 var plotContainer = new Panel { Dock = DockStyle.Fill, BackColor = Color.White }; mainPanel.Controls.Add(plotContainer); // 创建绘图面板(带滚动条) plotPanel = new Panel { Dock = DockStyle.Left, //Location = new Point(0, 0), Width = 365, //Height = plotContainer.Height, BackColor = Color.White }; plotContainer.Controls.Add(plotPanel); // 创建垂直滚动条 depthScrollBar = new VScrollBar { Dock = DockStyle.Right, Width = 20 }; depthScrollBar.Scroll += DepthScrollBar_Scroll; depthScrollBar.MouseDown += (s, e) => isScrollBarDragging = true; depthScrollBar.MouseUp += (s, e) => isScrollBarDragging = false; plotContainer.Controls.Add(depthScrollBar); // 创建PlotView plotView = new PlotView { Dock = DockStyle.Fill, BackColor = Color.White }; // 创建自定义控制器并禁用所有交互 var customController = new PlotController(); customController.UnbindAll(); // 解绑所有默认操作 plotView.Controller = customController; // 使用标准的MouseWheel事件处理 plotView.MouseWheel += PlotView_MouseWheel; plotPanel.Controls.Add(plotView); // 添加Resize事件处理 this.Resize += Form1_Resize; } // 优化的每米像素计算 private double CalculatePixelsPerMeter(double scaleRatio, double dpi) { // 比例尺1:100表示1厘米代表1米 // 公式: pixelsPerMeter = (100 / scaleRatio) * (dpi / 2.54) return (100.0 / scaleRatio) * (dpi / 2.54); } private void SetupScrollBar() { if (depthData.Count == 0) return; // 计算总深度范围 totalDepthRange = maxDepth - minDepth; // 设置滚动条范围 depthScrollBar.Minimum = 0; depthScrollBar.Maximum = (int)Math.Ceiling(totalDepthRange - visibleDepthRange); depthScrollBar.SmallChange = 1; // 滚动步长为1米 depthScrollBar.LargeChange = (int)visibleDepthRange; // 一页的大小为当前可见深度范围 // 设置滚动条当前位置 depthScrollBar.Value = (int)(minVisibleDepth - minDepth); } private void UpdateScrollBar() { if (depthData.Count == 0) return; // 更新滚动条范围 depthScrollBar.Maximum = (int)Math.Ceiling(totalDepthRange - visibleDepthRange); depthScrollBar.LargeChange = (int)visibleDepthRange; // 更新滚动条位置 depthScrollBar.Value = Math.Max(0, Math.Min(depthScrollBar.Maximum, (int)(minVisibleDepth - minDepth))); } private void DepthScrollBar_Scroll(object sender, ScrollEventArgs e) { // 实时更新,不再检查ScrollEventType double newMinDepth = minDepth + e.NewValue; double newMaxDepth = newMinDepth + visibleDepthRange; // 只有当深度范围确实变化时才更新 if (Math.Abs(newMinDepth - minVisibleDepth) > 0.01 || Math.Abs(newMaxDepth - maxVisibleDepth) > 0.01) { minVisibleDepth = newMinDepth; maxVisibleDepth = newMaxDepth; // 确保不超出数据范围 if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; } if (minVisibleDepth < minDepth) { minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; } // 立即更新图表 UpdatePlot(); // 如果正在拖动,强制重绘滚动条 if (isScrollBarDragging) { depthScrollBar.Refresh(); } } } private void Form1_Resize(object sender, EventArgs e) { // 仅重新计算可见深度范围(不改变每米像素数) CalculateVisibleDepthRange(); UpdateScrollBar(); UpdatePlot(); } private void CalculateVisibleDepthRange() { // 根据屏幕高度和固定的pixelsPerMeter计算可见深度范围 if (plotView != null && plotView.Height > 0) { // 获取绘图区域高度(像素) int plotAreaHeight = plotView.Height; // 使用固定的pixelsPerMeter计算可见深度范围(米) visibleDepthRange = plotAreaHeight / pixelsPerMeter; // 确保可见范围至少为10米 if (visibleDepthRange < 10) visibleDepthRange = 10; // 确保不超过总深度范围 if (visibleDepthRange > totalDepthRange) { visibleDepthRange = totalDepthRange; } // 更新当前可见范围 maxVisibleDepth = minVisibleDepth + visibleDepthRange; // 确保不超过最大深度 if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; if (minVisibleDepth < minDepth) minVisibleDepth = minDepth; } } } private void LoadData() { try { // 使用OpenFileDialog选择CSV文件 using (var openFileDialog = new OpenFileDialog()) { openFileDialog.Filter = "CSV文件|*.csv|所有文件|*.*"; openFileDialog.Title = "选择测井数据文件"; if (openFileDialog.ShowDialog() == DialogResult.OK) { statusLabel.Text = "正在加载数据..."; Application.DoEvents(); // 使用TextFieldParser读取CSV using (var parser = new TextFieldParser(openFileDialog.FileName)) { parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); parser.HasFieldsEnclosedInQuotes = true; // 跳过前3行头信息 for (int i = 0; i < 4; i++) { parser.ReadLine(); } // 读取数据 while (!parser.EndOfData) { string[] fields = parser.ReadFields(); if (fields.Length >= 2) { // 修复:先声明变量再在TryParse中使用 double depth; double gamma; // 假设第一列是深度,第二列是伽马值 if (double.TryParse(fields[2], out depth) && double.TryParse(fields[6], out gamma)) { depthData.Add(depth); gammaData.Add(gamma); } } } } statusLabel.Text = $"已加载 {depthData.Count} 个数据点"; } else { statusLabel.Text = "未选择文件,使用示例数据"; GenerateSampleData(); } } if (depthData.Count > 0) { minDepth = depthData.Min(); maxDepth = depthData.Max(); totalDepthRange = maxDepth - minDepth; // 设置初始可见深度范围 CalculateVisibleDepthRange(); minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; // 确保不超过最大深度 if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; if (minVisibleDepth < minDepth) minVisibleDepth = minDepth; } } } catch (Exception ex) { statusLabel.Text = $"错误: {ex.Message}"; GenerateSampleData(); } } private void GenerateSampleData() { // 生成示例数据 depthData.Clear(); gammaData.Clear(); double depth = 1000; Random rand = new Random(); for (int i = 0; i < 500; i++) { depth += 0.5; double gamma = 50 + 30 * Math.Sin(depth / 50) + rand.NextDouble() * 10; depthData.Add(depth); gammaData.Add(gamma); } // 设置示例数据的深度范围 minDepth = depthData.Min(); maxDepth = depthData.Max(); totalDepthRange = maxDepth - minDepth; // 设置初始可见深度范围 CalculateVisibleDepthRange(); minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; } private void SetupPlotModel() { plotModel = new PlotModel { Title = "自然伽马测井曲线", TitleFontSize = 14, TitleFontWeight = OxyPlot.FontWeights.Bold, PlotMargins = new OxyThickness(60, 40, 60, 60) }; // 设置Y轴(深度)在右侧 - 固定网格间距 var depthAxis = new LinearAxis { Position = AxisPosition.Right, Title = "深度 (m)", Key = "Depth", StartPosition = 1, EndPosition = 0, // 反转Y轴 MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, MajorGridlineColor = OxyColor.FromRgb(180, 180, 180), MinorGridlineColor = OxyColor.FromRgb(220, 220, 220), Minimum = minVisibleDepth, Maximum = maxVisibleDepth, // 固定网格间距:每5米主网格,每1米次网格 MajorStep = 5, // 固定值,不随窗体变化 MinorStep = 1, // 固定值 // 添加以下属性确保步长绝对不变 IsPanEnabled = false, IsZoomEnabled = false, AbsoluteMaximum = double.MaxValue, AbsoluteMinimum = double.MinValue, AxislineThickness = 2, ExtraGridlineThickness = 2 }; plotModel.Axes.Add(depthAxis); // 设置X轴(伽马值)在顶部 var gammaAxis = new LinearAxis { Position = AxisPosition.Top, Title = "伽马值 (API)", Key = "Gamma", Minimum = 0, Maximum = 200, MajorStep = 20, // 10个刻度 (200/20=10) MinorStep = 5, MajorGridlineStyle = LineStyle.Solid, //MinorGridlineStyle = LineStyle.Dot, MajorGridlineColor = OxyColor.FromRgb(180, 180, 180), //MinorGridlineColor = OxyColor.FromRgb(220, 220, 220), AxislineThickness = 2 }; plotModel.Axes.Add(gammaAxis); // 添加原始数据点 //var scatterSeries = new ScatterSeries //{ // Title = "原始数据点", // MarkerType = MarkerType.Circle, // MarkerSize = 2, // MarkerFill = OxyColor.FromRgb(100, 100, 100) //}; //for (int i = 0; i < depthData.Count; i++) //{ // scatterSeries.Points.Add(new ScatterPoint(gammaData[i], depthData[i])); //} //plotModel.Series.Add(scatterSeries); plotView.Model = plotModel; } private void PlotView_MouseWheel(object sender, MouseEventArgs e) { // 计算滚轮方向 double direction = e.Delta > 0 ? -1 : 1; double scrollAmount = direction * 1; // 每次滚动1米 // 更新可见深度范围 minVisibleDepth += scrollAmount; maxVisibleDepth += scrollAmount; // 确保不超出数据范围 if (minVisibleDepth < minDepth) { minVisibleDepth = minDepth; maxVisibleDepth = minDepth + visibleDepthRange; } if (maxVisibleDepth > maxDepth) { maxVisibleDepth = maxDepth; minVisibleDepth = maxDepth - visibleDepthRange; } // 更新滚动条位置 depthScrollBar.Value = (int)(minVisibleDepth - minDepth); // 更新图表 UpdatePlot(); } private void InterpolationComboBox_SelectedIndexChanged(object sender, EventArgs e) { UpdatePlot(); } private void UpdatePlot() { if (depthData.Count == 0) return; // 更新深度轴范围 var depthAxis = plotModel.Axes.FirstOrDefault(a => a.Key == "Depth") as LinearAxis; if (depthAxis != null) { depthAxis.Minimum = minVisibleDepth; depthAxis.Maximum = maxVisibleDepth; } // 移除之前的插值曲线 var seriesToRemove = plotModel.Series.Where(s => s is LineSeries).ToList(); foreach (var series in seriesToRemove) { plotModel.Series.Remove(series); } // 获取选择的插值方法 string method = interpolationComboBox.SelectedItem.ToString(); // 执行插值 IInterpolation interpolationFunc = CreateInterpolationFunction(depthData, gammaData, method); // 创建插值曲线 - 使用正确的平滑设置 var lineSeries = new LineSeries { Title = $"{method}插值", Color = OxyColor.FromRgb(0, 100, 200), StrokeThickness = 1.5 }; // 根据插值方法选择合适的平滑算法 if (method == "线性插值") { // 线性插值不需要额外平滑 } else { // 使用Catmull-Rom样条插值算法 lineSeries.InterpolationAlgorithm = InterpolationAlgorithms.CatmullRomSpline; // 使用CanonicalSpline并设置张力参数(如果可用) // lineSeries.InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline; // lineSeries.CanonicalSplineTension = 0.5; } // 添加当前可见范围内的点 double step = Math.Max(0.1, visibleDepthRange / 500); for (double d = minVisibleDepth; d <= maxVisibleDepth; d += step) { double gamma = interpolationFunc.Interpolate(d); lineSeries.Points.Add(new DataPoint(gamma, d)); } plotModel.Series.Add(lineSeries); plotModel.InvalidatePlot(true); // 计算5米网格线对应的像素距离 double gridSpacingPixels = 5 * pixelsPerMeter; statusLabel.Text = $"显示深度: {minVisibleDepth:F1}m - {maxVisibleDepth:F1}m ({visibleDepthRange:F1}m) | 总深度: {minDepth:F1}m - {maxDepth:F1}m | 插值: {method} | 比例: 1:{scaleRatio} | 网格间距: {gridSpacingPixels:F1}像素"; } private Tuple<List<double>, List<double>> SmoothCurve(List<double> depth, List<double> gamma, string method) { // 去除重复深度值 var uniqueDepth = new List<double>(); var uniqueGamma = new List<double>(); var seen = new HashSet<double>(); for (int i = 0; i < depth.Count; i++) { if (seen.Contains(depth[i])) continue; seen.Add(depth[i]); uniqueDepth.Add(depth[i]); uniqueGamma.Add(gamma[i]); } // 排序 var sorted = uniqueDepth.Select((d, i) => new { Depth = d, Gamma = uniqueGamma[i] }) .OrderBy(x => x.Depth) .ToList(); var sortedDepth = sorted.Select(x => x.Depth).ToArray(); var sortedGamma = sorted.Select(x => x.Gamma).ToArray(); // 生成新的深度点(用于插值) double minDepth = sortedDepth.Min(); double maxDepth = sortedDepth.Max(); int numPoints = 1000; // 插值点数 var xnew = Generate.LinearSpaced(numPoints, minDepth, maxDepth).ToList(); // 根据选择的插值方法进行插值 List<double> ynew; IInterpolation interpolation = null; switch (method) { case "线性插值": interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; case "三次样条": interpolation = Interpolate.CubicSpline(sortedDepth, sortedGamma); break; case "PCHIP": interpolation = CubicSpline.InterpolatePchipSorted(sortedDepth, sortedGamma); break; case "Akima": interpolation = CubicSpline.InterpolateAkima(sortedDepth, sortedGamma); break; default: interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; } ynew = xnew.Select(d => interpolation.Interpolate(d)).ToList(); return Tuple.Create(xnew, ynew); } private void ExportPdfButton_Click(object sender, EventArgs e) { if (depthData.Count == 0) return; using (var saveFileDialog = new SaveFileDialog()) { saveFileDialog.Filter = "PDF文件|*.pdf"; saveFileDialog.Title = "导出PDF"; saveFileDialog.FileName = "GammaLogCurve.pdf"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { statusLabel.Text = "正在生成PDF..."; Application.DoEvents(); try { GeneratePdfReport(saveFileDialog.FileName); statusLabel.Text = $"PDF已成功导出到: {saveFileDialog.FileName}"; } catch (Exception ex) { statusLabel.Text = $"PDF导出错误: {ex.Message}"; } } } } private void GeneratePdfReport(string filePath) { try { statusLabel.Text = "正在准备PDF导出..."; Application.DoEvents(); // 获取plotPanel的宽度(365像素) double panelWidth = plotPanel.Width; // 计算每米对应的点(point)数(1点=1/72英寸) double dotsPerMeter = pixelsPerMeter * (72.0 / dpi); double minDepth = depthData.Min(); double maxDepth = depthData.Max(); double depthRange = maxDepth - minDepth; // 设置横轴宽度为plotPanel的宽度(转换为点) double gammaAxisWidth = panelWidth * (72.0 / dpi); // 保持页面宽度不变(与之前相同) double pageWidth = 500 + 100; // 伽马轴宽度500点 + 左右边距100点 // 计算图形在页面中的起始位置(居左显示) double graphStartX = 40; // 左边距40点 // 创建PDF文档 var document = new PdfDocument(); document.Info.Title = "自然伽马测井曲线报告"; document.Info.Author = "测井曲线可视化工具"; // 分页参数 const double maxPageHeight = 14400; // 200英寸 * 72点/英寸 double currentStartDepth = minDepth; int pageIndex = 0; int maxPages = 1000; // 安全限制,防止无限循环 bool isLastPage = false; // 获取插值方法 string method = interpolationComboBox.SelectedItem.ToString(); // 创建整个深度范围的插值函数 IInterpolation interpolationFunc = CreateInterpolationFunction(depthData, gammaData, method); // 计算总页数(用于页码显示) int totalPages = (int)Math.Ceiling(depthRange / (maxPageHeight / dotsPerMeter)); while (pageIndex < maxPages && !isLastPage) { statusLabel.Text = $"正在生成第 {pageIndex + 1} 页..."; Application.DoEvents(); // 计算当前页的深度范围 double currentPageDepthRange = maxPageHeight / dotsPerMeter; double currentEndDepth = currentStartDepth + currentPageDepthRange; // 检查是否是最后一页 if (currentEndDepth >= maxDepth - 0.001) // 使用容差避免浮点误差 { currentEndDepth = maxDepth; isLastPage = true; } // 计算当前页实际高度 double pageHeight = (currentEndDepth - currentStartDepth) * dotsPerMeter; // 创建页面(动态高度) var page = document.AddPage(); page.Width = pageWidth; page.Height = pageHeight; page.Orientation = PdfSharp.PageOrientation.Portrait; using (var xgraphics = XGraphics.FromPdfPage(page)) { // 设置边距 double margin = 40; double topMargin = 0; // 上边距为0 double bottomMargin = 0; // 下边距为0 double usableHeight = page.Height - topMargin - bottomMargin; // 计算比例因子 double gammaRange = 200; // 伽马值范围0-200 double gammaScale = gammaAxisWidth / gammaRange; double depthScale = usableHeight / (currentEndDepth - currentStartDepth); // 仅在第一页绘制标题 if (pageIndex == 0) { var titleFont = new XFont("Arial", 16, XFontStyle.Bold); xgraphics.DrawString("自然伽马测井曲线", titleFont, XBrushes.Navy, new XPoint(page.Width / 2, 10), new XStringFormat { Alignment = XStringAlignment.Center }); } // 绘制坐标轴框架 - 居左显示 var axisPen = new XPen(XColors.Black, 1.5); double leftAxisX = graphStartX + 30; // 左边距+标签空间 double rightAxisX = leftAxisX + gammaAxisWidth; // Y轴(深度)从顶部到底部边缘 xgraphics.DrawLine(axisPen, leftAxisX, topMargin, leftAxisX, topMargin + usableHeight); // X轴(伽马值)- 只在第一页绘制顶部轴 if (pageIndex == 0) { xgraphics.DrawLine(axisPen, leftAxisX, topMargin, rightAxisX, topMargin); } // 绘制网格线 - 深度(Y轴)延伸到页面底部 var gridPen = new XPen(XColors.LightGray, 0.3); var majorGridPen = new XPen(XColors.Gray, 0.6); // 计算起始网格深度 double startGridDepth = Math.Floor(currentStartDepth); double endGridDepth = Math.Ceiling(currentEndDepth); for (double i = startGridDepth; i <= endGridDepth; i += 1) { double y = topMargin + (i - currentStartDepth) * depthScale; // 确保y在页面范围内 if (y < topMargin || y > topMargin + usableHeight) continue; // 每5米主网格 if (i % 5 == 0) { xgraphics.DrawLine(majorGridPen, leftAxisX, y, rightAxisX, y); // 主刻度标签 var labelFont = new XFont("Arial", 8, XFontStyle.Bold); xgraphics.DrawString($"{i:F1}", labelFont, XBrushes.Black, new XPoint(leftAxisX - 5, y), new XStringFormat { Alignment = XStringAlignment.Far, LineAlignment = XLineAlignment.Center }); } else // 1米次网格 { xgraphics.DrawLine(gridPen, leftAxisX, y, rightAxisX, y); } } // 绘制网格线 - 伽马值(X轴)每20单位,延伸到页面底部 for (int i = 0; i <= 10; i++) { double gammaValue = i * 20; double x = leftAxisX + gammaValue * gammaScale; xgraphics.DrawLine(majorGridPen, x, topMargin, x, topMargin + usableHeight); // 刻度标签(顶部)- 只在第一页绘制 if (pageIndex == 0) { var labelFont = new XFont("Arial", 8); xgraphics.DrawString($"{gammaValue}", labelFont, XBrushes.Black, new XPoint(x, topMargin - 10), new XStringFormat { Alignment = XStringAlignment.Center, LineAlignment = XLineAlignment.Far }); } } // 绘制原始数据点(仅当前页范围内的点) var pointPen = new XPen(XColors.Gray, 0.5); for (int i = 0; i < depthData.Count; i++) { double depth = depthData[i]; double gamma = gammaData[i]; if (depth >= currentStartDepth && depth <= currentEndDepth) { double x = leftAxisX + gamma * gammaScale; double y = topMargin + (depth - currentStartDepth) * depthScale; // 只绘制在有效伽马值范围内的点 if (gamma >= 0 && gamma <= 200) { xgraphics.DrawEllipse(pointPen, x - 0.5, y - 0.5, 1, 1); } } } // ===== 平滑曲线绘制部分 ===== var curvePen = new XPen(XColor.FromArgb(0, 100, 200), 1.5); // 收集当前页所有有效点 var validPoints = new List<XPoint>(); // 按0.1米步长生成当前页深度范围内的点 double step = 0.1; double d = currentStartDepth; while (d <= currentEndDepth) { double gamma = interpolationFunc.Interpolate(d); if (gamma >= 0 && gamma <= 200) // 只处理有效范围内的点 { double x = leftAxisX + gamma * gammaScale; double y = topMargin + (d - currentStartDepth) * depthScale; validPoints.Add(new XPoint(x, y)); } d += step; } // 使用直线连接密集的点(点足够密时看起来是平滑的) if (validPoints.Count >= 2) { xgraphics.DrawLines(curvePen, validPoints.ToArray()); } // ===== 结束平滑曲线绘制部分 ===== // 添加坐标轴标签 var axisFont = new XFont("Arial", 10, XFontStyle.Bold); // 深度标签(垂直文本) var depthLabelState = xgraphics.Save(); xgraphics.TranslateTransform(graphStartX, topMargin + usableHeight / 2); xgraphics.RotateTransform(-90); xgraphics.DrawString("深度 (m)", axisFont, XBrushes.Black, 0, 0, new XStringFormat { Alignment = XStringAlignment.Center, LineAlignment = XLineAlignment.Center }); xgraphics.Restore(depthLabelState); // 伽马值标签 - 只在第一页绘制 if (pageIndex == 0) { xgraphics.DrawString("伽马值 (API)", axisFont, XBrushes.Black, new XPoint(leftAxisX + gammaAxisWidth / 2, topMargin - 20), new XStringFormat { Alignment = XStringAlignment.Center }); } } // 更新下一页的起始深度 currentStartDepth = currentEndDepth; pageIndex++; // 终止条件检查 if (currentStartDepth >= maxDepth - 0.001 || pageIndex >= maxPages) { isLastPage = true; } } // 保存PDF document.Save(filePath); statusLabel.Text = $"PDF导出完成,共 {pageIndex} 页"; } catch (Exception ex) { statusLabel.Text = $"PDF导出错误: {ex.Message}"; MessageBox.Show($"导出PDF时发生错误:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } } // 创建插值函数的方法 private IInterpolation CreateInterpolationFunction(List<double> depth, List<double> gamma, string method) { // 去除重复深度值 var uniqueDepth = new List<double>(); var uniqueGamma = new List<double>(); var seen = new HashSet<double>(); for (int i = 0; i < depth.Count; i++) { if (seen.Contains(depth[i])) continue; seen.Add(depth[i]); uniqueDepth.Add(depth[i]); uniqueGamma.Add(gamma[i]); } // 排序 var sorted = uniqueDepth.Select((d, i) => new { Depth = d, Gamma = uniqueGamma[i] }) .OrderBy(x => x.Depth) .ToList(); var sortedDepth = sorted.Select(x => x.Depth).ToArray(); var sortedGamma = sorted.Select(x => x.Gamma).ToArray(); // 根据选择的插值方法进行插值 IInterpolation interpolation = null; switch (method) { case "线性插值": interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; case "三次样条": interpolation = Interpolate.CubicSpline(sortedDepth, sortedGamma); break; case "PCHIP": interpolation = CubicSpline.InterpolatePchipSorted(sortedDepth, sortedGamma); break; case "Akima": try { // 尝试使用Akima插值(如果可用) interpolation = CubicSpline.InterpolateAkima(sortedDepth, sortedGamma); } catch { // 如果Akima不可用,回退到PCHIP interpolation = CubicSpline.InterpolatePchipSorted(sortedDepth, sortedGamma); } break; default: interpolation = Interpolate.Linear(sortedDepth, sortedGamma); break; } return interpolation; } } }上述代码中,当改变窗体高度时,Y轴的最大值发生了变化,显示的网格数也发生了变化,这是正确的,但是在电脑屏幕上看到的剩余网格线的间距也发生了变化,这是不希望发生的。我们希望发生的是当改变窗体高度时,Y轴的最大值发生了变化,显示的网格数也发生变化,但是在电脑屏幕上看到的剩余网格线的间距不发生任何的改变,即剩余显示的所有参数的相对位置都不发生改变。
最新发布
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值