WPF開發經驗-實現一種機械泵控制項

来源:https://www.cnblogs.com/wwwen/archive/2023/01/28/17070859.html
-Advertisement-
Play Games

一 引入 考慮實現一種機械泵控制項。 機械泵是工業中通常用來製造真空的一類設備,我們在繪製界面UI時希望可以生動形象地來表述一個機械泵,下麵講述了一種簡單的實現。 二 MechanicalPumpControl 聲明一個MechanicalPumpControl的自定義控制項,它繼承自Control類。 ...


一 引入

考慮實現一種機械泵控制項。

機械泵是工業中通常用來製造真空的一類設備,我們在繪製界面UI時希望可以生動形象地來表述一個機械泵,下麵講述了一種簡單的實現。

二 MechanicalPumpControl

聲明一個MechanicalPumpControl的自定義控制項,它繼承自Control類。

對於一個MechanicalPump來說,它具有狀態,這裡定義一個State依賴屬性來簡單描述泵的狀態,State等於0表示泵停止狀態,State等於1表示泵運行狀態。

State可以綁定到實際的泵狀態數據源,當泵狀態數據變化時,MechanicalPumpControl需要相應動態顯示泵狀態動畫。

定義StartAnimation和StopAnimation兩個依賴屬性,來啟動和停止泵狀態動畫。

關於控制項的狀態切換也可以考慮用VisualStateManager來實現,可以更方便地管理控制項的狀態以及用於狀態過渡的邏輯,推薦用這種方式。

public class MechanicalPumpControl : Control
{
    static MechanicalPumpControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MechanicalPumpControl), new FrameworkPropertyMetadata(typeof(MechanicalPumpControl)));
    }

   
    public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
    "State",
    typeof(int),
    typeof(MechanicalPumpControl), new PropertyMetadata(PropertyChangedCallback));

    public int State
    {
        get => (int)GetValue(StateProperty);
        set => SetValue(StateProperty, value);
    }

    private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var pumpControl = d as MechanicalPumpControl;

        if ((int)e.OldValue == 0 && (int)e.NewValue == 1)
        {
            pumpControl.StopAnimation = false;
            pumpControl.StartAnimation = true;
        }
        else if ((int)e.OldValue == 1 && (int)e.NewValue == 0)
        {
            pumpControl.StopAnimation = true;
            pumpControl.StartAnimation = false;
        }
    }

    public static readonly DependencyProperty StartAnimationProperty = DependencyProperty.Register(
        "StartAnimation",
        typeof(bool),
        typeof(MechanicalPumpControl));

    public bool StartAnimation
    {
        get => (bool)GetValue(StartAnimationProperty);
        set => SetValue(StartAnimationProperty, value);
    }

    public static readonly DependencyProperty StopAnimationProperty = DependencyProperty.Register(
        "StopAnimation",
        typeof(bool),
        typeof(MechanicalPumpControl));

    public bool StopAnimation
    {
        get => (bool)GetValue(StopAnimationProperty);
        set => SetValue(StopAnimationProperty, value);
    }
}

 

三 Style

接下來編寫MechanicalPumpControl的樣式。

考慮繪製出不動的泵體部分,和動作的扇葉部分。當泵停止狀態時,扇葉靜止,當泵運轉狀態時,扇葉轉動。

