flutter3+dart3聊天室|Flutter3跨平臺仿微信App語音聊天/朋友圈

来源:https://www.cnblogs.com/xiaoyan2017/p/18008370
-Advertisement-
Play Games

全新研發flutter3+dart3+photo_view跨多端仿微信App界面聊天Flutter3-Chat。 flutter3-chat基於最新跨全平臺技術flutter3+dart3+material-design+shared_preferences+easy_refresh構建的仿微信AP ...


全新研發flutter3+dart3+photo_view跨多端仿微信App界面聊天Flutter3-Chat

flutter3-chat基於最新跨全平臺技術flutter3+dart3+material-design+shared_preferences+easy_refresh構建的仿微信APP界面聊天實例項目。實現發送圖文表情消息/gif大圖、長按仿微信語音操作面板、圖片預覽、紅包及朋友圈等功能。

技術架構

  • 編輯器:Vscode
  • 框架技術:Flutter3.16.5+Dart3.2.3
  • UI組件庫:material-design3
  • 彈窗組件:showDialog/SimpleDialog/showModalBottomSheet/AlertDialog
  • 圖片預覽:photo_view^0.14.0
  • 本地緩存:shared_preferences^2.2.2
  • 下拉刷新:easy_refresh^3.3.4
  • toast提示:toast^0.3.0
  • 網址預覽組件:url_launcher^6.2.4

Flutter3.x開發跨平臺項目,性能有了大幅度提升,官方支持編譯到android/ios/macos/windows/linux/web等多平臺,未來可期!

項目構建目錄

通過 flutter create app_project 命令即可快速創建一個跨平臺初始化項目。

通過命令創建項目後,項目結構就如上圖所示。

需要註意:flutter項目基於dart語音開發,需要首先配置Dart SDK和Flutter SDK開發環境,大家可以去官網查看配置文檔。

https://flutter.dev/

https://flutter.cn/

https://pub.flutter-io.cn/

https://www.dartcn.com/

另外使用VScode編輯器開發項目,可自行安裝Flutter / Dart擴展插件。

由於flutter3支持編譯到windows,大家可以開發初期在windows上面調試,後期release apk到手機上。

通過如下命令即可運行到windows平臺

flutter run -d windows 

運行後預設視窗大小為1280x720,可以修改windows/runner/main.cpp文件裡面的視窗尺寸。

同樣,可以通過 flutter run -d chrome 命令運行到web上預覽。

假如在沒有真機的情況下,我們可以選擇模擬器調試。目前市面上有很多類型模擬器,他們使用adb連接時都會有不同的預設埠,下麵列出了一些常用的模擬器及埠號。通過adb connect連接上指定模擬器之後,執行flutter run命令即可運行項目到模擬器上面。

flutter3實現圓角文本框及漸變按鈕

Container(
  height: 40.0,
  margin: const EdgeInsets.symmetric(vertical: 5.0, horizontal: 30.0),
  decoration: BoxDecoration(
    color: Colors.white,
    border: Border.all(color: const Color(0xffdddddd)),
    borderRadius: BorderRadius.circular(15.0),
  ),
  child: Row(
    children: [
      Expanded(
        child: TextField(
          keyboardType: TextInputType.phone,
          controller: fieldController,
          decoration: InputDecoration(
            hintText: '輸入手機號',
            suffixIcon: Visibility(
              visible: authObj['tel'].isNotEmpty,
              child: InkWell(
                hoverColor: Colors.transparent,
                highlightColor: Colors.transparent,
                splashColor: Colors.transparent,
                onTap: handleClear,
                child: const Icon(Icons.clear, size: 16.0,),
              )
            ),
            contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 12.0),
            border: const OutlineInputBorder(borderSide: BorderSide.none),
          ),
          onChanged: (value) {
            setState(() {
              authObj['tel'] = value;
            });
          },
        ),
      )
    ],
  ),
),

按鈕漸變則是通過Container組件的decotaion裡面的gradient屬性設置漸變效果。

