WPF之Slider控件详解
文章目录
- 1. 概述
- 2. 基本属性
- 2.1 值范围属性
- 2.2 滑动步长属性
- 2.3 刻度显示属性
- 2.4 方向属性
- 2.5 选择范围属性
- 3. 事件处理
- 3.1 值变化事件
- 3.2 滑块拖动事件
- 4. 样式和模板自定义
- 4.1 基本样式设置
- 4.2 控件模板自定义
- 5. 数据绑定
- 5.1 绑定到ViewModel
- 5.2 同步多个控件
- 6. 实际应用示例
- 6.1 音量控制器
- 6.2 颜色选择器
- 7. 范围滑块(RangeSlider)实现
- 7.1 基本实现思路
- 7.2 RangeSlider控件实现示例
- 7.3 RangeSlider控件的使用
- 8. 高级应用:可视化数据滑块
- 9. 注意事项和最佳实践
- 9.1 性能考虑
- 9.2 可访问性考虑
- 9.3 响应式设计
- 10. 总结
- 11. 相关资源
可以根据Github拉取示例程序运行
GitHub程序演示地址(点击直达)
也可以在本文资源中下载
1. 概述
Slider(滑块)控件是WPF中常用的一种范围控件,继承自RangeBase抽象类。它允许用户通过拖动滑块在一个指定的数值范围内选择一个值。Slider控件广泛应用于音量调节、亮度控制、进度显示等场景,为用户提供了直观的交互方式。
2. 基本属性
2.1 值范围属性
Slider控件的核心属性是其值范围,包括以下几个重要属性:
// 设置滑块的最小值,默认为0
slider.Minimum = 0;// 设置滑块的最大值,默认为10
slider.Maximum = 100;// 当前滑块的值
slider.Value = 50;
这些属性都是双精度浮点数(double)类型,可以通过XAML或代码设置:
<Slider Minimum="0" Maximum="100" Value="50" Width="200"/>
2.2 滑动步长属性
Slider控件提供了几个控制步长的属性,用于定义滑块移动的精度:
<!-- 定义每次鼠标单击或按键操作时滑块移动的小增量值 -->
<Slider SmallChange="1" <!-- 定义按Page Up/Page Down键时滑块移动的大增量值 -->LargeChange="10" <!-- 设置刻度频率,每5个单位显示一个刻度 -->TickFrequency="5" Minimum="0" Maximum="100"Width="300"/>
在代码中可以这样设置:
// 设置小步长值
slider.SmallChange = 1;// 设置大步长值
slider.LargeChange = 10;// 设置刻度频率
slider.TickFrequency = 5;
2.3 刻度显示属性
Slider控件可以显示刻度标记,用于指示值的位置:
<Slider TickPlacement="BottomRight" IsSnapToTickEnabled="True"TickFrequency="10"Minimum="0" Maximum="100"Width="300"/>
TickPlacement属性用于指定刻度线的位置,可选值包括:
None
:不显示刻度线(默认值)TopLeft
:刻度线显示在上方(水平方向)或左侧(垂直方向)BottomRight
:刻度线显示在下方(水平方向)或右侧(垂直方向)Both
:刻度线同时显示在两侧
IsSnapToTickEnabled
属性指定滑块是否自动吸附到最近的刻度位置,当设置为True时,用户释放滑块后,滑块会自动移动到最近的刻度位置。
除了TickFrequency
属性设置等间距的刻度外,还可以使用Ticks
属性设置不规则的刻度位置:
<Slider Minimum="0" Maximum="100" Width="300"><Slider.Ticks><x:Double>0</x:Double><x:Double>10</x:Double><x:Double>25</x:Double><x:Double>50</x:Double><x:Double>75</x:Double><x:Double>100</x:Double></Slider.Ticks>
</Slider>
2.4 方向属性
Slider控件可以水平或垂直放置,通过Orientation
属性控制:
<!-- 水平滑块(默认) -->
<Slider Orientation="Horizontal" Width="200"/><!-- 垂直滑块 -->
<Slider Orientation="Vertical" Height="200"/>
2.5 选择范围属性
Slider控件允许高亮显示一个范围区域:
<Slider Minimum="0" Maximum="100" Value="75"IsSelectionRangeEnabled="True"SelectionStart="25"SelectionEnd="75"Width="300"/>
IsSelectionRangeEnabled
:是否启用选择范围SelectionStart
:选择范围的起始值SelectionEnd
:选择范围的结束值
当IsSelectionRangeEnabled
设置为True时,滑块会在指定范围内显示一个不同颜色的区域。
3. 事件处理
Slider控件提供了多个事件用于响应用户交互:
3.1 值变化事件
最常用的事件是ValueChanged
,当滑块的值发生变化时触发:
<Slider ValueChanged="Slider_ValueChanged" Minimum="0" Maximum="100"Width="300"/>
对应的事件处理代码:
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{// 获取旧值double oldValue = e.OldValue;// 获取新值double newValue = e.NewValue;// 处理值变化valueTextBlock.Text = $"当前值: {newValue:F1}";
}
3.2 滑块拖动事件
滑块的拖动操作可以通过Thumb控件的事件来捕获:
// 获取滑块内部的Thumb控件
Track track = slider.Template.FindName("PART_Track", slider) as Track;
Thumb thumb = track?.Thumb;if (thumb != null)
{// 注册拖动开始事件thumb.DragStarted += Thumb_DragStarted;// 注册拖动进行中事件thumb.DragDelta += Thumb_DragDelta;// 注册拖动结束事件thumb.DragCompleted += Thumb_DragCompleted;
}
事件处理代码示例:
private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
{// 拖动开始时的处理statusTextBlock.Text = "开始拖动";
}private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{// 拖动过程中的处理Slider slider = (sender as Thumb)?.TemplatedParent as Slider;if (slider != null){statusTextBlock.Text = $"正在拖动: {slider.Value:F1}";}
}private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
{// 拖动结束时的处理statusTextBlock.Text = "拖动结束";
}
4. 样式和模板自定义
4.1 基本样式设置
可以通过设置Style
来自定义Slider的外观:
<Slider Width="300" Margin="10"><Slider.Style><Style TargetType="Slider"><Setter Property="Background" Value="LightGray"/><Setter Property="Foreground" Value="Blue"/><Setter Property="BorderBrush" Value="Gray"/><Setter Property="BorderThickness" Value="1"/></Style></Slider.Style>
</Slider>
4.2 控件模板自定义
要完全控制Slider的外观,可以通过重写ControlTemplate来实现:
<Slider Width="300" Margin="10"><Slider.Template><ControlTemplate TargetType="Slider"><Grid><Border x:Name="TrackBackground" Height="4" Background="#E0E0E0" CornerRadius="2"/><Border x:Name="PART_SelectionRange" Height="4" Background="#2196F3" HorizontalAlignment="Left" CornerRadius="2"/><Track x:Name="PART_Track"><Track.Thumb><Thumb Width="20" Height="20"><Thumb.Template><ControlTemplate TargetType="Thumb"><Ellipse Fill="#2196F3" Stroke="#1565C0" StrokeThickness="1"/></ControlTemplate></Thumb.Template></Thumb></Track.Thumb></Track></Grid><ControlTemplate.Triggers><Trigger Property="IsSelectionRangeEnabled" Value="True"><Setter TargetName="PART_SelectionRange" Property="Visibility" Value="Visible"/></Trigger><Trigger Property="IsSelectionRangeEnabled" Value="False"><Setter TargetName="PART_SelectionRange" Property="Visibility" Value="Collapsed"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Slider.Template>
</Slider>
注意:自定义模板时,必须保留名为PART_Track
和PART_SelectionRange
的元素,这些是Slider控件正常工作所必需的部分。
5. 数据绑定
5.1 绑定到ViewModel
Slider控件可以轻松地通过数据绑定与ViewModel集成:
<Slider Minimum="0" Maximum="100" Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"Width="300"/><TextBlock Text="{Binding SliderValue, StringFormat='当前值: {0:F1}'}" Margin="10"/>
对应的ViewModel代码:
public class MainViewModel : INotifyPropertyChanged
{private double _sliderValue;public double SliderValue{get => _sliderValue;set{if (_sliderValue != value){_sliderValue = value;OnPropertyChanged(nameof(SliderValue));}}}public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}
5.2 同步多个控件
可以使用数据绑定使多个控件同步显示相同的值:
<Slider x:Name="mainSlider" Minimum="0" Maximum="100" Value="{Binding Volume, Mode=TwoWay}"Width="300"/><ProgressBar Value="{Binding ElementName=mainSlider, Path=Value}" Maximum="{Binding ElementName=mainSlider, Path=Maximum}"Height="10" Width="300" Margin="0,10"/><TextBlock Text="{Binding ElementName=mainSlider, Path=Value, StringFormat='音量: {0:F0}%'}" Margin="10"/>
6. 实际应用示例
6.1 音量控制器
下面是一个简单的音量控制器实现:
<StackPanel Margin="20"><TextBlock Text="音量控制" FontWeight="Bold" Margin="0,0,0,10"/><!-- 音量图标 --><StackPanel Orientation="Horizontal" Margin="0,0,0,5"><Path Data="M0,5 L3,5 L7,1 L7,9 L3,5 Z" Fill="Black" Margin="0,0,5,0"/><!-- 音量滑块 --><Slider x:Name="volumeSlider" Minimum="0" Maximum="100" Value="50" Width="200"ValueChanged="VolumeSlider_ValueChanged"/><!-- 当前值显示 --><TextBlock Text="{Binding ElementName=volumeSlider, Path=Value, StringFormat='{0:F0}%'}" Width="40" VerticalAlignment="Center" Margin="5,0,0,0"/></StackPanel><!-- 静音切换 --><CheckBox x:Name="muteCheckBox" Content="静音" Checked="MuteCheckBox_Checked" Unchecked="MuteCheckBox_Unchecked"/>
</StackPanel>
private double previousVolume = 50;private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{// 这里可以调用系统API或媒体播放器的音量调节方法if (!muteCheckBox.IsChecked.Value){previousVolume = volumeSlider.Value;}
}private void MuteCheckBox_Checked(object sender, RoutedEventArgs e)
{// 保存当前音量并设置为0previousVolume = volumeSlider.Value;volumeSlider.Value = 0;
}private void MuteCheckBox_Unchecked(object sender, RoutedEventArgs e)
{// 恢复之前的音量volumeSlider.Value = previousVolume;
}
6.2 颜色选择器
使用多个Slider控制RGB颜色值:
<StackPanel Margin="20"><TextBlock Text="颜色选择器" FontWeight="Bold" Margin="0,0,0,10"/><!-- 红色滑块 --><StackPanel Orientation="Horizontal" Margin="0,5"><TextBlock Text="R:" Width="20"/><Slider x:Name="redSlider" Minimum="0" Maximum="255" Value="120" Width="200"ValueChanged="ColorSlider_ValueChanged"/><TextBlock Text="{Binding ElementName=redSlider, Path=Value, StringFormat='{0:F0}'}" Width="30" Margin="5,0,0,0"/></StackPanel><!-- 绿色滑块 --><StackPanel Orientation="Horizontal" Margin="0,5"><TextBlock Text="G:" Width="20"/><Slider x:Name="greenSlider" Minimum="0" Maximum="255" Value="180" Width="200"ValueChanged="ColorSlider_ValueChanged"/><TextBlock Text="{Binding ElementName=greenSlider, Path=Value, StringFormat='{0:F0}'}" Width="30" Margin="5,0,0,0"/></StackPanel><!-- 蓝色滑块 --><StackPanel Orientation="Horizontal" Margin="0,5"><TextBlock Text="B:" Width="20"/><Slider x:Name="blueSlider" Minimum="0" Maximum="255" Value="240" Width="200"ValueChanged="ColorSlider_ValueChanged"/><TextBlock Text="{Binding ElementName=blueSlider, Path=Value, StringFormat='{0:F0}'}" Width="30" Margin="5,0,0,0"/></StackPanel><!-- 颜色预览 --><Border x:Name="colorPreview" Height="50" Margin="0,10,0,0" BorderBrush="Black" BorderThickness="1"/><!-- 16进制颜色值 --><TextBlock x:Name="hexColorTextBlock" HorizontalAlignment="Center" Margin="0,5,0,0" FontFamily="Consolas"/>
</StackPanel>
private void ColorSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{// 创建色彩对象Color color = Color.FromRgb((byte)redSlider.Value,(byte)greenSlider.Value,(byte)blueSlider.Value);// 更新预览colorPreview.Background = new SolidColorBrush(color);// 更新16进制值显示hexColorTextBlock.Text = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
}
7. 范围滑块(RangeSlider)实现
WPF默认没有提供范围滑块控件,但我们可以通过自定义控件实现一个具有两个滑块的范围选择器。
7.1 基本实现思路
实现一个RangeSlider控件的基本思路是:
- 创建一个继承自Control的自定义控件
- 添加两个表示范围起始和结束的DependencyProperty
- 使用两个Thumb控件分别控制这两个值
- 通过XAML模板定义控件的外观
7.2 RangeSlider控件实现示例
以下是一个简化的RangeSlider实现:
// RangeSlider.cs
public class RangeSlider : Control
{static RangeSlider(){DefaultStyleKeyProperty.OverrideMetadata(typeof(RangeSlider), new FrameworkPropertyMetadata(typeof(RangeSlider)));}#region 依赖属性定义public static readonly DependencyProperty MinimumProperty =DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSlider),new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));public static readonly DependencyProperty MaximumProperty =DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSlider),new FrameworkPropertyMetadata(100.0, FrameworkPropertyMetadataOptions.AffectsRender));public static readonly DependencyProperty LowerValueProperty =DependencyProperty.Register("LowerValue", typeof(double), typeof(RangeSlider),new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender));public static readonly DependencyProperty UpperValueProperty =DependencyProperty.Register("UpperValue", typeof(double), typeof(RangeSlider),new FrameworkPropertyMetadata(80.0, FrameworkPropertyMetadataOptions.AffectsRender));#endregion#region 属性public double Minimum{get { return (double)GetValue(MinimumProperty); }set { SetValue(MinimumProperty, value); }}public double Maximum{get { return (double)GetValue(MaximumProperty); }set { SetValue(MaximumProperty, value); }}public double LowerValue{get { return (double)GetValue(LowerValueProperty); }set { SetValue(LowerValueProperty, Math.Max(Minimum, Math.Min(UpperValue, value))); }}public double UpperValue{get { return (double)GetValue(UpperValueProperty); }set { SetValue(UpperValueProperty, Math.Min(Maximum, Math.Max(LowerValue, value))); }}#endregion#region 事件public static readonly RoutedEvent LowerValueChangedEvent =EventManager.RegisterRoutedEvent("LowerValueChanged", RoutingStrategy.Bubble,typeof(RoutedPropertyChangedEventHandler<double>), typeof(RangeSlider));public static readonly RoutedEvent UpperValueChangedEvent =EventManager.RegisterRoutedEvent("UpperValueChanged", RoutingStrategy.Bubble,typeof(RoutedPropertyChangedEventHandler<double>), typeof(RangeSlider));public event RoutedPropertyChangedEventHandler<double> LowerValueChanged{add { AddHandler(LowerValueChangedEvent, value); }remove { RemoveHandler(LowerValueChangedEvent, value); }}public event RoutedPropertyChangedEventHandler<double> UpperValueChanged{add { AddHandler(UpperValueChangedEvent, value); }remove { RemoveHandler(UpperValueChangedEvent, value); }}#endregion// 其他必要的方法和事件处理
}
控件的XAML模板(通常放在Themes/Generic.xaml中):
<Style TargetType="{x:Type local:RangeSlider}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="{x:Type local:RangeSlider}"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><!-- 滑块轨道 --><Border x:Name="TrackBackground" Grid.Row="1" Height="4" Background="#E0E0E0" CornerRadius="2"Margin="10,0"/><!-- 选中范围 --><Border x:Name="SelectedRange" Grid.Row="1" Height="4" Background="#2196F3" CornerRadius="2"/><!-- 左侧滑块 --><Thumb x:Name="LowerThumb" Grid.Row="1" Width="16" Height="16"><Thumb.Template><ControlTemplate TargetType="Thumb"><Ellipse Fill="#2196F3" Stroke="#1565C0" StrokeThickness="1"/></ControlTemplate></Thumb.Template></Thumb><!-- 右侧滑块 --><Thumb x:Name="UpperThumb" Grid.Row="1" Width="16" Height="16"><Thumb.Template><ControlTemplate TargetType="Thumb"><Ellipse Fill="#2196F3" Stroke="#1565C0" StrokeThickness="1"/></ControlTemplate></Thumb.Template></Thumb></Grid></ControlTemplate></Setter.Value></Setter>
</Style>
7.3 RangeSlider控件的使用
在XAML中使用自定义的RangeSlider控件:
<local:RangeSlider Minimum="0" Maximum="100" LowerValue="20" UpperValue="80"Width="300"Margin="10"LowerValueChanged="RangeSlider_LowerValueChanged"UpperValueChanged="RangeSlider_UpperValueChanged"/><TextBlock x:Name="rangeTextBlock" Text="Range: 20 - 80" HorizontalAlignment="Center" Margin="10"/>
private void RangeSlider_LowerValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{UpdateRangeText();
}private void RangeSlider_UpperValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{UpdateRangeText();
}private void UpdateRangeText()
{var slider = sender as RangeSlider;if (slider != null){rangeTextBlock.Text = $"Range: {slider.LowerValue:F1} - {slider.UpperValue:F1}";}
}
8. 高级应用:可视化数据滑块
Slider控件可以与其他技术结合,创建更有吸引力和功能性的界面元素。下面是一个简单的可视化数据滑块示例:
<Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><!-- 数据可视化 --><Canvas x:Name="visualizationCanvas" Grid.Row="0" Height="100" Background="#F5F5F5"/><!-- 滑块 --><Slider x:Name="dataSlider" Grid.Row="1" Minimum="0" Maximum="100" Value="0"ValueChanged="DataSlider_ValueChanged"/>
</Grid>
private List<double> dataPoints;public MainWindow()
{InitializeComponent();// 生成示例数据点GenerateDataPoints();// 绘制数据可视化DrawDataVisualization();
}private void GenerateDataPoints()
{dataPoints = new List<double>();Random random = new Random();for (int i = 0; i < 100; i++){dataPoints.Add(random.NextDouble() * 80 + 10); // 值在10-90之间}
}private void DrawDataVisualization()
{visualizationCanvas.Children.Clear();double width = visualizationCanvas.ActualWidth;double height = visualizationCanvas.ActualHeight;if (width <= 0 || height <= 0 || dataPoints.Count == 0)return;double segmentWidth = width / dataPoints.Count;for (int i = 0; i < dataPoints.Count; i++){double value = dataPoints[i];double barHeight = height * (value / 100);Rectangle rect = new Rectangle{Width = segmentWidth - 1,Height = barHeight,Fill = new SolidColorBrush(Color.FromRgb(33, 150, 243)),Stroke = new SolidColorBrush(Colors.White),StrokeThickness = 0.5};Canvas.SetLeft(rect, i * segmentWidth);Canvas.SetBottom(rect, 0);visualizationCanvas.Children.Add(rect);}// 绘制当前位置指示器DrawPositionIndicator();
}private void DrawPositionIndicator()
{if (dataPoints.Count == 0)return;double width = visualizationCanvas.ActualWidth;double height = visualizationCanvas.ActualHeight;int index = (int)dataSlider.Value;if (index >= dataPoints.Count)index = dataPoints.Count - 1;double segmentWidth = width / dataPoints.Count;// 绘制位置指示线Line line = new Line{X1 = index * segmentWidth + segmentWidth / 2,Y1 = 0,X2 = index * segmentWidth + segmentWidth / 2,Y2 = height,Stroke = new SolidColorBrush(Colors.Red),StrokeThickness = 2};visualizationCanvas.Children.Add(line);// 显示当前值TextBlock textBlock = new TextBlock{Text = $"{dataPoints[index]:F1}",Foreground = new SolidColorBrush(Colors.Black),Background = new SolidColorBrush(Color.FromArgb(200, 255, 255, 255)),Padding = new Thickness(3)};Canvas.SetLeft(textBlock, index * segmentWidth);Canvas.SetTop(textBlock, height - 20);visualizationCanvas.Children.Add(textBlock);
}private void DataSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{DrawDataVisualization();
}
9. 注意事项和最佳实践
9.1 性能考虑
- 在频繁值变化的场景中,考虑节流(Throttling)或防抖(Debouncing)处理
ValueChanged
事件。 - 对于大型数据可视化,考虑使用虚拟化或分页加载技术以保持良好性能。
// 使用DispatcherTimer实现简单的节流效果
private DispatcherTimer _valueChangedTimer;
private double _lastValue;public MainWindow()
{InitializeComponent();_valueChangedTimer = new DispatcherTimer{Interval = TimeSpan.FromMilliseconds(100)};_valueChangedTimer.Tick += ValueChangedTimer_Tick;
}private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{_lastValue = e.NewValue;if (!_valueChangedTimer.IsEnabled){_valueChangedTimer.Start();}
}private void ValueChangedTimer_Tick(object sender, EventArgs e)
{_valueChangedTimer.Stop();// 执行实际的更新操作UpdateUI(_lastValue);
}
9.2 可访问性考虑
为了提升应用程序的可访问性,请考虑以下几点:
- 添加恰当的工具提示(ToolTip)以提供额外信息
- 确保控件可以通过键盘操作
- 考虑视觉障碍用户,使用足够的对比度和合适的大小
<Slider AutomationProperties.Name="Volume Control"AutomationProperties.HelpText="Adjust the volume from 0 to 100 percent"ToolTip="拖动调整音量"Minimum="0" Maximum="100" Width="300"><Slider.Resources><!-- 增加Thumb大小以便于点击 --><Style TargetType="Thumb"><Setter Property="Width" Value="24"/><Setter Property="Height" Value="24"/></Style></Slider.Resources>
</Slider>
9.3 响应式设计
在响应式设计中使用Slider时,可以结合其他布局控件实现良好的自适应布局:
<Grid><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><!-- 最小值标签 --><TextBlock Grid.Column="0" Text="0%" VerticalAlignment="Center"/><!-- 自适应宽度的滑块 --><Slider Grid.Column="1" Minimum="0" Maximum="100" Margin="10,0"/><!-- 最大值标签 --><TextBlock Grid.Column="2" Text="100%" VerticalAlignment="Center"/>
</Grid>
10. 总结
Slider控件是WPF中一个功能强大且灵活的UI元素,适用于各种需要在特定范围内选择值的场景。通过本文介绍的基本属性、事件处理、样式自定义和高级应用,您应该能够在实际应用中充分利用Slider控件的功能。对于需要范围选择的场景,自定义RangeSlider控件提供了更加丰富的用户交互体验。
11. 相关资源
- WPF Slider类官方文档
- WPF数据绑定概述
- Microsoft Learn - WPF UI设计
相关文章:

WPF之Slider控件详解
文章目录 1. 概述2. 基本属性2.1 值范围属性2.2 滑动步长属性2.3 刻度显示属性2.4 方向属性2.5 选择范围属性 3. 事件处理3.1 值变化事件3.2 滑块拖动事件 4. 样式和模板自定义4.1 基本样式设置4.2 控件模板自定义 5. 数据绑定5.1 绑定到ViewModel5.2 同步多个控件 6. 实际应用…...
极狐GitLab 如何将项目共享给群组?
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 共享项目和群组 (BASIC ALL) 在极狐GitLab 16.10 中,更改为在成员页面的成员选项卡上显示被邀请群组成员…...

企业微信自建消息推送应用
企业微信自建应用来推送消息 前言 最近有个给特定部门推送消息的需求,所以配置一个应用专门用来推送消息。实现过程大致为:服务器生成每天的报告,通过调用API来发送消息。以前一直都是发邮件,整个邮箱里全是报告文件,…...
【React】Hooks useReducer 详解,让状态管理更可预测、更高效
1.背景 useReducer是React提供的一个高级Hook,没有它我们也可以正常开发,但是useReducer可以使我们的代码具有更好的可读性,可维护性。 useReducer 跟 useState 一样的都是帮我们管理组件的状态的,但是呢与useState不同的是 useReducer 是集…...

日志之ClickHouse部署及替换ELK中的Elasticsearch
文章目录 1 ELK替换1.1 Elasticsearch vs ClickHouse1.2 环境部署1.2.1 zookeeper 集群部署1.2.2 Kafka 集群部署1.2.3 FileBeat 部署1.2.4 clickhouse 部署1.2.4.1 准备步骤1.2.4.2 添加官方存储库1.2.4.3 部署&启动&连接1.2.4.5 基本配置服务1.2.4.6 测试创建数据库和…...
亚远景-ASPICE vs ISO 21434:汽车软件开发标准的深度对比
ASPICE(Automotive SPICE)和ISO 21434是汽车软件开发领域的两大核心标准,分别聚焦于过程质量与网络安全。以下从核心目标、覆盖范围、实施重点、协同关系及行业价值五个维度进行深度对比分析: 一、核心目标对比 ASPICE࿱…...
51单片机快速成长路径
作为在嵌入式领域深耕18年的工程师,分享一条经过工业验证的51单片机快速成长路径,全程干货无注水: 一、突破认知误区(新手必看) 不要纠结于「汇编还是C」:现代开发90%场景用C,掌握指针和内存管…...
使用 NGINX 实现 HTTP Basic 认证ngx_http_auth_basic_module 模块
一、前言 在 Web 应用中,对部分资源进行访问控制是十分常见的需求。除了基于 IP 限制、JWT 验证、子请求校验等方式外,最经典也最简单的一种方式便是 HTTP Basic Authentication。NGINX 提供的 ngx_http_auth_basic_module 模块支持基于用户名和密码的基…...

解构与重构:自动化测试框架的进阶认知之旅
目录 一、自动化测试的介绍 (一)自动化测试的起源与发展 (二)自动化测试的定义与目标 (三)自动化测试的适用场景 二、什么是自动化测试框架 (一)自动化测试框架的定义 &#x…...

DockerDesktop替换方案
背景 由于DockerDesktop并非开源软件,如果在公司使用,可能就有一些限制,那是不是除了使用DockerDesktop外,就没其它办法了呢,现在咱们来说说替换方案。 WSL WSL是什么,可自行百度,这里引用WS…...

力扣热题100之搜索二维矩阵 II
题目 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性: 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 代码 方法一:直接全体遍历 这个方法很直接,但是居然没有超时,…...

docker操作镜像-以mysql为例
Docker安装使用-CSDN博客 docker操作镜像-以mysql为例 当安装一个新的镜像时可以登录https://hub.docker.com/直接搜索想要安装的镜像,查看文档 1)拉取镜像 docker pull mysql 或者 docker pull mysql:版本号 然后直接跳到第4)步即可 2…...

使用OpenCV 和 Dlib 进行卷积神经网络人脸检测
文章目录 引言1.准备工作2.代码解析2.1 导入必要的库2.2 加载CNN人脸检测模型2.3 加载并预处理图像2.4 进行人脸检测2.5 绘制检测结果2.6 显示结果 3.完整代码4.性能考虑5.总结 引言 人脸检测是计算机视觉中最基础也最重要的任务之一。今天我将分享如何使用dlib库中的CNN人脸检…...

React 实现 JWT 登录验证的最小可运行示例
下面是一个用 React 实现 JWT 登录验证的最小可运行示例,包含: React 前端:登录、保存 Token、获取用户数据。模拟后端:用 mock API(你也可以接真后端)。 🧱 技术栈 React(使用 Vi…...

Power Query精通指南1:查询结构设计、数据类型、数据导入与迁移(平面文件、Excel、Web)
文章目录 零、Power Query简介0.1 Power Query 主要功能0.2 Power Query 的优势0.3 Power Query 组件 一、Power Query数据处理基本流程1.1 前期准备1.2 提取1.3 转换1.3.1 Power Query 编辑器界面1.3.2 默认转换1.3.3 自定义转换 1.4 加载1.4.1 自动检测数据类型1.4.2 重命名查…...

vue2开发者sass预处理注意
vue2开发者sass预处理注意 sass的预处理器,早年使用node-sass,也就是vue2最初默认的编译器。 sass官方推出了dart-sass来替代。 node-sass已经停维很久了。 vue3默认使用的是dart-sass。 Uniapp的官方文档截图 从 HBuilderX 4.56 ,vue2 …...
淘宝按图搜索商品(拍立淘)Java 爬虫实战指南
在电商领域,按图搜索商品功能为用户提供了更直观、便捷的购物体验。淘宝的拍立淘功能更是凭借其强大的图像识别技术,成为许多开发者和商家关注的焦点。本文将详细介绍如何利用 Java 爬虫技术实现淘宝按图搜索商品功能,包括注册账号、上传图片…...
安卓基础(封装引用)
情况 1:普通 Java 项目(非 Android) src/ ├── com/ │ ├── example/ │ │ ├── utils/ │ │ │ └── A.java // 工具类 A │ │ └── main/ │ │ └── B.java // 主类 B A…...
深入理解 Docker 网络原理:构建高效、灵活的容器网络
在现代软件开发中,Docker 已经成为了容器化技术的代名词,广泛应用于开发、测试和生产环境。Docker 使得开发者能够将应用及其依赖打包成一个轻量级的容器,并通过 Docker 容器化技术来实现高效的部署与管理。 然而,在日常使用 Dock…...

使用 Selenium 爬取动态网页数据 —— 实战与坑点详解
本文记录了笔者在爬取网页数据过程中遇到的各种技术挑战,包括页面动态渲染、JavaScript 注入等问题,并最终给出一个可运行的完整方案。 文章目录 网页获取不到数据🚀 尝试用 Selenium 渲染页面 网页获取不到数据 某网页数据依赖大量 JavaSc…...
React 笔记[1] hello world
React 笔记[1] hello world 明白了!既然你已经安装了 Node.js,我们可以 从零开始搭建一个 React Tailwind CSS 的 Hello World 项目。我将一步步列出操作指令,你只需要在终端里依次执行。 ✅ 第一步:初始化项目 mkdir my-hello…...
Verilog Test Fixture 时钟激励
1、占空比50%时钟产生 always begin<clock> 1b0 ;#<PERIOD/2> ;<clock> 1b1 ;#<PERIOD/2> ; end reg <clock> 1b0 ;alwaysbegin#<PERIOD/2> ;<clock> ~<clock> ;end 2…...

守护数字家园:个人博客安全防护指南
前言 在之前的文章《WordPress个人博客搭建(一)》《WordPress个人博客搭建(二)》《WordPress个人博客搭建(三)》中,我们已经在非凡云云服务器上,借助1Panel搭建起属于自己的数字庭院…...

【网络编程】三、TCP网络套接字编程
文章目录 TCP通信流程Ⅰ. 服务器日志类实现Ⅱ. TCP服务端1、服务器创建流程2、创建套接字 -- socket3、绑定服务器 -- bind🎏4、服务器监听 -- listen🎏5、获取客户端连接请求 -- acceptaccept函数返回的套接字描述符是什么,不是已经有一个了…...

trae ai编程工具
Trae,致力于成为真正的 AI 工程师(The Real Al Engineer)。Trae 旗下的 AI IDE 产品,以智能生产力为核心,无缝融入你的开发流程,与你默契配合,更高质量、高效率完成每一个任务。 版本差异 国内…...
STM32系统定时器以及微秒延时函数分析
在CubeMX生成的工程中系统时钟节拍配置的函数为: __weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {HAL_StatusTypeDef status HAL_OK;/* Check uwTickFreq for MisraC 2012 (even if uwTickFreq is a enum type that doesnt take the value zero)…...

神经网络发展历程——积跬步至千里
神经网络类型层线性or非线性创新问题备注感知器单层线性模型,输出 1 1 1, − 1 -1 −1误差反馈学习阈值函数不可导,构造学习规则与感知器准则等价线性神经元单层线性模型梯度下降法训练参数线性函数,多层仍是线性变换本质上是最小…...
Java 24:重构数字信任边界 —— 后量子时代的智能安全防御体系构建
引言 在量子计算阴影与 AI 驱动攻击交织的网络安全新纪元,Java 平台正经历着自诞生以来最深刻的安全架构革新。作为企业级应用的核心基础设施,Java 24 不仅延续了 “一次编写,处处运行” 的跨平台基因,更以后量子密码学引擎、动态…...

荣耀A8互动娱乐组件部署实录(第2部分:界面逻辑与资源加载机制)
作者:从 Spine 骨骼动画里抠图三小时没睡的美术兼前端苦工 一、界面整体架构拆解 荣耀A8组件采用的是典型的分模块 UI 架构,即:主界面为入口容器,不同子页面(如商城、银行、客服、游戏入口)以逻辑功能划分…...

mac 使用 Docker 安装向量数据库Milvus独立版的保姆级别教程
Milvus 特点:开源的云原生向量数据库,支持多种索引类型和GPU加速,能够在亿级向量规模下实现低延迟高吞吐。具有灵活的部署选项和强大的社区支持。 适用场景:适合处理超大规模数据和高性能需求的应用,如图像搜索、推荐…...