微信原生組件|基於小程式實現音視頻通話

来源:https://www.cnblogs.com/zegodeveloper/archive/2022/09/21/16715772.html
-Advertisement-
Play Games

1 微信小程式原生推拉流組件功能簡介 本文將介紹如何使用微信小程式原生推拉流組件 <live-pusher> 和 <live-player> 進行推拉流,快速實現一個簡單的實時音視頻通話。 由於微信小程式原生推拉流組件使用起來比較複雜,推薦開發者使用即構封裝的音視頻SDK <zego-push> 和 ...


1 微信小程式原生推拉流組件功能簡介

本文將介紹如何使用微信小程式原生推拉流組件 <live-pusher> 和 <live-player> 進行推拉流,快速實現一個簡單的實時音視頻通話。

由於微信小程式原生推拉流組件使用起來比較複雜,推薦開發者使用即構封裝的音視頻SDK <zego-push> 和 <zego-player> 組件實現視頻通話,可參考 實現視頻通話

2 實現微信小程式音視頻通話的前提條件

在實現基本的實時音視頻功能之前,請確保:

3 即構音視頻SDK實現流程

用戶通過 ZEGO Express SDK 即構音視頻SDK進行視頻通話的基本流程為:

用戶 A、B 加入房間,用戶 B 預覽並將音視頻流推送到 ZEGO 雲服務(推流),用戶 A 收到用戶 B 推送音視頻流的通知之後,在通知中播放用戶 B 的音視頻流(拉流)。

下載.png

3.1 配置微信小程式後臺

在初始化 音視頻SDK 前,需要在 微信公眾平臺 中進行如下配置:

  • 伺服器功能變數名稱配置:在“小程式後臺 > 開發管理 > 開發設置 > 伺服器功能變數名稱”中,按照協議分類,將即構 Server 地址、LogUrl、以及用戶業務需要用到的地址填到指定的“request合法功能變數名稱”或“socket合法功能變數名稱”中。
    下載1.png

  • 相關功能開啟:在“小程式後臺 > 開發管理 > 介面設置 > 介面許可權”中,打開 實時播放音視頻流實時錄製音視頻流 功能開關。
    下載2.png

3.2 即構音視頻SDK初始化

1. 創建音視頻通話界面

根據音視頻場景需要,為您的項目創建音視頻通話的用戶界面。我們推薦您在項目中添加如下元素:

  • 本地預覽視窗
  • 遠端視頻視窗
  • 結束按鈕
    下載3.png

小程式推流組件 <live-pusher> 中的 "video-width" 和 "video-height" 存在相容性問題,可能會出現設置不生效的情況。

參考界面代碼:

<view wx:if="{{canShow== 1}}" class="">
  <view class="containerBase">
    <live-pusher class="testpusher" 
    wx:if="{{pusher.url}}" 
    url="{{pusher.url}}"  
    mode="{{pusher.mode}}"
    autopush="{{pusher.autopush}}"
    enable-camera="{{pusher.enableCamera}}"
    enable-mic="{{pusher.enableMic}}"
    muted="{{!pusher.enableMic}}"
    enable-agc="{{pusher.enableAgc}}"
    enable-ans="{{pusher.enableAns}}"
    zoom="{{pusher.enableZoom}}"
    min-bitrate="{{pusher.minBitrate}}"
    max-bitrate="{{pusher.maxBitrate}}"
    video-width="{{pusher.videoWidth}}"
    video-height="{{pusher.videoHeight}}"
    beauty="{{pusher.beautyLevel}}"
    whiteness="{{pusher.whitenessLevel}}"
    orientation="{{pusher.videoOrientation}}"
    device-position="{{pusher.frontCamera}}"
    remote-mirror="{{pusher.enableRemoteMirror}}"
    local-mirror="{{pusher.localMirror}}"
    background-mute="{{pusher.enableBackgroundMute}}"
    audio-quality="{{pusher.audioQuality}}"
    audio-volume-type="{{pusher.audioVolumeType}}"
    audio-reverb-type="{{pusher.audioReverbType}}"
    waiting-image="{{pusher.waitingImage}}"
    beauty-style="{{pusher.beautyStyle}}"
    filter="{{pusher.filter}}"
    bindstatechange="onPushStateChange" 
    bindaudiovolumenotify="bindaudiovolumenotify"  
    bindnetstatus="onPushNetStateChange"
    waiting-image="https://storage.zego.im/downloads/pause_publish.png"></live-pusher>
      <live-player  wx:for="{{playerList}}" wx:key="streamID" id="{{item.id}}" 
      src= "{{item.url}}"
      mode= "RTC"
      autoplay= "{{item.autoplay}}"
      mute-audio= "{{item.muteAudio}}"
      mute-video= "{{item.muteVideo}}"
      orientation= "{{item.orientation}}"
      object-fit= "{{item.objectFit}}"
      min-cache= "{{item.minCache}}"
      max-cache= "{{item.maxCache}}"
      sound-mode= "{{item.soundMode}}"
      enable-recv-message= "{{item.enableRecvMessage}}"
      auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
      auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}" enable-metadata="true" bindmetadatachange="binddatachange"  bindstatechange="onPlayStateChange" bindnetstatus="onPlayNetStateChange"></live-player>
  </view>
  <view class="index-container">
    <view class='input-container'>
      <input value="{{roomID}}" bindinput="bindKeyInput" placeholder="請輸入房間 ID" placeholder-style='color: #b3b3b3; font-size: 14px;' class="room-input" />
      <text class="tip"></text>
    </view>
    <view class="button-container">
      <button bindtap="openRoom" data-role="1" data-option="videoAndAudio" hover-class="none" class="openRoom">
        加入房間(推流)
      </button>
     
      
      <button bindtap="logout" hover-class="none">退出房間</button>
    </view>
  </view>
</view>
<view class="settings">
  <button wx:if="{{canShow==0}}" open-type="openSetting" bindopensetting="settingCallback">
    授權使用攝像頭和麥克風
  </button>
</view>

2. 創建音視頻SDK引擎

創建 ZegoExpressEngine 引擎實例,將申請到的 AppID 傳入參數 “appID”,將獲取到的 Server 地址傳入參數 “server”。

// 初始化實例
zg = new ZegoExpressEngine(appID, server);

如果需要註冊回調,開發者可根據實際需要,實現 ZegoEvent 中的某些方法,創建引擎後可通過調用 on 介面設置回調。

zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
    if (state == 'DISCONNECTED') {
        // 與房間斷開了連接
	// ...
    }

    if (state == 'CONNECTING') {
        // 與房間嘗試連接中
	// ...
    }

    if (state == 'CONNECTED') {
        // 與房間連接成功
	// ...
    }
})

3.3 登錄音視頻房間

1. 獲取房間登錄 Token

登錄房間需要用於驗證身份的 Token,獲取方式請參考 用戶許可權控制。如需快速調試,建議使用控制台生成的臨時 Token,生成臨時 Token 的具體操作請參考 控制台 - 項目管理

2. 登錄音視頻房間

您可以調用 SDK 的 loginRoom 介面,傳入房間 ID 參數 “roomID”、“token” 和用戶參數 “user”,登錄房間。您可通過監聽 roomStateUpdate 回調實時監控自己在本房間內的連接狀態,具體請參考 4.1 常見通知回調 中的“4.1.1 我在房間內的連接狀態變化通知”。

roomID 和 user 的參數由您本地生成,但是需要滿足以下條件:

  • 同一個 AppID 內,需保證 “roomID” 全局唯一。
  • 同一個 AppID 內,需保證 “userID” 全局唯一,建議開發者將 “userID” 與自己業務的賬號系統進行關聯。

為避免錯過任何通知,您需要在登錄房間前先設置所有的監聽回調(如房間狀態、用戶狀態、流狀態、推拉流狀態等),具體請參考 4.1 常見通知回調