Container(
  margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 30.0),
  decoration: BoxDecoration(
    borderRadius: BorderRadius.circular(15.0),
    // 自定義按鈕漸變色
    gradient: const LinearGradient(
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
      colors: [
        Color(0xFF0091EA), Color(0xFF07C160)
      ],
    )
  ),
  child: SizedBox(
    width: double.infinity,
    height: 45.0,
    child: FilledButton(
      style: ButtonStyle(
        backgroundColor: MaterialStateProperty.all(Colors.transparent),
        shadowColor: MaterialStateProperty.all(Colors.transparent),
        shape: MaterialStatePropertyAll(
          RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0))
        )
      ),
      onPressed: handleSubmit,
      child: const Text('登錄', style: TextStyle(fontSize: 18.0),),
    ),
  )
),

flutter實現60s倒計時發送驗證碼功能。

Timer? timer;
String vcodeText = '獲取驗證碼';
bool disabled = false;
int time = 60;

// 60s倒計時
void handleVcode() {
  if(authObj['tel'] == '') {
    snackbar('手機號不能為空');
  }else if(!Utils.checkTel(authObj['tel'])) {
    snackbar('手機號格式不正確');
  }else {
    setState(() {
      disabled = true;
    });
    startTimer();
  }
}
startTimer() {
  timer = Timer.periodic(const Duration(seconds: 1), (timer) {
    setState(() {
      if(time > 0) {
        vcodeText = '獲取驗證碼(${time--})';
      }else {
        vcodeText = '獲取驗證碼';
        time = 60;
        disabled = false;
        timer.cancel();
      }
    });
  });
  snackbar('簡訊驗證碼已發送,請註意查收', color: Colors.green);
}

Flutter3沉浸式漸變狀態導航欄

要實現如上圖漸變AppBar也非常簡單,只需要配置AppBar提供的可伸縮靈活區域屬性 flexibleSpace 配合gradient即可快速實現漸變導航欄。

AppBar(
  title: Text('Flutter3-Chat'),
  flexibleSpace: Container(
    decoration: const BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [
          Color(0xFF0091EA), Color(0xFF07C160)
        ],
      )
    ),
  )
),

Flutter3字體圖標/自定義badge

flutter內置了豐富的字體圖標,通過圖標組件 Icon(Icons.add) 引入即可使用。

https://api.flutter-io.cn/flutter/material/Icons-class.html

另外還支持通過自定義IconData方式自定義圖標,如使用阿裡iconfont圖表庫圖標。

Icon(IconData(0xe666, fontFamily: 'iconfont'), size: 18.0) 

把下載的字體文件放到assets目錄,

pubspec.yaml中引入字體文件。

class FStyle {
  // 自定義iconfont圖標
  static iconfont(int codePoint, {double size = 16.0, Color? color}) {
    return Icon(
      IconData(codePoint, fontFamily: 'iconfont', matchTextDirection: true),
      size: size,
      color: color,
    );
  }

  // 自定義Badge紅點
  static badge(int count, {
    Color color = Colors.redAccent,
    bool isdot = false,
    double height = 18.0,
    double width = 18.0
  }) {
    final num = count > 99 ? '99+' : count;
    return Container(
      alignment: Alignment.center,
      height: isdot ? height / 2 : height,
      width: isdot ? width / 2 : width,
      decoration: BoxDecoration(color: color, borderRadius: BorderRadius.circular(100.00)),
      child: isdot ? null : Text('$num', style: const TextStyle(color: Colors.white, fontSize: 12.0)),
    );
  }
}

FStyle.badge(23)
FStyle.badge(2, color: Colors.pink, height: 10.0, width: 10.0)
FStyle.badge(0, isdot: true)

Flutter仿微信PopupMenu下拉菜單/下拉刷新

通過flutter提供的PopupMenuButton組件實現下拉菜單功能。

PopupMenuButton(
  icon: FStyle.iconfont(0xe62d, size: 17.0),
  offset: const Offset(0, 50.0),
  tooltip: '',
  color: const Color(0xFF353535),
  itemBuilder: (BuildContext context) {
    return <PopupMenuItem>[
      popupMenuItem(0xe666, '發起群聊', 0),
      popupMenuItem(0xe75c, '添加朋友', 1),
      popupMenuItem(0xe603, '掃一掃', 2),
      popupMenuItem(0xe6ab, '收付款', 3),
    ];
  },
  onSelected: (value) {
    switch(value) {
      case 0:
        print('發起群聊');
        break;
      case 1:
        Navigator.pushNamed(context, '/addfriends');
        break;
      case 2:
        print('掃一掃');
        break;
      case 3:
        print('收付款');
        break;
    }
  },
)
// 下拉菜單項
static popupMenuItem(int codePoint, String title, value) {
  return PopupMenuItem(
    value: value,
    child: Row(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
        const SizedBox(width: 10.0,),
        FStyle.iconfont(codePoint, size: 21.0, color: Colors.white),
        const SizedBox(width: 10.0,),
        Text(title, style: const TextStyle(fontSize: 16.0, color: Colors.white),),
      ],
    ),
  );
}

