-QuQ-
Articles11
Tags6
Categories0

Archive

WinUI3基础

WinUI3基础

WinUI3 简介

WinUI3 是微软推出的又一个 Windows UI 框架

微软推出的 UI 框架按时间顺序如下:WinForm -> WPF -> UWP -> WinUI3

WinUI3 使用 xmal 编写界面,使用 C# 编写逻辑

WinUI3 微软官方文档

一个很无语的点是… WinUI3 到现在都没有设!计!器!! 微软画的饼,官方文档中提到 xaml 热重载可以一定程度上代替设计器,但是部分情况下无法热重载…

基础使用

WinUI3 Gallery, 这是一个所有控件与样式的演示 APP

控件

数据绑定

Data Binding

绑定就是一根连接 UI 和数据的绳索,每个绑定由绑定源和绑定目标组成

  • 绑定源:类示例(ViewModel)的属性
  • 绑定目标:UI 中元素属性

这部分我们会实现一个歌曲选择功能。

界面上有一个歌曲列表,点击对应歌曲能显示出歌曲详细信息


一种实现绑定的方式是将 ViewModel 作为窗口类的成员添加,并在 UI 的构造函数中初始化

// Model 与 ViewModel 定义
namespace DataBinding
{
	public class Recording
	{
		public string ArtistName { get; set; }
		public string CompisitionName { get; set; }
		public DateTime ReleaseDateTime { get; set; }

		public Recording()
		{
			ArtistName = "Supercell(ryo)";
			CompisitionName = "メルト";
			ReleaseDateTime = new DateTime(2007, 12, 7);
		}

		public string OneLineSummary => $"{CompisitionName} by {ArtistName}, released: {ReleaseDateTime.ToString("d")}";
	}

	public class RecordingViewModel
	{
		private Recording defaultRecording = new Recording();
		public Recording DefaultRecording => defaultRecording;
	}
}
// ViewModel 添加到窗口
public sealed partial class MainWindow : Window
{
	public MainWindow()
	{
		this.InitializeComponent();

		ViewModel = new RecordingViewModel(); // 初始化
	}

	public RecordingViewModel ViewModel { get; set; } // 添加为成员
}
<!-- 窗口中绑定到 ViewModel -->
<Window
	x:Class="DataBinding.MainWindow" ...> 
	<!-- 这是 xaml CodeBehind 的类名,如果没有的话会识别不到下面 Bind 的 ViewModel -->

	<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
		<!-- 绑定内容 -->
		<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"/>
	</StackPanel>
</Window>

结果如下:

alt text


上面绑定的是单个数据,如果我有一组数据呢?

使用 ObservableCollection<T>

// 数据集合的容器
public class RecordingViewModel
{
	...
	private ObservableCollection<Recording> recordings = new ObservableCollection<Recording>();
	public ObservableCollection<Recording> Recordings => recordings;

	public RecordingViewModel()
	{
		recordings.Add(new Recording() { ProducerName = "ika", SongName = "君のことミクミクにしてあげる", ReleaseDateTime = new DateTime(2007, 09, 20) });
		recordings.Add(new Recording() { ProducerName = "cosMo@暴走P", SongName = "初音ミクの消失", ReleaseDateTime = new DateTime(2008, 04, 08) });
		recordings.Add(new Recording() { ProducerName = "ryo(supercell)", SongName = "World is Mine", ReleaseDateTime = new DateTime(2008, 05, 31) });
	}
}
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
	<TextBlock Text="{x:Bind ViewModel.DefaultRecording.OneLineSummary}"/>
	<!-- 数据集合绑定方式 -->
	<ListView ItemsSource="{x:Bind ViewModel.Recordings}"/>
</StackPanel>

结果如下:

alt text

因为我们还没有给 Recording 类提供数据模板,所以没有内容显示

数据模板如下:

<ListView ItemsSource="{x:Bind ViewModel.Recordings}">
	<!-- 数据模板定义 -->
	<ListView.ItemTemplate>
		<!-- 此处的 local 在 Window 中的xmlns:local定义 -->
		<DataTemplate x:DataType="local:Recording">
			<!-- 数据模板相当于将单个数据从数据集合中抽出来,自然就能使用单个数据的属性 -->
			<TextBlock Text="{x:Bind OneLineSummary}"/>
		</DataTemplate>
	</ListView.ItemTemplate>
</ListView>

结果如下:

alt text


这种一股脑把数据全部丢出来显示的方式会占用很多屏幕空间

一种更好的方式是点击哪一条就显示哪个的信息

