<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

MVVM是什么?

汽車電子技術 ? 來源:工控羊 ? 作者: zyuanlbj ? 2023-02-07 14:32 ? 次閱讀

前言

最近在學習WPF,買了一本劉鐵錳老師的《深入淺出WPF》書籍,受益頗深。劉老師是微軟社區精英,他的C#視頻課程也是很受歡迎,感興趣的小伙伴可以去b站觀看。

這里,以一個在線訂餐系統為例,和大家一起分享MVVM的魅力。

MVVM

首先,我們來看看MVVM到底是什么?

MVVM是Model-View-ViewModel的簡寫,它是一種極度優秀的設計模式,也是MVC的增強版。

圖片

View:用戶界面,也叫視圖

由控件構成的、與用戶進行交互的界面,用于把數據展示給用戶并響應用戶的輸入。

ViewModel:MVVM的核心

ViewModel通過雙向數據綁定將View和Model連接了起來,而View和Model之間的同步工作都是完全自動的,無需人為操作。

Model:數據模型

現實世界中事物和邏輯的抽象。

在WPF中,數據占據主導地位,數據與界面之間的橋梁是數據關聯,通過這個橋梁,數據可以流向界面,也可以從界面流回數據源。

ViewModel的存在,使得界面交互業務邏輯處理導致的屬性變更會通知到View前端,讓View前端實時更新;View的變動,也會自動反應到ViewModel上。

MVVM的出現促進了前端開發與后端的分離,極大提高了前端的開發效率;幾乎完全解耦了視圖和業務邏輯的關系。

案例剖析

基于以上理解,我們來剖析下面這個在線訂餐系統。

圖片

將主界面劃分為三個區域:第一個區域是餐館的信息(名字、地址以及電話),中間區域是菜單列表,每個菜品都有名字、種類、點評、評分以及價格,還有選中框;第三個區域有菜品的選中總數和訂餐按鈕。

這里,我們忽視菜品數據的來源是來自數據庫還是其他什么存儲方式,也忽視點擊訂餐按鈕后訂餐數據的處理,主要是想將更多精力集中在MVVM實現上。

我們來找找有多少個數據屬性和命令屬性。

顯而易見能看出有一個 餐館Model ,它有名字、地址和電話三個屬性,因此,有一個餐館類數據屬性。

右下角有個Order按鈕,明顯是一個命令屬性。

當我們選中某個菜品時,這是一個命令屬性;同時共計框那里會有數值變化,所以,也會有一個菜品選中總數數據屬性。

中間區域菜單列表中,有很多不同的菜品,所以會有一個 菜品Model ,它有名字、種類、點評、評分以及價格屬性。

以上,我們都能輕易分析出來,但是還有一個選中框,理解起來有一點點難度。

當我們打開軟件時,所有的菜品以列表形式展示出來,它的屬性是固定不變的,只有后面的選中框是用戶點擊的,它的值是動態變化的。

因此,我們把不變的菜品和變化的選中框當做是一個ViewModel,它有兩個數據屬性,菜品類和是否被選中。

我們的主界面,也會有一個與之對應的ViewModel 。 它有三個數據屬性,餐館類、選中菜品總數和Dish列表;有兩個命令屬性,訂餐和菜品是否選中命令。

到這里,基于在線訂餐系統的MVVM各個模型,都已經清楚明了了。接下來,我們編程實現它。

案例實現

要實現MVVM,我們需借助Prism。

Prism是一個框架,用于在WPF和Xamarin Forms中構建松散耦合,它提供了一組設計模式的實現,這些設計模式有助于編寫結構良好且可維護的XAML應用程序,包括MVVM、依賴注入、命令、EventAggregator等。

這里用到的是Prism的NotificationObject基類和DelegateCommand,旨在幫助我們借助ViewModel實現View與Model的數據自動更新。

打開VS2019,新建一個解決方案,再新建幾個文件夾,分別是Data、Services、Views、Models和ViewModels。這樣,我們的項目整體架構就搭建好了。

圖片

右擊引用,通過管理NuGet程序包,在彈出的窗口瀏覽中輸入“Prism.MVVM”,在線安裝Prism。

Data文件夾中,存放的是Data.xml,里面是菜品信息。


