C# wpf 实现底部嵌入HwndHost

WPF Hwnd窗口互操作系列

第一章 嵌入Hwnd窗口
第二章 嵌入WinForm控件
第三章 嵌入WPF控件
第四章 底部嵌入HwndHost(本章)


文章目录

  • WPF Hwnd窗口互操作系列
  • 前言
  • 一、如何实现?
    • 1、底部创建窗口
      • (1)、创建透明窗口
      • (2)、监控顶部窗口事件
      • (3)、窗口保持在底部
    • 2、HwndHost加入底部窗口
    • 3、Clip穿透顶部窗口
    • 4、WPF控件放在装饰层
  • 二、完整代码
    • 1、对象定义
    • 2、完整代码
  • 三、使用示例
    • 1、嵌入Winform控件
    • 2、显示视频
    • 3、圆角矩形视频
    • 4、拖动位置大小
  • 总结


前言

前面三章内容是笔者基于本章内容研发过程中的附带产物,但是意外的发现第三章可嵌入wpf控件后,本章的意义就变得不那么大了。本章讲述如何从底部嵌入hwnd窗口,以此来做到嵌入窗口不覆盖wpf控件的效果,这种实现思路参考了flutter的一个插件flutter_native_view,其内部实现就用了这种方式。对于wpf实现会复杂一些,因为提供自绘没有BlendMode之类的东西,无法直接消除底部画面,能够使用的方式是Clip,这种方式限制比较多。不过最终还是实现了功能。


一、如何实现?

1、底部创建窗口

(1)、创建透明窗口

 _backgroundWindow = new Window() { WindowStyle = WindowStyle.None, ResizeMode = ResizeMode.NoResize, Focusable = false, Width = 0, Height = 0, ShowInTaskbar = false, ShowActivated = false, Background = Brushes.Transparent, Content = new Grid(), AllowsTransparency = true, };_backgroundHandle = (new WindowInteropHelper(_backgroundWindow)).EnsureHandle();

(2)、监控顶部窗口事件

添加hook监控上层窗口的事件

HwndSource.FromHwnd(_forgroundHandle).AddHook(new HwndSourceHook(WndProc));

(3)、窗口保持在底部

监控相关事件保证窗口始终贴在下面。

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{switch (msg){case WM_ACTIVATE:case WM_MOVE:case WM_SIZE:RECT rect;GetWindowRect(_forgroundHandle, out rect);SetWindowPos(_backgroundHandle, _forgroundHandle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE);break;}return IntPtr.Zero;
}

2、HwndHost加入底部窗口

需要嵌入的hwnd窗口,直接嵌入到底部窗口。下列代码中Content是HwndHost控件,在xaml中设置。在cs代码中直接将其添加到底部窗口中。

[ContentProperty("Content")]
public class HwndHostBottomEmbbeder : FrameworkElement
{public HwndHost Content{get { return (HwndHost)GetValue(HostProperty); }set { SetValue(HostProperty, value); }}
}
var grid = _backgroundWindow.Content as Grid;
grid.Children.Add(Content);

3、Clip穿透顶部窗口

和其他嵌入方式一样,需要先继承HwndHost。clip原理参考《C# wpf利用Clip属性实现截屏框》

