WPF MVVMLight 8:DispatchHelper在多线程和调度中的使用

WPF&MVVM线程问题(progressbar为例) WPF&MVVM线程问题 别让能力撑不起野心 WPFMVVM线程问题 后台UI线程 一般线程交互 a首次修改引入Dispatcher b再次修改引入Task c最后修改 MVVM线程交互后台,UI线程专业解释我就不贴,说说自己的个人愚见,线程有后台,UI(前台)之分,UI元素所使用的线程为UI线程,其他的可以理解为后台线程。 区别:程序要关闭,必须等待UI线程终止,而不用等待后台线程终止 阅读详情

在应用程序中,线程可以被看做是应用程序的一个较小的执行单位。每个应用程序都至少拥有一个线程,我们称为主线程,这是在启动时调用应用程序的主方法时由操作系统分配启动的线程。

 当调用和操作主线程的时候,该操作将动作添加到一个队列中。每个操作均按照将它们添加到队列中的顺序连续执行,但是可以通过为这些动作指定优先级来影响执行顺序,而负责管理此队列的对象称之为线程调度程序。

在很多情况下,我们启动新的线程主目的是执行操作(或等待某个操作的结果),而不会导致应用程序的其余部分被阻塞。密集型计算操作、高并发I/O操作等都是这种情况,所以现在的复杂应用程序日益多线程化了。

 当我们启动一个应用程序并创建对象时,就会调用构造函数方法所在的线程,对于 UI 元素,在加载 XAML 文档时,XAML 分析器会创建基于这些UI元素的对象。所以所有的对象(包括UI元素)的创建都归属于当前的主线程,当然也只有主线程可以访问他们。

但在实际情况中,有很多情况是要假手其他线程来处理的。

比如在一个长交互中,我们可能需要而外的线程来处理复杂的执行过程,以免造成线程阻塞,给用户界面卡死的错觉。
在这里插入图片描述
比如下面这个例子,我们使用委托的方式模拟用户执行数据创建的操作:

调用CreateUserInfoHelper帮助类 和 执行 CreateProcess方法 的代码如下:

 UserParam up = new UserParam() { UserAdd = txtUserAdd.Text, UserName = txtUserName.Text, UserPhone = txtUserPhone.Text, UserSex = txtUserSex.Text };
 CreateUserInfoHelper creatUser = new CreateUserInfoHelper(up);
 creatUser.CreateProcess += new EventHandler<CreateUserInfoHelper.CreateArgs>(CreateProcess); //注册事件
 creatUser.Create();
 processPanel.Visibility = Visibility.Visible; 
private void CreateProcess(object sender, CreateUserInfoHelper.CreateArgs args)//响应时间执行
        {
                processBar.Value = args.process;
                processInfo.Text = String.Format("创建进度:{0}/100",args.process);
                if (args.isFinish)
                {
                    if (args.userInfo != null)
                    {
                        ObservableCollection<UserParam> data = (ObservableCollection<UserParam>)dg.DataContext;
                        data.Add(args.userInfo);
                        dg.DataContext = data;
                    }
                    processPanel.Visibility = Visibility.Hidden;
                    ClearForm();
                }
        }

CreateUserInfoHelper帮助类代码如下:

 public class CreateUserInfoHelper
    {
        //执行进度事件(响应注册的事件)
        public event EventHandler<CreateArgs> CreateProcess;

        //待创建信息
        public UserParam up { get; set; }

        public CreateUserInfoHelper(UserParam _up)
        {
            up = _up;
        }

        public void Create()
        {
            Thread t = new Thread(Start);//抛出一个行线程
            t.Start();
        }

        private void Start()
        {
            try
            {
                //ToDo:编写创建用户的DataAccess代码
                for (Int32 idx = 1; idx <= 10; idx++)
                {
                    CreateProcess(this, new CreateArgs()
                    {
                        isFinish = ((idx == 10) ? true : false),
                        process = idx * 10,
                        userInfo =null
                    });
                    Thread.Sleep(1000);
                }

                CreateProcess(this, new CreateArgs()
                {
                    isFinish = true,
                    process = 100,
                    userInfo =up
                });
            }
            catch (Exception ex)
            {
                CreateProcess(this, new CreateArgs()
                {
                    isFinish = true,
                    process = 100,
                    userInfo = null
                });
            }
        }

        /// <summary>
        /// 创建步骤反馈参数
        /// </summary>
        public class CreateArgs : EventArgs
        {
            /// <summary>
            /// 是否创建结束
            /// </summary>
            public Boolean isFinish { get; set; }
            /// <summary>
            /// 进度
            /// </summary>
            public Int32 process { get; set; }
            /// <summary>
            /// 处理后的用户信息
            /// </summary>
            public UserParam userInfo { get; set; }
        }
    }

