想做一个下图所示的Slider,以冒泡的方式显示其Value值,该怎么做呢?

功能要求,当鼠标放在滑块上的时候,冒“泡”显示值;当滑块移动的时候,“泡”跟随移动。

看似简单的功能,但要完美实现,确实要花费不少心思的。

方法一:使用Canvas面板。

1. 改造Slider

Slider的样式改造,比较简单,重写其模板即可。

            <Slider.Template>
<ControlTemplate TargetType="Slider">
<Border Name="border" SnapsToDevicePixels="True"
Background="{TemplateBinding Panel.Background}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}" >
<Grid>
<Border Background="Gray" Height="4" CornerRadius="2"/>
<Track Name="PART_Track" Grid.Row="1">
<Track.DecreaseRepeatButton>
<RepeatButton>
<RepeatButton.Template>
<ControlTemplate TargetType="RepeatButton">
<Border Height="4" CornerRadius="2" Background="#FF006CBE"/>
</ControlTemplate>
</RepeatButton.Template>
</RepeatButton>
</Track.DecreaseRepeatButton> <Track.Thumb>
<Thumb Name="thumb" VerticalAlignment="Center" Focusable="False">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Grid>
<Ellipse Name="thumbEllipse" Width="10" Height="10" Fill="White" StrokeThickness="1" Stroke="#FF006CBE" />
</Grid>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</Slider.Template>
</Slider>

2. 添加“泡”

很明显,这个“泡”的形状、文字都是一个元素,因此需要使用面板来将两个元素组合起来。这里最好使用Canvas,因为它的子元素是绝对定位的,可以通过Canvas.Left、Canvas.Top等属性安排子元素的位置,且Canvas也不会影响父元素的布局。因此将Slider的模板继续改造一下,如下所示:

        <Slider Name="slider" Width="255">
<Slider.Template>
<ControlTemplate TargetType="Slider">
<Border Name="border" SnapsToDevicePixels="True"
Background="{TemplateBinding Panel.Background}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}" >
<Grid>
<Border Background="Gray" Height="4" CornerRadius="2"/>
<Track Name="PART_Track" Grid.Row="1">
<Track.DecreaseRepeatButton>
<RepeatButton>
<RepeatButton.Template>
<ControlTemplate TargetType="RepeatButton">
<Border Height="4" CornerRadius="2" Background="#FF006CBE"/>
</ControlTemplate>
</RepeatButton.Template>
</RepeatButton>
</Track.DecreaseRepeatButton>

<Track.Thumb>
<Thumb Name="thumb" VerticalAlignment="Center" Focusable="False">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Grid>
<Ellipse Name="thumbEllipse" Width="10" Height="10" Fill="White" StrokeThickness="1" Stroke="#FF006CBE" />
<Canvas Name="canvas" Opacity="0">
<Grid Canvas.Bottom="10" Canvas.Left="-8">
<Path Width="25" Stretch="Uniform" Fill="White" Stroke="#FF006CBE" StrokeThickness="1" Data="M172,633.14a339.4,339.4,0,0,1-169.9-294C2.13,151.76,154.2-.29,341.57-.29S681,151.76,681,339.11a339.38,339.38,0,0,1-169.9,294h0a183.88,183.88,0,0,0-84.27,106.38h0l-85.26,283.61L256.3,739.52A183.88,183.88,0,0,0,172,633.14Z"/>
<TextBlock HorizontalAlignment="Center" Margin="0,5,0,0" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Slider}, Path=Value, Converter={StaticResource double2Int}}"/>
</Grid>
</Canvas>
</Grid> <ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="canvas" Property="Opacity" Value="1"/>
</Trigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="canvas" Property="Opacity" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</Slider.Template>
</Slider>

红色部分为添加的代码。有两点需要注意:

1. TextBlock.Text属性的绑定,使用了RelativeSource模式。

2. 记得要添加触发器(Trigger),使的Canvas能在指定的状态下显示出来。

以这种方式改的Slider控件,有一个问题:“泡”不能超出窗体显示。也就是说当滑块处于屏幕的边缘时,这个“泡”是显示不出来的。

方法二:使用Window