//顶部窗口句柄
IntPtr _forgroundHandle = IntPtr.Zero;
//底部窗口句柄
IntPtr _backgroundHandle = IntPtr.Zero;
//底部窗口句柄
Window _backgroundWindow = null;
//顶部窗口
Window _foregroundWindow = null;
//顶部窗口的Content
FrameworkElement? _foreroundContent;
//嵌入窗口的区域
RectangleGeometry? _clipRect;
private void NativeHost_LayoutUpdated(object? sender, EventArgs e)
{if (_foreroundContent == null) _foreroundContent = _foregroundWindow.Content as FrameworkElement;//计算控件在底部窗口的位置var pos = TranslatePoint(new Point(0, 0), _foregroundWindow);var dp = pos - Content.TranslatePoint(new Point(0, 0), _backgroundWindow);Content.Margin = new Thickness(Content.Margin.Left + dp.X, Content.Margin.Top + dp.Y, Content.Margin.Right - dp.X, Content.Margin.Bottom - dp.Y);Content.SetSize(ActualWidth, ActualHeight);//创建clipvar gg = _foreroundContent.Clip as GeometryGroup;if (gg == null){_foreroundContent.Clip = gg = new GeometryGroup();gg.FillRule = FillRule.EvenOdd;var rg = new RectangleGeometry();rg.Rect = new Rect(0, 0, _foreroundContent.ActualWidth, _foreroundContent.ActualHeight);gg.Children.Add(rg);gg.Children.Add(new CombinedGeometry() { Geometry1 = new RectangleGeometry(), Geometry2 = new GeometryGroup() { FillRule = FillRule.Nonzero } });}//底下的rect必须保持和容器大小一致var backRg = gg.Children.First() as RectangleGeometry;//上层形状即为穿透区域,CombinedGeometry类型用于支持任意个区域穿透var foreRg = (gg.Children[1] as CombinedGeometry).Geometry2 as GeometryGroup;if (_clipRect == null){_clipRect = new RectangleGeometry();//添加当前控件的穿透区域foreRg.Children.Add(_clipRect);}var newRect = new Rect(0, 0, _foreroundContent.ActualWidth, _foreroundContent.ActualHeight);//判断窗口大小是否改变if (backRg.Rect != newRect){backRg.Rect = newRect;}pos = TranslatePoint(new Point(0, 0), _foreroundContent);newRect = new Rect(pos.X, pos.Y, ActualWidth, ActualHeight);//判断控件区域是否变化if (_clipRect.Rect != newRect){_clipRect.Rect = newRect;}
}

4、WPF控件放在装饰层

由于clip会截其范围内的所有控件,所有有clip的控件上面放置其他控件是看不到的,但是clip不会截取装饰层的控件,所有我们可将控件放到装饰层。
添加一个装饰器属性,在xaml中使用

public class HwndHostBottomEmbbeder : FrameworkElement{public UIElement Adorner{get { return (UIElement)GetValue(ContentProperty); }set { SetValue(ContentProperty, value); }}
}  

定义一个装饰器对象

class NormalAdorner : Adorner
{UIElement _child;/// <summary>/// 构造方法/// </summary>/// <param name="adornedElement">被添加装饰器的元素</param>/// <param name="child">放到装饰器中的元素</param>public NormalAdorner(UIElement adornedElement, UIElement child) : base(adornedElement){_child = child;AddVisualChild(child);}public UIElement Child => _child;protected override Visual GetVisualChild(int index) => _child;protected override int VisualChildrenCount => 1;protected override System.Windows.Size ArrangeOverride(Size finalSize){_child.Arrange(new Rect(new Point(0, 0), finalSize));return finalSize;}
}

在初始化代码中将xaml中设置的Adorner属性的控件加入到装饰层。要注意添加一个容器用于获取鼠标事件,将事件透传到原本的控件(clip后无法响应鼠标事件)。

var layer = AdornerLayer.GetAdornerLayer(c);
if (layer == null)throw new Exception("获取控件装饰层失败,控件可能没有装饰层!");                       
var grid = new Grid();
grid.Background = Brushes.Transparent;
grid.DataContext = c;
grid.SetBinding(Grid.VisibilityProperty, new Binding("Visibility") { Mode = BindingMode.OneWay });
//事件透传
c.MouseByPassFrom(grid);
if (adronerContent != null)
{grid.Children.Add(adronerContent);
}
layer.Add(new NormalAdorner((UIElement)c, grid));

二、完整代码

