当前位置: 首页 > news >正文

WPF之高级绑定技术

文章目录

    • 引言
    • 多重绑定(MultiBinding)
      • 基本概念
      • 实现自定义IMultiValueConverter
      • MultiBinding在XAML中的应用示例
      • 使用StringFormat简化MultiBinding
    • 优先级绑定(PriorityBinding)
      • 基本概念
      • PriorityBinding示例
      • 实现PriorityBinding的后台逻辑
    • 异步绑定(Asynchronous Binding)
      • 基本概念
      • 异步绑定示例
      • 异步绑定的后台实现
      • 异步绑定的注意事项
    • 延迟绑定(Delayed Binding)
      • 基本概念
      • 使用Delay属性
      • 延迟绑定的实际应用示例
      • 延迟绑定的ViewModel实现
    • 绑定群组(BindingGroup)
      • 基本概念
      • BindingGroup的应用示例
      • 实现PasswordMatchRule验证规则
      • BindingGroup的ViewModel实现
    • 绑定表达式(Binding Expressions)
      • 基本概念
      • 使用绑定表达式
      • 绑定表达式在XAML中的应用
      • 绑定表达式的ViewModel实现
    • 总结
    • 学习资源

可以根据Github拉取示例程序运行
GitHub程序演示地址(点击直达)
也可以在本文资源中下载
在这里插入图片描述

引言

WPF(Windows Presentation Foundation)的数据绑定系统是其最强大的特性之一,它允许开发者将UI元素与各种数据源无缝连接。基础绑定可以满足大多数场景需求,但在复杂应用程序开发中,我们常常需要更高级的绑定技术来解决特定问题。本文将深入探讨WPF中的高级绑定技术,包括多重绑定、优先级绑定、异步绑定等,帮助开发者充分利用WPF的数据绑定能力构建更强大、更灵活的应用程序。

数据源
绑定对象
目标对象
高级绑定技术
PriorityBinding
MultiBinding
异步绑定
延迟绑定
绑定群组
绑定表达式

多重绑定(MultiBinding)

基本概念

MultiBinding是一种将多个绑定源组合成单一值的强大技术。它通过值转换器(IMultiValueConverter)将多个源的值转换为一个可供目标属性使用的值。

绑定源1
MultiBinding
绑定源2
绑定源3
IMultiValueConverter
目标属性

实现自定义IMultiValueConverter

首先,我们需要实现IMultiValueConverter接口来定义如何将多个输入值转换为一个输出值:

using System;
using System.Globalization;
using System.Windows.Data;namespace WpfDemo.Converters
{// 将三个RGB值转换为一个颜色的转换器public class RgbToColorConverter : IMultiValueConverter{// 将多个值转换为一个值(从源到目标)public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture){try{// 检查是否有足够的值if (values.Length != 3)return null;// 解析RGB值(0-255)byte r = System.Convert.ToByte(values[0]);byte g = System.Convert.ToByte(values[1]);byte b = System.Convert.ToByte(values[2]);// 创建颜色对象return new System.Windows.Media.Color(){R = r,G = g,B = b,A = 255 // 不透明};}catch (Exception){// 转换失败时,返回默认颜色return System.Windows.Media.Colors.Black;}}// 将一个值转换回多个值(从目标到源)public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture){var color = (System.Windows.Media.Color)value;return new object[] { color.R, color.G, color.B };}}
}

MultiBinding在XAML中的应用示例

<Window x:Class="WpfDemo.MultiBindingDemo"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:conv="clr-namespace:WpfDemo.Converters"Title="MultiBinding示例" Height="300" Width="400"><Window.Resources><conv:RgbToColorConverter x:Key="RgbToColorConverter"/></Window.Resources><Grid Margin="10"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><!-- 红色滑块 --><TextBlock Grid.Row="0" Grid.Column="0" Text="红色:" VerticalAlignment="Center"/><Slider x:Name="sliderRed" Grid.Row="0" Grid.Column="1" Minimum="0" Maximum="255" Value="128"TickFrequency="1" IsSnapToTickEnabled="True"/><!-- 绿色滑块 --><TextBlock Grid.Row="1" Grid.Column="0" Text="绿色:" VerticalAlignment="Center"/><Slider x:Name="sliderGreen" Grid.Row="1" Grid.Column="1" Minimum="0" Maximum="255" Value="128"TickFrequency="1" IsSnapToTickEnabled="True"/><!-- 蓝色滑块 --><TextBlock Grid.Row="2" Grid.Column="0" Text="蓝色:" VerticalAlignment="Center"/><Slider x:Name="sliderBlue" Grid.Row="2" Grid.Column="1" Minimum="0" Maximum="255" Value="128"TickFrequency="1" IsSnapToTickEnabled="True"/><!-- 颜色预览矩形 --><Border Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,20,0,0" BorderThickness="1" BorderBrush="Black"><Border.Background><SolidColorBrush><SolidColorBrush.Color><!-- 使用MultiBinding绑定三个滑块的值 --><MultiBinding Converter="{StaticResource RgbToColorConverter}"><Binding ElementName="sliderRed" Path="Value"/><Binding ElementName="sliderGreen" Path="Value"/><Binding ElementName="sliderBlue" Path="Value"/></MultiBinding></SolidColorBrush.Color></SolidColorBrush></Border.Background><!-- 显示RGB值的文本 --><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"><TextBlock.Text><MultiBinding StringFormat="RGB({0:N0}, {1:N0}, {2:N0})"><Binding ElementName="sliderRed" Path="Value"/><Binding ElementName="sliderGreen" Path="Value"/><Binding ElementName="sliderBlue" Path="Value"/></MultiBinding></TextBlock.Text></TextBlock></Border></Grid>
</Window>

使用StringFormat简化MultiBinding

在上面的示例中,我们也展示了MultiBinding的另一个常用功能:使用StringFormat将多个值格式化为一个字符串,无需自定义转换器。

优先级绑定(PriorityBinding)

基本概念

PriorityBinding允许您指定多个绑定源,按优先级顺序尝试。当高优先级的绑定无法提供有效值或需要很长时间响应时,系统会回退到较低优先级的绑定。这对于创建响应式UI特别有用,可以先显示缓存或快速可用的数据,然后在获取到更新数据后再更新UI。

