欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > WPF基础知识(续)

WPF基础知识(续)

2025/4/2 7:22:20 来源:https://blog.csdn.net/m0_44975814/article/details/146600207  浏览:    关键词:WPF基础知识(续)

六、WPF 中的样式和模板

  • 样式定义
    • 可以在 XAML 中定义样式来统一 UI 元素的外观和风格。样式可以定义在资源字典中,也可以直接在窗口或控件的Resources属性中定义。例如,定义一个按钮的样式:
<Window.Resources><Style TargetType="Button"><Setter Property="Background" Value="Blue" /><Setter Property="Foreground" Value="White" /><Setter Property="FontSize" Value="16" /></Style>
</Window.Resources>
<Grid><Button Content="按钮1" /><Button Content="按钮2" />
</Grid>

  • 也可以为样式指定一个Key,然后通过StaticResource来应用样式。例如:
<Window.Resources><Style x:Key="MyButtonStyle" TargetType="Button"><Setter Property="Background" Value="Green" /><Setter Property="Foreground" Value="Yellow" /><Setter Property="FontSize" Value="18" /></Style>
</Window.Resources>
<Grid><Button Content="按钮1" Style="{StaticResource MyButtonStyle}" /><Button Content="按钮2" />
</Grid>

  • 模板创建
    • 可以创建控件模板来完全自定义控件的外观。例如,创建一个自定义的按钮模板:
<Window.Resources><ControlTemplate x:Key="MyButtonTemplate" TargetType="Button"><Grid><Rectangle Fill="{TemplateBinding Background}" /><TextBlock Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid></ControlTemplate><Style x:Key="MyButtonStyle" TargetType="Button"><Setter Property="Template" Value="{StaticResource MyButtonTemplate}" /></Style>
</Window.Resources>
<Grid><Button Content="按钮1" Style="{StaticResource MyButtonStyle}" Background="Red" />
</Grid>

七、WPF 中的动画

  • 简单动画
    • 可以使用Storyboard来定义动画。例如,实现一个按钮的宽度从 100 逐渐增加到 200 的动画:
<Window.Resources><Storyboard x:Key="ButtonWidthAnimation"><DoubleAnimation Storyboard.TargetProperty="Width" From="100" To="200" Duration="0:0:5" /></Storyboard>
</Window.Resources>
<Grid><Button Content="按钮1" Width="100" Click="Button_Click"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource ButtonWidthAnimation}" /></EventTrigger></Button.Triggers></Button>
</Grid>

  • 复杂动画
    • 可以组合多个动画来实现更复杂的效果。例如,同时实现按钮的宽度增加和颜色变化的动画:
<Window.Resources><Storyboard x:Key="ButtonAnimation"><DoubleAnimation Storyboard.TargetProperty="Width" From="100" To="200" Duration="0:0:5" /><ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" From="Red" To="Blue" Duration="0:0:5" /></Storyboard>
</Window.Resources>
<Grid><Button Content="按钮1" Width="100" Background="Red" Click="Button_Click"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource ButtonAnimation}" /></EventTrigger></Button.Triggers></Button>
</Grid>

八、WPF 应用程序的部署

  • 打包应用程序
    • 可以使用 Visual Studio 的发布功能来打包 WPF 应用程序。在项目属性中选择 “发布” 选项卡,然后按照向导进行操作,可以选择发布到本地文件夹、网络共享或创建安装包等。
    • 可以设置发布的目标平台、版本号、更新策略等选项。例如,如果希望应用程序能够自动更新,可以设置相关的更新选项,指定更新服务器的地址等。
  • 安装包创建
    • 可以使用专门的安装包制作工具,如 Inno Setup、WiX 等,来创建更复杂的安装包。这些工具可以让你更精细地控制安装过程,包括添加自定义安装界面、设置安装目录、注册系统服务等。
    • 例如,使用 Inno Setup 创建安装包时,需要编写一个脚本文件,定义安装包的各种属性和安装步骤。在脚本中可以指定应用程序的文件、图标、安装目录、开始菜单快捷方式等信息。

九、WPF 中的命令模式

1. 命令基础概念

在 WPF 里,命令模式是一种实现交互逻辑与 UI 分离的有效方式。它将操作封装成对象,这样就能在不同的 UI 元素之间复用,也方便进行测试和维护。WPF 自带了ICommand接口,这是命令的核心抽象,任何实现了该接口的类都能当作命令来用。

2. 自定义命令实现

下面是一个自定义命令的示例:

using System;
using System.Windows.Input;public class RelayCommand : ICommand
{private readonly Action<object> _execute;private readonly Predicate<object> _canExecute;public RelayCommand(Action<object> execute) : this(execute, null){}public RelayCommand(Action<object> execute, Predicate<object> canExecute){_execute = execute;_canExecute = canExecute;}public event EventHandler CanExecuteChanged{add { CommandManager.RequerySuggested += value; }remove { CommandManager.RequerySuggested -= value; }}public bool CanExecute(object parameter){return _canExecute == null || _canExecute(parameter);}public void Execute(object parameter){_execute(parameter);}
}

在上述代码中,RelayCommand类实现了ICommand接口。_execute字段存储要执行的操作,_canExecute字段用于判断命令是否可以执行。

3. 命令在 XAML 中的使用

以下是如何在 XAML 中使用自定义命令:

<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApp1"Title="MainWindow" Height="350" Width="525"><Window.DataContext><local:MainViewModel/></Window.DataContext><Grid><Button Content="执行命令" Command="{Binding MyCommand}" /></Grid>
</Window>

在这个例子中,按钮的Command属性绑定到了MainViewModel中的MyCommand命令。

4. 命令在 ViewModel 中的实现
using System.Windows.Input;public class MainViewModel
{public ICommand MyCommand { get; private set; }public MainViewModel(){MyCommand = new RelayCommand(ExecuteMyCommand);}private void ExecuteMyCommand(object parameter){// 这里是命令执行的具体逻辑}
}

MainViewModel类中,创建了一个RelayCommand实例并赋值给MyCommand属性,当按钮被点击时,ExecuteMyCommand方法就会被调用。

十、WPF 中的多线程

1. 线程安全问题

在 WPF 中,UI 元素只能由创建它们的线程来访问和修改,这就是所谓的单线程单元(STA)模型。如果在其他线程中尝试访问 UI 元素,就会抛出InvalidOperationException异常。因此,在进行多线程操作时,需要特别注意线程安全问题。

2. 使用Dispatcher更新 UI

Dispatcher是 WPF 中用于处理线程间通信的机制。可以使用Dispatcher.InvokeDispatcher.BeginInvoke方法来在 UI 线程中执行代码。以下是一个简单的示例:

using System;
using System.Threading;
using System.Windows;namespace WpfApp1
{public partial class MainWindow : Window{public MainWindow(){InitializeComponent();Thread workerThread = new Thread(DoWork);workerThread.Start();}private void DoWork(){// 模拟耗时操作Thread.Sleep(2000);// 使用Dispatcher更新UIthis.Dispatcher.Invoke(() =>{MessageBox.Show("工作完成");});}}
}

在这个例子中,创建了一个新线程来执行DoWork方法,在DoWork方法中使用Dispatcher.Invoke方法在 UI 线程中显示消息框。

3. 使用Taskasync/await

在.NET 中,Taskasync/await是处理异步操作的推荐方式。以下是一个使用async/await的示例:

using System;
using System.Threading.Tasks;
using System.Windows;namespace WpfApp1
{public partial class MainWindow : Window{public MainWindow(){InitializeComponent();DoAsyncWork();}private async void DoAsyncWork(){await Task.Run(() =>{// 模拟耗时操作Thread.Sleep(2000);});MessageBox.Show("异步工作完成");}}
}

在这个例子中,DoAsyncWork方法被标记为async,使用await关键字等待Task.Run方法返回的任务完成,之后的代码会在 UI 线程中继续执行,这样就避免了直接使用Dispatcher的复杂性。

十一、WPF 中的资源管理

1. 资源基础概念

在 WPF 中,资源是可以被多个元素共享的对象,例如画笔、画刷、样式、模板等。资源可以定义在多个位置,如窗口、控件、应用程序级别等。

2. 资源的定义和使用

以下是在窗口资源中定义一个画刷资源并使用它的示例:

<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="350" Width="525"><Window.Resources><SolidColorBrush x:Key="MyBrush" Color="Red"/></Window.Resources><Grid><Button Content="按钮" Background="{StaticResource MyBrush}" /></Grid>
</Window>

在这个例子中,在Window.Resources中定义了一个名为MyBrush的画刷资源,然后在按钮的Background属性中使用StaticResource引用该资源。

3. 资源的查找顺序

WPF 在查找资源时,会按照一定的顺序进行。首先会在当前元素的资源字典中查找,如果找不到,会向上查找父元素的资源字典,直到找到资源或者到达应用程序级别的资源字典。如果仍然找不到,就会抛出ResourceReferenceKeyNotFoundException异常。

4. 动态资源和静态资源
  • 静态资源:使用StaticResource标记扩展,在编译时解析资源引用。一旦解析完成,就不会再改变。
  • 动态资源:使用DynamicResource标记扩展,在运行时解析资源引用。如果资源发生变化,使用该资源的元素会自动更新。以下是使用动态资源的示例:
<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="350" Width="525"><Window.Resources><SolidColorBrush x:Key="MyBrush" Color="Red"/></Window.Resources><Grid><Button Content="按钮" Background="{DynamicResource MyBrush}" /></Grid>
</Window>

十二、WPF 中的打印功能

1. 基本打印流程

在 WPF 中实现打印功能,通常需要以下几个步骤:

  • 创建PrintDialog对象,用于显示打印对话框。
  • 获取要打印的内容,例如Visual对象。
  • 调用PrintDialogPrintVisual方法进行打印。
2. 简单打印示例
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;namespace WpfApp1
{public partial class MainWindow : Window{public MainWindow(){InitializeComponent();PrintButton.Click += PrintButton_Click;}private void PrintButton_Click(object sender, RoutedEventArgs e){PrintDialog printDialog = new PrintDialog();if (printDialog.ShowDialog() == true){// 创建一个简单的可视化内容StackPanel printContent = new StackPanel();TextBlock textBlock = new TextBlock();textBlock.Text = "这是要打印的内容";printContent.Children.Add(textBlock);// 打印可视化内容printDialog.PrintVisual(printContent, "打印内容");}}}
}

在这个示例中,点击按钮会弹出打印对话框,用户选择打印设置后,会打印一个包含文本的StackPanel

3. 打印复杂内容

如果要打印复杂的内容,例如文档、报表等,可以使用DocumentPaginatorFixedDocument等类。以下是一个打印FixedDocument的示例:

private void PrintFixedDocument()
{PrintDialog printDialog = new PrintDialog();if (printDialog.ShowDialog() == true){FixedDocument fixedDocument = new FixedDocument();// 创建一个页面FixedPage fixedPage = new FixedPage();TextBlock textBlock = new TextBlock();textBlock.Text = "这是固定文档的内容";fixedPage.Children.Add(textBlock);PageContent pageContent = new PageContent();((IAddChild)pageContent).AddChild(fixedPage);fixedDocument.Pages.Add(pageContent);// 打印固定文档printDialog.PrintDocument(fixedDocument.DocumentPaginator, "打印固定文档");}
}

十三、WPF 中的触控和手势支持

1. 触控事件

WPF 支持多种触控事件,如TouchDownTouchMoveTouchUp等。可以通过处理这些事件来实现与触控设备的交互。以下是一个简单的示例:

<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="350" Width="525"><Grid TouchDown="Grid_TouchDown"><TextBlock Text="触摸我" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center" /></Grid>
</Window>

private void Grid_TouchDown(object sender, TouchEventArgs e)
{MessageBox.Show("触摸事件触发");
}

在这个示例中,当用户触摸网格时,会弹出消息框。

2. 手势识别

WPF 还支持手势识别,例如捏合、旋转等手势。可以使用ManipulationStartingManipulationDeltaManipulationCompleted等事件来处理手势。以下是一个简单的捏合手势示例:

<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="350" Width="525"><Grid ManipulationStarting="Grid_ManipulationStarting" ManipulationDelta="Grid_ManipulationDelta"><Rectangle Width="100" Height="100" Fill="Red" RenderTransformOrigin="0.5,0.5"><Rectangle.RenderTransform><ScaleTransform /></Rectangle.RenderTransform></Rectangle></Grid>
</Window>

private void Grid_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
{e.ManipulationContainer = this;e.Handled = true;
}private void Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{Rectangle rectangle = sender as Rectangle;ScaleTransform scaleTransform = rectangle.RenderTransform as ScaleTransform;scaleTransform.ScaleX *= e.DeltaManipulation.Scale.X;scaleTransform.ScaleY *= e.DeltaManipulation.Scale.Y;e.Handled = true;
}

在这个示例中,用户可以通过捏合手势来缩放矩形。

十四、WPF 中的性能优化

1. 控件虚拟化

当需要显示大量数据时,使用控件虚拟化可以显著提高性能。例如,ListBoxListView等控件都支持虚拟化。可以通过设置VirtualizingStackPanel.IsVirtualizing属性为True来启用虚拟化。

<ListBox VirtualizingStackPanel.IsVirtualizing="True"><!-- 大量数据项 -->
</ListBox>

启用虚拟化后,只有当前可见的项会被创建和渲染,而不是一次性创建所有项。

2. 减少布局计算

布局计算是一个比较耗时的操作,因此可以尽量减少布局的嵌套层次。例如,避免使用过多的嵌套GridStackPanel。另外,使用MeasureArrange方法的优化版本,如MeasureOverrideArrangeOverride,可以自定义布局行为,减少不必要的计算。

3. 资源管理优化

合理管理资源,避免创建过多的资源对象。例如,对于一些常用的画刷、画笔等资源,可以定义为静态资源并在多个地方共享使用。另外,及时释放不再使用的资源,避免内存泄漏。

4. 图像优化

如果应用程序中使用了大量的图像,要注意图像的格式和大小。使用合适的图像格式,如 PNG、JPEG 等,并且对图像进行压缩处理,减少图像的文件大小。另外,使用BitmapCache可以缓存图像,提高渲染性能。

<Image Source="image.jpg"><Image.CacheMode><BitmapCache /></Image.CacheMode>
</Image>

十五、WPF 与数据库交互

1. 连接数据库

在 WPF 中可以使用多种方式连接数据库,例如使用Entity FrameworkADO.NET等。以下是一个使用ADO.NET连接 SQL Server 数据库的示例:

using System;
using System.Data.SqlClient;namespace WpfApp1
{class DatabaseHelper{public static void ConnectToDatabase(){string connectionString = "Data Source=YOUR_SERVER_NAME;Initial Catalog=YOUR_DATABASE_NAME;User ID=YOUR_USERNAME;Password=YOUR_PASSWORD";using (SqlConnection connection = new SqlConnection(connectionString)){try{connection.Open();Console.WriteLine("数据库连接成功");}catch (Exception ex){Console.WriteLine("数据库连接失败: " + ex.Message);}}}}
}
2. 数据查询和显示

连接数据库后,可以进行数据查询并将结果显示在 WPF 界面上。以下是一个简单的查询示例:

private void DisplayData()
{string connectionString = "Data Source=YOUR_SERVER_NAME;Initial Catalog=YOUR_DATABASE_NAME;User ID=YOUR_USERNAME;Password=YOUR_PASSWORD";using (SqlConnection connection = new SqlConnection(connectionString)){string query = "SELECT * FROM YourTable";SqlCommand command = new SqlCommand(query, connection);try{connection.Open();SqlDataReader reader = command.ExecuteReader();while (reader.Read()){// 处理查询结果string column1Value = reader["Column1"].ToString();// 显示数据}reader.Close();}catch (Exception ex){MessageBox.Show("数据查询失败: " + ex.Message);}}
}
3. 使用Entity Framework

Entity Framework是一个强大的对象关系映射(ORM)框架,可以简化数据库操作。以下是一个使用Entity Framework的示例:

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;namespace WpfApp1
{public class MyDbContext : DbContext{public DbSet<YourEntity> YourEntities { get; set; }}public class YourEntity{public int Id { get; set; }public string Name { get; set; }}class Program{static void Main(){using (MyDbContext context = new MyDbContext()){var entities = context.YourEntities.ToList();foreach (var entity in entities){// 处理实体数据}}}}
}

使用Entity Framework可以将数据库表映射为实体类,通过操作实体类来进行数据库操作,避免了直接编写 SQL 语句的复杂性。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com