實現的效果
如果你對此感興趣,可以接著往下閱讀。
實現過程
繪製矩形
比如說我想繪製一個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感興趣的同學有所幫助。