WPF框架中有Popup控件和Tooltip控件。通过微软的源代码可知,他们的实现都是使用Window控件。即在一定的条件下打开一个Windows,并显示内容,在另一个条件下,则关闭这个Window。

根据这个思路,就可以自定义一个控件,并实现更多的功能

1. 自定义控件

控件类:FellowPopup,其他的几个类为辅助类。

    /// <summary>
/// 坐标系转换类
/// </summary>
public static class CoordinateHelper
{
/// <summary>
/// 将用户空间坐标系的点,转换为屏幕坐标系的点。
/// 已考虑系统Dpi问题
/// </summary>
/// <param name="relativeTo">可视化对象。它的坐标系原点在左上角</param>
/// <param name="point">在可视化对象坐标系中的点</param>
/// <returns></returns>
public static Point ClientToScreen(Visual relativeTo, Point point)
{
if (relativeTo == null) return point;
Point targetToScreen = relativeTo.PointToScreen(point); // 将point的点转换为屏幕坐标系的点
var source = PresentationSource.FromVisual(relativeTo);
double dpiX = 96.0, dpiY = 96.0;
if (source?.CompositionTarget != null)
{
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
return new Point(targetToScreen.X * 96.0 / dpiX, targetToScreen.Y * 96 / dpiY);
}
} public static class DependencyPropertyHelper
{
/// <summary>
/// 为依赖属性注册值发生改变时的回调方法
/// </summary>
/// <param name="Register">含有依赖属性的对象</param>
/// <param name="dependencyProperty">依赖属性</param>
/// <param name="action">要注册的值发生改变时的回调方法</param>
/// <param name="runOnceImmediately">是否立即执行一次</param>
public static void RegisterDependencyPropertyChanged(this DependencyObject Register, DependencyProperty dependencyProperty, EventHandler action, bool runOnceImmediately = false)
{
DependencyPropertyDescriptor obj = DependencyPropertyDescriptor.FromProperty(dependencyProperty, Register.GetType());
if(obj != null) obj.AddValueChanged(Register, action);
if(runOnceImmediately) action?.Invoke(Register,new EventArgs());
}
} /// <summary>
/// 放置模式
/// </summary>
public enum PlacementMode
{
/// <summary>
/// 顶部靠左
/// </summary>
TopLeft,

/// <summary>
/// 顶部居中
/// </summary>
TopMiddle, /// <summary>
/// 顶部靠右
/// </summary>
TopRight,

/// <summary>
/// 左侧居中
/// </summary>
LeftMiddle, /// <summary>
/// 右侧居中
/// </summary>
RightMiddle, /// <summary>
/// 底部靠左
/// </summary>
BottomLeft, /// <summary>
/// 底部居中
/// </summary>
BottomMiddle, /// <summary>
/// 底部靠右
/// </summary>
BottomRight
} /// <summary>
/// 可跟随控件移动的弹出提示框。
/// </summary>
public class FellowPopup : ContentControl
{
private Window _popupWnd; /// <summary>
/// 水平位置偏移量
/// </summary>
public double HorizontalOffset
{
get => (double)GetValue(HorizontalOffsetProperty);
set => SetValue(HorizontalOffsetProperty, value);
} internal static readonly DependencyProperty HorizontalOffsetProperty = DependencyProperty.Register(
"HorizontalOffset", typeof(double), typeof(FellowPopup), new PropertyMetadata(0.0d, OnValueChanged)); /// <summary>
/// 垂直偏移量
/// </summary>
public double VerticalOffset
{
get => (double)GetValue(VerticalOffsetProperty);
set => SetValue(VerticalOffsetProperty, value);
}

internal static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register(
"VerticalOffset", typeof(double), typeof(FellowPopup), new PropertyMetadata(0.0d, OnValueChanged)); /// <summary>
/// 放置位置
/// </summary>
public PlacementMode Placement
{
get => (PlacementMode)GetValue(PlacementProperty);
set => SetValue(PlacementProperty, value);
} internal static readonly DependencyProperty PlacementProperty = DependencyProperty.Register(
"Placement", typeof(PlacementMode), typeof(FellowPopup), new PropertyMetadata(PlacementMode.BottomRight,OnValueChanged)); /// <summary>
/// 放置的目标元素
/// </summary>
public FrameworkElement PlacementTarget
{
get => (FrameworkElement)GetValue(PlacementTargetProperty);
set => SetValue(PlacementTargetProperty, value);
} internal static readonly DependencyProperty PlacementTargetProperty = DependencyProperty.Register(
"PlacementTarget", typeof(FrameworkElement), typeof(FellowPopup), new PropertyMetadata(null,OnValueChanged)); private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FellowPopup popup = (FellowPopup)d;
popup.UpdateLocation();
} /// <summary>
/// 是否打开。当其为true时,则显示提示框,false则不显示提示框
/// </summary>
public bool IsOpen
{
get => (bool)GetValue(IsOpenProperty);
set => SetValue(IsOpenProperty, value);
} internal static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
"IsOpen", typeof(bool), typeof(FellowPopup), new PropertyMetadata(false,OnIsOpenChanged)); private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FellowPopup fp = d as FellowPopup;
if((bool)e.NewValue ) fp.Open();
else fp.Close();
}
#region 触发位置更新 /// <summary>
/// 触发位置更新的控件
/// </summary>
public FrameworkElement TriggerElement
{
get => (FrameworkElement)GetValue(TriggerElementProperty);
set => SetValue(TriggerElementProperty, value);
} internal static readonly DependencyProperty TriggerElementProperty = DependencyProperty.Register(
"TriggerElement", typeof(UIElement), typeof(FellowPopup), new PropertyMetadata(null, OnTriggerChanged)); /// <summary>
/// 触发位置更新的控件的属性
/// 此属性应该是TriggerElement的属性。
/// 当此属性的值发生变化时,会触发FellowPopup的UpdateLocation方法。
/// </summary>
public DependencyProperty TriggerProperty
{
get => (DependencyProperty)GetValue(TriggerPropertyProperty);
set => SetValue(TriggerPropertyProperty, value);
}