如上圖:下拉刷新、上拉載入更多是通過 easy_refresh 組件實現功能。

EasyRefresh(
  // 下拉載入提示
  header: const ClassicHeader(
    // showMessage: false,
  ),
  // 載入更多提示
  footer: ClassicFooter(),
  // 下拉刷新邏輯
  onRefresh: () async {
    // ...下拉邏輯
    await Future.delayed(const Duration(seconds: 2));
  },
  // 上拉載入邏輯
  onLoad: () async {
    // ...
  },
  child: ListView.builder(
    itemCount: chatList.length,
    itemBuilder: (context, index) {
      return Ink(
        // ...
      );
    },
  ),
)

如上圖:彈窗功能均是自定義AlertDialog實現效果。通過無限制容器UnconstrainedBox配合SizedBox組件實現自定義視窗大小。

// 關於彈窗
void aboutAlertDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) {
      return UnconstrainedBox(
        constrainedAxis: Axis.vertical,
        child: SizedBox(
          width: 320.0,
          child: AlertDialog(
            contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
            backgroundColor: Colors.white,
            surfaceTintColor: Colors.white,
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
            content: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 10.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Image.asset('assets/images/logo.png', width: 90.0, height: 90.0, fit: BoxFit.cover,),
                  const SizedBox(height: 10.0),
                  const Text('Flutter3-WChat', style: TextStyle(color: Color(0xFF0091EA), fontSize: 22.0),),
                  const SizedBox(height: 5.0),
                  const Text('基於flutter3+dart3開發跨平臺仿微信App聊天實例。', style: TextStyle(color: Colors.black45),),
                  const SizedBox(height: 20.0),
                  Text('©2024/01 Andy   Q: 282310962', style: TextStyle(color: Colors.grey[400], fontSize: 12.0),),
                ],
              ),
            ),
          ),
        ),
      );
    }
  );
}

// 二維碼名片彈窗
void qrcodeAlertDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) {
      return UnconstrainedBox(
        constrainedAxis: Axis.vertical,
        child: SizedBox(
          width: 320.0,
          child: AlertDialog(
            contentPadding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
            backgroundColor: const Color(0xFF07C160),
            surfaceTintColor: const Color(0xFF07C160),
            shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3.0)),
            content: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 10.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Image.asset('assets/images/qrcode.png', width: 250.0, fit: BoxFit.cover,),
                  const SizedBox(height: 15.0),
                  const Text('掃一掃,加我公眾號', style: TextStyle(color: Colors.white60, fontSize: 14.0,),),
                ],
              ),
            ),
          ),
        ),
      );
    }
  );
}

// 退出登錄彈窗
void logoutAlertDialog(BuildContext context) {
  showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        content: const Text('確定要退出登錄嗎?', style: TextStyle(fontSize: 16.0),),
        backgroundColor: Colors.white,
        surfaceTintColor: Colors.white,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
        elevation: 2.0,
        actionsPadding: const EdgeInsets.all(15.0),
        actions: [
          TextButton(
            onPressed: () {Navigator.of(context).pop();},
            child: const Text('取消', style: TextStyle(color: Colors.black54),)
          ),
          TextButton(
            onPressed: handleLogout,
            child: const Text('退出登錄', style: TextStyle(color: Colors.red),)
          ),
        ],
      );
    }
  );
}

flutter實現微信朋友圈九宮格

GroupZone(images: item['images']),

GroupZone(
  images: uploadList,
  album: true,
  onChoose: () async {
    Toast.show('選擇手機相冊圖片', duration: 2, gravity: 1);
  },
),