PriorityBinding
绑定1 - 高优先级
绑定2 - 中优先级
绑定3 - 低优先级
是否有值?
使用绑定1的值
绑定2是否有值?
使用绑定2的值
使用绑定3的值

PriorityBinding示例

<Window x:Class="WpfDemo.PriorityBindingDemo"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="PriorityBinding示例" Height="300" Width="400"><Grid Margin="20"><StackPanel><TextBlock Text="天气预报:" FontWeight="Bold" Margin="0,0,0,10"/><TextBlock><TextBlock.Text><PriorityBinding><!-- 高优先级绑定 - 从网络获取最新天气 --><Binding Path="WeatherForecast" Source="{StaticResource WeatherService}"IsAsync="True"/><!-- 中优先级绑定 - 使用缓存的天气数据 --><Binding Path="CachedWeatherForecast"Source="{StaticResource WeatherCache}"/><!-- 低优先级绑定 - 显示加载中信息 --><Binding Source="正在加载天气数据..."/></PriorityBinding></TextBlock.Text></TextBlock><Button Content="刷新天气" Margin="0,10,0,0"Command="{Binding RefreshWeatherCommand}"/></StackPanel></Grid>
</Window>

实现PriorityBinding的后台逻辑

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;namespace WpfDemo.ViewModels
{// 天气服务类public class WeatherService : INotifyPropertyChanged{private string _weatherForecast;// 实现INotifyPropertyChanged接口public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 天气预报属性public string WeatherForecast{get => _weatherForecast;private set{if (_weatherForecast != value){_weatherForecast = value;OnPropertyChanged();}}}// 刷新天气命令public ICommand RefreshWeatherCommand => new RelayCommand(async () => await FetchWeatherAsync());// 构造函数public WeatherService(){// 初始时为空,触发PriorityBinding回退到下一级_weatherForecast = null;}// 模拟从网络获取天气数据private async Task FetchWeatherAsync(){// 设置为null触发绑定回退WeatherForecast = null;// 模拟网络延迟await Task.Delay(3000);// 更新天气数据WeatherForecast = $"多云转晴,温度26°C,{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}更新";// 同时更新缓存WeatherCache.Instance.CachedWeatherForecast = WeatherForecast;}}// 天气缓存类public class WeatherCache : INotifyPropertyChanged{private static WeatherCache _instance;public static WeatherCache Instance => _instance ?? (_instance = new WeatherCache());private string _cachedWeatherForecast;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}public string CachedWeatherForecast{get => _cachedWeatherForecast;set{if (_cachedWeatherForecast != value){_cachedWeatherForecast = value;OnPropertyChanged();}}}private WeatherCache(){// 初始缓存数据_cachedWeatherForecast = "昨日天气:晴,温度24°C (缓存数据)";}}// 简单的命令实现public class RelayCommand : ICommand{private readonly Action _execute;private readonly Func<bool> _canExecute;public RelayCommand(Action execute, Func<bool> canExecute = null){_execute = execute ?? throw new ArgumentNullException(nameof(execute));_canExecute = canExecute;}public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;public void Execute(object parameter) => _execute();public event EventHandler CanExecuteChanged{add => CommandManager.RequerySuggested += value;remove => CommandManager.RequerySuggested -= value;}}
}

异步绑定(Asynchronous Binding)

基本概念

默认情况下,WPF数据绑定是同步的,这意味着如果数据源需要很长时间来获取值(如从数据库查询或网络请求),UI线程可能会被阻塞,导致应用程序暂时无响应。通过设置IsAsync="True",可以让绑定异步执行,避免阻塞UI线程。

UI线程 绑定系统 数据源 请求数据 获取数据 返回数据(UI线程被阻塞) 更新UI 在后台线程获取数据 UI线程继续响应 返回数据(后台线程) 在UI线程更新UI alt [同步绑定] [异步绑定] UI线程 绑定系统 数据源

异步绑定示例

<Window x:Class="WpfDemo.AsyncBindingDemo"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="异步绑定示例" Height="300" Width="400"><Grid Margin="20"><StackPanel><TextBlock Text="数据加载示例:" FontWeight="Bold" Margin="0,0,0,10"/><!-- 异步绑定示例 --><TextBlock Text="{Binding LongRunningOperation, IsAsync=True}"FontSize="14"/><!-- 显示加载中状态 --><TextBlock Margin="0,5,0,0"><TextBlock.Style><Style TargetType="TextBlock"><Setter Property="Text" Value="数据加载中,请稍候..."/><Style.Triggers><DataTrigger Binding="{Binding LongRunningOperation, IsAsync=True}" Value="{x:Null}"><Setter Property="Visibility" Value="Visible"/></DataTrigger><DataTrigger Binding="{Binding LongRunningOperation, IsAsync=True}" Value="{x:Static x:String.Empty}"><Setter Property="Visibility" Value="Collapsed"/></DataTrigger></Style.Triggers></Style></TextBlock.Style></TextBlock><Button Content="重新加载数据" Margin="0,20,0,0"Command="{Binding ReloadDataCommand}"/></StackPanel></Grid>
</Window>

