工业上位机开发实战 WPF + MVVM 框架

这篇具有很好参考价值的文章主要介绍了工业上位机开发实战 WPF + MVVM 框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上一篇博客介绍了上位机实现MVVM 框架的步骤 MVVMtoolkit 学习_叮当说的博客-CSDN博客

下面我们继续来讲解下实现上位机中会遇到的一些小问题:

回顾:

之前的程序中我们已经知道了 ,当数据改变的时候,可以使用通知的方法来来告知其他的模块这样便可以实现多个界面的通讯,但是ContentControl中存放的page 如何实现自适应?

1、ContentControl 中的内容自适应

因为这个项目中ContentControl 控件内部存放的是 page,所以其实只要在改变窗口大小的时候将对应的page 尺寸进行更改就可以实现效果上面的自适应;

这里采用的方法是使用window 的sizechange 事件,在win的sizechange 的事件发生时,就会调用一个函数“resizeWindow” 该函数用来调整 当前page 的尺寸

注意: 由于这里使用的是MVVM 框架,正常事件触发需要将 该事件绑定到ViewModel 中,但是事件绑定 需要使用委托有兴趣的可以参考:MVVM设计模式和WPF中的实现(四)事件绑定 - durow - 博客园

该方法介绍:

事件绑定

但是由于上文中提到的插件 已经更新换代了,所以目前使用的是新的框架,该框架可以参考如下连接:

Open Sourcing XAML Behaviors for WPF - .NET Blog

解释下步骤,

Steps to migrate:

  1. Remove reference to “Microsoft.Expression.Interactions” and “System.Windows.Interactivity”
  2. Install the “Microsoft.Xaml.Behaviors.Wpf” NuGet package.
  3. XAML files – replace the xmlns namespaces “http://schemas.microsoft.com/expression/2010/interactivity” and “http://schemas.microsoft.com/expression/2010/interactions“with “http://schemas.microsoft.com/xaml/behaviors“
  4. C# files – replace the usings in c# files “Microsoft.Xaml.Interactivity” and “Microsoft.Xaml.Interactions” with “Microsoft.Xaml.Behaviors”

步骤如上所示;

1、将原有的旧版本引用去除

2、在nuget 管理中增加 Microsoft.Xaml.Interactions 工具

3、 然后再xmal 中引用该命名空间

      xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
 

然后就可以在按钮的下面增加事件的绑定了;绑定的代码如下:

                            <Button Content="xxx">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="Click">
                                        <i:InvokeCommandAction Command="{Binding DebugFunction}"  CommandParameter="holdcnc" />
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </Button>

需要注意一个细节:

在绑定事件得时候需要注意一个事情 就是 关于MouseLeftButtonDown事件失效的问题

工业上位机开发实战 WPF + MVVM 框架

 所以如果需要使用 按钮按下或者弹起的事件时,不能使用冒泡类的路由类型,而是要使用隧道的类型,否则会失效,所以这里我们要用Preview 的事件类型

参考连接:

WPF高级教程(八)专题:事件_白话屋-CSDN博客

wpf:关于MouseLeftButtonDown事件失效的问题_卐兜兜飞卍的专栏-CSDN博客

     

另外需要注意一点就是 但按钮按下如果触发了MessageBox 就会导致

PreviewMouseLeftButtonUp 事件无法触发,这个时候可以增加一个LostMouseCapture

的事件作为补充,另外为了避免因为触发瞬间要触发LostMouseCapture 事件,导致系统无法识别,建议在按钮按下需要执行的指令 增加Task 另外开一个现程;

            var myTask = new Task(() =>
            {
                MessageBox.Show("press");
            });

            myTask.Start();

           

因为这个实现比较复杂,我使用的方法依然是消息发送类型:

我这面使用的是将消息 发送成 string 然后让对应的模块接收后进行调整;当然这部分代码是写在了主界面的逻辑层
 

       private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            WeakReferenceMessenger.Default.Send<String>("resizeWindow");
        }

实现效果后如下:

工业上位机开发实战 WPF + MVVM 框架

2、如何增加软件得图标 ico?

首先,需要先建立一个ico 的图标文件

转换的网站为:ICO图标在线生成

然后将文件保存到 对应的img 文件夹中