// 創建可點擊預覽圖片
createImage(BuildContext context, String img, int key) {
  return GestureDetector(
    child: Hero(
      tag: img, // 放大縮小動畫效果標識
      child: img == '+' ? 
      Container(color: Colors.transparent, child: const Icon(Icons.add, size: 30.0, color: Colors.black45),)
      :
      Image.asset(
        img,
        width: width,
        fit: BoxFit.contain,
      ),
    ),
    onTap: () {
      // 選擇圖片
      if(img == '+') {
        onChoose!();
      }else {
        Navigator.of(context).push(FadeRoute(route: ImageViewer(
          images: album ? imgList!.sublist(0, imgList!.length - 1) : imgList,
          index: key,
        )));
      }
    },
  );
}

使用photo_view插件實現預覽大圖功能,支持預覽單張及多張大圖。

import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';

class ImageViewer extends StatefulWidget {
  const ImageViewer({
    super.key,
    this.images,
    this.index = 0,
  });

  final List? images; // 預覽圖列表
  final int index; // 當前預覽圖索引

  @override
  State<ImageViewer> createState() => _ImageViewerState();
}

class _ImageViewerState extends State<ImageViewer> {
  int currentIndex = 0;

  @override
  void initState() {
    super.initState();
    currentIndex = widget.index;
  }

  @override
  Widget build(BuildContext context) {
    var imgCount = widget.images?.length;

    return Scaffold(
      body: Stack(
        children: [
          Positioned(
            top: 0,
            left: 0,
            bottom: 0,
            right: 0,
            child: GestureDetector(
              child: imgCount == 1 ? PhotoView(
                imageProvider: AssetImage(widget.images![0]),
                backgroundDecoration: const BoxDecoration(
                  color: Colors.black,
                ),
                minScale: PhotoViewComputedScale.contained,
                maxScale: PhotoViewComputedScale.covered * 2,
                heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
                enableRotation: true,
              )
              :
              PhotoViewGallery.builder(
                itemCount: widget.images?.length,
                builder: (context, index) {
                  return PhotoViewGalleryPageOptions(
                    imageProvider: AssetImage(widget.images![index]),
                    minScale: PhotoViewComputedScale.contained,
                    maxScale: PhotoViewComputedScale.covered * 2,
                    heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
                  );
                },
                scrollPhysics: const BouncingScrollPhysics(),
                backgroundDecoration: const BoxDecoration(
                  color: Colors.black,
                ),
                pageController: PageController(initialPage: widget.index),
                enableRotation: true,
                onPageChanged: (index) {
                  setState(() {
                    currentIndex = index;
                  });
                },
              ),
              onTap: () {
                Navigator.of(context).pop();
              },
            ),
          ),
          // 圖片索引index
          Positioned(
            top: MediaQuery.of(context).padding.top + 15,
            width: MediaQuery.of(context).size.width,
            child: Center(
              child: Visibility(
                visible: imgCount! > 1 ? true : false,
                child: Text('${currentIndex+1} / ${widget.images?.length}', style: const TextStyle(color: Colors.white)),
              )
            ),
          ),
        ],
      ),
    );
  }
}

flutter3聊天模塊

文本框TextField設置maxLines: null即可實現多行文本輸入,支持圖文emoj混排,網址連接識別等功能。

// 輸入框
Offstage(
  offstage: voiceBtnEnable,
  child: TextField(
    decoration: const InputDecoration(
      isDense: true,
      hoverColor: Colors.transparent,
      contentPadding: EdgeInsets.all(8.0),
      border: OutlineInputBorder(borderSide: BorderSide.none),
    ),
    style: const TextStyle(fontSize: 16.0,),
    maxLines: null,
    controller: editorController,
    focusNode: editorFocusNode,
    cursorColor: const Color(0xFF07C160),
    onChanged: (value) {},
  ),
),

 支持仿微信語音按住說話,左滑取消發送、右滑轉換語音功能。