<Dishes>
  <Dish>
    <Name>水煮肉片Name>
    <Category>徽菜Category>
    <Comment>招牌菜Comment>
    <Score>9.7Score>
    <Price>45元Price>
  Dish>
  <Dish>
    <Name>椒鹽龍蝦Name>
    <Category>川菜Category>
    <Comment>招牌菜Comment>
    <Score>9.2Score>
    <Price>43元Price>
  Dish>
  <Dish>
    <Name>京醬豬蹄Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.8Score>
    <Price>51元Price>
  Dish>
  <Dish>
    <Name>爆炒魷魚Name>
    <Category>徽菜Category>
    <Comment>招牌菜Comment>
    <Score>9.3Score>
    <Price>54元Price>
  Dish>
  <Dish>
    <Name>可樂雞翅Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>44元Price>
  Dish>
  <Dish>
    <Name>涼拌龍須Name>
    <Category>湘菜Category>
    <Comment>涼拌Comment>
    <Score>8.6Score>
    <Price>18元Price>
  Dish>
  <Dish>
    <Name>麻辣花生Name>
    <Category>湘菜Category>
    <Comment>涼拌Comment>
    <Score>8.7Score>
    <Price>19元Price>
  Dish>
  <Dish>
    <Name>韭菜炒肉Name>
    <Category>湘菜Category>
    <Comment>炒菜Comment>
    <Score>9.4Score>
    <Price>25元Price>
  Dish>
  <Dish>
    <Name>青椒肉絲Name>
    <Category>湘菜Category>
    <Comment>炒菜Comment>
    <Score>9.1Score>
    <Price>26元Price>
  Dish>
  <Dish>
    <Name>紅燒茄子Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>24元Price>
  Dish>
  <Dish>
    <Name>紅燒排骨Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>42元Price>
  Dish>
  <Dish>
    <Name>番茄蛋湯Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>21元Price>
  Dish>
  <Dish>
    <Name>山藥炒肉Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>27元Price>
  Dish>
  <Dish>
    <Name>極品肥牛Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>58元Price>
  Dish>
  <Dish>
    <Name>香拌牛肉Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>48元Price>
  Dish>
  <Dish>
    <Name>手撕包菜Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>16元Price>
  Dish>
  <Dish>
    <Name>香辣花甲Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>36元Price>
  Dish>
  <Dish>
    <Name>酸菜魚Name>
    <Category>湘菜Category>
    <Comment>招牌菜Comment>
    <Score>9.4Score>
    <Price>56元Price>
  Dish>
Dishes>