// 登錄房間,成功則返回 true
const result = await zg.loginRoom(roomID, token, {userID, userName});

3.4 將自己的音視頻流推送到 ZEGO 即構音視頻雲

3.4.1 初始化小程式組件實例

調用 initContext 介面初始化小程式組件。

小程式組件中用於存儲推流屬性 pusher 和拉流屬性列表 playerList 兩個欄位需要傳給 SDK,SDK 後續將通過傳入的兩個欄位對相應的推拉流作狀態及視圖更新處理。

zg.initContext({
     wxContext: this,
     pushAtr: "pusher", // 對象名,對象屬性與 live-pusher 中的屬性為映射關係
     playAtr: "playerList" // 對象名,對象屬性與 live-player 中的屬性為映射關係
})

即構音視頻SDK 在內部會對推拉流實例進行操作以及視圖更新,開發者無需保存推拉流實例和調用小程式 setData 介面更新視圖,避免與 SDK 發生衝突。後續可通過 getPusherInstancegetPlayerInstance 介面獲取推拉流實例。

3.4.2 創建對應業務場景的 WXML

根據您的業務場景需求,編寫 WXML 文件,創建推拉流組件 <live-pusher> 和 <live-player>。

  • <live-pusher> 組件用於小程式的實時推送音視頻流功能。
  • <live-player> 組件用戶小程式的實時播放音視頻流功能。

WXML 的具體含義與用法請參考微信官網文檔中的介紹 WXML

WXML 中的 pusher 與 playerList,必須與初始化小程式組件 initContext 中定義的這兩個欄位屬性名保持一致,後續 SDK 調用推拉流介面之後才能正確地進行狀態及視圖更新。

bindstatechange 表示播放狀態變化事件;bindaudiovolumenotify 表示播放音量大小通知;bindnetstatus 表示網路狀態通知。

<live-pusher class="testpusher" 
    wx:if="{{pusher.url}}" 
    url="{{pusher.url}}"  
    mode="{{pusher.mode}}"
    autopush="{{pusher.autopush}}"
    enable-camera="{{pusher.enableCamera}}"
    enable-mic="{{pusher.enableMic}}"
    muted="{{!pusher.enableMic}}"
    enable-agc="{{pusher.enableAgc}}"
    enable-ans="{{pusher.enableAns}}"
    enable-ear-monitor="{{pusher.enableEarMonitor}}"
    auto-focus="{{pusher.enableAutoFocus}}"
    zoom="{{pusher.enableZoom}}"
    min-bitrate="{{pusher.minBitrate}}"
    max-bitrate="{{pusher.maxBitrate}}"
    video-width="{{pusher.videoWidth}}"
    video-height="{{pusher.videoHeight}}"
    beauty="{{pusher.beautyLevel}}"
    whiteness="{{pusher.whitenessLevel}}"
    orientation="{{pusher.videoOrientation}}"
    aspect="{{pusher.videoAspect}}"
    device-position="{{pusher.frontCamera}}"
    remote-mirror="{{pusher.enableRemoteMirror}}"
    local-mirror="{{pusher.localMirror}}"
    background-mute="{{pusher.enableBackgroundMute}}"
    audio-quality="{{pusher.audioQuality}}"
    audio-volume-type="{{pusher.audioVolumeType}}"
    audio-reverb-type="{{pusher.audioReverbType}}"
    waiting-image="{{pusher.waitingImage}}"
    beauty-style="{{pusher.beautyStyle}}"
    filter="{{pusher.filter}}"
    bindstatechange="onPushStateChange" 
    bindaudiovolumenotify="bindaudiovolumenotify"  
    bindnetstatus="onPushNetStateChange"
    waiting-image="https://storage.zego.im/downloads/pause_publish.png">