// 語音
Offstage(
  offstage: !voiceBtnEnable,
  child: GestureDetector(
    child: Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(5),
      ),
      alignment: Alignment.center,
      height: 40.0,
      width: double.infinity,
      child: Text(voiceTypeMap[voiceType], style: const TextStyle(fontSize: 15.0),),
    ),
    onPanStart: (details) {
      setState(() {
        voiceType = 1;
        voicePanelEnable = true;
      });
    },
    onPanUpdate: (details) {
      Offset pos = details.globalPosition;
      double swipeY = MediaQuery.of(context).size.height - 120;
      double swipeX = MediaQuery.of(context).size.width / 2 + 50;
      setState(() {
        if(pos.dy >= swipeY) {
          voiceType = 1; // 鬆開發送
        }else if (pos.dy < swipeY && pos.dx < swipeX) {
          voiceType = 2; // 左滑鬆開取消
        }else if (pos.dy < swipeY && pos.dx >= swipeX) {
          voiceType = 3; // 右滑語音轉文字
        }
      });
    },
    onPanEnd: (details) {
      // print('停止錄音');
      setState(() {
        switch(voiceType) {
          case 1:
            Toast.show('發送錄音文件', duration: 1, gravity: 1);
            voicePanelEnable = false;
            break;
          case 2:
            Toast.show('取消發送', duration: 1, gravity: 1);
            voicePanelEnable = false;
            break;
          case 3:
            Toast.show('語音轉文字', duration: 1, gravity: 1);
            voicePanelEnable = true;
            voiceToTransfer = true;
            break;
        }
        voiceType = 0;
      });
    },
  ),
),

按住錄音顯示面板