目的很简单:就是在创建用户信息的时候,使用另外一个线程执行创建工作,最后将结果呈现在试图列表上,而在这个创建过程中会相应的呈现进度条。

来看下效果:
在这里插入图片描述
立马报错了,原因很简单,在创建对象时,该操作发生在调用CreateUserInfoHelper帮助类方法所在的线程中。

对于 UI 元素,在加载 XAML 文档时,XAML 分析器会创建对象。所有这一切都在主线程上进行。因此,所有这些 UI 元素都属于主线程,这也通常称为 UI 线程。

当先前代码中的后台线程尝试修改 UI主线程的元素 属性时,则会导致非法的跨线程访问。因此会引发异常。

解决办法就是去通知主线程来处理UI, 通过向主线程的Dispatcher队列注册工作项,来通知UI线程更新结果。

Dispatcher提供两个注册工作项的方法:Invoke 和 BeginInvoke。

这两个方法均调度一个委托来执行。Invoke 是同步调用,也就是说,直到 UI 线程实际执行完该委托它才返回。BeginInvoke是异步的,将立即返回。

所以我们修改上面的代码如下:

private void CreateProcess(object sender, CreateUserInfoHelper.CreateArgs args)
        {
            this.Dispatcher.BeginInvoke((Action)delegate()
            {
                processBar.Value = args.process;
                processInfo.Text = String.Format("创建进度:{0}/100",args.process);
                if (args.isFinish)
                {
                    if (args.userInfo != null)
                    {
                        ObservableCollection<UserParam> data = (ObservableCollection<UserParam>)dg.DataContext;
                        data.Add(args.userInfo);
                        dg.DataContext = data;
                    }
                    processPanel.Visibility = Visibility.Hidden;
                    ClearForm();
                }
            });
        }

结果如下:
在这里插入图片描述
在这里插入图片描述
实现异步执行的结果。

MVVM 应用程序中的调度
当从 ViewModel 执行后台操作时,情况略有不同。通常,ViewModel 不从 DispatcherObject 继承。它们是执行 INotifyPropertyChanged 接口的 Plain Old CLR Objects (POCO)。

因为 ViewModel 是一个 POCO,它不能访问 Dispatcher 属性,因此我需要通过另一种方式来访问主线程,以将操作加入队列中。这是 MVVM Light DispatcherHelper 组件的作用。

实际上,该类所做的是将主线程的调度程序保存在静态属性中,并公开一些实用工具方法,以便通过便捷且一致的方式访问。为了实现正常功能,需要在主线程上初始化该类。

最好应在应用程序生命周期的初期进行此操作,使应用程序一开始便能够访问这些功能。通常,在 MVVM Light 应用程序中,DispatcherHelper 在 App.xaml.cs 中进行初始化,App.xaml.cs 是定义应用程序启动类的文件。在 Windows Phone 中,在应用程序的主框架刚刚创建之后,在 InitializePhoneApplication 方法中调用 Dispatcher­Helper.Initialize。在 WPF 中,该类是在 App 构造函数中进行初始化的。在 Windows 8 中,在窗口激活之后便立刻在 OnLaunched 中调用 Initialize 方法。

完成了对 DispatcherHelper.Initialize 方法的调用后,DispatcherHelper 类的 UIDispatcher 属性包含对主线程的调度程序的引用。相对而言很少直接使用该属性,但如果需要可以这样做。但最好使用 CheckBeginInvokeOnUi 方法。此方法将委托视为参数。