</live-pusher>
<live-player  wx:for="{{playerList}}" wx:key="streamID" id="{{item.id}}" 
      src= "{{item.url}}"
      mode= "RTC"
      autoplay= "{{item.autoplay}}"
      mute-audio= "{{item.muteAudio}}"
      mute-video= "{{item.muteVideo}}"
      orientation= "{{item.orientation}}"
      object-fit= "{{item.objectFit}}"
      min-cache= "{{item.minCache}}"
      max-cache= "{{item.maxCache}}"
      sound-mode= "{{item.soundMode}}"
      enable-recv-message= "{{item.enableRecvMessage}}"
      auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
      auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}" enable-metadata="true" bindmetadatachange="binddatachange"  bindstatechange="onPlayStateChange" bindnetstatus="onPlayNetStateChange">
</live-player>

3.4.3 推送音視頻流到 ZEGO 即構音視頻雲

必須完成初始化小程式組件實例和創建業務場景的 WXML 之後,才能調用 SDK 介面創建推流和拉流實例。

用戶調用 SDK 的 createPusher 介面創建推流實例,並通過調用實例對象上的 start 介面,傳入流 ID 參數 “streamID”。您可通過監聽 publisherStateUpdate 回調知曉推流是否成功,具體請參考 4.1 常見通知回調 中的“4.1.4 用戶推送音視頻流的狀態通知”。