internal static readonly DependencyProperty TriggerPropertyProperty = DependencyProperty.Register(
"TriggerProperty", typeof(DependencyProperty), typeof(FellowPopup), new PropertyMetadata(null, OnTriggerChanged)); private static void OnTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FellowPopup fp = (FellowPopup)d;
if (fp.TriggerElement == null || fp.TriggerProperty == null) return; // 如果定义属性的类,与TriggerElement类相同,或者TriggerElement类是它的子类
Type propertyType = fp.TriggerProperty.OwnerType;
Type elementType = fp.TriggerElement.GetType(); if ((elementType != propertyType) && (!elementType.IsSubclassOf(propertyType))) throw new ArgumentException("TriggerElement不包含TriggerProperty属性");
DependencyPropertyHelper.RegisterDependencyPropertyChanged(fp.TriggerElement, fp.TriggerProperty, delegate (object sender, EventArgs args)
{
fp.UpdateLocation();
}, false);
}
#endregion static FellowPopup()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FellowPopup), new FrameworkPropertyMetadata(typeof(FellowPopup)));
} /// <summary>
/// 打开窗体
/// </summary>
private void Open()
{
_popupWnd = new Window()
{
Topmost = true,
ShowActivated = false,
AllowsTransparency = true,
WindowStyle = WindowStyle.None,
BorderThickness = new Thickness(0),
SizeToContent = SizeToContent.WidthAndHeight,
WindowStartupLocation = WindowStartupLocation.Manual, Background = this.Background,
Content = this.Content,
ContentStringFormat = this.ContentStringFormat,
ContentTemplate = this.ContentTemplate,
ContentTemplateSelector = this.ContentTemplateSelector,
};
UpdateLocation();
_popupWnd.Show();
} // 关闭_popup窗体,并回收
private void Close()
{
if (_popupWnd != null)
{
_popupWnd.Close();
GC.SuppressFinalize(_popupWnd);
}
} /// <summary>
/// 更新弹出窗体的位置。
/// </summary>
private void UpdateLocation()
{
if (_popupWnd == null) return;
if(PlacementTarget == null)
{
_popupWnd.Left = 0;
_popupWnd.Top = 0;
return;
} double contentWidth = _popupWnd.Content == null ? 0 : ((FrameworkElement)_popupWnd.Content).ActualWidth;
double contentHeght = _popupWnd.Content == null ? 0 : ((FrameworkElement)_popupWnd.Content).ActualHeight; Point targetToScreen;
switch (Placement)
{
case PlacementMode.TopLeft:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget,new Point(0, 0));
_popupWnd.Left = targetToScreen.X - contentWidth - HorizontalOffset;
_popupWnd.Top = targetToScreen.Y - contentHeght - VerticalOffset;
break; case PlacementMode.TopMiddle:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth/2, 0));
_popupWnd.Left = targetToScreen.X - contentWidth / 2 + HorizontalOffset;
_popupWnd.Top = targetToScreen.Y - contentHeght - VerticalOffset;
break; case PlacementMode.TopRight:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth, 0));
_popupWnd.Left = targetToScreen.X + HorizontalOffset;
_popupWnd.Top = targetToScreen.Y - contentHeght - VerticalOffset;
break; case PlacementMode.LeftMiddle:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(0, PlacementTarget.ActualHeight / 2));
_popupWnd.Left = targetToScreen.X - contentWidth - HorizontalOffset;
_popupWnd.Top = targetToScreen.Y - contentHeght / 2;
break; case PlacementMode.RightMiddle:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth, PlacementTarget.ActualHeight / 2));
_popupWnd.Left = targetToScreen.X + HorizontalOffset;
_popupWnd.Top = targetToScreen.Y - contentHeght / 2;
break; case PlacementMode.BottomLeft:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(0, PlacementTarget.ActualHeight));
_popupWnd.Left = targetToScreen.X - contentWidth - HorizontalOffset;
_popupWnd.Top = targetToScreen.Y + VerticalOffset;
break; case PlacementMode.BottomMiddle:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth / 2, PlacementTarget.ActualHeight));
_popupWnd.Left = targetToScreen.X - contentWidth / 2 - HorizontalOffset;
_popupWnd.Top = targetToScreen.Y + VerticalOffset;
break; case PlacementMode.BottomRight:
targetToScreen = CoordinateHelper.ClientToScreen(PlacementTarget, new Point(PlacementTarget.ActualWidth, PlacementTarget.ActualHeight));
_popupWnd.Left = targetToScreen.X + HorizontalOffset;
_popupWnd.Top = targetToScreen.Y + VerticalOffset;
break;
}
}
}