异步绑定的后台实现

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;namespace WpfDemo.ViewModels
{public class AsyncBindingViewModel : INotifyPropertyChanged{private string _longRunningOperation;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 耗时操作的属性public string LongRunningOperation{get{// 首次访问时模拟耗时操作if (_longRunningOperation == null){// 这里会阻塞UI线程,但因为使用了IsAsync=True,所以不会导致UI无响应Task.Delay(3000).Wait(); // 模拟延迟_longRunningOperation = $"数据加载完成,时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}";}return _longRunningOperation;}private set{if (_longRunningOperation != value){_longRunningOperation = value;OnPropertyChanged();}}}// 重新加载数据的命令public ICommand ReloadDataCommand => new RelayCommand(() =>{// 设置为null触发重新加载_longRunningOperation = null;OnPropertyChanged(nameof(LongRunningOperation));});}
}

异步绑定的注意事项

  1. FallbackValue 和 TargetNullValue: 可以设置这些属性来提供在异步加载过程中或结果为null时显示的值
  2. 绑定错误处理: 使用PriorityBinding可以与异步绑定结合提供更好的用户体验
  3. 取消异步操作: WPF不提供直接取消异步绑定的方法,需要在数据源中实现取消逻辑
  4. UI更新: 异步绑定完成后,更新会在UI线程上进行,无需手动调用Dispatcher
<!-- 使用FallbackValue在加载期间显示占位符 -->
<TextBlock Text="{Binding LongRunningOperation, IsAsync=True, FallbackValue='加载中...'}"FontSize="14"/>

延迟绑定(Delayed Binding)

基本概念

延迟绑定是一种优化技术,用于推迟数据绑定的更新,直到用户完成输入或交互。这对于减少在用户输入过程中频繁更新绑定源(如在TextBox中输入文本时)特别有用。

用户 UI控件 绑定系统 数据源 输入文本 每次按键都更新 频繁更新源属性 等待用户停止输入 等待延迟后更新 减少更新次数 alt [没有延迟(默认)] [使用延迟] 用户 UI控件 绑定系统 数据源

使用Delay属性

在WPF中,可以通过设置Binding.Delay属性实现延迟绑定:

<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged, Delay=500}"/>

这会创建一个500毫秒的延迟,在用户停止输入500毫秒后才将值更新到数据源。

延迟绑定的实际应用示例

<Window x:Class="WpfDemo.DelayedBindingDemo"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="延迟绑定示例" Height="350" Width="500"><Grid Margin="20"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><!-- 标题 --><TextBlock Grid.Row="0" Text="搜索示例" FontWeight="Bold" FontSize="16" Margin="0,0,0,15"/><!-- 搜索框 - 使用延迟绑定 --><StackPanel Grid.Row="1" Orientation="Horizontal"><TextBlock Text="搜索:" VerticalAlignment="Center" Margin="0,0,10,0"/><TextBox Width="300" Padding="5,3"Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged, Delay=500}"/></StackPanel><!-- 搜索结果 --><Border Grid.Row="2" BorderBrush="LightGray" BorderThickness="1" Margin="0,15,0,0" Padding="10"><StackPanel><TextBlock><Run Text="当前搜索内容: "/><Run Text="{Binding SearchText}" FontWeight="Bold"/></TextBlock><TextBlock Text="搜索结果:" FontWeight="Bold" Margin="0,10,0,5"/><ListView ItemsSource="{Binding SearchResults}" MaxHeight="150"><ListView.ItemTemplate><DataTemplate><TextBlock Text="{Binding}"/></DataTemplate></ListView.ItemTemplate></ListView><TextBlock Margin="0,10,0,0"><TextBlock.Style><Style TargetType="TextBlock"><Setter Property="Text" Value="没有搜索结果"/><Style.Triggers><DataTrigger Binding="{Binding HasResults}" Value="True"><Setter Property="Visibility" Value="Collapsed"/></DataTrigger></Style.Triggers></Style></TextBlock.Style></TextBlock></StackPanel></Border></Grid>
</Window>

延迟绑定的ViewModel实现

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;namespace WpfDemo.ViewModels
{public class DelayedBindingViewModel : INotifyPropertyChanged{private string _searchText;private ObservableCollection<string> _searchResults;// 所有可能的项目private readonly string[] _allItems = new string[]{"苹果", "香蕉", "橙子", "草莓", "西瓜", "葡萄","菠萝", "桃子", "梨", "樱桃", "蓝莓", "柠檬","猕猴桃", "芒果", "石榴", "椰子", "无花果", "柿子"};public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 搜索文本属性public string SearchText{get => _searchText;set{if (_searchText != value){_searchText = value;OnPropertyChanged();// 当搜索文本变化时执行搜索PerformSearch();}}}// 搜索结果集合public ObservableCollection<string> SearchResults{get => _searchResults;private set{if (_searchResults != value){_searchResults = value;OnPropertyChanged();OnPropertyChanged(nameof(HasResults));}}}// 是否有搜索结果public bool HasResults => SearchResults != null && SearchResults.Count > 0;// 构造函数public DelayedBindingViewModel(){_searchText = string.Empty;_searchResults = new ObservableCollection<string>();}// 执行搜索private void PerformSearch(){// 创建新的集合避免修改现有集合导致的UI更新问题var results = new ObservableCollection<string>();// 如果搜索文本不为空if (!string.IsNullOrWhiteSpace(_searchText)){// 查找包含搜索文本的项目var filteredItems = _allItems.Where(i => i.Contains(_searchText)).OrderBy(i => i);// 添加到结果集合foreach (var item in filteredItems){results.Add(item);}}// 更新搜索结果SearchResults = results;}}
}

绑定群组(BindingGroup)

基本概念

BindingGroup是一个强大的WPF功能,它允许您将多个绑定组合在一起进行批量验证和更新。通过使用绑定群组,您可以实现以下功能:

  1. 对相关联的多个字段进行一次性验证
  2. 批量提交或取消多个绑定的更改
  3. 实现事务性数据更新
绑定1
BindingGroup
绑定2
绑定3
绑定4
批量验证
批量更新
批量取消

BindingGroup的应用示例

<Window x:Class="WpfDemo.BindingGroupDemo"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="绑定群组示例" Height="400" Width="500"><Window.Resources><!-- 定义验证规则 --><ControlTemplate x:Key="ValidationTemplate"><StackPanel Orientation="Horizontal"><AdornedElementPlaceholder/><TextBlock Foreground="Red" Margin="5,0,0,0" Text="!" VerticalAlignment="Center"ToolTip="{Binding/ErrorContent}"/></StackPanel></ControlTemplate></Window.Resources><Grid Margin="20"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><!-- 标题 --><TextBlock Grid.Row="0" Text="用户注册表单" FontWeight="Bold" FontSize="16" Margin="0,0,0,15"/><!-- 表单区域 --><Grid Grid.Row="1"><!-- 创建绑定群组 --><Grid.BindingGroup><BindingGroup Name="RegistrationForm" NotifyOnValidationError="True"><BindingGroup.ValidationRules><!-- 自定义验证规则 - 密码匹配验证 --><local:PasswordMatchRule ValidatesOnTargetUpdated="True"/></BindingGroup.ValidationRules></BindingGroup></Grid.BindingGroup><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><!-- 用户名 --><TextBlock Grid.Row="0" Grid.Column="0" Text="用户名:" Margin="0,5,10,5"VerticalAlignment="Center"/><TextBox Grid.Row="0" Grid.Column="1" Margin="0,5"Validation.ErrorTemplate="{StaticResource ValidationTemplate}"Text="{Binding User.Username, UpdateSourceTrigger=PropertyChanged}"/><!-- 电子邮箱 --><TextBlock Grid.Row="1" Grid.Column="0" Text="电子邮箱:" Margin="0,5,10,5"VerticalAlignment="Center"/><TextBox Grid.Row="1" Grid.Column="1" Margin="0,5"Validation.ErrorTemplate="{StaticResource ValidationTemplate}"Text="{Binding User.Email, UpdateSourceTrigger=PropertyChanged}"/><!-- 密码 --><TextBlock Grid.Row="2" Grid.Column="0" Text="密码:" Margin="0,5,10,5"VerticalAlignment="Center"/><PasswordBox x:Name="passwordBox" Grid.Row="2" Grid.Column="1" Margin="0,5"/><!-- 确认密码 --><TextBlock Grid.Row="3" Grid.Column="0" Text="确认密码:" Margin="0,5,10,5"VerticalAlignment="Center"/><PasswordBox x:Name="confirmPasswordBox" Grid.Row="3" Grid.Column="1" Margin="0,5"/><!-- 验证错误消息 --><TextBlock Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Foreground="Red" Margin="0,10,0,0"Text="{Binding ValidationError}"/></Grid><!-- 按钮区域 --><StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,15,0,0" HorizontalAlignment="Right"><Button Content="重置" Width="80" Margin="0,0,10,0"Command="{Binding ResetCommand}"/><Button Content="提交" Width="80" Command="{Binding SubmitCommand}"CommandParameter="{Binding BindingGroup, RelativeSource={RelativeSource FindAncestor, AncestorType=Grid}}"/></StackPanel></Grid>
</Window>

实现PasswordMatchRule验证规则

using System;
using System.Globalization;
using System.Windows.Controls;
using System.Windows.Data;namespace WpfDemo
{// 密码匹配验证规则public class PasswordMatchRule : ValidationRule{public override ValidationResult Validate(object value, CultureInfo cultureInfo){// 获取绑定组var bindingGroup = value as BindingGroup;if (bindingGroup == null)return new ValidationResult(false, "无效的绑定组");// 获取密码框的值var passwordBox = bindingGroup.Owner.FindName("passwordBox") as PasswordBox;var confirmPasswordBox = bindingGroup.Owner.FindName("confirmPasswordBox") as PasswordBox;if (passwordBox == null || confirmPasswordBox == null)return new ValidationResult(false, "无法找到密码框");// 获取密码string password = passwordBox.Password;string confirmPassword = confirmPasswordBox.Password;// 验证密码长度if (string.IsNullOrEmpty(password))return new ValidationResult(false, "密码不能为空");if (password.Length < 6)return new ValidationResult(false, "密码长度不能少于6个字符");// 验证密码匹配if (password != confirmPassword)return new ValidationResult(false, "两次输入的密码不匹配");// 验证通过return ValidationResult.ValidResult;}}
}

BindingGroup的ViewModel实现

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;namespace WpfDemo.ViewModels
{public class BindingGroupViewModel : INotifyPropertyChanged{private UserModel _user;private string _validationError;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 用户模型public UserModel User{get => _user;set{if (_user != value){_user = value;OnPropertyChanged();}}}// 验证错误消息public string ValidationError{get => _validationError;set{if (_validationError != value){_validationError = value;OnPropertyChanged();}}}// 提交命令public ICommand SubmitCommand => new RelayCommand<BindingGroup>(SubmitForm);// 重置命令public ICommand ResetCommand => new RelayCommand(ResetForm);// 构造函数public BindingGroupViewModel(){_user = new UserModel();_validationError = string.Empty;}// 提交表单private void SubmitForm(BindingGroup bindingGroup){try{// 清除之前的错误ValidationError = string.Empty;// 验证绑定组bool isValid = bindingGroup.ValidateWithoutUpdate();if (isValid){// 提交更改bindingGroup.CommitEdit();// 获取密码框的值var passwordBox = bindingGroup.Owner.FindName("passwordBox") as PasswordBox;string password = passwordBox?.Password ?? string.Empty;// 处理注册逻辑MessageBox.Show($"注册成功!\n用户名: {User.Username}\n邮箱: {User.Email}", "注册成功", MessageBoxButton.OK, MessageBoxImage.Information);}else{// 显示验证错误ValidationError = "表单验证失败,请检查输入";}}catch (Exception ex){ValidationError = $"提交表单出错: {ex.Message}";}}// 重置表单private void ResetForm(){User = new UserModel();ValidationError = string.Empty;// 重置密码框(需要在代码后台处理)}}// 用户模型public class UserModel : INotifyPropertyChanged{private string _username;private string _email;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}public string Username{get => _username;set{if (_username != value){_username = value;OnPropertyChanged();}}}public string Email{get => _email;set{if (_email != value){_email = value;OnPropertyChanged();}}}public UserModel(){_username = string.Empty;_email = string.Empty;}}
}

绑定表达式(Binding Expressions)

基本概念

在WPF中,每个绑定都由一个BindingExpression对象表示,它提供了对绑定的底层控制。通过访问和操作BindingExpression,开发者可以实现更高级的绑定功能,如:

  1. 手动触发绑定更新
  2. 获取绑定的错误信息
  3. 清除绑定错误
  4. 强制重新评估绑定
Binding
BindingExpression
UpdateSource
UpdateTarget
GetValidationErrors
ValidateWithoutUpdate

使用绑定表达式

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;namespace WpfDemo
{public partial class BindingExpressionDemo : Window{public BindingExpressionDemo(){InitializeComponent();}// 手动更新源private void UpdateSourceButton_Click(object sender, RoutedEventArgs e){// 获取TextBox的绑定表达式TextBox textBox = nameTextBox;BindingExpression bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);// 如果绑定存在,手动更新源if (bindingExpression != null){// 将TextBox的当前值推送到绑定源bindingExpression.UpdateSource();// 可以检查验证错误if (Validation.GetHasError(textBox)){var errors = Validation.GetErrors(textBox);MessageBox.Show($"验证错误: {errors[0].ErrorContent}");}}}// 手动更新目标private void UpdateTargetButton_Click(object sender, RoutedEventArgs e){TextBox textBox = nameTextBox;BindingExpression bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);if (bindingExpression != null){// 从绑定源获取最新值,更新到TextBoxbindingExpression.UpdateTarget();}}// 清除验证错误private void ClearErrorsButton_Click(object sender, RoutedEventArgs e){TextBox textBox = nameTextBox;// 移除所有验证错误Validation.ClearInvalid(textBox.GetBindingExpression(TextBox.TextProperty));}}
}

绑定表达式在XAML中的应用

<Window x:Class="WpfDemo.BindingExpressionDemo"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="绑定表达式示例" Height="300" Width="450"><Grid Margin="20"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><!-- 标题 --><TextBlock Grid.Row="0" Text="绑定表达式控制" FontWeight="Bold" FontSize="16" Margin="0,0,0,15"/><!-- 输入字段 --><StackPanel Grid.Row="1"><TextBlock Text="姓名:" Margin="0,0,0,5"/><TextBox x:Name="nameTextBox" Padding="5,3"Text="{Binding PersonName, UpdateSourceTrigger=Explicit, ValidatesOnDataErrors=True}"/></StackPanel><!-- 按钮区域 --><StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,15,0,0"><Button Content="更新源 (UpdateSource)" Width="160" Margin="0,0,10,0"Click="UpdateSourceButton_Click"/><Button Content="更新目标 (UpdateTarget)" Width="160" Margin="0,0,10,0"Click="UpdateTargetButton_Click"/><Button Content="清除错误" Width="80"Click="ClearErrorsButton_Click"/></StackPanel><!-- 信息显示区域 --><Border Grid.Row="3" BorderBrush="LightGray" BorderThickness="1" Margin="0,15,0,0" Padding="10"><StackPanel><TextBlock><Run Text="当前源值: "/><Run Text="{Binding PersonName}" FontWeight="Bold"/></TextBlock><TextBlock Margin="0,10,0,0" Text="操作说明:" FontWeight="Bold"/><TextBlock TextWrapping="Wrap" Margin="0,5,0,0">1. 修改文本框内容 (不会立即更新源值,因为UpdateSourceTrigger=Explicit)<br/>2. 点击"更新源"按钮手动将值更新到数据源<br/>3. 点击"更新目标"按钮从数据源重新加载值到文本框<br/>4. 点击"清除错误"移除验证错误</TextBlock></StackPanel></Border></Grid>
</Window>

绑定表达式的ViewModel实现

using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;namespace WpfDemo.ViewModels
{public class BindingExpressionViewModel : INotifyPropertyChanged, IDataErrorInfo{private string _personName;public event PropertyChangedEventHandler PropertyChanged;protected void OnPropertyChanged([CallerMemberName] string propertyName = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 人员姓名属性public string PersonName{get => _personName;set{if (_personName != value){_personName = value;OnPropertyChanged();}}}// 构造函数public BindingExpressionViewModel(){_personName = "张三";}// IDataErrorInfo接口实现 - 验证public string Error => null;public string this[string columnName]{get{if (columnName == nameof(PersonName)){if (string.IsNullOrWhiteSpace(PersonName))return "姓名不能为空";if (PersonName.Length < 2)return "姓名长度不能少于2个字符";}return null;}}}
}

总结

在WPF应用程序开发中,掌握高级绑定技术可以大幅提升UI与数据交互的灵活性和性能。本文详细介绍了六种关键的高级绑定技术:

  1. 多重绑定(MultiBinding):将多个绑定源组合为单一目标值,通过IMultiValueConverter实现复杂的数据转换。

  2. 优先级绑定(PriorityBinding):按优先级顺序尝试多个绑定源,在长时间操作过程中提供更好的用户体验。

  3. 异步绑定(Asynchronous Binding):通过IsAsync属性将耗时操作移至后台线程,避免阻塞UI线程。

  4. 延迟绑定(Delayed Binding):使用Delay属性推迟数据绑定更新,减少频繁输入时的性能开销。

  5. 绑定群组(BindingGroup):将多个绑定组合在一起进行批量验证和更新,实现事务性数据处理。

  6. 绑定表达式(Binding Expressions):提供对绑定的底层控制,包括手动触发更新、处理验证错误等。

这些高级绑定技术能够帮助开发者构建更高效、更灵活的WPF应用程序。根据具体的需求场景,选择合适的绑定技术可以显著改善应用程序的性能和用户体验。

基础绑定
高级绑定技术
更强的灵活性
更好的性能
更佳的用户体验
更简洁的代码

学习资源

以下是一些有关WPF高级绑定技术的学习资源,可以帮助您进一步深入了解本文所介绍的概念:

  1. Microsoft Docs - 数据绑定概述

  2. Microsoft Docs - 绑定声明

  3. Microsoft Docs - 多重绑定

  4. Microsoft Docs - 异步绑定

  5. Microsoft Docs - 绑定群组

  6. CodeProject - WPF中的高级数据绑定技术

  7. GitHub - WPF绑定示例集合

相关文章:

WPF之高级绑定技术

文章目录 引言多重绑定&#xff08;MultiBinding&#xff09;基本概念实现自定义IMultiValueConverterMultiBinding在XAML中的应用示例使用StringFormat简化MultiBinding 优先级绑定&#xff08;PriorityBinding&#xff09;基本概念PriorityBinding示例实现PriorityBinding的后…...

k8s高可用集群,自动化更新证书脚本

#!/bin/bash # 切换到证书目录 cd /etc/kubernetes/pki || exit # 备份原有证书&#xff08;重要&#xff01;&#xff09; sudo cp -r apiserver.crt apiserver.key \ apiserver-etcd-client.crt apiserver-etcd-client.key \ apiserver-kubelet-client…...

【Python 函数】

Python 中的函数&#xff08;Function&#xff09;是可重复使用的代码块&#xff0c;用于封装特定功能并提高代码复用性。以下是函数的核心知识点&#xff1a; 一、基础语法 1. 定义函数 def greet(name):"""打印问候语""" # 文档字符串&…...

Filecoin矿工资金管理指南:使用lotus-shed actor withdraw工具

Filecoin矿工资金管理指南&#xff1a;使用lotus-shed actor withdraw工具 引言lotus-shed actor withdraw命令概述命令语法参数选项详解常见使用场景1. 提取全部可用余额2. 提取指定数量的FIL3. 通过受益人地址发送交易 最佳实践资金安全管理操作流程优化 常见问题与解决方案提…...

AI辅助DevOps与自动化测试:重构软件工程效率边界

随着AI技术渗透至软件开发生命周期&#xff0c;DevOps与自动化测试领域正经历颠覆性变革。本文系统性解析AI在需求分析、测试用例生成、部署决策、异常检测等环节的技术实现路径&#xff0c;结合微软Azure DevOps、Tesla自动驾驶测试等典型场景&#xff0c;探讨AI如何突破传统效…...

css内容省略——text-overflow: ellipsis

title: css内容省略 date: 2025-05-07 19:41:17 tags: css text-overflow: ellipsis text-overflow: ellipsis用于在文本溢出容器时显示省略号(…) 1.单行省略 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"&g…...

nginx性能优化与深度监控

一、性能调优方向 1. 系统层面优化 内核参数调整 TCP队列与连接管理&#xff1a; net.core.somaxconn&#xff08;最大连接队列长度&#xff0c;建议设为65535&#xff09;net.ipv4.tcp_max_syn_backlog&#xff08;SYN队列长度&#xff0c;建议65535&#xff09;net.ipv4.tc…...

leetcode 70.爬楼梯(c++详细最全解法+补充知识)

目录 题目 解答过程 补充哈希表知识 哈希表基本特性 常用成员函数 基本用法 实现代码 1.递归 2.循环遍历 3.哈希表 题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#…...

护照阅读器简介

护照阅读器简介 护照阅读器&#xff08;Passport Reader&#xff09;是一种专用设备&#xff0c;用于快速、准确地读取护照、身份证、签证等旅行证件的机读区&#xff08;MRZ&#xff09;和芯片&#xff08;ePassport&#xff09;信息&#xff0c;广泛应用于出入境管理、机场安…...

切片和边缘计算技术分析报告

切片和边缘计算技术分析报告 一、引言 随着 5G 通信技术的快速发展&#xff0c;网络切片和边缘计算技术逐渐成为通信领域的热点研究方向。网络切片技术通过将物理网络划分为多个逻辑上的虚拟网络&#xff0c;以满足不同业务场景对网络性能的差异化需求。边缘计算则将计算、存…...

vue3笔记(自存)

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…...

多线服务器具有什么优势

在当今数字化飞速发展的时代&#xff0c;多线服务器宛如一位低调的幕后英雄&#xff0c;默默为我们的网络世界提供着强大的支持。那么&#xff0c;多线服务器到底具有哪些令人瞩目的优势呢 首先&#xff0c;多线服务器的最大优势之一就是网络访问的高速与稳定。想象一下&#x…...

Azure OpenAI 聊天功能全解析:Java 开发者指南

Azure OpenAI 聊天功能全解析&#xff1a;Java 开发者指南 前言 在当今人工智能飞速发展的时代&#xff0c;AI 驱动的文本生成技术正深刻改变着众多应用场景。Azure OpenAI 作为这一领域的重要参与者&#xff0c;由 ChatGPT 提供支持&#xff0c;不仅具备传统 OpenAI 的功能&…...

【情感关系】健全自我

一些看到后深有感触的文字 请大家无论如何也不要相信这种&#xff1a;“童年/原生家庭经历决定人生走向”的论调。 过去可以影响我们但是无法主宰我们&#xff0c;人是有主观能动意识的&#xff0c;认识自己的问题就是改变人生轨迹的第一步。 后来我们会发现&#xff0c;对于…...

SLAM:单应矩阵,本质矩阵,基本矩阵详解和对应的c++实现

单应矩阵(Homography Matrix) 单应矩阵(Homography Matrix)是计算机视觉中描述同一平面在不同视角下投影变换的核心工具,广泛应用于图像校正、拼接、虚拟广告牌替换等场景。以下从原理、求解方法和C++实现三方面展开详解: 一、单应矩阵的数学原理 定义与作用 单应矩阵是…...

数据报(Datagram)与虚电路(Virtual Circuit)的区别

数据报&#xff08;Datagram&#xff09;与虚电路&#xff08;Virtual Circuit&#xff09;的区别 数据报和虚电路是计算机网络中两种不同的通信方式&#xff0c;主要区别体现在 连接方式、路由选择、可靠性、延迟和适用场景 等方面。以下是它们的详细对比&#xff1a; 1. 基本…...

工业现场ModbusTCP转EtherNETIP网关引领生物现场领新浪潮

生物质发生器是一种能够产生、培养生物的设备。客户现场需要将生物发生器连接到罗克韦尔系统&#xff0c;但是二者协议无法直接通讯&#xff0c;需要通过ModbusTCP转Ethernet/IP网关将两者进行通讯连接&#xff0c;生物质发生器以其独特的工作原理和优势&#xff0c;使得生物的…...

DeepSeek的100个应用场景

在春节前夕&#xff0c;浙江杭州的AI企业DeepSeek推出了其开源模型DeepSeek-R1&#xff0c;以仅相当于Open AI最新模型1/30的训练成本&#xff0c;在数学、编程等关键领域展现出媲美GPT-o1的出色性能。发布仅数日&#xff0c;DeepSeek-R1便迅速攀升至中美两国苹果应用商店免费榜…...

【Linux 系统调试】Linux 调试工具strip使用方法

‌ 目录 ‌ 一. strip 工具的定义与核心作用‌ ‌1. strip 是什么&#xff1f;‌ 2. strip 工具调试符号的作用‌ 3. strip 工具调试符号的重要性‌ 二. 如何确认文件是否被 strip 处理&#xff1f;‌ 1. 通过 file 命令检查文件状态 2. strip 的典型用法‌ ‌基础命…...

Solana批量转账教程:提高代币持有地址和生态用户空投代币

前言 Solana区块链因其高吞吐量和低交易费用成为批量操作&#xff08;如空投&#xff09;的理想选择。本教程将介绍几种在Solana上进行批量转账的方法&#xff0c;帮助您高效地向多个地址空投代币。 solana 账户模型 在Solana中有三类账户&#xff1a; 数据账户&#xff0c;…...

leetcode hot100 技巧

如有缺漏谬误&#xff0c;还请批评指正。 1.只出现一次的数字 利用异或运算相同得0的特点。所有出现过两次的数字都会在异或运算累加过程中被抵消。、 class Solution { public:int singleNumber(vector<int>& nums) {int res0;for(int i0;i<nums.size();i) re…...

搭建spark伪分布集群

1.先查看虚拟机的默认名称&#xff0c;将其修改为vm01 2.更改了主机名&#xff0c;还需要修改/etc/hosts文件&#xff0c;在这个文件设定了IP地址与主机名的对应关系&#xff0c;类似DNS域名服务器的功能 3.修改spark相关配置文件&#xff0c;包括spark-env.sh和slave两个文件 …...

vue3自定义audio音频播放【进度条,快进,后退,音量加减,播放速度】

本文将介绍如何使用Vue3构建一个功能完备的自定义音频播放器&#xff0c;包含进度条控制、快进/后退、音量调节和播放速度控制等功能。相比使用浏览器默认的audio控件&#xff0c;自定义播放器可以提供更一致的用户体验和更灵活的设计空间&#xff0c;复制粘贴即可使用&#xf…...

学习基本开锁知识

本文主要内容 目前市面上锁的种类有哪些 机械锁 钥匙开锁 &#xff1a;这是最常见的传统开锁方式&#xff0c;通过插入匹配的钥匙转动来开锁&#xff0c;如常见的家用门锁、汽车门锁等&#xff0c;钥匙形状和齿纹各异&#xff0c;有单排齿的一字锁、双排齿的双面锁&#xff0c;…...

泛微ECOLOGY9 流程表单中添加按钮的方法

使用场景介绍 有时需要在流程表单中添加一个按钮来实现弹窗、打开指定的页面等需求。 实现方式一:通过ID 在流程表单中想要生成按钮的地方指定一个ID,然后再到ecode中创建按钮及方法。 具体步骤如下: 一、表单中指定ID为 exceldc 二、在ecode中实现按钮及功能。 1.建立…...

小刚说C语言刷题—1331 做彩纸花边

1.题目描述 李晓芳用一条长为 n 米的彩纸制作花边&#xff0c;每朵花李晓芳用一条长为 n 米的彩纸制作花边&#xff0c;每朵花的宽度为 x 厘米&#xff0c;花与花之间的间隔为 y 厘米。请问 n 米的彩纸最多能做多少朵花的花边。 如&#xff0c;图中的案例花的宽度为 4.5cm &a…...

【Python】读取excel文件的时候,遇到“Excel file format cannot be determined”的问题

使用os.path 读取路径下的文件&#xff0c;并拼接文件名&#xff0c;可能会遇到这个问题&#xff1a; ValueError: Excel file format cannot be determined, you must specify an engine manually. 因为我用的是相对路径的拼接的方法&#xff0c;读取出来会有这样的问题&#…...

天气预报、天气查询API接口文档 | 实时天气 | 七日天气 | 15日天气查询

天气预报、天气查询API接口文档 | 实时天气 | 七日天气 | 15日天气查询 这篇文章详细介绍了一种天气查询服务&#xff0c;提供了实时天气(1天)、7天预报和15天预报三个RESTful接口&#xff0c;支持通过地区名称、编码、IP或经纬度等多种方式查询&#xff0c;返回数据包含温度、…...

Linux中的线程安全与线程同步详解

在Linux系统中&#xff0c;线程安全性是指在多个线程同时访问共享资源时&#xff0c;能够确保这些共享资源不被破坏或者产生数据错误。线程同步是一种机制&#xff0c;用于保证多个线程之间的操作次序和协调&#xff0c;以避免竞态条件、死锁等问题。 以下是线程安全和线程同步…...

qwen2.5vl

多模态大模型通用架构&#xff1a; 在通用的MM-LLM&#xff08;Multi-Modality LLM&#xff09;框架里&#xff0c;共有五个模块&#xff0c;整体以LLM为核心主干&#xff0c;分别在前后有一个输入、输出的投影模块&#xff08;Projector&#xff09;&#xff0c;投影模块主要…...

国产Word处理控件Spire.Doc教程:在Java中为Word文本和段落设置边框

在 Word 文档中添加边框是一种突显重点信息的有效方式&#xff0c;尤其适用于包含大量文本的内容场景。相比普通格式&#xff0c;给字符或段落添加边框不仅能强化视觉层次&#xff0c;还能提升文档的专业感与可读性。E-iceblue旗下Spire系列产品是国产文档处理领域的优秀产品&a…...

【CUDA C实战演练】CUDA介绍、安装、C代码示例

文章目录 0. 前言1. 并行计算与异构计算1.1 并行计算&#xff08;Parallel Computing&#xff09;1.2 异构计算&#xff08;Heterogeneous Computing&#xff09; 2. CUDA 的核心概念2.1 主机&#xff08;Host&#xff09;与设备&#xff08;Device&#xff09;2.2 线程层次结构…...

滑动窗口——无重复字符最长的字串

题目&#xff1a; 子字符串&#xff0c;我们也可以看成子数组。 题意不难理解&#xff0c;这个题我们暴力枚举的思路是把每一个字符遍历存到hash桶中&#xff0c;如果放两次就进行结果更新。 但这个题我们有更优化的方法&#xff0c;利用数组代替hash&#xff08;重点不在这&…...

QT中connect高级链接——指针、lambda、宏

1、connect使用指针 connect(button,&QPushButton::released,this,&MainWidget::mySlot); //【抬起】按钮button时&#xff0c;修改按钮b2的标题 2、使用lambda表达式 引入lambda表达式&#xff0c;类似内联函数&#xff0c;可以用于不会被重用的短代码片段&#x…...

说说es配置项的动态静态之分和集群配置更新API

这天因为某件工作来到了es官网某个参数配置相关的页面&#xff0c;注意到了下图圆圈里的“Dynamic”&#xff1a; 链接&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/8.1/modules-cluster.html#misc-cluster-settings 显然这是对配置项的一个描述&am…...

如何有效防御服务器DDoS攻击

分布式拒绝服务&#xff08;DDoS&#xff09;攻击通过大量恶意流量淹没服务器资源&#xff0c;导致服务瘫痪。本文将提供一套结合代码实现的主动防御方案&#xff0c;涵盖流量监控、自动化拦截和基础设施优化。 1. 实时流量监控与告警 目标&#xff1a;检测异常流量并触发告警…...

C#上传文件到腾讯云的COS

测试环境&#xff1a; vs2022 .net 6控制台应用程序 测试步骤如下&#xff1a; 1 添加子用户&#xff0c;目前是为了拿到secretId和secretKey&#xff0c;打开添加子用户界面链接&#xff1a;https://console.cloud.tencent.com/cam 并为子用户添加API 密钥 2 通过链接htt…...

强缓存与协商缓存的实现机制

文章目录 前言**1. 强缓存&#xff08;强制缓存&#xff09;****强缓存生效流程**&#xff1a;**2. 协商缓存&#xff08;对比缓存&#xff09;****协商缓存生效流程**&#xff1a;**对比总结****实际应用建议** **1. 缓存配置的三种主要实现方式** 前言 强缓存与协商缓存的实…...

【云备份】项目展示项目总结

目录 一. 项目展示 二. 项目总结 一. 项目展示 首先我们打开服务端&#xff0c;把没有用的东西都删干净&#xff0c;包括备份的文件信息啊什么的 这个时候我们启动服务器 我们先用浏览器去看看 什么东西都没有。 好&#xff0c;我们现在去启动客户端&#xff0c;下面这个是客…...

嵌入式 Linux Platform 驱动模型测试

文章目录 一、为什么要用 Platform 驱动模型&#xff1f; 二、Platform 驱动模型的三大核心组件 1.Platform 总线&#xff08;虚拟总线&#xff09; 2.Platform 设备&#xff08;platform_device&#xff09; 3.Platform 驱动&#xff08;platform_driver&#xff09; 三、Plat…...

Linux:web服务nginx

一.Nginx简介 Nginx (engine x) 是一个高性能的Web和反向代理服务器&#xff0c;同时也是一个 IMAP/POP3/SMTP 代理服器。Nginx处理高并发能力是十分强大的&#xff0c;能经受高负载的考验。而且支持热部署&#xff0c;几乎可以做到 7 * 24 小时不间断运行&#xff0c;即使运行…...

【“星睿O6”评测】Armv9.2a、KLEIDIAI及vulkan加速llamacpp部署本地AI

llamacpp 简介 llama.cpp 的主要目标是通过最小的设置&#xff0c;实现 LLM 推理&#xff0c;在各种硬件上&#xff08;无论是本地还是云端&#xff09;提供最先进的性能。 纯 C/C实现&#xff0c;无任何依赖苹果 M1/M2 芯片&#xff08;Apple silicon&#xff09;优化&#…...

Advanced Installer 22.5打包windows 安装包

Advanced Installer 22.5打包windows 安装包 1、打开Advanced Installer 22.5打包工具&#xff0c;绿色免安装下载链接&#xff1a;https://download.csdn.net/download/LongtengGensSupreme/90778405 2、选择需要打包的文件 3、设置安装文件夹权限 4、安装参数设置 5、构建设…...

Qt界面设计时窗口中各控件布局及自适应方法

在进行Qt界面设计时,往往会因为控件的布局以及伴随窗口拉伸时控件没有做好自适应部署而导致界面效果大打折扣。 这里简单介绍一下QtDesigner实现界面自适应布局,包括水平布局、垂直布局、栅格布局应用,确保界面元素随窗口变化自动调整。 图1 如图所示,这是我们的设计目标…...

添加地形与自定义地形

在先前我们设置的地图中,放大后不难发现这是2d平面 此时我们可以使用cesium自带的方法对图层进行修改 terrainProvider:Cesium.createWorldTerrainAsync(_) 修改后重新打开地图明显有了3d效果 还可以自定义添加水纹效果 requestWaterMask: true, // 可选&#xff1a;请求水面掩…...

livedata使用,完整的livedata的Demo

完整 LiveData Demo&#xff08;Java 实现&#xff09; 下面是一个可直接运行的完整 LiveData 示例&#xff0c;包含 ViewModel、LiveData 的基本使用以及与 UI 的交互。 项目结构 app/ ├── java/ │ ├── com.example.livedatademo/ │ │ ├── MainActivity.java │ …...

SpringBoot+Vue全栈的一个简单入门案例

目录 开发环境 Github开源项目 入门一&#xff1a;跑起别人的开源项目 跑起前端Vue 跑起后端SpringBoot 运行效果 入门二&#xff1a;将HTML模板转成Vue组件 下载一个免费的HTML模板 通过Trae将HTML转成Vue组件 将生成Vue组件增加为Vue的一个路由节点 运行Vue看看效…...

Nginx 性能调优与深度监测全攻略

目录 Nginx 性能调优与深度监测全攻略 一、引言 二、Nginx 核心性能调优策略 2.1 基础参数优化 2.1.1 worker 进程配置 2.1.2 事件驱动模型优化 2.2 缓存与资源优化 2.2.1 静态资源缓存 2.2.2 反向代理缓存 2.3 网络与连接优化 2.3.1 TCP 参数调整 2.3.2 Keep-Aliv…...

bad interpreter: No such file or directory

shell脚本加sqlite插入语句&#xff0c;内容如下&#xff1a; #!/bin/bash # 数据库文件路径 DB_FILE"/opt/ecu/db/Test.db" # 检查数据库文件所在目录是否存在&#xff0c;若不存在则创建 DB_DIR$(dirname "$DB_FILE") if [ ! -d "$DB_FILE" ]…...

python安装pyqt5失败

记录 PyQt5不支持3.10及以上版本&#xff0c;pip install PyQt5时报错raise PyProjectOptionException(qmake 详细错误信息如下&#xff1a; $ pip install PyQt5 Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple Collecting pyqt5Using cached https://pypi…...