所以将上述代码改装程:

View代码(学过Bind和Command之后应该很好理解下面这段代码,没什么特别的):

<Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type Border}" x:Key="ProcessBarBorder">
                <Setter Property="BorderBrush" Value="LightGray" ></Setter>
                <Setter Property="BorderThickness" Value="1" ></Setter>
                <Setter Property="Background" Value="White" ></Setter>
            </Style>
        </Grid.Resources>

        <!-- 延迟框 -->
        <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
            <Border Style="{StaticResource ProcessBarBorder}" Padding="5" Visibility="{Binding IsWaitingDisplay,Converter={StaticResource boolToVisibility}}" Panel.ZIndex="999" HorizontalAlignment="Center"  VerticalAlignment="Center" Height="50">
                <StackPanel Orientation="Vertical" VerticalAlignment="Center" >
                    <ProgressBar Value="{Binding ProcessRange}" Maximum="100" Width="400" Height="5" ></ProgressBar>
                    <TextBlock Text="{Binding ProcessRange,StringFormat='执行进度:\{0\}/100'}" Margin="0,10,0,0" ></TextBlock>
                </StackPanel>
            </Border>
        </Grid>

        <StackPanel Orientation="Vertical" IsEnabled="{Binding IsEnableForm}" >
            <StackPanel>
                <DataGrid ItemsSource="{Binding UserList}" AutoGenerateColumns="False" CanUserAddRows="False"
                                      CanUserSortColumns="False" Margin="10" AllowDrop="True" IsReadOnly="True" >
                    <DataGrid.Columns>
                        <DataGridTextColumn Header="学生姓名" Binding="{Binding UserName}" Width="100" />
                        <DataGridTextColumn Header="学生家庭地址"  Binding="{Binding UserAdd}" Width="425" >
                            <DataGridTextColumn.ElementStyle>
                                <Style TargetType="{x:Type TextBlock}">
                                    <Setter Property="TextWrapping" Value="Wrap"/>
                                    <Setter Property="Height" Value="auto"/>
                                </Style>
                            </DataGridTextColumn.ElementStyle>
                        </DataGridTextColumn>
                        <DataGridTextColumn Header="电话" Binding="{Binding UserPhone}" Width="100" />
                        <DataGridTextColumn Header="性别" Binding="{Binding UserSex}" Width="100" />
                    </DataGrid.Columns>
                </DataGrid>
            </StackPanel>

            <StackPanel Orientation="Horizontal"  Margin="10,10,10,10">
                <StackPanel Orientation="Vertical" Margin="0,0,10,0" >
                    <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
                        <TextBlock Text="学生姓名" Width="80" ></TextBlock>
                        <TextBox Text="{Binding User.UserName}" Width="200" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="0,0,0,5">
                        <TextBlock Text="学生电话" Width="80" ></TextBlock>
                        <TextBox Text="{Binding User.UserPhone}" Width="200" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="0,0,0,5">
                        <TextBlock Text="学生家庭地址" Width="80"></TextBlock>
                        <TextBox Text="{Binding User.UserAdd}" Width="200"/>
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="0,0,0,5" >
                        <TextBlock Text="学生性别" Width="80" ></TextBlock>
                        <TextBox Text="{Binding User.UserSex}" Width="200" />
                    </StackPanel>
                    <StackPanel>
                        <Button Content="提交" Width="100" Command="{Binding AddRecordCmd}" ></Button>
                    </StackPanel>
                </StackPanel>
            </StackPanel>

        </StackPanel>
    </Grid>

ViewModel代码:

(先初始化 DispatcherHelper,再调用 CheckBeginInvokeOnUI 方法来实现对UI线程的调度)