Models文件夾中,存放的是Dish類和Restaurant類。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace zy.CrazyElephant.Client.Models
{
    public class Dish
    {
        public string Name { get; set; }
        public string Category { get; set; }
        public string Comment { get; set; }
        public double Score { get; set; }
        public string Price { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace zy.CrazyElephant.Client.Models
{
   public class Restaurant
    {
        public string Name { get; set; }
        public string Address { get; set; }
        public string PhoneNumber { get; set; }
    }
}

ViewModels中,存放的是兩個ViewModel。

using Microsoft.Practices.Prism.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using zy.CrazyElephant.Client.Models;

namespace zy.CrazyElephant.Client.ViewModels
{
   public class DishMenuItemViewModel:NotificationObject
    {
        public Dish Dish{ get; set; }

        private bool isSelected;

        public bool IsSelected
        {
            get { return isSelected; }
            set
            {
                isSelected = value;
                this.RaisePropertyChanged("IsSelected");
            }
        }

    }
}
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using zy.CrazyElephant.Client.Models;
using zy.CrazyElephant.Client.Services;

namespace zy.CrazyElephant.Client.ViewModels
{
    public class MainWindowViewModel:NotificationObject
    {
        public MainWindowViewModel()
        {
            this.LoadRestaurant();
            this.LoadMenu();

            this.PlaceOrderCommand = new DelegateCommand(PlaceOrderCommandExecute);
            this.SelectMenuItemCommand = new DelegateCommand(SelectMenuItemExecute);
        }

        public DelegateCommand PlaceOrderCommand { get; set; }
        public DelegateCommand SelectMenuItemCommand { get; set; }

        private int count;

        public int Count
        {
            get { return count; }
            set
            {
                count = value;
                this.RaisePropertyChanged("Count");
            }
        }

        private Restaurant restaurant;

        public Restaurant Restaurant
        {
            get { return restaurant; }
            set
            {
                restaurant = value;
                this.RaisePropertyChanged("Restaurant");
            }
        }

        private List dishMenu;

        public List DishMenu
        {
            get { return dishMenu; }
            set
            {
                dishMenu = value;
                this.RaisePropertyChanged("DishMenu");
            }
        }

        private void LoadRestaurant()
        {
            this.Restaurant = new Restaurant();
            this.Restaurant.Name = "聚賢莊";
            this.Restaurant.Address = "xx省xx市xx區xx街道xx樓xx層xx號";
            this.Restaurant.PhoneNumber = "18888888888 or 6666-6666666";
        }

        private void LoadMenu()
        {
            XmlDataService ds = new XmlDataService();
            var dishes = ds.GetAllDishes();
            this.DishMenu = new List();
            foreach (var dish in dishes)
            {
                DishMenuItemViewModel item = new DishMenuItemViewModel();
                item.Dish = dish;
                this.DishMenu.Add(item);
            }
        }

        private void PlaceOrderCommandExecute()
        {
            var selectedDishes = this.DishMenu.Where(i => i.IsSelected == true).Select(i => i.Dish.Name).ToList();
            IOrderService os = new MockOrderService();
            os.PlaceOrder(selectedDishes);
            MessageBox.Show("訂餐成功!");
        }

        private void SelectMenuItemExecute()
        {
            this.Count = this.DishMenu.Count(i => i.IsSelected == true);
        }
    }
}

Services中,IDataService和IOrderService,是兩個基接口,前一個用于獲取菜品數據;后一個用于訂餐處理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using zy.CrazyElephant.Client.Models;

namespace zy.CrazyElephant.Client.Services
{
    public interface IDataService
    {
        List GetAllDishes();
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace zy.CrazyElephant.Client.Services
{
    public interface IOrderService
    {
        void PlaceOrder(List dishes);
    }
}

XmlDataService類,繼承自IDataService,用于讀取xml文件,獲取菜品數據。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using zy.CrazyElephant.Client.Models;

namespace zy.CrazyElephant.Client.Services
{
    public class XmlDataService : IDataService
    {
        public List GetAllDishes()
        {
            List dishList = new List();
            string xmlFileName = System.IO.Path.Combine(Environment.CurrentDirectory, @"Data\\Data.xml");
            XDocument doc = XDocument.Load(xmlFileName);
            var dishes = doc.Descendants("Dish");
            foreach (var d in dishes)
            {
                Dish dish = new Dish();
                dish.Name = d.Element("Name").Value;
                dish.Category = d.Element("Category").Value;
                dish.Comment = d.Element("Comment").Value;
                dish.Score = Convert.ToDouble(d.Element("Score").Value);
                dish.Price = d.Element("Price").Value;
                dishList.Add(dish);
            }
            return dishList;
        }
    }
}

MockOrderService類,繼承自IOrderService,用于處理訂餐邏輯,這里是把選中的菜品名字以txt文件保存到硬盤中。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace zy.CrazyElephant.Client.Services
{
    public class MockOrderService : IOrderService
    {
        public void PlaceOrder(List dishes)
        {
            System.IO.File.WriteAllLines(Environment.CurrentDirectory + "\\\\order.txt", dishes.ToArray());
        }
    }
}

只有一個界面,即MainWindow.xaml,代碼如下。

<Window x:Class="zy.CrazyElephant.Client.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:zy.CrazyElephant.Client"
        mc:Ignorable="d"
        Title="{Binding Restaurant.Name,StringFormat=\\{0\\}-在線訂餐}" Height="600" Width="1000" WindowStartupLocation="CenterScreen">
    <Border BorderBrush="Orange" BorderThickness="3" CornerRadius="6" Background="AliceBlue">
        <Grid x:Name="Root" Margin="4">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="auto"/>
            Grid.RowDefinitions>
            <Border BorderBrush="Orange" BorderThickness="1" CornerRadius="6" Padding="4">
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <StackPanel.Effect>
                            <DropShadowEffect Color="LightGray"/>
                        StackPanel.Effect>
                        <TextBlock Text="歡迎光臨-" FontSize="60" FontFamily="LiShu"/>
                        <TextBlock Text="{Binding Restaurant.Name}" FontSize="60" FontFamily="LiShu"/>
                    StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="小店地址:" FontSize="24" FontFamily="LiShu"/>
                        <TextBlock Text="{Binding Restaurant.Address}" FontSize="24" FontFamily="LiShu"/>
                    StackPanel>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="訂餐電話:" FontSize="24" FontFamily="LiShu"/>
                        <TextBlock Text="{Binding Restaurant.PhoneNumber}" FontSize="24" FontFamily="LiShu"/>
                    StackPanel>
                StackPanel>
            Border>
            <DataGrid AutoGenerateColumns="False" GridLinesVisibility="None" CanUserAddRows="False" CanUserDeleteRows="False" Margin="0.4" Grid.Row="1" FontSize="16" ItemsSource="{Binding DishMenu}">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="菜品" Binding="{Binding Dish.Name}" Width="120"/>
                    <DataGridTextColumn Header="種類" Binding="{Binding Dish.Category}" Width="120"/>
                    <DataGridTextColumn Header="點評" Binding="{Binding Dish.Comment}" Width="120"/>
                    <DataGridTextColumn Header="推薦分數" Binding="{Binding Dish.Score}" Width="120"/>
                    <DataGridTextColumn Header="價格" Binding="{Binding Dish.Price}" Width="120"/>
                    <DataGridTemplateColumn Header="選中" SortMemberPath="IsSelected" Width="120">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Path=IsSelected,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Center" Command="{Binding Path=DataContext.SelectMenuItemCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}"/>
                            DataTemplate>
                        DataGridTemplateColumn.CellTemplate>
                    DataGridTemplateColumn>
                DataGrid.Columns>
            DataGrid>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="2">
                <TextBlock Text="共計" VerticalAlignment="Center"/>
                <TextBox IsReadOnly="True" TextAlignment="Center" Width="120" Text="{Binding Count}" Margin="4,0"/>
                <Button Content="Order" Height="24" Width="120" Command="{Binding PlaceOrderCommand}"/>
            StackPanel>
        Grid>
    Border>
Window>

MainWindow.xaml.cs中,代碼很簡單,只需要添加一行即可。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using zy.CrazyElephant.Client.ViewModels;

namespace zy.CrazyElephant.Client
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }
}

無論界面如何變化,只要符合這種邏輯形式的軟件通過前端界面Binding的方式,我們的業務邏輯代碼就不會變動,通用性很強。實現了前端界面與后端邏輯分離,開閉原則應用的很到位。

寫在最后

基本上,絕大多數軟件所做的工作無非就是從數據存儲中讀出數據,展現到用戶界面上,然后從用戶界面接收輸入,寫入到數據存儲里面去。所以,對于數據存儲(Model)和界面(View)這兩層,大家基本沒什么異議。但是,如何把Model展現到View上,以及如何把數據從View寫入到Model里,不同的人有不同的意見。

MVC派的看法是,界面上的每個變化都是一個事件,我只需要針對每個事件寫一堆代碼,來把用戶的輸入轉換成Model里的對象就行了,這堆代碼可以叫Controller。

而MVVM派的看法是,我給View里面的各種控件也定義一個對應的數據對象,這樣,只要修改這個數據對象,View里面顯示的內容就自動跟著刷新;而在View里做了任何操作,這個數據對象也跟著自動更新,這樣多美。

所以,ViewModel就是與View對應的Model。因為,數據庫結構往往是不能直接跟界面控件一一對應上的,因此,需要再定義一個數據對象專門對應View上的控件。而ViewModel的職責就是把Model對象封裝成可以顯示和接受輸入的界面數據對象。

至于ViewModel的數據隨著View自動刷新,并且同步到Model里去,這部分代碼可以寫成公用的框架,不用程序員自己操心了。

簡單的說,ViewModel就是View與Model的連接器,View與Model通過ViewModel實現數據雙向綁定。

END

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Model
    +關注

    關注

    0

    文章

    331

    瀏覽量

    24832
  • 設計模式
    +關注

    關注

    0

    文章

    53

    瀏覽量

    8602
  • MVC
    MVC
    +關注

    關注

    0

    文章

    73

    瀏覽量

    13794
收藏 人收藏

    評論

    相關推薦

    #硬聲創作季 Java項目實戰(金融項目)-MVVM

    JAVA編程語言
    Mr_haohao
    發布于 :2022年09月07日 10:55:17

    Vue2.0+3.0-Day3-03.簡介 - mvvm

    vue
    電子學習
    發布于 :2023年01月08日 21:10:12

    89.089 尚硅谷 尚融寶 MVVM

    項目開發
    充八萬
    發布于 :2023年07月18日 18:21:32

    AWTK-MVVM在STM32H743上是怎樣去移植的

    AWTK-MVVM 在 STM32H743 上的移植筆記本項目除了實現基本功能的移植外,還提供了如下功能:集成實時操作系統 (RTOS)(騰訊的 TinyOS)集成 FATFS 文件系統,訪問 SD
    發表于 08-24 06:45

    AWTK-MVVM是什么?其功能有哪些

    AWTK-MVVM是一套為AWTK用C語言開發,并支持各種腳本語言的MVVM框架,實現了數據綁定、命令綁定和窗口導航等基本功能,使用AWTK-MVVM開發應用程序,無需學習AWTK本身的API,只需
    發表于 12-15 06:07

    iOS中怎樣用代碼實現mvvm的記錄

    卷首 最近新工作中用到的RAC+MVVM的開發模式,由于之前都是用MVC,從自己的菜雞水平感覺這兩種設計模式在思想上還是有些微區別的,然后自己也是看了挺多關于這兩個模式異同與使用利弊的文章,但是
    發表于 09-25 11:19 ?0次下載
    iOS中怎樣用代碼實現<b class='flag-5'>mvvm</b>的記錄

    前端渲染引擎的優勢分析

    React、Vue、Angular等均屬于MVVM模式,在一些只需完成數據和模板簡單渲染的場合,顯得笨重且學習成本較高,而解決該問題非常優秀框架之一是doT.js,本文將對它進行詳解。 背景 前端
    發表于 09-30 13:14 ?0次下載
    前端渲染引擎的優勢分析

    如何使用協議的實現 MVVM 架構

    使用值類型,而轉為使用繼承來實現。 通過 Natasha 在 do{iOS} 2015上對 MVVM 的介紹,您可以學習到如何使用協議來實現這個功能,而不再采用繼承的方式!Natasha The Robot
    發表于 10-11 15:13 ?0次下載
    如何使用協議的實現 <b class='flag-5'>MVVM</b> 架構

    淺談javascript技術的雙向數據綁定

    近幾年前端技術棧真是發展的太迅速了,從以前的針對dom操作的框架如jquery,ext.js等框架逐步過渡到當前的mvvm模式,讓前端開發者將注意力從dom操作逐漸解脫出來,專注于邏輯的實現,個人認為開發效率至少提升了1倍,mvvm模式的一個核心便是數據的雙向綁定。
    發表于 10-29 11:49 ?4244次閱讀

    MVC、MVP與MVVM的異同介紹

    View和ViewModel內部通過一個Binder進行事件交互,該Binder通過雙向綁定將View與ViewModel中與對于數據操作的部分鏈接,當對應數據由更新時同樣會自動地反饋到View層上。
    的頭像 發表于 06-22 15:34 ?4565次閱讀

    基于MVVM模式的氣動數據可視化分析系統

    基于MVVM模式的氣動數據可視化分析系統
    發表于 06-23 16:05 ?27次下載

    AWTK-MVVM C語言MVVM框架

    ./oschina_soft/gitee-awtk-mvvm.zip
    發表于 06-21 11:33 ?2次下載
    AWTK-<b class='flag-5'>MVVM</b> C語言<b class='flag-5'>MVVM</b>框架

    工業上位機開發實戰WPF+MVVM框架

    上一篇博客介紹了上位機實現MVVM 框架的步驟 MVVMtoolkit 學習_叮當說的博客-CSDN博客 下面我們繼續來講解下實現上位機中會遇到的一些小問題:之前的程序中我們已經知道了 ,當數據改變
    發表于 05-09 11:30 ?0次下載
    工業上位機開發實戰WPF+<b class='flag-5'>MVVM</b>框架

    MVVM+RAC的基本概念和使用方式

    在iOS開發中,采用合適的架構模式能夠提高代碼的可維護性和可測試性。
    的頭像 發表于 06-06 14:55 ?881次閱讀

    使用MVVM框架實現一個簡單加法器

    使用MVVM框架來實現一個簡單加法器。最終效果如下,點擊按鈕可以對上面兩個文本框中的數字進行相加得出結果顯示在第三個文本框中。重點在于看mvvm框架下程序該怎么寫。使用CommunityToolkit.Mvvm框架,通過nuge
    的頭像 發表于 10-24 14:23 ?289次閱讀
    使用<b class='flag-5'>MVVM</b>框架實現一個簡單加法器
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>