工业上位机开发实战 WPF + MVVM 框架

右击项目的名字,然后选择属性

工业上位机开发实战 WPF + MVVM 框架

 然后选择自己的图标文件

工业上位机开发实战 WPF + MVVM 框架

 最后运行就可以了;

工业上位机开发实战 WPF + MVVM 框架

3、点击图中作者栏的头像如何跳转到对应的网页

如:

工业上位机开发实战 WPF + MVVM 框架

 因为作者图片使用的是Image 控件

在前台写一个事件:

                <Image Style="{DynamicResource ImageStyle1}" Source="Img/3.jpg" Width="50" Height="50" Margin="0,0,0,0"
                       MouseLeftButtonDown="Image_MouseLeftButtonDown">

 然后事件可以直接在逻辑层写吧,因为比较简单,使用发送消息反而有点浪费了:

访问对应的网址:

System.Diagnostics.Process.Start("explorer.exe", "https://blog.csdn.net/qq_34995963");

如何实现次级子界面:

工业上位机开发实战 WPF + MVVM 框架

下面以数据库为例,创建如上图所示的子界面

首先在Views 中新键一个page 比如 叫做: Page3.xmal

然后对前台的代码写法如下:

 工业上位机开发实战 WPF + MVVM 框架

    <Page.Resources>
        <Style x:Key="RadioButtonStyle" TargetType="{x:Type RadioButton}">
            <Setter Property="FocusVisualStyle">
                <Setter.Value>
                    <Style>
                        <Setter Property="Control.Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Setter.Value>
            </Setter>
            <Setter Property="FontSize" Value="26"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RadioButton}">
                        <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                            <Border x:Name="border" BorderBrush="Red" BorderThickness="0" CornerRadius="10"  SnapsToDevicePixels="True"/>
                            <Border x:Name="bd2"/>
                            <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}"
                                              Content="{TemplateBinding Content}" Grid.Column="1" 
                                              ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" 
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="True">
                                <Setter Property="FocusVisualStyle">
                                    <Setter.Value>
                                        <Style>
                                            <Setter Property="Control.Template">
                                                <Setter.Value>
                                                    <ControlTemplate>
                                                        <Rectangle Margin="14,0,0,0" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
                                                    </ControlTemplate>
                                                </Setter.Value>
                                            </Setter>
                                        </Style>
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Padding" Value="4,-1,0,0"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="Transparent" TargetName="border"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="true">
                                <Setter Property="Background" Value="#ffffff" TargetName="border"/>
                                <Setter Property="Opacity" Value="0.15" TargetName="border"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="{x:Null}"/>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="MinHeight" Value="50"/>
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
        </Style>

    </Page.Resources>

    <Grid>

        <!--关于界面-->
        <Grid>

            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="220"/>
                <ColumnDefinition Width="4*"/>
            </Grid.ColumnDefinitions>

            <Border Background="#ffffff" Margin="10 10 10 10" CornerRadius="10" Opacity="0.1"/>

            <Grid>

                <Grid.RowDefinitions>
                    <RowDefinition Height="100"/>
                    <RowDefinition/>
                </Grid.RowDefinitions>

                <TextBlock Text="数据库" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" FontSize="30" />

                <!--左侧的工具列-->
                <ListBox Grid.Row="1" ItemsSource="{Binding MenuModels}"  Background="Transparent"  BorderThickness="0" Width="220" HorizontalAlignment="Left">
                    <ListBox.ItemContainerStyle>
                        <Style TargetType="ListBoxItem">
                            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                            <Setter Property="BorderThickness" Value="0"/>
                            <Setter Property="Background" Value="Transparent"/>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                        <Grid>
                                            <Border Name="border"/>
                                            <ContentPresenter/>
                                        </Grid>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="IsSelected" Value="True">
                                                <Setter Property="Background" TargetName="border" Value="Transparent"/>
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ListBox.ItemContainerStyle>

                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <RadioButton Margin="30 10 30 0"
                           VerticalContentAlignment="Center" 
                            Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:Page3}},
                                Path=DataContext.SelectedCommand,Mode=TwoWay}" Style="{StaticResource RadioButtonStyle}"
                                         CommandParameter="{Binding Index}">
                                <RadioButton.IsChecked>
                                    <Binding Path="IsSelected" RelativeSource="{RelativeSource AncestorType=ListBoxItem}" Mode="TwoWay"/>
                                </RadioButton.IsChecked>
                                <StackPanel  Orientation="Horizontal">
                                    <TextBlock Grid.Column="0" Text="{Binding IconFont}" FontFamily="../Fonts/#iconfont" FontSize="28" 
                                                   Margin="10 0 0 0" Foreground="White"/>
                                    <TextBlock Margin="15 0 0 0" Text="{Binding Title}"  FontSize="18" VerticalAlignment="Center" 
                                   Foreground="White"/>

                                </StackPanel>
                            </RadioButton>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </Grid>

            <!--右侧的装子控件的容器-->
            <ContentControl Grid.Row="1" Grid.Column="1" x:Name="PageContent" HorizontalAlignment="Left">
                <Border >
                    <Grid  Width=" 600">
                        <Border Background="#ffffff"   Margin="10" CornerRadius="15" Opacity="0.1"/>
                        <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0 30 0 0">
                            <TextBlock Text="Kohli tells India fans not to boo Smith" Foreground="White" FontSize="18" FontWeight="Bold" Width="230" TextWrapping="Wrap"/>
                            <TextBlock Text="India captain makes intervention from the middle after 'Cheater!' chants directed at Steven Smith" FontSize="12" Margin="0 5 0 0" Foreground="White" Width="230" TextWrapping="Wrap"/>
                            <TextBlock Foreground="White" Margin="0 20 0 0">
                                <Hyperlink  NavigateUri="https://www.baidu.com" Foreground="#E0D1EA" FontWeight="Bold" FontSize="18">
                                    READ FULL STORY
                                </Hyperlink>
                            </TextBlock>
                        </StackPanel>
                    </Grid>
                </Border>
            </ContentControl>

        </Grid>


    </Grid>
