从零搭建工业控制系统(十六):界面布局——Dock面板与多窗口

界面布局:Dock面板与多窗口

这是「从零搭建工业控制系统」系列第16篇。后端逻辑聊了十几篇,终于说到前端了。工业系统的界面不像网页那样花哨,但布局复杂度一点不低——十几个面板同屏显示,还要可拖拽、可隐藏、可保存布局。本篇只讲布局,自定义控件下篇再说。


第一次布局的灾难

最初版本的界面我用Grid硬切,三行五列,每个格子塞一个UserControl。看起来整齐,但操作员投诉说面板太小看不清数据,想拖大一点又拖不动。

后来换成了DockPanel + AvalonDock(DockingManager),面板可拖拽、可浮动、可钉住。操作员想看哪个面板就拖大哪个,不想看的折叠起来。布局还能保存,下次启动自动恢复。


整体布局结构

主窗口用Grid分三块:顶部导航栏、中间DockingManager、底部状态栏:

图1:主窗口整体布局结构

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />   <!-- 导航栏 -->
        <RowDefinition Height="*" />      <!-- DockingManager -->
        <RowDefinition Height="Auto" />   <!-- 状态栏 -->
    </Grid.RowDefinitions>

    <!-- 顶部导航:Logo + 用户信息 + 权限显示 -->
    <StackPanel Grid.Row="0" Orientation="Horizontal">
        <TextBlock Text="控制系统" FontWeight="Bold" />
        <TextBlock Text="{Binding PermissionService.UserDisplayText}" />
    </StackPanel>

    <!-- 中间:可拖拽面板区域 -->
    <dock:DockingManager Grid.Row="1">
        <!-- 各种可停靠面板 -->
    </dock:DockingManager>

    <!-- 底部状态栏:连接状态 + 报警状态 -->
    <StatusBar Grid.Row="2">
        <StatusBarItem Content="{Binding DeviceStatusText}" />
    </StatusBar>
</Grid>

Dock面板布局

DockingManager 里放 LayoutDocumentPaneLayoutAnchorablePane。Document是标签页式面板,Anchorable是可停靠可隐藏的面板:

图2:Dock面板两种类型对比

<dock:DockingManager>
    <dock:LayoutRoot>
        <dock:LayoutPanel Orientation="Horizontal">
            <!-- 左侧:自动运行面板 -->
            <dock:LayoutAnchorablePane DockWidth="300">
                <dock:LayoutAnchorable Title="自动运行" ContentId="AutoPanel">
                    <views:AutoRunView />
                </dock:LayoutAnchorable>
            </dock:LayoutAnchorablePane>

            <!-- 中间:主操作区 -->
            <dock:LayoutDocumentPane>
                <dock:LayoutDocument Title="设备控制" ContentId="DevicePanel">
                    <views:DevicePanel />
                </dock:LayoutDocument>
            </dock:LayoutDocumentPane>

            <!-- 右侧:日志面板 -->
            <dock:LayoutAnchorablePane DockWidth="400">
                <dock:LayoutAnchorable Title="日志" ContentId="LogPanel">
                    <views:LogView />
                </dock:LayoutAnchorable>
            </dock:LayoutAnchorablePane>
        </dock:LayoutPanel>
    </dock:LayoutRoot>
</dock:DockingManager>

权限控制面板显隐

面板的显隐也受权限控制。操作员不该看到的面板直接不创建:

<dock:LayoutAnchorable 
    Title="原始IO调试"
    Visibility="{Binding PermissionService.IsAdmin, 
               Converter={StaticResource BoolToVisibilityConverter}}">
    <views:RawIOPanel />
</dock:LayoutAnchorable>

布局保存与恢复

AvalonDock支持布局序列化。程序关闭时保存,启动时恢复:

// 保存布局
private void SaveLayout()
{
    var serializer = new XmlLayoutSerializer(dockingManager);
    using var writer = new StreamWriter(layoutFilePath);
    serializer.Serialize(writer);
}

// 恢复布局
private void LoadLayout()
{
    if (!File.Exists(layoutFilePath)) return;

    var serializer = new XmlLayoutSerializer(dockingManager);
    serializer.LayoutSerializationCallback += (s, e) =>
    {
        // 根据ContentId找回ViewModel
        e.Content = FindContent(e.Model.ContentId);
    };

    using var reader = new StreamReader(layoutFilePath);
    serializer.Deserialize(reader);
}

踩过的坑:反序列化时如果找不到对应的ContentId,面板会消失。回调函数里要处理找不到的情况,给个默认面板。

图3:布局保存与恢复流程


ViewModel与View的映射

因为代码混淆,默认的ViewModel自动定位会失效。在 App.xaml.cs 里手动注册映射:

protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();

    ViewModelLocationProvider.Register<MainWindow, MainWindowViewModel>();
    ViewModelLocationProvider.Register<Views.DevicePanel, ViewModels.DevicePanelViewModel>();
    ViewModelLocationProvider.Register<Views.Dock.LogView, ViewModels.Dock.LogViewModel>();
    // ... 其他映射
}

不注册的话,Release版混淆后View找不到对应的ViewModel,界面全是空的。

图4:ViewModel 与 View 手动映射


界面踩坑清单

现象解决
Grid硬切面板不能拖拽用DockingManager
混淆后VM丢失Release版界面空白手动注册ViewModel映射
布局恢复失败面板消失处理LayoutSerializationCallback
权限面板可见操作员看到调试面板Visibility绑定权限属性

本篇小结

知识点关键做法
Dock布局AvalonDock DockingManager
面板类型Document(标签页) + Anchorable(可停靠)
权限控制Visibility绑定IsAdmin等属性
布局序列化XmlLayoutSerializer保存/恢复
VM映射ViewModelLocationProvider手动注册

工业界面的核心不是好看,是信息密度——一屏能看到多少关键数据,能多快找到要操作的按钮。


下期预告

第17篇:自定义控件设计

布局讲完了,下篇讲自定义控件——线圈开关、模拟仪表、DataTemplate选择器,怎么封装可复用的工业UI组件。

【内容概要】概述 该数据集提供了印度主要股票基准指数的精心策划的端到端历史记录。仅限标准价格的数据集不同,该资源将主要交易所数据先进的技术指标和基本估值比率相结合,提供了印度市场生态系统的360度视图。 涵盖的指数 该数据集包括以下13个主要指标的每日数据: SENSEX(BSE 30) 漂亮50 第九银行(银行业) 第九届IT(信息技术) 九药集团(制药) NIFTY快速消费品(快速消费品) 第九金属(金属采矿) 九号汽车 NIFTY NEXT 50(大盘股出现) 漂亮的中帽150 漂亮的小帽250 漂亮100 漂亮500 数据来源和方法 基础数据(OHLCV):来源于雅虎财经(yfinance),提供可靠、高频调整的每日定价。 估值数据:通过nsepy库进行增强,直接抓取NSE印度官方数据,提供准确的市盈率(市盈率)、市盈率(市净率)和股息收益率(如有历史数据)。 合并策略:来自两个来源的数据点在每日时间戳上智能合并,以最大限度地扩大覆盖范围并填补任何空白。 时间范围 开始日期:2000年1月1日 结束日期:当前交易日(动态更新为现在)。 特征分解(按指数) 该数据集远远超出了简单的价格历史,包含30多个工程列: 基础市场数据: 日期(每日时间戳)、开盘、高、低、收盘、调整收盘、成交量。 估值基本面: 市盈率(PE)、市盈率(PB)、股息收益率(股息收益率%)。 趋势和移动平均线: MA_50、MA_200、价格_MA50_比率、价格_MA200_比率。 动量和振荡器: RSI_14(相对强弱指数)、MACD、MACD_Signal、MACD_Histogram。 波动性和风险: 波动率_20d(20天滚动标准偏差),ATR_14(平均真实范围),日变化率_%。 价格行动: Daily_Return_%、Log_Return__%、Gap_%(开盘价上一...
【单相单级并网逆变器】模拟了一个具有最大功率点追踪(MPPT)功能的单相单级脉宽调制(PWM)光伏逆变器,并且支持并网运行(Simulink仿真实现)内容概要:本文针对传统三电平并网逆变器存在的谐波含量高、对电网不平衡工况适应性差及动态响应慢等问题,研究了一种基于有源中点箝位(ANPC)结构的单相单级并网逆变器,并提出融合双极性倍频脉宽调制(DPWMA)、正负序分离锁相电网电压前馈控制的复合控制策略。通过Simulink搭建仿真模型,验证了该系统在稳态运行、电网电压不平衡和动态扰动工况下的优越性能,结果表明所提方法能显著降低并网电流谐波畸变率,提升锁相精度系统抗扰能力,实现高质量、高稳定性的并网运行。; 适合人群:具备电力电子电力系统基础知识,从事新能源并网、逆变器控制或相关领域研究的研发人员及研究生。; 使用场景及目标:①用于光伏发电、储能系统等新能源并网场景中高性能逆变器的设计优化;②为解决电网电压不平衡、谐波干扰等复杂工况下的并网稳定性问题提供技术参考仿真验证方案; 阅读建议:建议结合Simulink仿真模型同步学习,重点关注DPWMA调制实现、正负序分离锁相算法设计前馈控制环节的协同机制,深入理解控制策略对电能质量和动态性能的提升作用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值