“streamID” 由您本地生成,但是需要保證:

  • 同一個 AppID 下,“streamID” 全局唯一。如果同一個 AppID 下,不同用戶各推了一條 “streamID” 相同的流,後推流的用戶推流失敗。
  • “streamID” 長度不超過 256 位元組的字元串。僅支持數字,英文字元和 “~”,“!”,“@”,“$”,“%”,“^”,“&”,“*”,“(”,“)”,“_”,“+”,“=”,“-”,“`”,“;”,“’”,“,”,“.”,“<”,“>”,“/”,“\”。
// 推流方登錄房間成功後觸發推流
 const pusher = zg.createPusher();
 pusher.start("streamID_xxx");

3.5 拉取其他用戶的音視頻

進行視頻通話時,我們需要拉取到其他用戶的音視頻。

用戶先調用 getPlayerInstance介面,根據傳入的流 ID 參數 “streamID”,獲取 streamID 對應的拉流實例,然後通過調用拉流實例對象的 play 介面開始拉流。您可通過監聽 playerStateUpdate 回調知曉是否成功拉取音視頻,具體請參考 4.1 常見通知回調 中的“4.1.5 用戶拉取音視頻流的狀態通知”。

遠端用戶推送的 “streamID” 可以從 roomStreamUpdate 回調中獲得,具體回調設置請參考 4.1 常見通知回調 中的“4.1.3 房間內流狀態變更的通知”。

// 在 SDK 的回調 roomStreamUpdate 中獲取拉流 streamID
// 當用戶加入或離開房間時,該事件被觸發
zg.on("roomStreamUpdate", (roomID, updateType, streamList) => {
    console.log("roomStreamUpdate", roomID, updateType, streamList);
    if (updateType === "ADD") {
        streamList.forEach(i => {
              zg.getPlayerInstance(i.streamID).play();
        })
    } else {
       streamList.forEach(i => {
              zg.getPlayerInstance(i.streamID).stop();
       })
    }
});

4 小程式音視頻通話的常用功能

4.1 常見音視頻房間通知回調

4.1.1 我在房間內的連接狀態變化通知

roomStateUpdate:本地調用 loginRoom 加入房間時,您可通過監聽該回調實時監控自己在本房間內的連接狀態。

用戶可以在回調中根據不同狀態處理業務邏輯。

zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
    if (state == 'DISCONNECTED') {
        // 與房間斷開了連接
	// ...
    }

    if (state == 'CONNECTING') {
        // 與房間嘗試連接中
	// ...
    }

    if (state == 'CONNECTED') {
        // 與房間連接成功
	// ...
    }
})

4.png

4.1.2 其他用戶進出房間的通知

roomUserUpdate:同一房間內的其他用戶進出房間時,您可通過此回調收到通知。登錄房間後,當房間內有用戶新增或刪除時,SDK 會通過該回調通知。

只有調用 loginRoom 介面登錄房間時傳入 ZegoRoomConfig 配置,且 “userUpdate” 參數取值為 “true” 時,用戶才能收到 roomUserUpdate 回調。

// 用戶狀態更新回調
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
    console.warn(
        `roomUserUpdate: room ${roomID}, user ${updateType === 'ADD' ? 'added' : 'left'} `,
        JSON.stringify(userList),
    );
});

4.1.3 房間內流狀態變更的通知

roomStreamUpdate:流狀態更新回調。登錄房間後,當房間內有用戶新推送或刪除音視頻流時,SDK 會通過該回調通知。

// 流狀態更新回調
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
    if (updateType == 'ADD') {
        // 流新增,開始拉流
    } else if (updateType == 'DELETE') {
        // 流刪除,停止拉流
    }
});

4.1.4 用戶推送音視頻流的狀態通知

  • 推流狀態事件

微信小程式會在 <live-pusher> 的 bindstatechange 綁定的方法中通知出推流狀態事件,開發者需要:

a. 在 bindstatechange 綁定的回調函數中,調用 SDK 的 updatePlayerState 介面將推流狀態事件透傳給 SDK。

b. 在 SDK 的 publisherStateUpdate 回調中處理推流的開始、失敗狀態。

// live-pusher 綁定推流事件
onPushStateChange(e) {
    // 透傳推流事件給 SDK
    zg.updatePlayerState(this.data.publishStreamID, e);
},

// 推流後,伺服器主動推過來的,流狀態更新
// NO_PUBLISH:未推流狀態,PUBLISH_REQUESTING:正在請求推流狀態,PUBLISHING:正在推流狀態
// state: "PUBLISHING" | "NO_PUBLISH" | "PUBLISH_REQUESTING";
zg.on("publisherStateUpdate", (result) => {
    console.log("publishStateUpdate", result.state);
});
  • 推流網路事件

微信小程式會在 <live-pusher> 的 bindnetstatus 綁定的方法中通知出推流網路事件,開發者需要在對應的小程式回調中,調用 SDK 的 updatePlayerNetStatus 介面將推流網路事件透傳給 SDK。

// live-pusher 綁定網路狀態事件
onPushNetStateChange(e) {
    //透傳網路狀態事件給 SDK
    zg.updatePlayerNetStatus(this.data.publishStreamID, e);
},


// SDK 推流網路質量回調
zg.on("publishQualityUpdate", (streamID, publishStats) => {
    console.log("publishQualityUpdate", streamID, publishStats);
});

4.1.5 用戶拉取音視頻流的狀態通知

  • 拉流狀態事件

微信小程式會在 <live-player> 的 bindstatechange 綁定的方法中通知出拉流狀態事件,開發者需要:

a. 在 bindstatechange 綁定的回調函數中,調用 SDK 的 updatePlayerState 介面將拉流狀態事件透傳給 SDK。

b. 在 SDK 提供的 playerStateUpdate 回調中處理拉流的開始或失敗狀態。

// live-player 綁定的拉流事件
onPlayStateChange(e) {
    // 透傳拉流事件給 SDK
    zg.updatePlayerState(e.currentTarget.id, e);
},

// 伺服器主動推過來的流的播放狀態
// 視頻播放狀態通知;state: "NO_PLAY" | "PLAY_REQUESTING" | "PLAYING";
zg.on("playerStateUpdate", (result) => {
    console.log("playStateUpdate", result.state);
});
  • 拉流網路事件

微信小程式會在 <live-player> 的 bindnetstatus 綁定的方法中通知出拉流網路事件,開發者需要在對應的小程式回調中,調用 SDK 的 updatePlayerNetStatus 介面將推流網路事件透傳給 SDK。

// live-player 綁定網路狀態事件
onPlayNetStateChange(e) {
    // 透傳網路狀態事件給 SDK
    zg.updatePlayerNetStatus(playStreamID, e);
},

// SDK 拉流網路質量回調
zg.on("playQualityUpdate", (playStreamID, playStats) => {
    console.log("playQualityUpdate", playStreamID, playStats);
});

4.2 停止fang jian音視頻通話

4.2.1 停止推送/拉取音視頻流

1. 停止推流

調用 SDK 的 getPusherInstance 介面獲取推流實例,並調用推流實例的 stop 方法停止推流。

// 停止推流
zg.getPusherInstance().stop();

2. 停止拉流

調用 SDK 的 getPlayerInstance 介面獲取拉流實例,並調用推流實例的 stop 方法停止拉流。

// 停止拉流
zg.getPlayerInstance(streamID).stop();

4.2.2 退出房間

調用 SDK 的 logoutRoom 介面退出房間。

zg.logoutRoom(roomID);

5 調試視頻通話功能

在真機中運行項目,運行成功後,可以看到本端視頻畫面。

為方便體驗,ZEGO 提供了一個 Web 端調試示例 ,在該頁面下,輸入相同的 AppID、RoomID,輸入一個不同的 UserID,即可加入同一房間與真機設備互通。當成功開始音視頻通話時,可以聽到遠端的音頻,看到遠端的視頻畫面。

6 視頻通話 API 調用時序

整個推拉流過程的 API 調用時序可參考下圖:
下載5.png

音視頻場景解決方案分享,更多詳情可搜索官網(https://zegoguanwang.datasink.sensorsdata.cn/t/pB)
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • AU上傳ipa出現下圖紅框提示說明成功上傳,如果App Store後臺沒有出現構建版本, 請登錄 apple賬號對應的郵箱查看反饋,特別留意垃圾郵箱,無論成功還是失敗,apple都會發郵件 一、首先登錄iTunes Connect 後臺、查看ipa構建情況 https://appstoreconne ...
  • 現如今,人們在網上聊天、發帖時越來越愛用表情包,表情包一方面是一種個性化的表達方式,另一方面更能傳達出當下的心理活動,可以說在網路社交中表情包是一個不可或缺的存在。加上近年來元宇宙的興起,3D虛擬形象廣泛應用,用戶可以通過自己的表情來控制虛擬形象的表情,做一系列專屬的表情包,更加生動形象。 那麼,如 ...
  • ##vue+element-ui後臺管理系統模板 前端:基於vue2.0+或3.0+加上element-ui組件框架 後端:springboot+mybatis-plus寫介面 通過Axios調用介面完成數據傳遞 通過router路由完成各頁面的跳轉 ###全局配置 App.vue <templat ...
  • 好家伙,繼續優化, 好家伙,我把我的飛機大戰發給我的小伙伴們玩 期待著略微的贊賞之詞,然後他們用手機打開我的給他們的網址 然後點一下飛機就炸了。 游戲體驗零分 (滑鼠點擊在移動端依舊可以生效) 好了所以我們來優化一下這個觸屏移動事件 由於沒有參考,就去翻文檔了 觸摸事件分三個:touchstart、 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. 定義整體結構 先寫出構造函數,將Promise向外暴露 /* 自定義Promise函數模塊:IIFE */ (function (window) { /* Promise構造函數 executor:執行器函數 */ function ...
  • 一.分類 1.1 頁面佈局 首先創建cate的分支 定義基本結構,因為是兩個需要滾動的區域,所以這裡要用到組件 ==scroll== 這個組件如果是y scroll那就要固定高度,x scroll那就要固定寬度 這裡有個問題就是,我們應該把高度限定在整個屏幕高度 這裡用到uniapp一個api == ...
  • 每日3題 1 以下代碼執行後,控制臺中的輸出內容為? class A { static a = "123"; } class B extends A {} console.log(B.a); 2 以下3句語句,哪句是合法的 1.toString(); 1..toString(); 1...toStr ...
  • 1. 安裝sass 較新的版本不需要配置sass-loader等一系列插件,安裝即用。 npm install --save-dev sass 2. 編寫App.tsx中的基本DOM 更改app.css為app.scss,並刪除其中全部內容 使用如下代碼替換app.tsx中的內容 import ". ...
一周排行
    -Advertisement-
    Play Games
  • 最近做項目過程中,使用到了海康相機,官方只提供了C/C++的SDK,沒有搜尋到一個合適的封裝了的C#庫,故自己動手,簡單的封裝了一下,方便大家也方便自己使用和二次開發 ...
  • 前言 MediatR 是 .NET 下的一個實現消息傳遞的庫,輕量級、簡潔高效,用於實現進程內的消息傳遞機制。它基於中介者設計模式,支持請求/響應、命令、查詢、通知和事件等多種消息傳遞模式。通過泛型支持,MediatR 可以智能地調度不同類型的消息,非常適合用於領域事件處理。 在本文中,將通過一個簡 ...
  • 前言 今天給大家推薦一個超實用的開源項目《.NET 7 + Vue 許可權管理系統 小白快速上手》,DncZeus的願景就是做一個.NET 領域小白也能上手的簡易、通用的後臺許可權管理模板系統基礎框架。 不管你是技術小白還是技術大佬或者是不懂前端Vue 的新手,這個項目可以快速上手讓我們從0到1,搭建自 ...
  • 第1章:WPF概述 本章目標 瞭解Windows圖形演化 瞭解WPF高級API 瞭解解析度無關性概念 瞭解WPF體繫結構 瞭解WPF 4.5 WPF概述 ​ 歡迎使用 Windows Presentation Foundation (WPF) 桌面指南,這是一個與解析度無關的 UI 框架,使用基於矢 ...
  • 在日常開發中,並不是所有的功能都是用戶可見的,還在一些背後默默支持的程式,這些程式通常以服務的形式出現,統稱為輔助角色服務。今天以一個簡單的小例子,簡述基於.NET開發輔助角色服務的相關內容,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 第3章:佈局 本章目標 理解佈局的原則 理解佈局的過程 理解佈局的容器 掌握各類佈局容器的運用 理解 WPF 中的佈局 WPF 佈局原則 ​ WPF 視窗只能包含單個元素。為在WPF 視窗中放置多個元素並創建更貼近實用的用戶男面,需要在視窗上放置一個容器,然後在這個容器中添加其他元素。造成這一限制的 ...
  • 前言 在平時項目開發中,定時任務調度是一項重要的功能,廣泛應用於後臺作業、計劃任務和自動化腳本等模塊。 FreeScheduler 是一款輕量級且功能強大的定時任務調度庫,它支持臨時的延時任務和重覆迴圈任務(可持久化),能夠按秒、每天/每周/每月固定時間或自定義間隔執行(CRON 表達式)。 此外 ...
  • 目錄Blazor 組件基礎路由導航參數組件參數路由參數生命周期事件狀態更改組件事件 Blazor 組件 基礎 新建一個項目命名為 MyComponents ,項目模板的交互類型選 Auto ,其它保持預設選項: 客戶端組件 (Auto/WebAssembly): 最終解決方案裡面會有兩個項目:伺服器 ...
  • 先看一下效果吧: isChecked = false 的時候的效果 isChecked = true 的時候的效果 然後我們來實現一下這個效果吧 第一步:創建一個空的wpf項目; 第二步:在項目裡面添加一個checkbox <Grid> <CheckBox HorizontalAlignment=" ...
  • 在編寫上位機軟體時,需要經常處理命令拼接與其他設備進行通信,通常對不同的命令封裝成不同的方法,擴展稍許麻煩。 本次擬以特性方式實現,以兼顧維護性與擴展性。 思想: 一種命令對應一個類,其類中的各個屬性對應各個命令段,通過特性的方式,實現其在這包數據命令中的位置、大端或小端及其轉換為對應的目標類型; ...