1、对象定义

 /************************************************************************* @Project:  	HwndHostBottomEmbbeder* @Decription:  底部Hwnd嵌入器,是一个控件,可包含HwndHost控件将其变成底部嵌入,wpf控件显示在装饰层上。*               当前版本使用限制:*               1、窗口需要设置WindowChrome,*               2、会占用Window.Content的Clip属性,*               3、不能点击穿透到Hwnd窗口。*               4、不支持半透明或全透明背景色窗口,因为嵌入hwnd窗口拖动会有残影。*               当前版本适用于显示视频之类的不需要交互的hwnd控件。* @Verision:  	v1.0.0* @Author:  	Xin Nie* @Create:  	2024/03/29 17:33:00* @LastUpdate:  2024/03/29 17:33:00************************************************************************* Copyright @ 2024. All rights reserved.************************************************************************/
[ContentProperty("Content")]
public class HwndHostBottomEmbbeder : FrameworkElement
{/// <summary>/// HwndHost控件/// </summary>public HwndHost Content { get; set; }/// <summary>/// 装饰层控件/// </summary>public UIElement Adorner { get; set; }
}

2、完整代码

之后上传


三、使用示例

1、嵌入Winform控件

MainWindow.xaml

<Window x:Class="WpfHwndElement.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:WpfHwndElement"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Background="Transparent"WindowStyle="None"   ResizeMode="NoResize"Title="MainWindow" Height="360" Width="640"       xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  ><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"   CaptionHeight="0"   /></WindowChrome.WindowChrome ><Grid  Margin="0" Background="#ffffffff"  ><local:HwndHostBottomEmbbeder   Height="200" Width="200" ><!--将WindowsFormsHost转换到底部--><WindowsFormsHost><wf:TextBox  Text="WinForm Text Box"    BackColor="255,	77,78,141" /></WindowsFormsHost><!--在装饰层放wpf控件--><local:HwndHostBottomEmbbeder.Adorner><ToggleButton Margin="5"  Width="25" Height="30" Cursor="Hand"  ><ToggleButton.Template><ControlTemplate TargetType="ToggleButton"><Grid Background="Transparent"><Polygon x:Name="pol" Points="0,0 25,15 0,30" Fill="Gray" Visibility="Visible"></Polygon><Rectangle  x:Name="rec1" HorizontalAlignment="Left" Width="8" Fill="Gray" Visibility="Hidden"></Rectangle><Rectangle  x:Name="rec2" HorizontalAlignment="Right"   Width="8" Fill="Gray" Visibility="Hidden"></Rectangle></Grid></ControlTemplate></ToggleButton.Template></ToggleButton></local:HwndHostBottomEmbbeder.Adorner></local:HwndHostBottomEmbbeder></Grid>
</Window>

效果预览
在这里插入图片描述

2、显示视频

示例代码依赖了《播放器》
MainWindow.xaml

<Window x:Class="WpfHwndElement.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:WpfHwndElement"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Background="Transparent"WindowStyle="None"   ResizeMode="NoResize"Title="MainWindow" Height="360" Width="640"    xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  ><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"   CaptionHeight="0"   /></WindowChrome.WindowChrome ><Grid  Margin="0" Background="#ffffffff"  ><local:HwndHostBottomEmbbeder   Height="200" Width="200" ><!--将WindowsFormsHost转换到底部--><WindowsFormsHost><wf:TextBox  x:Name="tb_video" Text="WinForm Text Box"    BackColor="255,77,78,141" /></WindowsFormsHost><!--在装饰层放wpf控件--><local:HwndHostBottomEmbbeder.Adorner><Border Width="150" Height="150" ><ToggleButton Margin="5"  Width="25" Height="30" Cursor="Hand"   Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked"><ToggleButton.Template><ControlTemplate TargetType="ToggleButton"><Grid Background="Transparent"><Polygon x:Name="pol" Points="0,0 25,15 0,30" Fill="Gray" Visibility="Visible"></Polygon><Rectangle  x:Name="rec1" HorizontalAlignment="Left" Width="8" Fill="Gray" Visibility="Hidden"></Rectangle><Rectangle  x:Name="rec2" HorizontalAlignment="Right"   Width="8" Fill="Gray" Visibility="Hidden"></Rectangle></Grid><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter TargetName="pol" Property="Visibility" Value="Hidden"></Setter><Setter TargetName="rec1" Property="Visibility" Value="Visible"></Setter><Setter TargetName="rec2" Property="Visibility" Value="Visible"></Setter></Trigger></ControlTemplate.Triggers></ControlTemplate></ToggleButton.Template></ToggleButton></Border></local:HwndHostBottomEmbbeder.Adorner></local:HwndHostBottomEmbbeder></Grid>
</Window>