樣式代碼如下:

 <Style TargetType="{x:Type Rectangle}" x:Key="FanRec">
     <Setter Property="Canvas.Left" Value="7"/>
     <Setter Property="Width" Value="48"/>
     <Setter Property="Height" Value="6"/>
     <Setter Property="RadiusX" Value="0.5"/>
     <Setter Property="RadiusY" Value="0.5"/>
     <Setter Property="Fill" Value="SlateGray"/>
     <Setter Property="RenderTransform">
         <Setter.Value>
             <SkewTransform AngleX="-20" AngleY="-7"/>
         </Setter.Value>
     </Setter>
 </Style>

 <Style TargetType="{x:Type local:MechanicalPumpControl}">
     <Setter Property="Template">
         <Setter.Value>
             <ControlTemplate TargetType="{x:Type local:MechanicalPumpControl}">
                 <Viewbox Stretch="Fill">
                     <Canvas Width="180" Height="100">
                         <Rectangle Width="60" Height="60" Canvas.Top="10" Stroke="Gray"  StrokeThickness="3" RadiusX="3" RadiusY="3" />
                         <Canvas  Width="60" Height="54" Canvas.Top="14" ClipToBounds="True">
                             <Canvas x:Name="canvas1" Width="60" Height="54">
                                 <Rectangle x:Name="rec1" Style="{StaticResource FanRec}" Canvas.Top="6"/>
                                 <Rectangle x:Name="rec2" Style="{StaticResource FanRec}" Canvas.Top="16"/>
                                 <Rectangle x:Name="rec3" Style="{StaticResource FanRec}" Canvas.Top="26"/>
                                 <Rectangle x:Name="rec4" Style="{StaticResource FanRec}" Canvas.Top="36"/>
                                 <Rectangle x:Name="rec5" Style="{StaticResource FanRec}" Canvas.Top="46"/>
                             </Canvas>
                             <Canvas x:Name="canvas2" Width="60" Height="54">
                                 <Rectangle x:Name="rec6" Style="{StaticResource FanRec}" Canvas.Top="56"/>
                                 <Rectangle x:Name="rec7" Style="{StaticResource FanRec}" Canvas.Top="66"/>
                                 <Rectangle x:Name="rec8" Style="{StaticResource FanRec}" Canvas.Top="76"/>
                                 <Rectangle x:Name="rec9" Style="{StaticResource FanRec}" Canvas.Top="86"/>
                                 <Rectangle x:Name="rec10" Style="{StaticResource FanRec}" Canvas.Top="96"/>
                                 <Rectangle x:Name="rec11" Style="{StaticResource FanRec}" Canvas.Top="106"/>
                             </Canvas>
                         </Canvas>
                         <Rectangle  Width="5" Height="40" Canvas.Left="60" Canvas.Top="20"  Stroke="Gray"  StrokeThickness="4"/>
                         <Rectangle  Width="110" Height="82" Canvas.Left="65"  RadiusX="3" RadiusY="3" Stroke="Gray"  StrokeThickness="4"/>
                         <Rectangle  Width="10" Height="8" Canvas.Left="75" Canvas.Top="82" Fill="Gray" />
                         <Rectangle  Width="10" Height="8" Canvas.Left="155" Canvas.Top="82" Fill="Gray" />
                         <Rectangle  Width="140" Height="10" Canvas.Left="40" Canvas.Top="90" Fill="Black" RadiusX="2" RadiusY="2"/>
                     </Canvas>
                 </Viewbox>
                 <ControlTemplate.Triggers>
                     <Trigger Property="StartAnimation" Value="true">
                         <Trigger.EnterActions>
                             <BeginStoryboard Name="moveStory">
                                 <Storyboard RepeatBehavior="Forever" SpeedRatio="4">
                                     <DoubleAnimationUsingKeyFrames 
                                                 Storyboard.TargetName="canvas1" 
                                                 Storyboard.TargetProperty="(Canvas.Top)" 
                                                 Duration="0:0:6" >
                                         <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/>
                                         <LinearDoubleKeyFrame Value="-10" KeyTime="0:0:1"/>
                                         <LinearDoubleKeyFrame Value="-20" KeyTime="0:0:2"/>
                                         <LinearDoubleKeyFrame Value="-30" KeyTime="0:0:3"/>
                                         <LinearDoubleKeyFrame Value="-40" KeyTime="0:0:4"/>
                                         <LinearDoubleKeyFrame Value="-50" KeyTime="0:0:5"/>
                                         <LinearDoubleKeyFrame Value="-60" KeyTime="0:0:6"/>
                                     </DoubleAnimationUsingKeyFrames>
                                     <DoubleAnimationUsingKeyFrames 
                                                 Storyboard.TargetName="canvas2" 
                                                 Storyboard.TargetProperty="(Canvas.Top)" 
                                                 Duration="0:0:6" >
                                         <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/>
                                         <LinearDoubleKeyFrame Value="-10" KeyTime="0:0:1"/>
                                         <LinearDoubleKeyFrame Value="-20" KeyTime="0:0:2"/>
                                         <LinearDoubleKeyFrame Value="-30" KeyTime="0:0:3"/>
                                         <LinearDoubleKeyFrame Value="-40" KeyTime="0:0:4"/>
                                         <LinearDoubleKeyFrame Value="-50" KeyTime="0:0:5"/>
                                         <LinearDoubleKeyFrame Value="-60" KeyTime="0:0:6"/>
                                     </DoubleAnimationUsingKeyFrames>
                                 </Storyboard>
                             </BeginStoryboard>
                         </Trigger.EnterActions>
                     </Trigger>
                     <Trigger Property="StopAnimation" Value="true">
                         <Trigger.EnterActions>
                             <StopStoryboard BeginStoryboardName="moveStory">
                             </StopStoryboard>
                         </Trigger.EnterActions>
                     </Trigger>
                     <Trigger Property="State" Value="1">
                         <Setter TargetName="rec1" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec2" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec3" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec4" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec5" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec6" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec7" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec8" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec9" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec10" Property="Fill" Value="SeaGreen"/>
                         <Setter TargetName="rec11" Property="Fill" Value="SeaGreen"/>
                     </Trigger>
                 </ControlTemplate.Triggers>
             </ControlTemplate>
         </Setter.Value>
     </Setter>
 </Style>

 

