首页 > 编程语言 >WPF/C#:让绘制的图形可以被选中并将信息显示在ListBox中

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

时间:2024-04-19 10:44:49浏览次数:19  
标签:C# System Shapes Windows 选中 Rectangle WPF ListBox rectangle

实现的效果

最后的实现效果

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

实现过程

绘制矩形

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

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

标签:C#,System,Shapes,Windows,选中,Rectangle,WPF,ListBox,rectangle
From: https://www.cnblogs.com/mingupupu/p/18145295

相关文章

  • UE虚幻引擎CSV转Excel
    #CSV转Excel>-数据表导入/导出Excel用法>-####物品数据表导出CSV>>-描述:我导出的文件名字物品数据表.csv作为案例大家可以随意名字,记得备份一份导出的物品数据表.csv>>  --->-####打开Excel>>-点击数据->从文本/CSV->物品数据表.csv//物品数据表导......
  • docker容器内部CLOSE_WAIT调优
    这2天遇见一个神奇的事情使用netstat-pan|grepCLOSE_WAIT|wc-l命令对docker宿主机上CLOSE_WAIT状态统计出来为0,进入到容器内部发现CLOSE_WAIT状态已经600多了。来吧,操作起来,先研究研究官方文档https://docs.docker.com/compose/compose-file/05-services/如何配置首先我们需......
  • [491] Non-decreasing Subsequences
    算法助手用户:这个题目有什么好的思路吗?“Givenanintegerarraynums,returnallthedifferentpossiblenon-decreasingsubsequencesofthegivenarraywithatleasttwoelements.Youmayreturntheanswerinanyorder.”我的代码是这样的:/**@lcapp=leetcod......
  • cache和buffer
    buffer,缓冲区,buffers是给写入数据加速的Cache,缓存,Cached是给读取数据时加速的Cachecache是指,把读取磁盘而来的数据保存在内存中,再次读取不用读取硬盘,而直接从内存中读取,加速数据读取过程。Cache,缓存,Cached是给读取数据时加速的硬件读取速度排名磁盘<内存<CPU磁盘最......
  • yolo,rcnn,fastrcnn,ssd等算法有的区别
    chatgpt回答:YOLO(YouOnlyLookOnce),RCNN(Region-basedConvolutionalNeuralNetworks),FasterR-CNN,SSD(SingleShotMultiBoxDetector)等算法都是用于目标检测的经典算法,它们在实现目标检测任务时有一些区别。YOLO:YOLO是一种单阶段(single-stage)目标检测算......
  • c# .net core 将对应的语句转成dsl
    痛点:遇到一个问题,代码嵌套的es查询逻辑,太乱,太复杂。通过代码直观人工去转dsl发现根本不可能。后老看到调试模式能拿到dsl,方法如下https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/debug-mode.html使用debug模式,按文档的设置。设置完成之后,按文档https:......
  • Qt:通过QCompleter类提供的补全框completion box, 根据用户输入的内容提供可能的补全选
    1.创建一个字符串列表作为补全数据源QStringListwordlist;wordlist<<"apple"<<"banana"<<"orange";2.将QCompleter的数据源设置为之前创建的QStringList,与QLineEdit控件关联QCompleter*completer=newQCompleter(wordlist,this);......
  • docker - [03] docker原理
    题记 一、Docker是怎么工作的Docker是一个CS(Client-Server)结构的系统,Docker的守护进程运行在主机上,通过Socket从客户端访问、DockerServer接收到DockerClient的指令就会执行、 二、Docker为什么比虚拟机快1、docker有着比虚拟机更少的抽象层2、docker利用的是宿主......
  • [转]AspNetCore容器化(Docker)部署(二) —— 多容器通信
    一.前言着上一篇 AspNetCore容器化(Docker)部署(一)——入门,在单个容器helloworld的基础上引入nginx反向代理服务器组成多容器应用。 二.配置反向代理转接配置转接头。详见:https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-2.2......
  • [转]AspNetCore容器化(Docker)部署(一) —— 入门
    一.docker注册安装WindowsDockerDesktop https://www.docker.com/products/docker-desktopLinuxDockerCE https://docs.docker.com/install/linux/docker-ce/ubuntu/本文使用Windows环境Linuxcontainer做演示,安装完后切换到Linuxcontainer,“SwitchtoLinuxcontainers......