<Grid>
	<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
		<ListView x:Name="recordingsListView" ItemsSource="{x:Bind ViewModel.Recordings}">
			<ListView.ItemTemplate>
				<DataTemplate x:DataType="local:Recording">
					<StackPanel Orientation="Horizontal" Margin="6">
						<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
						<StackPanel>
							<TextBlock Text="{x:Bind SongName}"/>
						</StackPanel>
					</StackPanel>
				</DataTemplate>
			</ListView.ItemTemplate>
		</ListView>
		<!-- 上面还是一个 ListView,只显示曲名 -->
		<!-- 下面设置 StackPanel 的 DataContext为 SelectedItem,显示被选择对象的属性 -->
		<StackPanel DataContext="{Binding SelectedItem, ElementName=recordingsListView}"
								Margin="0,24,0,0">
			<TextBlock Text="{Binding ProducerName}"/>
			<TextBlock Text="{Binding SongName}"/>
			<TextBlock Text="{Binding ReleaseDateTime}"/>
		</StackPanel>
	</StackPanel>
</Grid>

注意上面 selectedItem 使用了 Binding 而不是 x:Bind,后面会有对这两种方式的分析

  • x:Bind 会有对类属性的自动补全
  • Binging 没有自动补全,性能比 x:Bind 低,但是更灵活

刚刚我们使用的是 SelectedItem,还可以将 ViewModel.Recordings 注册为资源进行绑定

<Grid>
	<!-- 将 ViewModel.Recordings 注册为 Grid 静态资源 -->
	<!-- 由于 Window 没有 Resources 属性,所以此处资源是注册到顶级 Grid 中 -->
	<!-- Page 有 Resoueces 属性,资源可以直接写 Page 里面 -->
	<Grid.Resources>
		<CollectionViewSource x:Name="Recordingscollection" Source="{x:Bind ViewModel.Recordings}"/>
	</Grid.Resources>
	
	<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
		<ListView ItemsSource="{Binding Source={StaticResource Recordingscollection}}">
			<ListView.ItemTemplate>
				<DataTemplate x:DataType="local:Recording">
					<StackPanel Orientation="Horizontal" Margin="6">
						<SymbolIcon Symbol="Audio" Margin="0,0,12,0"/>
						<StackPanel>
							<TextBlock Text="{x:Bind SongName}"/>
						</StackPanel>
					</StackPanel>
				</DataTemplate>
			</ListView.ItemTemplate>
		</ListView>
		
		<!-- 使用静态资源作为 DataContext -->
		<!-- 也就是说 SatckPanel 内部所有绑定都会使用 Recordingscollection 作为数据源 --> 
		<StackPanel DataContext="{Binding Source={StaticResource Recordingscollection}}"
					Margin="0,24,0,0">
			<TextBlock Text="{Binding ProducerName}"/>
			<TextBlock Text="{Binding SongName}"/>
			<TextBlock Text="{Binding ReleaseDateTime}"/>
		</StackPanel>
	</StackPanel>
</Grid>

两种方法的外观都是一样的

alt text

但是时间这一栏还是一个 DateTime, 显示的过于精确。我们不想显示秒数

你可以在 Recordings 类中将 ReleaseDateTimeget 属性返回ToString("d")

但是这可能会带来别的麻烦。如果其他地方需要秒数那就不行了

你还可以写两个属性… 一个给 UI, 另一个给其他地方。但是这太 SB 了

更灵活的解决方案是使用值转换器:

public class StringFormatter : Microsoft.UI.Xaml.Data.IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, string language)
	{
		// value 是绑定传来的数据 (RealeaseDateTime: 2007/9/20 0:00:00)
		// parameter 是 xaml 里面传的参数 (Released:{0:d})
		string? formatString = parameter as string;
		if (!string.IsNullOrEmpty(formatString))
		{
			return string.Format(formatString, value);
		}

		return value.ToString();
	}

	// 单向绑定不需要实现这个
	public object ConvertBack(object value, Type targetType, object parameter, string language)
	{
		throw new NotImplementedException();

	}
}
``` xml
...
<Grid.Resources>
	<CollectionViewSource x:Name="RecordingsCollection" Source="{x:Bind ViewModel.Recordings}"/>
	<local:StringFormatter x:Key="StringFormatterValueConverter"/>
</Grid.Resources>
...
<TextBlock Text="{Binding ReleaseDateTime, Converter={StaticResource StringFormatterValueConverter}, ConverterParameter=Released:\{0:d\}}"/>
...

结果如下:

alt text


以上就是数据绑定的基本使用,在这一部分我们提到了

  1. 绑定基本方式: ViewModel 作为成员添加到 UI
  2. 单个数据的绑定: x:Bind
  3. 数据集合的绑定: ObservableCollection<> 添加到 BindingSource

更多内容:

Author:-QuQ-
Link:http://qqqquq.me/2025/03/01/WinUI3%E5%9F%BA%E7%A1%80/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可