四 效果演示

Xaml代碼如下:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="768" Width="1366">
    <Canvas>
        <local:MechanicalPumpControl x:Name="pump" Canvas.Left="115" Canvas.Top="85"/>
        <Button Content="START" Width="60" Height="30"  Canvas.Left="363" Canvas.Top="94" Click="STARTButton_Click"/>
        <Button Content="STOP"  Width="60" Height="30"  Canvas.Left="363" Canvas.Top="143" Click="STOPButton_Click"/>
    </Canvas>
</Window>

後臺代碼如下:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void STARTButton_Click(object sender, RoutedEventArgs e)
    {
        pump.State = 1;
    }
    private void STOPButton_Click(object sender, RoutedEventArgs e)
    {
        pump.State = 0;
    }
}

 

__ 以 上 __

一團靜火的頭像
  • 本文作者: 一團靜火
  • 本文鏈接: https://www.cnblogs.com/wwwen/p/17070859.html
  • 關於博主: 水平有限,不足或錯誤之處,請批評指正。如果文章對您有幫助,可以點擊文章右下角【推薦】一下。
  • 版權聲明: 本博客所有文章除特別聲明外,系原創文章,均採用 BY-NC-SA 許可協議。轉載請註明出處!並保留此段內容。

  • 您的分享是我們最大的動力!

    -Advertisement-
    Play Games
    更多相關文章
    • 談談你對 Java 平臺的理解?“Java 是解釋執行”,這句話正確嗎? Java 本身是一種面向對象的語言,最顯著的特性有兩個方面,一是所謂的“一處編譯,處處運行”(Write once,run anywhere),能夠非常容易地獲得跨平臺能力;另外就是垃圾收集(GC,Garbage Collec ...
    • 首先我們來嘗試將分片的圖片複原為正常的圖片 這裡是六張切成小細條的圖片,原本是一張大圖的,現在我們用python將他們合併到一塊,題外話圖片來源於中華連環畫,*http://www.zhlhh.com/* 這個網站內有很多優秀的連環畫,而且大部分都是免費,推薦給大家 我的思路是用matlib讀圖片, ...
    • IoC 反轉控制原則也被叫做依賴註入 DI, 容器按照配置註入實例化的對象. 本文將實現一個輕量化的 IoC 容器, 完成對象的實例化和註入, 基於註解不依賴於任何庫. (註解參考 JSR-330) ...
    • 隨著業務的發展,系統會越來越龐大,原本簡單穩定的功能,可能在不斷迭代後複雜度上升,潛在的風險也隨之暴露,導致最終服務不穩定,造成業務價值的損失。而為了減少這種情況,其中一種比較好的方式就是提高代碼質量,比如通過代碼審查,從而降低錯誤風險,但是,代碼審查難度大,代碼缺陷、漏洞不易發現,且審查工作隨著代 ...
    • 作者:京東物流 王北永 姚再毅 1 背景 日常開發過程中,尤其在 DDD 過程中,經常遇到 VO/MODEL/PO 等領域模型的相互轉換。此時我們會一個欄位一個欄位進行 set|get 設置。要麼使用工具類進行暴力的屬性拷貝,在這個暴力屬性拷貝過程中好的工具更能提高程式的運行效率,反之引起性能低下、 ...
    • 對於logback的模板來說,我們是可以自定義的,同時它也提供了一些公開的常量,比如%level,%thread這些,我們如果希望自定義這些常量,需要實現ClassicConverter抽象類,重寫它的convert方法。 系統變數 %thread 當前線程 %d{yyyy-MM-dd HH:mm: ...
    • 一、Lua應用場景 游戲開發 獨立應用腳本 Web 應用腳本 擴展和資料庫插件如:MySQL Proxy 和 MySQL WorkBench 安全系統,如入侵檢測系統 教程採用Aide Lua Pro或AndLua+開發安卓應用。在學習開發安卓應用前,先學習lua的基礎課程。 二、配置手機開發環境 ...
    • 怎麼從菜鳥程式員變成架構師 一、正確理解架構師的工作 架構師一般是不會去探討業務的範疇,他是把整個項目的結構搭出來,並讓程式員去填肉(業務功能部分) ,一般架構師的好壞決定這個項目的工期與質量,現在市面上看見的架構師一般都是別人的框架直接拿來用的,所以就不存在技術提升的範疇。很少會自己搭建框架。如果 ...
    一周排行
      -Advertisement-
      Play Games
    • 前言 插件化的需求主要源於對軟體架構靈活性的追求,特別是在開發大型、複雜或需要不斷更新的軟體系統時,插件化可以提高軟體系統的可擴展性、可定製性、隔離性、安全性、可維護性、模塊化、易於升級和更新以及支持第三方開發等方面的能力,從而滿足不斷變化的業務需求和技術挑戰。 一、插件化探索 在WPF中我們想要開 ...
    • 歡迎ReaLTaiizor是一個用戶友好的、以設計為中心的.NET WinForms項目控制項庫,包含廣泛的組件。您可以使用不同的主題選項對項目進行個性化設置,並自定義用戶控制項,以使您的應用程式更加專業。 項目地址:https://github.com/Taiizor/ReaLTaiizor 步驟1: ...
    • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
    • Channel 是乾什麼的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume ...
    • efcore如何優雅的實現按年分庫按月分表 介紹 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵適配 距離上次發文.net相關的已經有很久了,期間一直在從事java相關的 ...
    • 前言 Spacesniffer 是一個免費的文件掃描工具,通過使用樹狀圖可視化佈局,可以立即瞭解大文件夾的位置,幫助用戶處理找到這些文件夾 當前系統C盤空間 清理後系統C盤空間 下載 Spacesniffer 下載地址:https://spacesniffer.en.softonic.com/dow ...
    • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
    • 一、ReZero簡介 ReZero是一款.NET中間件 : 全網唯一開源界面操作就能生成API , 可以集成到任何.NET6+ API項目,無破壞性,也可讓非.NET用戶使用exe文件 免費開源:MIT最寬鬆協議 , 一直從事開源事業十年,一直堅持開源 1.1 純ReZero開發 適合.Net Co ...
    • 一:背景 1. 講故事 停了一個月沒有更新文章了,主要是忙於寫 C#內功修煉系列的PPT,現在基本上接近尾聲,可以回頭繼續更新這段時間分析dump的一些事故報告,有朋友微信上找到我,說他們的系統出現了大量的http超時,程式不響應處理了,讓我幫忙看下怎麼回事,dump也抓到了。 二:WinDbg分析 ...
    • 開始做項目管理了(本人3年java,來到這邊之後真沒想到...),天天開會溝通整理需求,他們講話的時候忙裡偷閑整理一下常用的方法,其實語言還是有共通性的,基本上看到方法名就大概能猜出來用法。出去打水的時候看到外面太陽好好,真想在外面坐著曬太陽,回來的時候好兄弟三年前送給我的鍵盤D鍵不靈了,在打"等待 ...