public class DispatcherHelperViewModel:ViewModelBase
    {
        /// <summary>
        /// 构造行数
        /// </summary>
        public DispatcherHelperViewModel()
        {
            InitData();
            DispatcherHelper.Initialize();
        }


        #region 全局属性

        private ObservableCollection<UserParam> userList;
        /// <summary>
        /// 数据列表
        /// </summary>
        public ObservableCollection<UserParam> UserList
        {
            get { return userList; }
            set { userList = value; RaisePropertyChanged(() => UserList); }
        }

        private UserParam user;
        /// <summary>
        /// 当前用户信息
        /// </summary>
        public UserParam User
        {
            get { return user; }
            set { user = value; RaisePropertyChanged(()=>User); }
        }


        private Boolean isEnableForm;
        /// <summary>
        /// 是否表单可用
        /// </summary>
        public bool IsEnableForm
        {
            get { return isEnableForm; }
            set { isEnableForm = value; RaisePropertyChanged(()=>IsEnableForm); }
        }

        private Boolean isWaitingDisplay;
        /// <summary>
        /// 是都显示延迟旋转框
        /// </summary>
        public bool IsWaitingDisplay
        {
            get{ return isWaitingDisplay; }
            set{ isWaitingDisplay = value; RaisePropertyChanged(()=>IsWaitingDisplay);}
        }

        private Int32 processRange;
        /// <summary>
        /// 进度比例
        /// </summary>
        public int ProcessRange
        {
            get { return processRange; }
            set { processRange = value; RaisePropertyChanged(()=>ProcessRange);}
        }

        #endregion


        #region 全局命令
        private RelayCommand addRecordCmd;
        /// <summary>
        /// 添加资源
        /// </summary>
        public RelayCommand AddRecordCmd
        {
            get
            {
                if (addRecordCmd == null) addRecordCmd = new RelayCommand(()=>ExcuteAddRecordCmd());
                return addRecordCmd;
            }
            set
            {
                addRecordCmd = value;
            }
        }
        #endregion


        #region 辅助方法
        /// <summary>
        /// 初始化数据
        /// </summary>
        private void InitData()
        {
            UserList = new ObservableCollection<UserParam>()
            {
                 new UserParam(){ UserName="周杰伦", UserAdd="周杰伦地址", UserPhone ="88888888", UserSex="男" },
                 new UserParam(){ UserName="刘德华", UserAdd="刘德华地址", UserPhone ="88888888", UserSex="男" },
                 new UserParam(){ UserName="刘若英", UserAdd="刘若英地址", UserPhone ="88888888", UserSex="女" }
            };
            User = new UserParam();
            IsEnableForm = true;
            IsWaitingDisplay = false;
        }

        /// <summary>
        /// 执行命令
        /// </summary>
        private void ExcuteAddRecordCmd()
        {
            UserParam up = new UserParam { UserAdd = User.UserAdd, UserName = User.UserName, UserPhone = User.UserPhone, UserSex = User.UserSex };
            CreateUserInfoHelper creatUser = new CreateUserInfoHelper(up);
            creatUser.CreateProcess += new EventHandler<CreateUserInfoHelper.CreateArgs>(CreateProcess);
            creatUser.Create();
            IsEnableForm = false;
            IsWaitingDisplay = true;
        }

        /// <summary>
        /// 创建进度
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void CreateProcess(object sender, CreateUserInfoHelper.CreateArgs args)
        {
            DispatcherHelper.CheckBeginInvokeOnUI(() =>
            {
                if (args.isFinish)
                {
                    if (args.userInfo != null)
                    {
                        UserList.Add(args.userInfo);
                    }

                    IsEnableForm = true;
                    IsWaitingDisplay = false;
                }
                else
                {
                    ProcessRange = args.process;
                }
            });
        }
        #endregion

    }

结果如下:
在这里插入图片描述
示例代码下载

PS:本文转载自博客园的作者Brand的原创文章,感谢原作者的分享。原文链接:https://blog.youkuaiyun.com/Guarder_7/article/details/125891689

WPF 跨线程-Dispatcher:详解与示例 阅读详情

相关推荐

【朝夕教育】2023年09月 WPF+上位机+工业互联 080-MvvmLight框架的DispatcherHelper和IOC注入