MainWindow.xaml.cs

using AC;
using System.Windows;namespace WpfHwndElement
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{Play _play;public MainWindow(){InitializeComponent();}private void ToggleButton_Checked(object sender, RoutedEventArgs e){if (_play == null){_play = new Play();_play.IsLoop = true;//获取tb_video的句柄,通过句柄渲染_play.Window = tb_video.Handle;_play.HardwareAccelerateType = HardwareAccelerateType.Dxva2;//开始播放,播放器会在传入的句柄窗口中渲染视频。_play.Start(@"D:\测试\Sony_4K_Camp_Main.mp4");      }else{_play.IsPause = false;}}private void ToggleButton_Unchecked(object sender, RoutedEventArgs e){_play.IsPause = true;}}
}

效果预览
效

3、圆角矩形视频

示例代码依赖了《播放器》
MainWindow.xaml

<Window x:Class="WpfHwndElement.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:WpfHwndElement"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Background="Transparent"WindowStyle="None"   ResizeMode="NoResize"Title="MainWindow" Height="360" Width="640"   ac:Resize.IsResizeable="True"ac:Resize.IsWindowDragSmooth="True"ac:Move.IsDragMoveable="True"      xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  ><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"   CaptionHeight="0"   /></WindowChrome.WindowChrome ><Grid  Margin="0" Background="#ffffffff"  ><local:HwndHostBottomEmbbeder   Height="200" Width="200"   ><!--裁剪圆角矩形--><local:HwndHostBottomEmbbeder.Clip><RectangleGeometry Rect="0 0 200 200" RadiusX="10" RadiusY="10"></RectangleGeometry></local:HwndHostBottomEmbbeder.Clip><!--将WindowsFormsHost转换到底部--><WindowsFormsHost><wf:TextBox  x:Name="tb_video" Text="WinForm Text Box"  BorderStyle="None"  BackColor="255,77,78,141" /></WindowsFormsHost><!--在装饰层放wpf控件--><local:HwndHostBottomEmbbeder.Adorner><Border Width="150" Height="150" ><ToggleButton Margin="5"  Width="25" Height="30" Cursor="Hand"   Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked"><ToggleButton.Template><ControlTemplate TargetType="ToggleButton"><Grid Background="Transparent"><Polygon x:Name="pol" Points="0,0 25,15 0,30" Fill="Gray" Visibility="Visible"></Polygon><Rectangle  x:Name="rec1" HorizontalAlignment="Left" Width="8" Fill="Gray" Visibility="Hidden"></Rectangle><Rectangle  x:Name="rec2" HorizontalAlignment="Right"   Width="8" Fill="Gray" Visibility="Hidden"></Rectangle></Grid><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter TargetName="pol" Property="Visibility" Value="Hidden"></Setter><Setter TargetName="rec1" Property="Visibility" Value="Visible"></Setter><Setter TargetName="rec2" Property="Visibility" Value="Visible"></Setter></Trigger></ControlTemplate.Triggers></ControlTemplate></ToggleButton.Template></ToggleButton></Border></local:HwndHostBottomEmbbeder.Adorner></local:HwndHostBottomEmbbeder></Grid>
</Window>

MainWindow.xaml.cs
cs代码同上略。
效果预览
在这里插入图片描述

4、拖动位置大小

示例代码依赖了《拖动》和《调整大小》还有《播放器》
MainWindow.xaml

<Window x:Class="WpfHwndElement.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:WpfHwndElement"xmlns:ac="clr-namespace:AC"mc:Ignorable="d"Background="Transparent"WindowStyle="None"   ResizeMode="NoResize"Title="MainWindow" Height="360" Width="640"   ac:Resize.IsResizeable="True"ac:Move.IsDragMoveable="True"      xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"  ><WindowChrome.WindowChrome><WindowChrome GlassFrameThickness="-1"   CaptionHeight="0"   /></WindowChrome.WindowChrome ><Grid  Margin="0" Background="#ffffffff"  ><local:HwndHostBottomEmbbeder   Height="200" Width="200"  ac:Resize.IsResizeable="True"  ac:Move.IsDragMoveable="True" ><!--将WindowsFormsHost转换到底部--><WindowsFormsHost><!--使用窗体设置全透明--><wf:Form  x:Name="tb_video"  TopLevel="False" FormBorderStyle="None"  /></WindowsFormsHost><!--在装饰层放wpf控件--><local:HwndHostBottomEmbbeder.Adorner><!--非播放时wpf层填充颜色,避免直接透到桌面--><Border x:Name="bd_mask"  Background="RoyalBlue"><ToggleButton Margin="5"  Width="25" Height="30" Cursor="Hand"   Checked="ToggleButton_Checked" Unchecked="ToggleButton_Unchecked"><ToggleButton.Template><ControlTemplate TargetType="ToggleButton"><Grid Background="Transparent"><Polygon x:Name="pol" Points="0,0 25,15 0,30" Fill="Gray" Visibility="Visible"></Polygon><Rectangle  x:Name="rec1" HorizontalAlignment="Left" Width="8" Fill="Gray" Visibility="Hidden"></Rectangle><Rectangle  x:Name="rec2" HorizontalAlignment="Right"   Width="8" Fill="Gray" Visibility="Hidden"></Rectangle></Grid><ControlTemplate.Triggers><Trigger Property="IsChecked" Value="True"><Setter TargetName="pol" Property="Visibility" Value="Hidden"></Setter><Setter TargetName="rec1" Property="Visibility" Value="Visible"></Setter><Setter TargetName="rec2" Property="Visibility" Value="Visible"></Setter></Trigger></ControlTemplate.Triggers></ControlTemplate></ToggleButton.Template></ToggleButton></Border></local:HwndHostBottomEmbbeder.Adorner></local:HwndHostBottomEmbbeder></Grid>
</Window>

MainWindow.xaml.cs

using AC;
using System.Reflection;
using System.Windows;namespace WpfHwndElement
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{Play _play;public MainWindow(){InitializeComponent();                 }private void ToggleButton_Checked(object sender, RoutedEventArgs e){if (_play == null){_play = new Play();_play.IsLoop = true;//获取tb_video的句柄,通过句柄渲染_play.Window = tb_video.Handle;_play.HardwareAccelerateType = HardwareAccelerateType.Dxva2;//开始播放,播放器会在传入的句柄窗口中渲染视频。_play.Start(@"D:\Sony_4K_Camp.mp4");//实现透明窗口,避免拖动时与视频绘制冲突,出现界面闪烁tb_video.GetType().GetMethod("SetStyle", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(tb_video, new object[] { ControlStyles.Opaque, true });}else{_play.IsPause = false;}bd_mask.Background =System.Windows.Media. Brushes.Transparent;}private void ToggleButton_Unchecked(object sender, RoutedEventArgs e){_play.IsPause = true;bd_mask.Background = System.Windows.Media.Brushes.RoyalBlue;}}
}

在这里插入图片描述


总结

以上就是今天要讲的内容,本章的实现是有一定难度的,其灵感来源有flutter,也是刚好发现wpf的Clip属性能做到穿透,才有了实现本章的基础,但是限制也不小,当然作为初步探索的版本,以后可以继续优化。本章实现的嵌入方式虽然有限制,但还是能够用来做视频渲染的,尤其是需要做圆角边框以及拖动的场景,就能做到渲染性能和界面效果的完美结合。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/779767.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FebHost:什么是哈萨克斯坦.KZ域名?

哈萨克斯坦&#xff0c;作为中亚地区重要的一员,其国家域名”.kz”正成为这个独立国家在网络世界中的代表。作为一个经济快速发展的国家,哈萨克斯坦的互联网基础设施和网络应用也在蓬勃发展。而.kz域名正是哈萨克斯坦网络身份的重要体现。 作为注册和管理.kz域名的主要机构,哈…

2020年30米二级分类北京市土地利用数据

引言 北京市省土地利用数据产品是指基于Landsat TM/ETM/OLI遥感影像&#xff0c;采用遥感信息提取方法&#xff0c;并结合野外实测&#xff0c;以及参照国内外现有的土地利用/土地覆盖分类体系&#xff0c;经过波段选择及融合&#xff0c;图像几何校正及配准并对图像进行增强处…

Elasticsearch 开放 inference API 增加了对 Cohere Embeddings 的支持

作者&#xff1a;来自 Elastic Serena Chou, Jonathan Buttner, Dave Kyle 我们很高兴地宣布 Elasticsearch 现在支持 Cohere 嵌入&#xff01; 发布此功能是与 Cohere 团队合作的一次伟大旅程&#xff0c;未来还会有更多合作。 Cohere 是生成式 AI 领域令人兴奋的创新者&…

SpringBoot+Prometheus+Grafana实现应用监控和报警

一、背景 SpringBoot的应用监控方案比较多&#xff0c;SpringBootPrometheusGrafana是目前比较常用的方案之一。它们三者之间的关系大概如下图&#xff1a; 关系图 二、开发SpringBoot应用 首先&#xff0c;创建一个SpringBoot项目&#xff0c;pom文件如下&#xff1a; <…

蓝桥杯嵌入式老竞赛板在MDK5上使用CooCox下载出现unknown device的问题

本文是在参考网上博客并经过实操解决自己遇到的问题总结而成&#xff0c;只是为了让后来者少走弯路。 本文是在在LED闪烁实验时遇到这个问题 蓝桥杯嵌入式老竞赛板在MDK5上使用CooCox下载出现unknown device的问题 环境&#xff1a;win11系统&#xff0c;keil MDK 518 老竞赛…

【Unity】TextMeshPro富文本

启用富文本 在Unity里&#xff0c;如果需要使用富文本&#xff0c;首先需要开启Rich Text 如果不开启Rich Text&#xff0c;就会在UI上显示富文本代码 1.粗体 <b>Game</b> Over2.斜体 <i>Game</i> Over3.下划线 <u>Game</u> Over4…

.Net 知识杂记

记录平日中琐碎的.net 知识点。不定期更新 目标框架名称(TFM) 我们创建C#应用程序时&#xff0c;在项目的工程文件(*.csproj)中都有targetFramework标签&#xff0c;以表示项目使用的目标框架 各种版本的TFM .NET Framework .NET Standard .NET5 及更高版本 UMP等 参考文档&a…

Tomcat配置https

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、证书 CA证书申请下载不赘述了。 二、上传证书 进入tomcat根目录&#xff0c;conf同级目录下创建cert文件夹&#xff0c;并将证书两个文件上传到该文件夹&#xff1b; 三、编辑conf/server.xml文件 ① …

unity3d for web

时光噶然 一晃好多年过去了&#xff08;干了5年的u3d游戏&#xff09;&#xff0c;记得最后一次使用的版本好像是 unity 2017。 那个是 unity3d for webgl 还需要装个插件。用起来很蛋疼。 最近做一个小项目 在选择是用 Layabox 还是 cocosCreate 的时候 我想起了老战友 Uni…

iOS - Runloop的运行逻辑

文章目录 iOS - Runloop的运行逻辑1. 苹果官方的Runloop执行图2. Mode里面的东西2.1 Source02.2 Source12.3 Timers2.4 Observers 3. 执行流程3.1 注意点 4. Runloop休眠 iOS - Runloop的运行逻辑 1. 苹果官方的Runloop执行图 2. Mode里面的东西 2.1 Source0 触摸事件处理pe…

中企出海,在地合规、境外支出管理,该怎么做?

从组团出海抢订单,到“新三样”拉动外贸新增长......"出海",已经成了中国企业的关键战略。但企业出海水深浪高,由于在全球税财法管控体系上缺乏经验,在地合规风险、内控管理漏洞、资金支出隐患等方面可能“陷阱”重重。 基于此,分贝通联合安永,以“破局!中国企业出海…

MTransE阅读笔记

Multilingual Knowledge Graph Embeddings for Cross-lingual Knowledge Alignment 用于交叉知识对齐的多语言知识图谱嵌入(MTransE) Abstract 最近的许多工作已经证明了知识图谱嵌入在完成单语知识图谱方面的好处。由于相关的知识库是用几种不同的语言构建的&#xff0c;因…

Java代码基础算法练习-求偶数和-2024.03.29

任务描述&#xff1a; 编制程序&#xff0c;输入n个整数&#xff08;n从键盘输入&#xff0c;n>0&#xff0c;整数取值范围&#xff1a;0~1000&#xff09;&#xff0c;输出这n个 数当中的偶数和。 任务要求&#xff1a; 代码示例&#xff1a; package M0317_0331;import…

集合,排序查找算法,可变参数

文章目录 集合Set集合TreeSet集合 Map集合概述特点子类及其底层数据结构常用方法遍历 数据结构常见的数据结构二叉树 可变参数介绍格式注意 Collections工具类方法 排序查找算法冒泡排序介绍原理注意代码 选择排序介绍原理规律代码 二分查找前提介绍原理注意代码 集合 Set集合 …

大话设计模式之代理模式

代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许通过代理对象控制对另一个对象的访问。代理对象充当客户端和实际对象之间的中介&#xff0c;客户端通过代理对象间接访问实际对象&#xff0c;从而可以在访问控制、缓存、延迟加载等方面提…

蓝桥杯刷题第四天

思路&#xff1a; 这道题很容易即可发现就是简单的暴力即可完成题目&#xff0c;我们只需满足所有数的和为偶数即可保证有满足条件的分法&#xff0c;同时也不需要存下每个输入的数据&#xff0c;只需要知道他是偶数还是奇数即可&#xff0c;因为我们只需要偶数个奇数搭配在一块…

如何使用命令行对RK开发板进行OpenHarmony版本烧录?

问题 在 OpenHarmony 自动化测试环境中&#xff0c;需要对流水线上的 RK 设备进行烧录&#xff0c;图形工具只能人工操作&#xff0c;那么有什么方法可以纯命令行进行自动化烧录呢&#xff1f; 思路 我们发现 RK 开发板实际是使用 upgrade_tool 的执行文件进行烧录的&#x…

公众号文章怎么写?手把手教你

公众号文章写作是讲究技巧和经验的&#xff0c;按照爆款文章的逻辑和结构走&#xff0c;你也能写出精彩的公众号推文&#xff0c;本文伯乐网络传媒将为你揭秘公众号文章的写作之道&#xff0c;纯干货&#xff0c;建议收藏起来慢慢看。 一、文章结构安排 1. 标题策划&#xff1…

使用Leaflet.rotatedMaker进行航班飞行航向模拟的实践

目录 前言 一、Leaflet的不足 1、方向插件 2、方向控制脚本说明 二、实时航向可视化实现 1、创建主体框架 2、飞机展示 3、位置和方位模拟 三、成果及分析 1、成果展示 2、方向绑定解读 总结 前言 众所周知&#xff0c;物体在空间中的运动&#xff08;比如飞行、跑步…

Windows11 使用 VirtualBox 安装创建 Ubuntu虚拟机

〇、背景 开发者大比例习惯都是Windows下编辑代码&#xff0c;比如使用Windows的Visual Studio Code进行代码的开发。但不管是AOSP还是鸿蒙开发&#xff0c;目前都不支持windows本地环境编译的&#xff0c;建议使用Ubuntu操作系统环境对源码进行编译。 因此&#xff0c;没有U…