WPF设计学习记录滴滴滴5
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<CheckBox x:Name="checkbox" Content="WPF中文网" Height="30" Margin="5" ToolTip="WPF中文网之控件课程"/>
<Popup Name="myPopup"
IsOpen="{Binding IsChecked,ElementName = checkbox}"
PlacementTarget="{Binding ElementName=checkBox}"
StaysOpen="True">
<Border BorderThickness="1" Background="LightBlue">
<StackPanel>
<TextBlock Text="官方网站" FontWeight="Bold"/>
<TextBlock Text="点击这个按钮,进入WPF中文网站"/>
<Border BorderBrush="Silver" BorderThickness="0 1 0 0" Margin="0 4"/>
<TextBlock Text="http://www.wpfsoft.com" FontStyle="Italic"/>
</StackPanel>
</Border>
</Popup>
</StackPanel>
<!-- 水平排列的容器,用于承载复选框和弹出框 -->
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<!-- 复选框控件,作为弹出框的触发器 -->
<CheckBox x:Name="checkbox"
Content="WPF中文网"
Height="30"
Margin="5"
ToolTip="WPF中文网之控件课程"/> <!-- 鼠标悬停提示 -->
<!-- 弹出框控件,与复选框状态绑定 -->
<Popup Name="myPopup"
IsOpen="{Binding IsChecked, ElementName=checkbox}" <!-- 绑定复选框的选中状态控制弹出 -->
PlacementTarget="{Binding ElementName=checkBox}" <!-- 指定弹出定位目标(注意大小写问题) -->
StaysOpen="True"> <!-- 保持打开状态,需手动关闭 -->
<!-- 弹出框的边框样式 -->
<Border BorderThickness="1"
Background="LightBlue"
CornerRadius="3"> <!-- 建议可添加圆角属性 -->
<!-- 垂直排列的弹出内容 -->
<StackPanel Margin="4">
<TextBlock Text="官方网站"
FontWeight="Bold"/> <!-- 标题文本 -->
<TextBlock Text="点击这个按钮,进入WPF中文网站"
Margin="0 4"/> <!-- 操作提示文本 -->
<!-- 分隔线 -->
<Border BorderBrush="Silver"
BorderThickness="0 1 0 0" <!-- 仅显示上边框 -->
Margin="0 4"/>
<!-- 网站地址 -->
<TextBlock Text="http://www.wpfsoft.com"
FontStyle="Italic"
Foreground="Blue"/> <!-- 建议添加超链接功能 -->
</StackPanel>
</Border>
</Popup>
</StackPanel>
统一资源标识Uri
WPF引入了统一资源标识Uri(Unified Resource Identifier)来标识和访问资源。其中较为常见的情况是用Uri加载图像。Uri表达式的一般形式为:协议+授权+路径,协议:pack://,授权:有两种。
一种用于访问编译时已经知道的文件,用application:///
一种用于访问编译时不知道、运行时才知道的文件,用siteoforigin:///
一般用逗号代替斜杠,也就是改写作application:,和pack:,
路径:分为绝对路径和相对路径。一般选用相对路径,普适性更强
<!--
〖图像控件声明〗
功能说明:显示应用程序内嵌资源中的品牌LOGO
技术细节:
-->
<Image Source="pack://application:,,,/Images/logo.png" <!-- 资源URI规范:使用WPF标准pack协议加载程序集资源 -->
Width="120" <!-- 显示宽度:建议与设计稿等比缩放 -->
Height="120" <!-- 显示高度:注意保持原始图片宽高比 -->
RenderOptions.BitmapScalingMode="HighQuality" <!-- 渲染优化:启用高质量图像缩放(建议添加) -->
ToolTip="应用程序品牌标识" <!-- 交互提示:鼠标悬停显示文字说明 -->
VerticalAlignment="Center"/> <!-- 布局校准:在容器中垂直居中 -->
<!--
〖图像容器布局声明〗
核心功能:实现图片元素的自动换行排列布局
技术特性分析:
-->
<WrapPanel Orientation="Horizontal" <!-- 布局方向:默认水平排列(可显式声明) -->
ItemWidth="120" <!-- 建议添加:统一子项宽度保障换行整齐度 -->
ItemHeight="120" <!-- 建议添加:约束高度避免尺寸溢出 -->
HorizontalAlignment="Center" <!-- 布局优化:在父容器中水平居中 -->
Margin="10" <!-- 建议添加:增加外边距避免贴边显示 -->
VerticalAlignment="Top"> <!-- 定位策略:根据场景选择顶部对齐 -->
<!-- 静态图片资源 -->
<Image Source="/Images/新.png" <!-- 资源路径:注意中文字符可能导致部署问题 -->
Width="120"
Height="120"
Stretch="Uniform" <!-- 显示优化:保持原始比例防止变形 -->
RenderOptions.BitmapScalingMode="HighQuality" <!-- 渲染增强:高质量插值 -->
ToolTip="品牌标识图案" <!-- 交互优化:悬停显示说明 -->
Cursor="Hand"/> <!-- 建议添加:手型光标暗示可点击 -->
<!-- 动态图片容器 -->
<Image x:Name="image2" <!-- 命名规范:建议采用imageTwo等明确语义 -->
Width="120"
Height="120"
Source="{Binding DynamicImage}" <!-- 建议改造:采用数据绑定模式 -->
Visibility="Collapsed" <!-- 状态管理:初始隐藏避免空白区域 -->
Loaded="Image2_Loaded" <!-- 事件处理:建议异步加载大尺寸图片 -->
Style="{StaticResource ImageStyle}"> <!-- 样式复用:统一图片效果 -->
</Image>
</WrapPanel>
// 2025年4月3日 21:07 代码注释(农历三月初六 乙巳蛇年)
/*
* █████████████████████████████████████████████████████████████
* 图片动态加载功能模块
* █████████████████████████████████████████████████████████████
*/
var path = Environment.CurrentDirectory + "\\" + "logo.png";
// [!] 潜在风险点:路径拼接方式存在三个问题
// 1. 建议改用 Path.Combine() 方法兼容不同操作系统路径分隔符
// 2. CurrentDirectory 可能被其他代码修改,推荐使用 AppDomain.CurrentDomain.BaseDirectory
// 3. 硬编码文件名不利于维护,建议配置在 appsettings.json
var imageSource = BitmapFrame.Create(
new Uri(path), // [!] Uri构造注意:若路径含特殊字符需Uri.EscapeDataString处理
BitmapCreateOptions.None, // 创建选项:默认不延迟解码(可改用DelayCreation提升性能)
BitmapCacheOption.OnLoad // [!] 关键参数:立即释放文件句柄(避免文件锁定)
); // 异常风险:文件不存在时会抛出System.NotSupportedException
image2.Source = imageSource; // 界面线程操作:若加载大文件应改用异步加载模式
// 建议增加加载状态指示器(如ProgressBar)
/*
* █████████████████████████████████████████████████████████████
* 优化建议(工程级)
* █████████████████████████████████████████████████████████████
*/
// ✅ 改进方案1:安全路径处理
// var safePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets", "logo.png");
// ✅ 改进方案2:异常处理封装
// try {
// using var stream = new FileStream(safePath, FileMode.Open, FileAccess.Read);
// var bitmap = new BitmapImage();
// bitmap.BeginInit();
// bitmap.CacheOption = BitmapCacheOption.OnLoad;
// bitmap.DecodePixelWidth = 800; // 内存优化
// bitmap.StreamSource = stream;
// bitmap.EndInit();
// image2.Dispatcher.Invoke(() => image2.Source = bitmap);
// } catch (Exception ex) {
// Logger.Error($"图片加载失败:{ex.Message}");
// // 显示备用图片:image2.Source = (ImageSource)FindResource("DefaultLogo");
// }
// ✅ 改进方案3:依赖注入配置
// services.Configure<ImageSettings>(config =>
// config.LogoPath = Configuration["AppAssets:LogoPath"]);
/*
* █████████████████████████████████████████████████████████████
* 版本控制标识
* █████████████████████████████████████████████████████████████
* 最后更新:2025-04-03 v2.1.5
* 变更记录:新增动态图片加载模块
* 兼容性:.NET 8.0+ / Windows 11 23H2+
*/
<!--
〖2025-04-03 21:15 代码注释〗
功能描述:创建可换行的图片展示面板,适用于产品相册、图标集等场景
技术特性:采用WrapPanel实现响应式布局,每个图片单元包含装饰性边框
-->
<WrapPanel Orientation="Horizontal" <!-- 流式布局容器:子元素自动换行 -->
HorizontalAlignment="Center" <!-- 水平居中显示 -->
VerticalAlignment="Top" <!-- 顶部对齐 -->
Margin="10" <!-- 整体外边距 -->
ItemWidth="110" <!-- 建议添加:统一子项宽度保障布局整齐度 -->
ItemHeight="110"> <!-- 建议添加:约束高度避免尺寸溢出 -->
<!-- 图片单元模板(重复5次) -->
<Border BorderBrush="LightGray" <!-- 浅灰色边框 -->
BorderThickness="1"
CornerRadius="5" <!-- 圆角效果(建议提升至8px增强现代感)-->
Padding="3" <!-- 内边距 -->
Margin="3" <!-- 单元间距 -->
Background="WhiteSmoke" <!-- 建议添加:浅色背景提升层次感 -->
Effect="{StaticResource Shadow}"><!-- 建议添加:投影效果 -->
<Image Source="pack://application:,,,/Images/新.png"
Width="100"
Height="100"
Stretch="Uniform" <!-- 保持比例缩放(关键参数) -->
RenderOptions.BitmapScalingMode="Fant" <!-- 高质量缩放 -->
ToolTip="产品示意图" <!-- 悬停提示 -->
Cursor="Hand" <!-- 手型光标暗示可交互 -->
AutomationProperties.Name="产品展示图"/> <!-- 无障碍访问 -->
</Border>
<!-- 重复单元(建议改用ItemsControl优化) -->
...(此处重复4个相同结构单元)...
</WrapPanel>
【深度优化建议】(基于.NET 8最佳实践)
1. 动态生成方案(数据驱动)
```xml
<ItemsControl ItemsSource="{Binding Products}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
ItemWidth="110"
ItemHeight="110"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource ImageBorderStyle}">
<Image Source="{Binding ImagePath}"
Style="{StaticResource GalleryImageStyle}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- 2025年4月3日 22:21 代码注释(乙巳蛇年三月初六 星期四) -->
<Grid x:Name="viewport">
<!--
〖三级网格布局结构〗
第1行:固定高度115px的图片展示区
第2行:自适应高度的滚动条控制区
第3行:剩余空间的文字展示区
-->
<Grid.RowDefinitions>
<RowDefinition Height="115"/> <!-- 固定高度展示区域 -->
<RowDefinition Height="auto"/> <!-- 滚动条自适应高度 -->
<RowDefinition/> <!-- 剩余空间分配 -->
</Grid.RowDefinitions>
<!--
〖可横向滚动的图片画廊实现方案〗
技术特性:Canvas+StackPanel实现自定义滚动布局
存在问题:重复代码过多,性能待优化
-->
<Canvas>
<!-- 水平堆叠面板绑定Canvas.Left实现滚动效果 -->
<StackPanel x:Name="element"
Orientation="Horizontal"
Canvas.Left="{Binding CanvasLeft}"> <!-- 需实现INotifyPropertyChanged通知 -->
<!-- 重复单元结构(共12个相同元素) -->
<Border BorderBrush="LightGray"
BorderThickness="1"
CornerRadius="5"
Padding="3"
Margin="3"
Effect="{StaticResource CommonShadow}"> <!-- 建议添加阴影效果 -->
<Image Source="pack://application:,,,/Images/新.png"
Width="100"
Height="100"
Stretch="UniformToFill" <!-- 建议改为保持比例填充 -->
RenderOptions.BitmapScalingMode="Fant"/> <!-- 高质量缩放 -->
</Border>
...(重复11个相同单元)...
</StackPanel>
</Canvas>
<!--
〖滚动控制交互系统〗
绑定关系:
Maximum ↔ 可滚动最大偏移量(需后台计算)
Value ↔ 当前滚动位置X
ViewportSize ↔ 可视区域宽度
-->
<ScrollBar Grid.Row="1"
Orientation="Horizontal"
Maximum="{Binding Maximum}" <!-- 需计算:总宽度-可视宽度 -->
Value="{Binding X, Mode=TwoWay}" <!-- 建议启用双向绑定 -->
ViewportSize="{Binding ElementName=viewport,Path=ActualWidth}" <!-- 动态视口尺寸 -->
Style="{StaticResource ModernScrollBar}"/> <!-- 建议自定义样式 -->
<!-- 底部文字展示区 -->
<TextBlock Grid.Row="2"
Text="ScorllBar" <!-- 存在拼写错误:应为ScrollBar -->
FontSize="24"
VerticalAlignment="Center"
Foreground="#333333" <!-- 建议使用资源色值 -->
AutomationProperties.LiveSetting="Assertive"/> <!-- 无障碍阅读支持 -->
</Grid>
【深度优化建议】(基于.NET 8最佳实践)
1. 架构优化方案
```xml
<!-- 改造为MVVM模式 -->
<ItemsControl Grid.Row="0" ItemsSource="{Binding ImageItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- 复用单元模板 -->
<Border Style="{StaticResource GalleryItemStyle}">
<Image Source="{Binding Path}"
Style="{StaticResource AdaptiveImage}"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// 2025年4月3日 22:23 代码注释(乙巳蛇年三月初六 星期四)
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this; // 自绑定数据上下文(MVVM模式基础)
Loaded += (s, e) => // 窗口加载完成事件
{
// [!] 潜在问题:ActualWidth在未渲染时可能为0
Maximum = element.ActualWidth - viewport.ActualWidth; // 计算滚动边界
};
}
/*
██████████████████████████████████████████████████████████████
滚动控制核心逻辑
██████████████████████████████████████████████████████████████ */
private double maximum = 0;
/// <summary>
/// 滚动范围最大值(动态计算:内容总宽 - 可视区域宽度)
/// [!] 建议:添加窗口SizeChanged事件监听实时更新
/// </summary>
public double Maximum
{
get => maximum;
set { maximum = value > 0 ? value : 0; NotifyPropertyChanged(); }
}
private double x = 0;
/// <summary>
/// 滚动位置同步器(双向绑定滚动条)
/// [!] 注意:未做边界校验,可能产生负值或超限值
/// </summary>
public double X
{
get => x;
set {
x = Math.Clamp(value, 0, Maximum); // 建议添加数值限制
CanvasLeft = -x; // 核心滚动逻辑:通过负边距实现视觉位移
NotifyPropertyChanged();
}
}
private double canvasLeft = 0;
/// <summary>
/// Canvas布局绑定目标(驱动UI元素位移)
/// [!] 性能注意:频繁变更可能引发渲染性能问题
/// </summary>
public double CanvasLeft
{
get => canvasLeft;
set { canvasLeft = value; NotifyPropertyChanged(); }
}
/*
██████████████████████████████████████████████████████████████
架构优化建议
██████████████████████████████████████████████████████████████ */
// ✅ 建议1:窗口尺寸响应式处理
// SizeChanged += (s, e) => Maximum = element.ActualWidth - viewport.ActualWidth;
// ✅ 建议2:命令模式改造(示例)
// public ICommand ScrollCommand => new RelayCommand(offset => X += (double)offset);
// ✅ 建议3:异步加载优化
// Dispatcher.InvokeAsync(() => { /* 图片加载逻辑 */ }, DispatcherPriority.Background);
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 属性变更通知方法(.NET 8推荐使用CallerMemberName)
/// [!] 注意:建议改用nameof()运算符避免拼写错误
/// </summary>
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/*
██████████████████████████████████████████████████████████████
扩展功能预留区(被注释代码分析)
██████████████████████████████████████████████████████████████ */
// 原始按钮事件(建议改用命令模式)
// btn.Click += btn_Click;
// 文件路径加载方案(存在三个问题)
// var path = Environment.CurrentDirectory + "\\" + "新.png";
// 问题1:应使用Path.Combine()
// 问题2:中文文件名可能引发编码问题
// 问题3:未做文件存在性检查
}
/*
█████████████████████████████████████████████████████████████████
【工程化改进方案】
█████████████████████████████████████████████████████████████████
1. 性能优化
- 虚拟化布局:改用 VirtualizingStackPanel 处理大量元素
- 图片缓存:对重复图片使用 BitmapImage.CacheOption = OnLoad
- 异步解码:BitmapCreateOptions.DelayCreation
2. 健壮性增强
- 添加 try-catch 块处理文件加载异常
- 实现 IScrollInfo 接口支持标准滚动行为
- 添加默认图片加载失败占位符
3. 架构升级
- 分离ViewModel:将滚动逻辑移至独立类
- 依赖注入:通过 IOC 容器管理服务
- 行为封装:创建 ScrollSyncBehavior 附加行为
4. 可观测性
- 添加性能计数器监控渲染耗时
- 实现调试面板显示实时滚动数据
- 集成日志系统记录用户滚动行为
*/
// 2025年4月3日 23:34 代码注释(乙巳蛇年三月初六 星期四)
Loaded += (s, e) =>
{
// 异步启动进度模拟任务(窗口加载完成后触发)
Task.Factory.StartNew(() =>
{
for (int i = 0; i <= 100; i++)
{
// 通过UI线程更新进度组件(强制跨线程安全)
Dispatcher.Invoke(() =>
{
_txtBlock.Text = $"{i}%"; // 文本进度显示
_ProgressBar.Value = i; // 进度条数值更新
});
Task.Delay(25).Wait(); // 模拟耗时操作(25ms步进)
}
});
};
/*
█████████████████████████████████████████████████████████████████
【技术特性与潜在问题】
█████████████████████████████████████████████████████████████████
1. 核心功能
- 实现加载进度可视化(0-100%动态展示)
- 异步任务与UI线程协同机制
2. 关键技术
- Task.Factory.StartNew:创建后台线程
- Dispatcher.Invoke:跨线程UI更新
- Task.Delay:模拟耗时操作
3. 现存问题
- 未实现任务取消机制(窗口关闭时可能引发资源泄漏)
- 硬编码总进度100(缺乏灵活性)
- 同步等待(Task.Wait)阻塞线程(建议改用async/await)
- 未处理异常(任务崩溃将导致静默失败)
█████████████████████████████████████████████████████████████████
【深度优化建议】
█████████████████████████████████████████████████████████████████
1. 架构优化方案
- 采用async/await模式重构(避免线程阻塞)
```csharp
Loaded += async (s, e) =>
{
using var cts = new CancellationTokenSource();
await Task.Run(() => LoadProgress(cts.Token), cts.Token);
};
<!-- 2025年4月3日 23:51 代码注释(乙巳蛇年三月初六 星期四) -->
<StackPanel HorizontalAlignment="Center"
Margin="30"
VerticalAlignment="Center"
Background="{DynamicResource PanelBackground}"> <!-- 建议应用主题背景色 -->
<!-- 〖日期范围选择模块〗 -->
<StackPanel Orientation="Horizontal" Margin="10"
ToolTip="选择分析时段起点">
<TextBlock Text="开始日期"
VerticalAlignment="Center"
Margin="10 0"
Style="{StaticResource FieldLabelStyle}"/> <!-- 标签标准化 -->
<DatePicker x:Name="_DataPickerStart"
VerticalAlignment="Center"
Width="120"
SelectedDate="{x:Static sys:DateTime.Now.AddMonths(-1)}" <!-- 默认值设置 -->
IsTodayHighlighted="True"
FirstDayOfWeek="Monday"/> <!-- 周起始日本地化 -->
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10"
ToolTip="选择分析时段终点">
<TextBlock Text="结束日期"
VerticalAlignment="Center"
Margin="10 0"
Style="{StaticResource FieldLabelStyle}"/>
<DatePicker x:Name="_DataPickerEnd"
VerticalAlignment="Center"
Width="120"
SelectedDate="{x:Static sys:DateTime.Now}" <!-- 动态默认值 -->
IsInactiveSelectionHighlightEnabled="True"
DisplayDateEnd="{x:Static sys:DateTime.Today}"/> <!-- 禁止选择未来日期 -->
</StackPanel>
<!-- 〖数据查询交互系统〗 -->
<Button Content="查询"
Click="btn_Click"
Margin="10 0"
Style="{StaticResource PrimaryButtonStyle}" <!-- 强调操作按钮 -->
Command="{Binding QueryCommand}" <!-- 建议改用命令模式 -->
AutomationProperties.HelpText="点击生成时段分析报告"/>
</StackPanel>
/*
█████████████████████████████████████████████████████████████████
【深度优化建议】(基于企业级开发标准)
█████████████████████████████████████████████████████████████████
一、交互增强方案
1. 动态日期验证
```csharp
// 在ViewModel中添加验证逻辑
public DateTime StartDate {
get => _startDate;
set {
if(value > EndDate)
throw new ArgumentException("开始日期不能晚于结束日期");
_startDate = value;
}
}
// 2025年4月3日 23:57 代码注释(乙巳蛇年三月初六 星期四)
var selectedDate = $"查询日期:{_DataPickerStart.SelectedDate} {_DataPickerEnd.SelectedDate}\r\n";
var text = $"文本值:{_DataPickerStart.Text} {_DataPickerEnd.Text}";
MessageBox.Show($"{selectedDate}{text}");
/*
█████████████████████████████████████████████████████████████████
【技术分析与优化建议】
█████████████████████████████████████████████████████████████████
一、核心功能解析
1. 数据获取逻辑
- SelectedDate:获取日期选择器的解析值(DateTime? 类型)
- Text:获取用户原始输入字符串(可能包含格式错误)
- 双模式输出:同时显示格式化日期和原始文本供对比验证
2. 信息呈现方式
- 使用Windows原生消息框(MessageBox)展示结果
- 换行符\r\n实现多行文本布局
- 字符串插值语法构建复合消息
二、潜在问题诊断
1. 空值风险(当用户未选择日期时SelectedDate为null)
2. 文本解析不一致(Text可能包含"2025/04/03"或"April 3, 2025"等格式)
3. 缺乏输入验证机制(未检测开始日期>结束日期的逻辑错误)
4. 国际化缺失(日期格式未适配系统区域设置)
三、企业级优化方案
1. 增强数据验证
```csharp
if(_DataPickerStart.SelectedDate > _DataPickerEnd.SelectedDate){
MessageBox.Show("开始日期不能晚于结束日期", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}