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


    实现的效果

    最后的实现效果

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

    实现过程

    绘制矩形

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

    现在查看实现的效果:

    点击单元格改变颜色的效果

    将每个单元格与其中的信息对应起来

    在这里可以发现每个单元格与其中的信息是一一对应的关系,我们就可以采用字典这种数据结构。

    Dictionarystring> rectangleText = new Dictionarystring>();
    
     // 将单元格与对应的信息存入字典
     rectangleText[rectangle] = textBlock.Text;
    

    这样就实现了每个单元格与其中信息的一一对应。

    ListBox的使用

    首先设计两个类。

    public class SelectedRect
    {      
        public string? Name {  get; set; }
    }
    

    表示选中的单元格,只有一个属性就是它所存储的信息。

    public class SelectedRects : ObservableCollection<SelectedRect>
    {
    
    }
    

    表示选中的多个单元格,继承自ObservableCollection

    ObservableCollection是.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设置数据绑定:

     
        
     
    

    现在来看看效果:

    image-20240419093037392

    我们会发现在ListBox中只会显示类名,并不会显示类中的信息。

    这是为什么呢?

    因为我们只设置了数据绑定,ListBox知道它的数据来自哪里了,但是我们没有设置数据模板,ListBox不知道该按怎样的方式显示数据。

    数据模板的使用

    现在我们就来设置一下数据模板,先来介绍一下数据模板。

    WPF(Windows Presentation Foundation)中,数据模板(DataTemplate)是一种定义数据视觉表示的方式。它允许你自定义如何显示绑定到控件的数据。

    数据模板非常强大,它可以包含任何类型的元素,并可以使用复杂的绑定和样式。通过使用数据模板,你可以创建丰富和个性化的UI,而无需在代码中手动创建和管理元素。

    现在开始尝试去使用数据模板吧。

    在xaml中添加:

    
        
            
        
    
    

    :这是一个资源字典,它包含了在整个窗口中都可以使用的资源。在这个例子中,它包含了一个数据模板。
    :这定义了一个数据模板,并给它指定了一个键"MyTemplate"。这个键可以用来在其他地方引用这个模板。
    :这是数据模板的内容。它是一个TextBlock,其Text属性绑定到数据对象的Name属性。{Binding Path=Name}是一个绑定表达式,它告诉WPF查找数据对象中名为Name的属性,并将其值绑定到TextBlock的Text属性。

    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:

    
        
            
                
            
        
        
            
                
                    
                
                
                    

    cs:

    namespace xxx
    {
        /// 
        /// Drawing.xaml 的交互逻辑
        /// 
        public partial class Drawing : System.Windows.Window
        {
            Dictionarystring> rectangleText = new Dictionarystring>();
            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感兴趣的同学有所帮助。

  • 相关阅读:
    ABAP CLEAR REFRESH FREE 说明(刘欣)
    ZooKeeper 核心知识总结
    设计模式之策略模式(一)
    CSP-J第二轮试题-2022年-1.2题
    【unity2023打包安卓工程】踩坑记录
    linux系统---安装使用nginx
    微信小程序的生命周期概览
    通过 Azure OpenAI 服务使用 GPT-35-Turbo and GPT-4(win版)
    计算机毕业设计Java山西农谷企业产品推广展网(源码+系统+mysql数据库+lw文档)
    linux 不依赖xcode成功运行appium+ios自动化
  • 原文地址:https://www.cnblogs.com/mingupupu/p/18145295