// 錄音主體(按住說話/鬆開取消/語音轉文本)
Visibility(
  visible: voicePanelEnable,
  child: Material(
    color: const Color(0xDD1B1B1B),
    child: Stack(
      children: [
        // 取消發送+語音轉文字
        Positioned(
          bottom: 120,
          left: 30,
          right: 30,
          child: Visibility(
            visible: !voiceToTransfer,
            child: Column(
              children: [
                // 語音動畫層
                Stack(
                  children: [
                    Container(
                      height: 70.0,
                      margin: const EdgeInsets.symmetric(horizontal: 50.0),
                      decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.circular(15.0),
                      ),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Image.asset('assets/images/voice_record.gif', height: 30.0,)
                        ],
                      ),
                    ),
                    Positioned(
                      right: (MediaQuery.of(context).size.width - 60) / 2,
                      bottom: 1,
                      child: RotatedBox(
                        quarterTurns: 0,
                        child: CustomPaint(painter: ArrowShape(arrowColor: Colors.white, arrowSize: 10.0)),
                      )
                    ),
                  ],
                ),
                const SizedBox(height: 50.0,),
                // 操作項
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    // 取消發送
                    Container(
                      height: 60.0,
                      width: 60.0,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(50.0),
                        color: voiceType == 2 ? Colors.red : Colors.black38,
                      ),
                      child: const Icon(Icons.close, color: Colors.white54,),
                    ),
                    // 語音轉文字
                    Container(
                      height: 60.0,
                      width: 60.0	   

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

-Advertisement-
Play Games
更多相關文章
  • 在進行WPF界面設計時,我們需要在很多地方設置顏色屬性,比如元素的背景色、前景色以及邊框的顏色,還有形狀的內部填充和筆畫,這些顏色的設置在WPF中都以畫刷(Brush)的形式實現。比如最常用的畫刷就是SolidColorBrush,它表示一種純色。 public abstract class Bru ...
  • 代碼片段: 文末附鏈接。 using DataSync.Core; using Furion.Logging.Extensions; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; using System.Da ...
  • Ultralytics YOLOv8 基於深度學習和電腦視覺領域的尖端技術,在速度和準確性方面具有無與倫比的性能。其流線型設計使其適用於各種應用,並可輕鬆適應從邊緣設備到雲 API 等不同硬體平臺。YOLOv8 OBB 模型是YOLOv8系列模型最新推出的任意方向的目標檢測模型,可以檢測任意方向的... ...
  • RotateTransform旋轉 RotateTransform表示旋轉一個對象的角度。首先我們來看一下它的定義 public sealed class RotateTransform : Transform { public static readonly DependencyProperty ...
  • 最近做了幾個 WPF + MudBlazor 的小東西,每次從頭搭建環境比較繁瑣,然鵝搭建過程還沒啥技術含量,索性就直接做了個模板,方便以後使用。 1. 介紹 一個用來創建 .NET 8 + WPF + MudBlazor 的項目模板 適用於 VS2022 用法:vs插件市場下載 or 自己通過 G ...
  • 準備 今天學習的Demo是Data Binding中的Linq: 創建一個空白解決方案,然後添加現有項目,選擇Linq,解決方案如下所示: 查看這個Demo的效果: 開始學習這個Demo xaml部分 查看MainWindow.xaml: <Window x:Class="Linq.MainWind ...
  • 我一直有個夢想,就是自己開發App,運行在自己的手機上。但是選什麼語言呢?C#,Java或者其它? 我想,既然我已經用華為的手機,擁有鴻蒙系統,為什麼不嘗試一下鴻蒙開發呢?說乾就乾,找到官網,一步一步操作。首先是裝好開發環境: 按照官方的文檔操作是不難的,但還是遇到一些小問題,node.js的安裝路 ...
  • 摘要: 在開發iOS應用程式時,一個重要的任務是確保應用在不同的屏幕和設備上呈現出良好的用戶體驗。為了實現這一目標,iOS提供了尺寸類別、Auto Layout和Size Classes等強大的工具和技術。 尺寸類別是描述設備屏幕尺寸的屬性,它可以幫助開發者瞭解當前界面所在的設備環境。明確的設備尺寸... ...
一周排行
    -Advertisement-
    Play Games
  • 在C#中使用SQL Server實現事務的ACID(原子性、一致性、隔離性、持久性)屬性和使用資料庫鎖(悲觀鎖和樂觀鎖)時,你可以通過ADO.NET的SqlConnection和SqlTransaction類來實現。下麵是一些示例和概念說明。 實現ACID事務 ACID屬性是事務處理的四個基本特征, ...
  • 我們在《SqlSugar開發框架》中,Winform界面開發部分往往也用到了自定義的用戶控制項,對應一些特殊的界面或者常用到的一些局部界面內容,我們可以使用自定義的用戶控制項來提高界面的統一性,同時也增強了使用的便利性。如我們Winform界面中用到的分頁控制項、附件顯示內容、以及一些公司、部門、菜單的下... ...
  • 在本篇教程中,我們學習瞭如何在 Taurus.MVC WebMVC 中進行數據綁定操作。我們還學習瞭如何使用 ${屬性名稱} CMS 語法來綁定頁面上的元素與 Model 中的屬性。通過這些步驟,我們成功實現了一個簡單的數據綁定示例。 ...
  • 是在MVVM中用來傳遞消息的一種方式。它是在MVVMLight框架中提供的一個實現了IMessenger介面的類,可以用來在ViewModel之間、ViewModel和View之間傳遞消息。 Send 接受一個泛型參數,表示要發送的消息內容。 Register 方法用於註冊某個對象接收消息。 pub ...
  • 概述:在WPF中,通過EventHandler可實現基礎和高級的UI更新方式。基礎用法涉及在類中定義事件,併在UI中訂閱以執行更新操作。高級用法藉助Dispatcher類,確保在非UI線程上執行操作後,通過UI線程更新界面。這兩種方法提供了靈活而可靠的UI更新機制。 在WPF(Windows Pre ...
  • 概述:本文介紹了在C#程式開發中如何利用自定義擴展方法測量代碼執行時間。通過使用簡單的Action委托,開發者可以輕鬆獲取代碼塊的執行時間,幫助優化性能、驗證演算法效率以及監控系統性能。這種通用方法提供了一種便捷而有效的方式,有助於提高開發效率和代碼質量。 在軟體開發中,瞭解代碼執行時間是優化程式性能 ...
  • 概述:Cron表達式是一種強大的定時任務調度工具,通過配置不同欄位實現靈活的時間規定。在.NET中,Quartz庫提供了簡便的方式配置Cron表達式,實現精準的定時任務調度。這種靈活性和可擴展性使得開發者能夠根據需求輕鬆地制定和管理定時任務,例如每天備份系統日誌或其他重要操作。 Cron表達式詳解 ...
  • 概述:.NET提供多種定時器,如System.Windows.Forms.Timer適用於UI,System.Web.UI.Timer用於Web,System.Diagnostics.Timer用於性能監控,System.Threading.Timer和System.Timers.Timer用於一般 ...
  • 問題背景 有同事聯繫我說,在生產環境上,訪問不了我負責的common服務,然後我去檢查common服務的health endpoint, 沒問題,然後我問了下異常,timeout導致的System.OperationCanceledException。那大概率是客戶端的問題,會不會是埠耗盡,用ne ...
  • 前言: 在本篇 Taurus.MVC WebMVC 入門開發教程的第四篇文章中, 我們將學習如何實現數據列表的綁定,通過使用 List<Model> 來展示多個數據項。 我們將繼續使用 Taurus.Mvc 命名空間,同時探討如何在視圖中綁定並顯示一個 Model 列表。 步驟1:創建 Model ...