(1)在FellowPopup类中,定义了一个Window类型的变量_popup,用于展示“泡”的内容。PlacementTarget、Placement、HorizontalOffset、VerticalOffset四个属性,用于设置_popup的Left和Top属性,以确定_popup在屏幕中的位置。

  这里需要注意,Visual.PointToScreen()方法,参数point是在Visual坐标空间的点,其返回值为将这个点,转换为屏幕坐标系的点。但转换后的点,未考虑到系统DPI及屏幕缩放的情况。因此,需要将这个点再运算,使之成为当前屏幕DPI及缩放模式下的点坐标。

(2)TriggerElement、TriggerProperty属性,用于指示,当哪个元素的哪个属性值发生变化时,更新位置。

  要想让一个窗体跟着控件移动,就必须告诉窗体,控件当前的位置在哪儿,然后去计算窗体应该在哪儿。而在WPF中,几乎没有控件会实时报告自己的位置。因此,必须找到元素的一个属性,当元素的“位置”发生改变的时候,这个属性值会发生变化。

  除了找到这样的一个属性外,还需要注册一个属性值发生改变时的回调方法。在本例中,滑块在滑动的过程中,本身没有属性(公开的)值发生改变,但是Slider的Value属性会发生改变。因此可以选择它,来触发FellowPopup的位置改变。

  由此可见,PlacementTarge系列与TriggerElement可以是不同的。一个用于“布置”窗体,一个用于“触发”窗体。

2. 使用自定义控件

定义好了控件后,就可以在Xaml中使用它了。继续改造Slider的模板:

        <!-- 别忘了转换器 -->