</Page>

Page 后台的代码为:

    public partial class Page3 : Page
    {
        public static Page3 page;
        public Page3()
        {
            InitializeComponent();
            this.DataContext = new Page3ViewModel();
            page = this;
        }
    }

另外这个位置用到了MVVM 的模型所以,需要建立一个名称为

Page3ViewModel  viewModel的类;

需要添加的命名空间如下:

using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
using Microsoft.Toolkit.Mvvm.Messaging;
using Mvvmtoolkit.Models;
using Mvvmtoolkit.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

Page3ViewModel 的代码为:

using Microsoft.Toolkit.Mvvm.ComponentModel;
using Microsoft.Toolkit.Mvvm.Input;
using Microsoft.Toolkit.Mvvm.Messaging;
using Mvvmtoolkit.Models;
using Mvvmtoolkit.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Mvvmtoolkit.ViewModels
{
    class Page3ViewModel : ObservableRecipient, IRecipient<MessengerA>, IRecipient<MessengerB>, IRecipient<string>
    {

        static Views.PageDatabaseSubs.Page1 subPage1 = new Views.PageDatabaseSubs.Page1();
        static Views.PageDatabaseSubs.Page2 subPage2 = new Views.PageDatabaseSubs.Page2();
        static Views.PageDatabaseSubs.Page4 subPage4 = new Views.PageDatabaseSubs.Page4();




        // 如果需要绑定到 mainViewmodel 上面的话 需要在这里
        Frame frame1 = new Frame() { Content = subPage1 };
        Frame frame2 = new Frame() { Content = subPage2 };
        Frame frame4 = new Frame() { Content = subPage4 };

        //Frame frame1 = new Frame() { Content = new Views.PageDatabaseSubs.Page1() };
        //Frame frame2 = new Frame() { Content = new Views.PageDatabaseSubs.Page2() };

        Frame frame3 = new Frame() { Content = new Views.PageDatabaseSubs.Page3() };
        //Frame frame4 = new Frame() { Content = new Views.PageDatabaseSubs.Page4() };
        Frame frame5 = new Frame() { Content = new Views.PageDatabaseSubs.Page5() };
        Frame nowFrame = new Frame();




        private ObservableCollection<MenuModel> menuModels;

        public ObservableCollection<MenuModel> MenuModels
        {
            get { return menuModels; }
            set { menuModels = value; }
        }


        private int _value;

        public int Value
        {
            get { return _value; }
            set
            {
                // 这种方式只要变更就会通知,不管数值是否有变化
                _value = value;
                this.OnPropertyChanged();
                //SetProperty<int>(ref _value, value);
                // 数据有变化才会通知
            }
        }



        public void resizeWindow()
        {
            // 赋值小于0 会报错
            nowFrame.Height = Math.Max(MainWindow.win.ActualHeight - 140, 0);
            nowFrame.Width = Math.Max(MainWindow.win.ActualWidth - 350, 0);
        }



        public ICommand SelectedCommand { get; set; }

        public void changePage(object obj)
        {
            if (obj != null)
            {
                //MessageBox.Show(obj.ToString());
            }

            switch (obj.ToString())
            {
                case "1":
                    subPage1.DataContext = MainWindow.win.DataContext;
                    nowFrame = frame1;
                    //nowFrame.DataContext= MainWindow.win.DataContext;
                    break;
                case "2":
                    // 在这个位置进行数据上下文的绑定 在后台绑定会报错
                    subPage2.DataContext = MainWindow.win.DataContext;
                    nowFrame = frame2;
                    break;
                case "3":
                    nowFrame = frame3;
                    break;
                case "4":
                    // 绑定数据上下文
                    subPage4.DataContext = MainWindow.win.DataContext;
                    nowFrame = frame4;
                    break;
                case "5":
                    nowFrame = frame5;
                    break;

                default:
                    break;
            }

            Page3.page.PageContent.Content = nowFrame;
            resizeWindow();


        }

        public Page3ViewModel()
        {


            this.IsActive = true;

            MenuModels = new ObservableCollection<MenuModel>();
            MenuModels.Add(new MenuModel() { IconFont = "\xe617", Title = "ini", Index = "1", });
            MenuModels.Add(new MenuModel() { IconFont = "\xec5d", Title = "Json", Index = "2", });
            MenuModels.Add(new MenuModel() { IconFont = "\xe62b", Title = "cyc", Index = "3", });
            MenuModels.Add(new MenuModel() { IconFont = "\xe6f6", Title = "xml", Index = "4", });
            MenuModels.Add(new MenuModel() { IconFont = "\xe647", Title = "SQL", Index = "5", });
            //MenuModels.Add(new MenuModel() { IconFont = "\xe790", Title = "Stats", Index = "6", });
            //MenuModels.Add(new MenuModel() { IconFont = "\xe672", Title = "World cu", Index = "7", });


            //初始话command
            SelectedCommand = new RelayCommand<object>(changePage);


        }





        public void Receive(MessengerA message)
        {

            System.Windows.MessageBox.Show("我收到了收到MessengerA");
        }

        public void Receive(MessengerB message)
        {
            MessageBox.Show("收到MessengerB");
        }

        public void Receive(string message)
        {
            if (message.ToString() == "resizeWindow")
            {
                resizeWindow();
                //nowFrame.Refresh();
            }
        }
    }

}

