3

WPF/C#:让绘制的图形可以被选中并将信息显示在ListBox中 - mingupupup

 2 weeks ago
source link: https://www.cnblogs.com/mingupupu/p/18145295
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

实现的效果

最后的实现效果

如果你对此感兴趣,可以接着往下阅读。

比如说我想绘制一个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);
            
         }
        
       
     }

实现的效果:

image-20240418110949637

现在又想画4行3列的表格了,只需修改这里:

int Row = 4;
int Col = 3;

实现的效果:

image-20240418111330852

为每个单元格添加信息

绘制了单元格之后,我们想要在单元格中添加它所在的行与列的信息。

在绘制矩形后面添加:

 // 在矩形内部添加文字
 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);

现在实现的效果如下所示:

image-20240419084117023

让每个单元格可以被选中与取消选中

我们设定鼠标左键点击表示选中,鼠标右键点击表示取消选中,选中之后,单元格边框会变红,取消选中后又恢复原来的颜色。

为每个单元格添加鼠标点击事件处理程序:

 // 添加鼠标事件处理器,左键点击表示选中
 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>

现在来看看效果:

image-20240419093037392

我们会发现在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>

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感兴趣的同学有所帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK