下面是几种常见基于.Net的界面开发框架,都是Microsoft提供,下面简介由GPT生成
WinForms (Windows Forms
WPF (Windows Presentation Foundation)
UWP (Universal Windows Platform)
MAUI (Multi-platform App UI)
winform过于古早
uwp的话,我们就用windows电脑,没必要
maui因为跨平台,导致了其臃肿
所以我们选择WPF,下面文章也是主要讲WPF的
先根据功能,设计布局
然后就是如果项目比较大,要考虑设计模式的选取,让前后端分离
因为UI的开发都是基于界面和控件的,而我们常用的控件都大同小异,需要的时候自行查阅文档就好了
信息类 label
编辑类 文本编辑 textbox 状态编辑 checkbox
按钮 button
拖拽添加
代码添加
anchor,锚定某个边缘
dock,停靠,按弹出的选项进行设置
重写Onlayout函数(布局改变时触发)
panel,多层次布局,将控件绑定到panel上,便于组合整体控制
即界面获取操作,传到后端进行相应
VS选择基于C#的和.Net的窗体应用创建
直接运行,可以看到一个空的界面
然后可以看解决方案管理,可以看到有
new Form1()
来示例化我们的窗口通过拖拽添加控件,通过属性添加事件.界面的主要代码都在designer中,我们最好不要动它,需要的改动都在Form1.cs中
因为应用场景少,所以不深入学习
StartupUri
设置启动的窗口,Resources
样式模板然后我们直接运行项目,可以看到一个空白窗口
xaml是一种基于xml的标记语言,用于定义UI和属性,这里面都会根据xaml生成对应的cs文件
就下面这个语法
<CMD ..... />
等价于<CMD>.....</CMD>
我们看``MainWindow.xaml`
<Window x:Class="WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
解释如下
<Window>
元素:
x:Class="WPF.MainWindow"
:这个属性指定了窗口的类名。在这里,窗口的类名是 MainWindow
,它与代码中的类名相对应。这个属性通常用于将XAML文件与后端代码(例如C#)中的类关联起来。xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
:指定默认的XML命名空间,该命名空间用于定义WPF的基本呈现元素,如窗口、面板、按钮等。xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
:指定XAML专用命名空间,其中包含一些用于XAML文件的特殊属性。xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
:用于Blend工具的命名空间,该工具通常用于设计和交互式开发。xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
:定义了一些用于在不同XAML编译器版本之间保持兼容性的属性。xmlns:local="clr-namespace:WPF"
:指定了一个本地的命名空间,它与命名空间 WPF
相关联。这使得在XAML文件中可以引用 WPF
命名空间中定义的类和资源。mc:Ignorable="d"
:
d
前缀时忽略错误。在这里,d
是Blend工具的命名空间,而 Ignorable
属性允许在不影响编译的情况下忽略这个未知的前缀。Title="MainWindow"
:设置窗口的标题为 “MainWindow”。Height="450"
和 Width="800"
:设置窗口的高度和宽度分别为450和800。所有东西都可以通过属性来改变
StackPanel
StackPanel 主要用于垂直或水平排列元素、在容器的可用尺寸内放置有限个元素,元素的 尺寸总和(长/高)不允许超过 StackPanel 的尺寸, 否则超出的部分不可见。
WrapPanel
WrapPanel 默认排列方向与 StackPanel 相反、WrapPanel 的 Orientation 默认为 Horizontal。 WrapPanel 具备 StackPanel 的功能基础上具备在尺寸变更后自动适应容器的宽高进行换行换列处理。
DockPanel
停靠式布局
内部控件通过DockPanel.Dock 属性来布局,默认添加 Left。
DockPanel 有一个 LastChildFill 属性, 该属性默认为 true, 该属性作用为, 当容器中的最后一个元素时, 默认该元素填充 DockPanel 所有空间。
Grid
Grid 具备分割空间的能力。
RowDefinitions / ColumnDefinitions 用于给 Grid 分配行与列。
Row / Column 来确定控件位置
ColumnSpan / RowSpan来设置控件长宽
如下来进行分割与分配
尺寸中auto表示跟网格内的控件一样,*表示比例划分auto剩下的
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="37*" />
<RowDefinition Height="138*" />
<RowDefinition Height="175*" />
<RowDefinition Height="175*" />
<RowDefinition Height="175*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="58*" />
<ColumnDefinition Width="58*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="362*" />
<ColumnDefinition Width="71*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="1" Content="Label" Grid.ColumnSpan="1" Grid.RowSpan="3" HorizontalAlignment="Center" Grid.Row="2" VerticalAlignment="Center"/>
ContentControl 类
内容属性为 Content,只能设置一次,但可以嵌套
ItemsControl 类
此类控件大多数属于显示列表类的数据、设置数据源的方式一般通过 ItemSource 设置。
HeaderedContentControl 类
相对于 ContentControl 来说、这类控件即可设置 Content, 还有带标题的 Header。 像比较常见的分组控件 GroupBox、TabControl 子元素 TabItem、它们都是具备标题和内容的控件。
常用控件
在MainWindow.xaml里面设置,用于触发函数
namespace WPF
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();//界面显示
}
//添加的事件函数
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
}
可设置样式
全局的在app中的<Application.Resources>中添加
局部的在当前xaml里面添加<Window.Resources>(与grid同级)
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Text" Value="TextBlock" />
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="60" />
<Setter Property="Background" Value="Red" />
</Style>
使用
<TextBlock TextWrapping="Wrap" Style="{StaticResource TextBlockStyle}"/>
<!--这里也可以再修改样式,优先级高于Style-->
一样是style,在对应事件触发时执行
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
我们在样式中所能做的修改有限,
于是可以右键控件,编辑副本来创建新的控件
如给按钮添加圆角
在编辑副本后,在生成的副本中,给Border标签加上CornerRadius="10"
然后设置按钮Style="{StaticResource ButtonStyle1}"
表格实现,利用grid
模板直接放在里,这个表格设置了三列,并在第四列设置了两个按钮
<DataGrid Name="gd" AutoGenerateColumns="False" CanUserSortColumns="True" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding UserName}" Width="100" Header="学生姓名"/>
<DataGridTextColumn Binding="{Binding ClassName}" Width="100" Header="班级名称"/>
<DataGridTextColumn Binding="{Binding Address}" Width="200" Header="地址"/>
<DataGridTemplateColumn Header="操作" Width="100" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<Button Content="编辑"/>
<Button Margin="8 0 0 0" Content="删除" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
绑定数据,在InitializeComponent();后面
public class Student
{
public string UserName { get; set; }
public string ClassName { get; set; }
public string Address { get; set; }
}
List<Student> students = new List<Student>();
students.Add(new Student() { UserName = "小王", ClassName = "高二三班", Address = "广州市" });
students.Add(new Student() { UserName = "小李", ClassName = "高三六班", Address = "清远市" });
students.Add(new Student() { UserName = "小张", ClassName = "高一一班", Address = "深圳市" });
students.Add(new Student() { UserName = "小黑", ClassName = "高一三班", Address = "赣州市" });
gd.ItemsSource = students;
用来绑定控件内容,如下
<Window.Resources>
<DataTemplate x:Key="comTemplate">
<StackPanel Orientation="Horizontal" Margin="5,0">
<Border Width="10" Height="10" Background="{Binding Code}"/>
<TextBlock Text="{Binding Code}" Margin="5,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ComboBox Name="cob" Width="120" Height="30" ItemTemplate="{StaticResource comTemplate}"/>
<ListBox Name="lib" Width="120" Height="100" Margin="5,0" ItemTemplate="{StaticResource comTemplate}"/>
</StackPanel>
</Grid>
绑定数据
public class Color
{
public string? Code { get; set; }
}
List<Color> ColorList = new List<Color>();
ColorList.Add(new Color() { Code = "#FF8C00" });
ColorList.Add(new Color() { Code = "#FF7F50" });
ColorList.Add(new Color() { Code = "#FF6EB4" });
ColorList.Add(new Color() { Code = "#FF4500" });
ColorList.Add(new Color() { Code = "#FF3030" });
ColorList.Add(new Color() { Code = "#CD5B45" });
cob.ItemsSource = ColorList;
lib.ItemsSource = ColorList;
用得很少,不提了
前端显示后后端数据的关联
通过给控件取名,然后根据名字将text的值绑定为上面slider控件的value
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Slider Name="slider" Width="200" />
<TextBlock Text="{Binding ElementName=slider, Path=Value}" HorizontalAlignment="Center" />
</StackPanel>
绑定模式
如下进行设置
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Slider Name="slider" Width="200" />
<TextBox Width="200" Text="{Binding ElementName=slider, Path=Value, Mode=OneWay}" HorizontalAlignment="Center" />
</StackPanel>
常用模式
OneWay : 单向绑定,绑定对象会决定自己
TwoWay : 双向绑定,下面输入后tab即可显示
OneTime : 只绑定第一次的值,这里直接绑定启动的值了
OneWayToSource : 与OneTime一样,对象相反
Default : 默认单项或双向
Source
<Window.Resources>
<TextBox x:Key="txt1">ABC</TextBox>
</Window.Resources>
<Grid>
<TextBox Text="{Binding Source={StaticResource txt1}, Path=Text}" />
</Grid>
RelativeSource
查看相关标签的数据
<StackPanel Width="20">
<StackPanel Width="60">
<TextBlock Text="{Binding Path=Width,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}}" />
</StackPanel>
</StackPanel>
DataContext
与代码数据绑定
<Grid>
<DataGrid Grid.Row="1" ItemsSource="{Binding Students}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="名称" />
<DataGridTextColumn Binding="{Binding Age}" Header="年龄" />
<DataGridTextColumn Binding="{Binding Sex}" Header="性别" />
</DataGrid.Columns>
</DataGrid>
</Grid>
绑定
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
PageModel page = new PageModel();
page.Students = new List<Student>();
page.Students.Add(new Student() { Name = "张三", Age = "18", Sex = "男" });
page.Students.Add(new Student() { Name = "李四", Age = "19", Sex = "男" });
page.Students.Add(new Student() { Name = "王二", Age = "20", Sex = "女" });
this.DataContext = page;
}
}
public class PageModel
{
public List<Student> ?Students { get; set; }
}
public class Student
{
public string? Name { get; set; }
public string? Age { get; set; }
public string? Sex { get; set; }
}
在开发大型项目时收益高
我们的页面设计xaml和我们的数据分开,利用框架,可以二者完全分离
在MainWindow.xaml中设置界面和绑定(数据,函数(函数参数))
在MainViewModel.cs中来实现数据的处理和提供函数接口,(替换原生在MainWindow.xaml.cs里来设置响应)
让前后端的工作分开,专心处理一种逻辑
有很多MVVM框架,这里使用prism
把页面划分为区域,用区域便于设置
安装框架 : 项目->管理NuGet程序包->Prism.DryIoc(Prism Dry Inversion of Control)
NuGet(发音为"New Get")是一个用于.NET平台的包管理系统
对App.xaml进行操作
cs
修改继承并重写方法
public partial class App : PrismApplication
{
protected override Window CreateShell()
{//初始页
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{//用于注册要实现的页面
}
}
xaml
添加命名空间prism并删除StartupUri
<prism:PrismApplication x:Class="WPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF"
xmlns:prism ="http://prismlibrary.com/">
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>
扩展->管理扩展->Prism Template Pack->然后新建该模板的项目,就会自动配置好上面使用框架的内容
框架自动生成两个文件夹 :
Views用来存放我们的界面
ViewModels来存放我们的指令
只要放在这两个文件夹下就会自动关联,不需要麻烦的指定
然后就是了解region,在框架中,界面通过region来控制.
在MainWindow.xaml
代码中定义region
<ContentControl prism:RegionManager.RegionName="ContentRegion" Grid.ColumnSpan="2" />
如果需要显示,在MainWindowViewModel.cs
中注册区域,
下面将显示ViewA这个界面
public class MainWindowViewModel : BindableBase
{
private IRegionManager regionManager;
public MainWindowViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
}
}
创建 -> 模块
模块的ModuleAModule.cs
文件
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
然后主程序的App
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModuleA.ModuleAModule>();
}
}
需要下载.NET Multi_platform App UI开发
新建->.NET MAUI->.net6.0->运行
因为跨平台,没办法像WPF那样实时渲染,得自己运行来看效果,不过确实方便
因为跨平台,开发成本低,运行成本高
用虚拟机或真机
对所有输入一定要谨慎,(正则匹配),防止胡乱的输入让程序崩溃,即提高鲁棒性
如在计算器中输入1***************1*