模型(Model):数据层,负责数据的获取、存储和处理。视图(View):用户界面,负责显示数据和与用户交互。视图模型(ViewModel):业务逻辑层,负责将模型和视图连接起来,并将模型数据转化为可供视图使用的数据。Mvvm框架通过数据绑定和命令绑定来实现视图模型与视图之间的通信,使代码和UI分离,提高程序的可维护性和可扩展性。常用的Mvvm框架有Prism、Caliburn.Micro、MVVMLight等。

微软MVP Eleven 272

推荐开源项目:MVVM助手 - 提升你的MVVM开发效率

在软件开发中,特别是移动应用领域,MVVM(Model-View-ViewModel)模式已经变得越来越流行。它分离了视图和业务逻辑,使代码更易于维护和测试。今天,我想向你推荐一个非常实用的开源项目——[MVVM Helpers](https://github.com/jamesmontemagno/mvvm-helpers),这是一个为任何应用程序设计的MVVM辅助类库。 ## 项目介绍 M

gitblog_00055的博客 477

mvvmhelpers:所有基于XAML的框架的MVVM,转换器和行为库

MvvmHelpers是一个跨平台的库,用于使用XAML框架流行的Model-View-ViewModel模式创建应用程序。 该库支持: .NET 4.5 单声道4.5 Windows应用商店应用(WinRT) Windows Phone应用程序(WinPRT) Xamarin.iOS Xamarin.Android 有几个可用的例子,我一直在博客上如何使用这个库。 我也会在这里推送一些信息。 您可以通过NuGet 安装MvvmHelpers-只需搜索MVVMHelpers.PCL,如果您安装NuGet插件,它将从Visual Studio中自动将最新版本添加到您的项目中。

WPF中DispatchHelper

虽然WPF本身没有直接提供名为的类,但很多开发者会创建一个这样的辅助类来简化跨线程访问UI的操作。以下是一个简化的// 当前已经在UI线程action();else// 不在UI线程,需要调度到UI线程执行});请注意,实际项目中可能需要考虑异常处理、调度优先级调整等更细致的逻辑。此外,随着.NET框架和C#语言的进化,也可以探索使用模式结合来进一步简化异步编程中的UI更新逻辑。

以梦为马 不负韶华 921

JLINK/SWD/STLINK下载选项区别

摘要: 本文对比了JLINK/SWD/STLINK的6种下载模式:Normal(硬件复位)、Hot plug(无复位)、Under reset(持续复位后连接)、Software reset(软件复位)、Hardware reset(全复位)和Core reset(内核复位)。重点分析了各模式的操作逻辑、适用场景及注意事项,其中Under reset模式是解决SWD禁用/程序异常的首选方案。通过对比表归纳了复位范围、NRST操作等核心差异,并给出场景选择建议:排查连接问题优先选Under reset,常规调

亚洲程序员盟主得博客 395

手把手教你用思岚RPLIDAR C1激光雷达实现简单环境扫描(树莓派4B+Ubuntu22.04)

本文详细介绍了如何在树莓派4B(Ubuntu 22.04系统)上驱动思岚RPLIDAR C1激光雷达,从硬件连接、权限配置、SDK编译到运行示例程序,最终实现环境扫描数据的可视化。通过手把手教程,帮助开发者和爱好者快速上手激光雷达应用,构建简单的环境扫描图。

j2k3l4的博客 1131

DispatcherHelper

DispatcherHelper 通常,WPF 应用程序从两个线程开始:一个用于处理呈现, 一个用于管理 UI。呈现线程有效地隐藏在后台运行,而 UI 线程则接收输入、处理事件、绘制屏幕 以及运行应用程序代码。所以我们的大多数操作都会在UI线程中执行,同时它也处理绘制屏幕,如果我们 的一个操作相当耗时,那么它就没有机会处理绘制屏幕,此时我们是不能够拖动窗口的,也就是通常 说的屏幕卡住了。 Disp...

weixin_33869377的博客 611

ESD放电模式以及电源箝位 (power clamp )电路

由于ESD电压具有很快的上升速度(其ESD脉冲上升时间约在10ns左右),Vrc端的电压因为RC延迟效应无法跟得上VDD端的ESD电压上升速度,Vrc端的为低电位导致反相器工由于ESD电压具有很快的上升速度(其ESD脉冲上升时间约在10ns左右),Vrc端的电压因为RC延迟效应无法跟得上VDD端的ESD电压上升速度,Vrc端的为低电位导致反相器工作将输出端Vout电压上拉到高电位。从而Vg端的高电位触发导通了NMOS元件(Mesd)的晶体管,因而ESD电流便经由Mesd而泄放掉。由于晶体管Mesd的栅极导通

DOG_BITE_DOG的博客 2万+

利刃 MVVMLight 8:DispatchHelper在多线程和调度中的使用

原文:利刃 MVVMLight 8:DispatchHelper在多线程和调度中的使用     在应用程序中,线程可以被看做是应用程序的一个较小的执行单位。每个应用程序都至少拥有一个线程,我们称为主线程,这是在启动时调用应用程序的主方法时由操作系统分配启动的线程。     当调用和操作主线程的时候,该操作将动作添加到一个队列中。每个操作均按照将它们添加到队列中的顺序连续执行,但是可以通过为这些动作...

狂奔之林的博客 1138

Trae+Playwright MCP自动化测试全流程指南:从环境搭建到智能体运行

本文提供了一份详细的Trae与Playwright MCP集成指南,涵盖从Node.js、Python环境搭建到智能体创建的全流程。通过结合MCP协议,AI智能体能够理解任务并驱动Playwright执行复杂的浏览器自动化操作,实现从网页数据抓取到端到端测试的智能工作流。

banana的博客 531

通达信源码-主力建仓-AI投研图灵室出品

AI投研图灵室1:=3*SMA((CLOSE-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1)-2*SMA(SMA((CLOSE-LLV(LOW,75))/(HHV(HIGH,75)-LLV(LOW,75))*100,20,1),15,1);AI投研图灵室2W:=100-100*(HHV(HIGH,14)-CLOSE)/(HHV(HIGH,14)-LLV(LOW,14));AI投研图灵室3:=SMA(SMA(AI投研图灵室2,3,1),3,1);

qq_57222058的博客 4384

基于WPF的Dispatcher应用及探讨(三)

现在我们在搭建好的MVVM架构中实现Dispatcher: 我们把之前作成的CustomizeDispatcher放入PresenterBase中调用, using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Input; using S

harly5555的专栏 944

使用 MATLAB/Simulink + Communications Toolbox + 5G Toolbox,构建一个2×2 MIMO-OFDM通信系统

本文通过Simulink+CommunicationsToolbox构建2×2 MIMO-OFDM通信系统,重点实现信道均衡滤波器(ZF与MMSE)的设计与应用。详细介绍了系统架构、建模步骤(包括信道估计、均衡器实现)和仿真场景设计,通过BER、星座图和EVM对比分析验证了MMSE均衡器在抑制噪声和信号恢复方面的优势。文章总结了MIMO均衡的核心原理与实践经验,并提出了扩展至大规模MIMO、深度学习均衡器等优化方向,为无线通信系统设计提供实用指导。

amy_mhd的博客 127

WPF学习笔记(5)——WPF+Stylet+MVVM:通过线程实现进度条的动态加载,解决UI阻塞问题

问题描述 在winform中,如果要执行一个耗时操作,通常会使用进度条ProgressBar来显示耗时操作的进度,在winform中耗时操作的同时更改进度条的值,在UI上面可以看到进度条动态增长。 但是在WPF中(Stylet框架+MVVM开发模式),如果用同样的方法去实现,则会造成UI阻塞,进度条卡死,当耗时操作执行完毕后UI才刷新(参考下图)。 为什么会这样? 先看一下ViewModel ViewModel定义了用于表示耗时操作进度的两个属性以及耗时操作,这些属性或者方法会在View中被绑定。 usi

做一名持续学习的GIS Coder 2427

TenserRt平台SMOKE 3D目标检测部署完整教程:从理论到实践

本教程详细介绍了在RK3588平台上部署SMOKE 3D目标检测模型的完整流程。SMOKE是一种创新的单阶段单目3D检测方法,通过关键点估计直接预测3D目标信息,有效解决了深度信息缺失等挑战。文章从理论基础出发,解析了SMOKE的核心算法和网络架构,包括DLA-34主干网络和关键点中心化策略。同时提供了详细的Docker环境配置、模型导出到ONNX格式的实践指南,以及TensorRT引擎构建和性能优化的关键技术要点。该教程为开发者在嵌入式平台上实现高效3D目标检测提供了完整的技术参考。

LXY_LOVE36的博客 371

BevFusion (5):逐行代码详解

论文中给出了Bevfusion的架构图,它的输入是多视角的相机和点云图,经过两个特定的head,分别用于做检测任务和分割任务。图1 Bevfusion 结构图Bevfusion因为有两个模态的输入:多视角相机和点云,所以对应两个分支:相机分支和点云分支。相机分支:输入6个视角的图片后,提取特征,然后经VT(View Transformer)转换之后得到相机的Bev特征。激光雷达分支:输入点云数据,经过Encoder编码得到体素化Lidar特征,然后沿z方向展平,得到Lidar Bev特征。

@bangbang的博客 3714

WPF - DispatcherHelper

类似System.Windows.Forms.Application.DoEvents 处理当前在消息队列中的所有消息//Author: Andy//created 2013.08.04private static readonly DispatcherOperationCallback _exit_frame_callback = ( state )=>{ var frame = state a

回归 2533

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

WPF教程(四)--Dispatcher

WPF教程(四)--Dispatcher

Blue 1198

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

WPF UI多线程:Dispatcher正确使用方式

在WPF应用开发中,你是否遇到过以下问题: - 后台任务更新UI时抛出"调用线程无法访问此对象,因为另一个线程拥有该对象"异常 - 长时间运算导致界面卡顿失去响应 - 异步操作完成后无法正确更新数据绑定 - 程序在高负载下出现随机的UI崩溃 这些问题的根源都指向WPF的单线程公寓模型(STA)限制。WPF UI元素只能由创建它们的线程(通常是主线程)访问和修改。**Dispatcher(调度器)...

gitblog_00824的博客 1575

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

MVVM Light需要注意的10个问题

MVVM Light需要注意的10个问题 从使用XAML技术基础开始(实际上并不是很久以前),我便关注MVVM(Model – View – ViewModel)模式。偶然接触到MVVM Light不久后便喜欢上它的工作方式。不光我包括业余和专业开发人员在内的很多开发人员都喜欢这个函数库。按照开发者意愿,MVVM Light 不是一个框架而是函数库,该函数库注重于探究建立一个MVVM结构并且提供

郗富琦的专栏 9409

WPF中如何在MVVM模式下跨线程更新UI

Model:数据模型,表示应用程序的核心业务数据和逻辑。View:界面视图,负责显示UI。ViewModel:视图模型,连接View和Model,处理UI展示的数据及逻辑,充当View与Model之间的中介。通过数据绑定,View与ViewModel之间的交互可以通过属性自动同步,这样UI的变化就能响应到数据的变化,而无需直接操作UI控件。尽管MVVM模式通过数据绑定解耦了UI和业务逻辑,但当涉及到跨线程更新UI时,我们仍然需要使用Dispatcher机制确保UI线程的安全性。在WPF中,通过。

玉面小浪君的博客 2505

android MVVM框架学习总结(三)

1. 为什么需要在项目中引入架构或者框架 2. 当前有哪些架构或者框架可以使用 2.1 MVC 2.2 MVP 2.3 MVVM 2.4 MVVM架构在Android上的架构实现 3. DataBinding框架介绍及使用 3.1 DataBinding简介 3.2 构建环境 3.3 DataBinding使用 1. 为什么需要在项目中引入架构或者框架 使用架构的...

虎哥LoveDroid 786
上一篇: WPF MVVMLight 7:命令深入
下一篇: WPF MVVMLight 9:Messenger
Echo_Lee0
博客等级 码龄9年 576粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值