<local:Double2IntConverter x:Key="double2Int" /> <Slider Name="slider" Width="255">
<Slider.Template>
<ControlTemplate TargetType="Slider">
<Border Name="border" SnapsToDevicePixels="True"
Background="{TemplateBinding Panel.Background}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}" >
<Grid>
<Border Background="Gray" Height="4" CornerRadius="2"/>
<Track Name="PART_Track" Grid.Row="1">
<Track.DecreaseRepeatButton>
<RepeatButton>
<RepeatButton.Template>
<ControlTemplate TargetType="RepeatButton">
<Border Height="4" CornerRadius="2" Background="#FF006CBE"/>
</ControlTemplate>
</RepeatButton.Template>
</RepeatButton>
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Name="thumb" VerticalAlignment="Center" Focusable="False">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Grid>
<Ellipse Name="thumbEllipse" Width="10" Height="10" Fill="White" StrokeThickness="1" Stroke="#FF006CBE" />
<local:FellowPopup x:Name="popup" Background="Transparent"
Placement="TopMiddle" PlacementTarget="{Binding ElementName=thumbEllipse}"
TriggerElement="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Slider}}"
TriggerProperty="Slider.Value">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Width="25" Stretch="Uniform" Fill="White" Stroke="#FF006CBE" StrokeThickness="1" Data="M172,633.14a339.4,339.4,0,0,1-169.9-294C2.13,151.76,154.2-.29,341.57-.29S681,151.76,681,339.11a339.38,339.38,0,0,1-169.9,294h0a183.88,183.88,0,0,0-84.27,106.38h0l-85.26,283.61L256.3,739.52A183.88,183.88,0,0,0,172,633.14Z"/>
<TextBlock HorizontalAlignment="Center" Margin="0,5,0,0"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Slider}, Path=Value, Converter={StaticResource double2Int}}"/>
</Grid>
</local:FellowPopup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="popup" Property="IsOpen" Value="True"/>
</Trigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="popup" Property="IsOpen" Value="True"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Track.Thumb>
</Track>
</Grid>
</Border> </ControlTemplate>
</Slider.Template>
</Slider>

注意Placement、TriggerElement、TriggerProperty、Text等属性的绑定方式。

至此,改造搞定。以第二种方式改造的Slider,“泡”可以显示在主窗体之上。没有任何东西会挡住他,以为它是TopMost的。另外也可以再添加自己的逻辑去设置“泡”的位置。

以上为自己学习总结,如有错误,请指正。

转摘请注明出处。

WPF学习:Slider — 冒泡显示值的更多相关文章

  1. WPF学习之路初识

    WPF学习之路初识   WPF 介绍 .NET Framework 4 .NET Framework 3.5 .NET Framework 3.0 Windows Presentation Found ...

  2. WPF学习(8)数据绑定

    说到数据绑定,其实这并不是一个新的玩意儿.了解asp.net的朋友都知道,在asp.net中已经用到了这个概念,例如Repeater等的数据绑定.那么,在WPF中的数据绑定相比较传统的asp.net中 ...

  3. WPF学习(8)数据绑定 https://www.cnblogs.com/jellochen/p/3541197.html

    说到数据绑定,其实这并不是一个新的玩意儿.了解asp.net的朋友都知道,在asp.net中已经用到了这个概念,例如Repeater等的数据绑定.那么,在WPF中的数据绑定相比较传统的asp.net中 ...

  4. 【WPF学习】第五十章 故事板

    正如上一章介绍,WPF动画通过一组动画类(Animation类)表示.使用少数几个熟悉设置相关信息,如开始值.结束值以及持续时间.这显然使得它们非常适合于XAML.不是很清晰的时:如何为特定的事件和属 ...

  5. sql server 自增长id 允许插入显示值

    --允许插入显示插入主键id的值SET IDENTITY_INSERT [T0002_SType] ON 执行insert插入语句------------------ --关闭 插入显示值SET ID ...

  6. WPF学习开发客户端软件-任务助手(下 2015年2月4日代码更新)

    时光如梭,距离第一次写的 WPF学习开发客户端软件-任务助手(已上传源码)  已有三个多月,期间我断断续续地对该项目做了优化.完善等等工作,现在重新向大家介绍一下,希望各位可以使用,本软件以实用性为主 ...

  7. 使用Aspose.Cells 设置chart的y坐标轴显示值

    目的:设置chart的y坐标轴显示值 用aspose.cell生成的chart生成的Y轴是默认生成的,自己要定义y轴坐标值1.把数据源写到excel里面,list里面2.y轴坐标自己定义 第一种:默认 ...

  8. devexpress表格控件gridcontrol设置隔行变色、焦点行颜色、设置(改变)显示值、固定列不移动(附源码)

    介绍一些常用的gridcontrol设置. 1.设置隔行变色.首先设置显示隔行变色,步骤:OptionsView-->EnableAppearanceEvenRow-->true和Opti ...

  9. WPF学习05:2D绘图 使用Transform进行控件变形

    在WPF学习04:2D绘图 使用Shape绘基本图形中,我们了解了如何绘制基本的图形. 这一次,我们进一步,研究如何将图形变形. 例子 一个三角形,经Transform形成组合图形: XAML代码: ...

  10. WPF学习之资源-Resources

    WPF学习之资源-Resources WPF通过资源来保存一些可以被重复利用的样式,对象定义以及一些传统的资源如二进制数据,图片等等,而在其支持上也更能体现出这些资源定义的优越性.比如通过Resour ...