注意这个位置使用了一个名称为:MenuModel的类,所以需要在Model中声明该类

该类的写法为;

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

namespace Mvvmtoolkit.Models
{
    public class MenuModel
    {
        public string IconFont { get; set; }

        public string Title { get; set; }
        public string Index { get; set; }

    }
}

最后需要在Views 中建立如下的sub界面

文件夹为PageDatabaseSubs文章来源地址https://www.toymoban.com/news/detail-439646.html

到了这里,关于工业上位机开发实战 WPF + MVVM 框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android端MVVM从入门到实战(第一篇) - MVVM和四大官方组件

    MVVM是相对于MVC和MVP的一个概念,是一种架构模式。 1.1 MVC 传统的MVC中,View改变通知Controller进行处理,Controller处理结束后通知Model层更新,Model层更新以后通知View层渲染,指令单项流动,角色分工明确。但是MVC有三个缺点,1、三个角色互相持有对方依赖,因此很难复用其中任

    2024年02月04日
    浏览(43)
  • Wpf在.Net 6 下该用哪个Mvvm框架-Microsoft.Toolkit.Mvvm

    前言 在Wpf下最常使用的就是Mvvm模式了,有自己造轮子构建Mvvm框架的,也有使用现成的开源项目,我之前一直使用的是轻量级的MvvmLight了,这个框架还是非常不错的,使用也简单,不占用太大空间,其中最喜欢的莫过于全局Messenger了,可谓是神器。最近有个项目使用.Net6开发

    2024年02月06日
    浏览(53)
  • mvvm框架下对wpf的DataGrid多选,右键操作

    第一步:在DataGrid中添加ContextMenu 第二步:在ViewModel中创建一个命令( DeleteSelectedCommand )来处理删除选中项的逻辑。确保为ViewModel设置了DataContext。其中Items就是DataGrid中每行的对象集合 第三步:在MainWindow.xaml.cs中设置DataContext和DataGrid的关联: 总结:xaml中:对DataGrid添加Co

    2024年02月09日
    浏览(83)
  • WPF新境界:MVVM设计模式解析与实战

    了解MVVM设计模式在WPF开发中的应用,通过分离模型、视图和视图模型,构建清晰可维护的用户界面。实例展示了如何利用XAML、ViewModel和数据绑定创建可测试的界面逻辑。

    2024年02月19日
    浏览(37)
  • WPF MvvM框架(MvvMLight,Microsoft Toolkit Mvvm,CommunityToolkit.Mvvm;鼠标,键盘,手写等事件绑定如:抬起按下事件)

    目录 1.MvvMLight(已废弃) 2. Microsoft Toolkit Mvvm(已废弃)  3.CommunityToolkit.Mvvm框架 4.Mvvm中的事件绑定 4.1 DataGrid表中按钮点击事件 4.2 绑定 鼠标事件,键盘事件,手写笔事件,多点触控事件 5.和PLC设备建立联系(联调)    本文中,继承接口,同步数据方法,command用法均一致; 1.继

    2024年02月04日
    浏览(61)
  • WPF超好用的框架Prism入门使用,上位机赶紧学起来!

    WPF框架Prism是一种用于开发模块化、可重用和可测试的WPF应用程序的框架。它提供了一种简单而强大的方式来管理复杂应用程序的代码和构建高度可扩展的应用程序。 如果您想使用Prism框架来开发WPF应用程序,需要学习以下几个方面: MVVM模式 :Prism基于MVVM模式,因此需要了

    2024年02月01日
    浏览(39)
  • 【.NET6+WPF】WPF使用prism框架+Unity IOC容器实现MVVM双向绑定和依赖注入

    前言:在C/S架构上,WPF无疑已经是“桌面一霸”了。在.NET生态环境中,很多小伙伴还在使用Winform开发C/S架构的桌面应用。但是WPF也有很多年的历史了,并且基于MVVM的开发模式,受到了很多开发者的喜爱。 并且随着工业化的进展,以及几年前微软对.NET平台的开源,国内大多

    2024年02月06日
    浏览(63)
  • 使用MVVM Toolkit简化WPF开发

    最近. NET 8 的 WPF 推出了 WPF File Dialog改进,这样无需再引用 Win32 命名空间就可以实现文件夹的选择与存储了,算是一个很方便的改进了。顺手写了一个小的 WPF 程序,在使用 Model-View-ViewModel(MVVM) 模式的时候,我不想使用 Prism 等重量级的框架,找了一个轻量级的 MVVM Community T

    2024年02月05日
    浏览(61)
  • 【自动化】在WPF应用程序中使用MVVM框架实现Modbus协议通信

    Modbus是一种广泛应用于工业领域的通信协议,主要用于设备间的数据交换。在WPF应用程序中,我们可以使用MVVM(Model-View-ViewModel)框架来实现Modbus协议的通信。本文将详细介绍如何实现这一功能。 为了在WPF应用程序中实现Modbus协议通信,我们需要安装一些第三方库。可以使用

    2024年03月22日
    浏览(48)
  • C# WPF上位机开发(键盘绘图控制)

    【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】         在软件开发中,如果存在canvas图像的话,一般有几种控制方法。一种是鼠标控制;一种是键盘控制;还有一种是定时器控制。定时器控制,多常见动画、游戏、3d视频当中。而鼠标控制

    2024年02月02日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包