实现的效果
如果你对此感兴趣,可以接着往下阅读。
实现过程
绘制矩形
比如说我想绘制一个3行4列的表格:
private void Button_Click_DrawRect(object sender, RoutedEventArgs e) { int Row = 3; int Col = 4; for(int i = 0; i < Row; i++) { for(int j = 0; j< Col; j++) { // 添加矩形 System.Windows.Shapes.Rectangle rectangle = new System.Windows.Shapes.Rectangle { Width = 50, Height = 50, Stroke = System.Windows.Media.Brushes.Blue, // 设置填充颜色为透明色 Fill = System.Windows.Media.Brushes.Transparent, StrokeThickness = 1 }; Canvas.SetLeft(rectangle, 80 + 50 * j); Canvas.SetTop(rectangle, 50 + 50 * i); myCanvas1.Children.Add(rectangle); } }
实现的效果:
现在又想画4行3列的表格了,只需修改这里:
int Row = 4; int Col = 3;
实现的效果:
为每个单元格添加信息
绘制了单元格之后,我们想要在单元格中添加它所在的行与列的信息。
在绘制矩形后面添加:
// 在矩形内部添加文字 TextBlock textBlock = new TextBlock { Text = i + "-" + j, Foreground = System.Windows.Media.Brushes.Black, FontSize = 12 }; Canvas.SetLeft(textBlock, 80 + 50 * j + 10); Canvas.SetTop(textBlock, 50 + 50 * i + 10); myCanvas1.Children.Add(textBlock);
现在实现的效果如下所示:
让每个单元格可以被选中与取消选中
我们设定鼠标左键点击表示选中,鼠标右键点击表示取消选中,选中之后,单元格边框会变红,取消选中后又恢复原来的颜色。
为每个单元格添加鼠标点击事件处理程序:
// 添加鼠标事件处理器,左键点击表示选中 rectangle.MouseLeftButtonDown += Rectangle_MouseLeftButtonDown; // 添加鼠标事件处理器,右键点击表示取消选中 rectangle.MouseRightButtonDown += Rectangle_MouseRightButtonDown;
鼠标点击事件处理程序:
// 鼠标事件处理程序,左键点击表示选中 private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被选中 rectangle.Stroke = System.Windows.Media.Brushes.Red; } } // 鼠标事件处理器,右键点击表示选中 private void Rectangle_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被取消选中 rectangle.Stroke = System.Windows.Media.Brushes.Blue; } }
现在查看实现的效果:
将每个单元格与其中的信息对应起来
在这里可以发现每个单元格与其中的信息是一一对应的关系,我们就可以采用字典这种数据结构。
Dictionary<System.Windows.Shapes.Rectangle, string> rectangleText = new Dictionary<System.Windows.Shapes.Rectangle, string>();
// 将单元格与对应的信息存入字典 rectangleText[rectangle] = textBlock.Text;
这样就实现了每个单元格与其中信息的一一对应。
ListBox的使用
首先设计两个类。
public class SelectedRect { public string? Name { get; set; } }
表示选中的单元格,只有一个属性就是它所存储的信息。
public class SelectedRects : ObservableCollection<SelectedRect> { }
表示选中的多个单元格,继承自ObservableCollection<SelectedRect>
。
ObservableCollection<T>
是.NET框架中的一个类,它表示一个动态数据集合,当添加、删除项或者刷新整个列表时,它会提供通知。这对于数据绑定非常有用,因为当集合改变时,UI可以自动更新以反映这些更改。
SelectedRects selectedRects; public Drawing() { InitializeComponent(); this.selectedRects = new SelectedRects(); DataContext = selectedRects; }
在WPF(Windows Presentation Foundation)
中,DataContext
是一个非常重要的概念,它是数据绑定的基础。
DataContext
是定义在FrameworkElement
类中的一个属性,几乎所有的WPF控件都继承自FrameworkElement
,因此几乎所有的WPF控件都有DataContext
属性。
DataContext
属性通常被设置为一个对象,这个对象包含了绑定到界面元素的数据。当你在XAML中创建数据绑定时,绑定表达式会查找DataContext
中的属性。
需要注意的是,DataContext
是可以继承的,如果一个元素的DataContext
没有被显式设置,它将使用其父元素的DataContext
。这使得你可以在窗口级别设置DataContext
,然后在窗口的所有子元素中使用数据绑定。
在这里我们就是这样设置了窗口的DataContext
属性为selectedRects
。
现在我们修改点击事件处理程序:
private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被选中 rectangle.Stroke = System.Windows.Media.Brushes.Red; string text = rectangleText[rectangle]; SelectedRect selectedRect = new SelectedRect(); selectedRect.Name = text; selectedRects.Add(selectedRect); } } private void Rectangle_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被取消选中 rectangle.Stroke = System.Windows.Media.Brushes.Blue; string text = rectangleText[rectangle]; var selectedRect = selectedRects.Where(x => x.Name == text).FirstOrDefault(); if (selectedRect != null) { selectedRects.Remove(selectedRect); } } }
在ListBox设置数据绑定:
<ListBox Grid.Column="1" SelectedIndex="0" Margin="10,0,10,0" ItemsSource="{Binding}"> </ListBox>
现在来看看效果:
我们会发现在ListBox中只会显示类名,并不会显示类中的信息。
这是为什么呢?
因为我们只设置了数据绑定,ListBox知道它的数据来自哪里了,但是我们没有设置数据模板
,ListBox不知道该按怎样的方式显示数据。
数据模板的使用
现在我们就来设置一下数据模板,先来介绍一下数据模板。
在WPF(Windows Presentation Foundation)
中,数据模板(DataTemplate)
是一种定义数据视觉表示的方式。它允许你自定义如何显示绑定到控件的数据。
数据模板非常强大,它可以包含任何类型的元素,并可以使用复杂的绑定和样式。通过使用数据模板,你可以创建丰富和个性化的UI,而无需在代码中手动创建和管理元素。
现在开始尝试去使用数据模板吧。
在xaml中添加:
<Window.Resources> <DataTemplate x:Key="MyTemplate"> <TextBlock Text="{Binding Path=Name}"/> </DataTemplate> </Window.Resources>
<Window.Resources>
:这是一个资源字典,它包含了在整个窗口中都可以使用的资源。在这个例子中,它包含了一个数据模板。
<DataTemplate x:Key="MyTemplate">
:这定义了一个数据模板,并给它指定了一个键"MyTemplate"。这个键可以用来在其他地方引用这个模板。
<TextBlock Text="{Binding Path=Name}"/>
:这是数据模板的内容。它是一个TextBlock,其Text属性绑定到数据对象的Name属性。{Binding Path=Name}
是一个绑定表达式,它告诉WPF查找数据对象中名为Name的属性,并将其值绑定到TextBlock的Text属性。
让ListBox
使用这个数据模板:
<ListBox Grid.Column="1" SelectedIndex="0" Margin="10,0,10,0" ItemsSource="{Binding}" ItemTemplate="{StaticResource MyTemplate}"> </ListBox>
现在再来看一下效果:
发现可以正常显示数据了,但是还有一个问题,就是会重复添加,最后解决这个问题就好了!
修改鼠标左键点击事件处理程序:
// 鼠标事件处理器,左键点击表示选中 private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被选中 rectangle.Stroke = System.Windows.Media.Brushes.Red; string text = rectangleText[rectangle]; if (selectedRects.Where(x => x.Name == text).Any()) { } else { SelectedRect selectedRect = new SelectedRect(); selectedRect.Name = text; selectedRects.Add(selectedRect); } } }
现在再来看看最后的效果:
全部代码
xaml:
<Window x:Class="" 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="" xmlns:hc="https://handyorg.github.io/handycontrol" mc:Ignorable="d" Title="Drawing" Height="450" Width="800"> <Window.Resources> <DataTemplate x:Key="MyTemplate"> <TextBlock Text="{Binding Path=Name}"/> </DataTemplate> </Window.Resources> <StackPanel> <hc:Row Margin="0,20,0,0"> <hc:Col Span="8"> <Label Content="画矩形"></Label> </hc:Col> <hc:Col Span="8"> <Button Style="{StaticResource ButtonPrimary}" Content="开始" Click="Button_Click_DrawRect"/> </hc:Col> <hc:Col Span="8"> <Button Style="{StaticResource ButtonPrimary}" Content="清空" Click="Button_Click_Clear"/> </hc:Col> </hc:Row> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Canvas Grid.Column="0" Background="Azure" x:Name="myCanvas1" Height="400"> <!-- 在这里添加你的元素 --> </Canvas> <ListBox Grid.Column="1" SelectedIndex="0" Margin="10,0,10,0" ItemsSource="{Binding}" ItemTemplate="{StaticResource MyTemplate}"> </ListBox> </Grid> </StackPanel> </Window>
cs:
namespace xxx { /// <summary> /// Drawing.xaml 的交互逻辑 /// </summary> public partial class Drawing : System.Windows.Window { Dictionary<System.Windows.Shapes.Rectangle, string> rectangleText = new Dictionary<System.Windows.Shapes.Rectangle, string>(); SelectedRects selectedRects; public Drawing() { InitializeComponent(); this.selectedRects = new SelectedRects(); DataContext = selectedRects; } private void Button_Click_DrawRect(object sender, RoutedEventArgs e) { int Row = 4; int Col = 3; for(int i = 0; i < Row; i++) { for(int j = 0; j< Col; j++) { // 添加矩形 System.Windows.Shapes.Rectangle rectangle = new System.Windows.Shapes.Rectangle { Width = 50, Height = 50, Stroke = System.Windows.Media.Brushes.Blue, // 设置填充颜色为透明色 Fill = System.Windows.Media.Brushes.Transparent, StrokeThickness = 1 }; // 添加鼠标事件处理器,左键点击表示选中 rectangle.MouseLeftButtonDown += Rectangle_MouseLeftButtonDown; // 添加鼠标事件处理器,右键点击表示取消选中 rectangle.MouseRightButtonDown += Rectangle_MouseRightButtonDown; Canvas.SetLeft(rectangle, 80 + 50 * j); Canvas.SetTop(rectangle, 50 + 50 * i); myCanvas1.Children.Add(rectangle); // 在矩形内部添加文字 TextBlock textBlock = new TextBlock { Text = i + "-" + j, Foreground = System.Windows.Media.Brushes.Black, FontSize = 12 }; Canvas.SetLeft(textBlock, 80 + 50 * j + 10); Canvas.SetTop(textBlock, 50 + 50 * i + 10); myCanvas1.Children.Add(textBlock); // 将单元格与对应的信息存入字典 rectangleText[rectangle] = textBlock.Text; } } } private void Button_Click_Clear(object sender, RoutedEventArgs e) { myCanvas1.Children.Clear(); } // 鼠标事件处理器,左键点击表示选中 private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被选中 rectangle.Stroke = System.Windows.Media.Brushes.Red; string text = rectangleText[rectangle]; if (selectedRects.Where(x => x.Name == text).Any()) { } else { SelectedRect selectedRect = new SelectedRect(); selectedRect.Name = text; selectedRects.Add(selectedRect); } } } // 鼠标事件处理器,右键点击表示选中 private void Rectangle_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { System.Windows.Shapes.Rectangle? rectangle = sender as System.Windows.Shapes.Rectangle; if (rectangle != null) { // 改变矩形的颜色以表示它被取消选中 rectangle.Stroke = System.Windows.Media.Brushes.Blue; string text = rectangleText[rectangle]; var selectedRect = selectedRects.Where(x => x.Name == text).FirstOrDefault(); if (selectedRect != null) { selectedRects.Remove(selectedRect); } } } } }
总结
本文通过一个小示例,跟大家介绍了如何在WPF上绘制矩形,并在其中添加文本,同时也介绍了ListBox的使用,通过数据绑定与数据模板显示我们选中的单元格内的文本信息。希望对与我一样正在学习WPF或者对WPF感兴趣的同学有所帮助。