随机推荐

  1. pbootcms对接微信扫码登录代码核心片段和步骤(前后端)

    首先需要在微信公众平台或开放平台中创建应用,并获取到AppID和AppSecret. 在pbootcms中创建一个自定义模板页面(例如:wechat_login.html),并在该页面中添加以下代码, ...

  2. MAX30102采集心率数据

    一个100行的代码调试都可能会让程序员遇到很多挫折,所以,面对挫折,我们永远不能低头. 关于MAX30102驱动配置程序,网上搜索博客有一堆资料,c/c++写的驱动代码都有, 可参考博客: MAX30 ...

  3. Could not resolve com.android.tools.lint:lint-kotlin:26.2.0.

    好久没有使用weexplus publish android 打包apk, 今一运行失败了,提示Could not resolve com.android.tools.lint:lint-kotlin ...

  4. cv学习总结(11.6-11.13)

    两层全连接神经网络的内容要比想象中的多很多,代码量也很多,在cs231n只用了15分钟讲解的东西我用了一周半的时间才完全的消化理解,这周终于完成了全连接神经网络博客的书写https://www.cnb ...

  5. JupyterLab Server 搭建与使用笔记

    两三个月前,有幸拿到了云筏的一个 4 核 16G,1TB硬盘,300M带宽位于欧洲的云服务器,自带的开箱即用的 RStudio Server 也非常给力,但最近这两天在升级 R 的时候遇上了不少问题, ...

  6. 【HarmonyOS】一文教你如何在低代码项目中跳转H5页面

    ​ [关键字] 元服务.低代码.H5页面跳转.WebView [1.写在前面] 今天我们来实现一个在低代码项目中通过按钮跳转到H5页面的功能,本项目是基于API6的JS工程,我们的实现思路是在页面B中 ...

  7. Pinot2的无人机传感器和摄像头

    目录 1. 引言 2. 技术原理及概念 2.1 基本概念解释 2.2 技术原理介绍 2.3 相关技术比较 无人机传感器和摄像头在Pinot 2中得到广泛应用,其目的是为Pinot 2提供全面的传感器和 ...

  8. 深度解读AIGC存储解决方案

    5月26日,2023数据基础设施技术峰会在苏州举办,腾讯云首席存储技术专家温涛受邀出席并分享了腾讯云领先的存储技术在AIGC场景中的应用,通过对AIGC业务流程和场景的提炼,从内容生成.内容审核和内容 ...

  9. BUUCTF-MISC-九连环(steghide隐写+伪加密)

    开局一张图 丢入winhex里,尾部有其他文件名 kali中binwalk -e x.jpg 得到的zip中 图片打不开 丢入winhex,发现jpg处是伪加密 修改之后,解压可打开图片,另一个压缩包 ...

  10. 面试官:一个 SpringBoot 项目能处理多少请求?(小心有坑)

    你好呀,我是歪歪. 这篇文章带大家盘一个读者遇到的面试题哈. 根据读者转述,面试官的原问题就是:一个 SpringBoot 项目能同时处理多少请求? 不知道你听到这个问题之后的第一反应是什么. 我大概 ...