优化 WPF 中由单元格组成的网格以获得最短路径

2024-05-11

我目前正在尝试在 WPF 中制作一个由 Cell 对象组成的网格。我需要将单元格绑定到对象,该对象需要位于二维数组中。 - 我需要它很大,可扩展,并改变单元格的颜色并将数据存储在对象中!

我已经实现了,但是绘制网格似乎很慢! (100x100 网格需要 >10 秒!) 这是我已经制作的图片:

我在 ItemsControl 的 XAML 中使用数据绑定。这是我的 XAML:

<ItemsControl x:Name="GridArea" ItemsSource="{Binding Cellz}" Grid.Column="1" BorderBrush="Black" BorderThickness="0.1">
        <ItemsControl.Resources>
            <DataTemplate DataType="{x:Type local:Cell}">
                <Border BorderBrush="Black" BorderThickness="0.1">
                    <Grid Background="{Binding CellColor}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="MouseMove" >
                                <ei:CallMethodAction TargetObject="{Binding}" MethodName="MouseHoveredOver"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                    </Grid>
                </Border>

            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Rows="{Binding Rows}" Columns="{Binding Columns}" MouseDown="WrapPanelMouseDown" MouseUp="WrapPanelMouseUp" MouseLeave="WrapPanelMouseLeave" >
                    <!--<UniformGrid.Background>
                        <ImageBrush/>
                    </UniformGrid.Background>-->
                </UniformGrid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

在我的代码隐藏中,我实例化了一个名为 Grid 的类的对象,它使用 Cell 类的对象创建一个 2D 数组(和一个列表,这是我绑定的列表)。用秒表检查后,我发现这并不花时间。它是实际的绑定和绘制网格,所以我想我的优化应该在我的 XAML 中进行,如果有任何优化可用的话。

但为了提供一切,这是我的代码以及网格类和单元格类:

    public MainWindow()
    {
        InitializeComponent();  

        NewGrid = new Grid(75, 75);
        DataContext = NewGrid;

    }

    public class Grid
    {
    public int Columns { get; set; }
    public int Rows { get; set; }

    public ObservableCollection<Cell> Cellz {get;set;}

    public Cell[,] CellArray { get; set; }

    public Grid(int columns, int rows)
    {
        Columns = columns;
        Rows = rows;

        Cellz = new ObservableCollection<Cell>();
        CellArray = new Cell[Rows,Columns];
        InitializeGrid();

    }

    public void InitializeGrid()
    {
        Color col = Colors.Transparent;
        SolidColorBrush Trans = new SolidColorBrush(col);
        for (int i = 0; i < Rows; i++)
        {
            for (int j = 0; j < Columns; j++)
            {
                var brandNewCell = new Cell(i, j) { CellColor = Trans};
                Cellz.Add(brandNewCell);
                CellArray[i, j] = brandNewCell;
            }
        }
    }

    public class Cell : INotifyPropertyChanged
    {
        public int x, y;   // x,y location
        public Boolean IsWall { get; set; }

        private SolidColorBrush _cellcolor;
        public SolidColorBrush CellColor
        {
            get { return _cellcolor; }
            set
            {
                _cellcolor = value;
                OnPropertyChanged();
            }
        }
        public Cell(int tempX, int tempY)
        {
            x = tempX;
            y = tempY;
        }


        public bool IsWalkable(Object unused)
        {
            return !IsWall;
        }
    public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(
            [CallerMemberName] string caller = "")
        {
            if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
        }
    }
}

我喜欢带有绑定的非常简单的实现,但是加载时间确实令人无法接受 - 任何建议将不胜感激!


好吧,我重新创建了您的示例,并进行了一些更改。我主要摆脱了 DataContext 上的绑定,并专门为您的用例创建了一个视图模型,该视图模型直接绑定到 itemscontrol。

绘图速度肯定低于 10 秒,但我想我给了你尽可能多的相关代码,这样你就可以比较解决方案......

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using TestSO.model;

namespace TestSO.viewmodel
{
    public class ScreenViewModel : INotifyPropertyChanged, IDisposable
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private IList<Cell> cells;
        public IList<Cell> Cells
        {
            get
            {
                return cells;
            }
            set
            {
                if (object.Equals(cells, value))
                {
                    return;
                }
                UnregisterSource(cells);
                cells = value;
                RegisterSource(cells);
                RaisePropertyChanged("Cells");
            }
        }

        private int rows;
        public int Rows
        {
            get
            {
                return rows;
            }
            set
            {
                if (rows == value)
                {
                    return;
                }
                rows = value;
                RaisePropertyChanged("Rows");
            }
        }

        private int columns;
        public int Columns
        {
            get
            {
                return columns;
            }
            set
            {
                if (columns == value)
                {
                    return;
                }
                columns = value;
                RaisePropertyChanged("Columns");
            }
        }

        private Cell[,] array;
        public Cell[,] Array
        {
            get
            {
                return array;
            }
            protected set
            {
                array = value;
            }
        }

        protected void RaisePropertyChanged(string propertyName)
        {
            var local = PropertyChanged;
            if (local != null)
            {
                App.Current.Dispatcher.BeginInvoke(local, this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void RegisterSource(IList<Cell> collection)
        {
            if (collection == null)
            {
                return;
            }
            var colc = collection as INotifyCollectionChanged;
            if (colc != null)
            {
                colc.CollectionChanged += OnCellCollectionChanged;
            }
            OnCellCollectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, collection, null));
        }

        protected virtual void OnCellCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                Array = null;
            }
            if (e.OldItems != null)
            {
                foreach (var item in e.OldItems)
                {
                    var cell = item as Cell;
                    if (cell == null)
                    {
                        continue;
                    }
                    if (Array == null)
                    {
                        continue;
                    }
                    Array[cell.X, cell.Y] = null;
                }
            }
            if (e.NewItems != null)
            {
                if (Array == null)
                {
                    Array = new Cell[Rows, Columns];
                }
                foreach (var item in e.NewItems)
                {
                    var cell = item as Cell;
                    if (cell == null)
                    {
                        continue;
                    }
                    if (Array == null)
                    {
                        continue;
                    }
                    Array[cell.X, cell.Y] = cell;
                }
            }
        }

        protected void UnregisterSource(IList<Cell> collection)
        {
            if (collection == null)
            {
                return;
            }
            var colc = collection as INotifyCollectionChanged;
            if (colc != null)
            {
                colc.CollectionChanged -= OnCellCollectionChanged;
            }
            OnCellCollectionChanged(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public ScreenViewModel()
        {
        }

        public ScreenViewModel(int row, int col)
            : this()
        {
            this.Rows = row;
            this.Columns = col;
        }

        bool isDisposed = false;
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (isDisposed)
                {
                    return;
                }
                isDisposed = true;
                Cells = null;
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
    }
}

我创建了一个额外的控制器,它是 ObservableCollection 的所有者,主要目的是不对 viewModel 进行任何更改,而是更改控制器内的集合(或向其添加添加、删除、清除方法) ),并让事件链为我完成工作,使 ScreenViewModel 中的二维数组保持最新

using System.Collections.Generic;
using System.Collections.ObjectModel;
using TestSO.model;

namespace TestSO.controller
{
    public class GenericController<T>
    {
        private readonly IList<T> collection = new ObservableCollection<T>();
        public IList<T> Collection
        {
            get
            {
                return collection;
            }
        }

        public GenericController()
        {
        }
    }

    public class CellGridController : GenericController<Cell>
    {
        public CellGridController()
        {
        }
    }
}

对于您的细胞类别,我稍微调整了它,仅引发更改事件,以防实际上发生更改

using System.ComponentModel;
using System.Windows;
using System.Windows.Media;

namespace TestSO.model
{
    public class Cell : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void RaisePropertyChanged(string propertyName)
        {
            var local = PropertyChanged;
            if (local != null)
            {
                Application.Current.Dispatcher.BeginInvoke(local, this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private int x;
        public int X
        {
            get
            {
                return x;
            }
            set
            {
                if (x == value)
                {
                    return;
                }
                x = value;
                RaisePropertyChanged("X");
            }
        }

        private int y;
        public int Y
        {
            get
            {
                return y;
            }
            set
            {
                if (y == value)
                {
                    return;
                }
                y = value;
                RaisePropertyChanged("Y");
            }
        }

        private bool isWall;
        public bool IsWall
        {
            get
            {
                return isWall;
            }
            set
            {
                if (isWall == value)
                {
                    return;
                }
                isWall = value;
                RaisePropertyChanged("IsWall");
            }
        }

        private SolidColorBrush _cellColor;
        public SolidColorBrush CellColor
        {
            get
            {
                // either return the _cellColor, or say that it is transparent
                return _cellColor ?? Brushes.Transparent;
            }
            set
            {
                if (SolidColorBrush.Equals(_cellColor, value))
                {
                    return;
                }
                _cellColor = value;
                RaisePropertyChanged("CellColor");
            }
        }

        public Cell()
        {
        }

        public Cell(int x, int y)
            : this()
        {
            this.X = x;
            this.Y = y;
        }
    }
}

然后我稍微更改了xaml(尽管没有接管交互点),通过为ScreenViewModel、控制器和DataTemplate创建资源,这个模板随后也通过ItemTemplate直接添加到ItemsControl中,而不是使用 DataTemplate 功能(没有看到上面的要求吗?)

<Window x:Class="TestSO.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:model="clr-namespace:TestSO.model"
        xmlns:viewmodel="clr-namespace:TestSO.viewmodel"
        xmlns:controller="clr-namespace:TestSO.controller"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <controller:CellGridController x:Key="CellController" />
        <viewmodel:ScreenViewModel x:Key="GridViewModel" Rows="75" Columns="75" />
        <DataTemplate x:Key="CellTemplate">
            <Border BorderBrush="Black" BorderThickness="0.5">
                <Grid Background="{Binding CellColor}">
                </Grid>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding Cells,Source={StaticResource GridViewModel}}" BorderBrush="Black" BorderThickness="0.1" ItemTemplate="{StaticResource CellTemplate}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid
                        Rows="{Binding Rows,Source={StaticResource GridViewModel}}" 
                        Columns="{Binding Columns,Source={StaticResource GridViewModel}}" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

在 main.cs 页面内,我加载了将控制器的集合与 ScreenViewModel.Cells 属性链接起来,并加载了一些模板数据。只是非常基本的模拟数据(您还可以将屏幕模型附加到 DataContext 并在其他地方定义控制器,然后更改 xaml 中的绑定以返回到 DataContext,但是通过资源,您还可以访问已创建的实例(初始化组件后)

protected ScreenViewModel ScreenViewModel
{
    get
    {
        return this.Resources["GridViewModel"] as ScreenViewModel;
    }
}

protected CellGridController Controller
{
    get
    {
        return this.Resources["CellController"] as CellGridController;
    }
}

protected void Load()
{
    var controller = Controller;
    controller.Collection.Clear();
    string[] rows = colorToCellSource.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
    string row;
    for (int x = 0; x < rows.Length; x++)
    {
        int length = rows[x].Length;
        ScreenViewModel.Rows = rows.Length;
        ScreenViewModel.Columns = length;
        row = rows[x];
        for (int y = 0; y < length; y++)
        {
            Cell cell = new Cell(x, y);
            cell.CellColor = row[y] == '0' ? Brushes.Transparent : Brushes.Blue;
            controller.Collection.Add(cell);
        }
    }
}

public MainWindow()
{
    InitializeComponent();
    if (Controller != null && ScreenViewModel != null)
    {
        ScreenViewModel.Cells = Controller.Collection;
        Load();
    }
}

屏幕在 1 秒内重新绘制,调整大小和最大化需要一点延迟,但我想这是可以预料的......(我的测试模板是 105x107)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

优化 WPF 中由单元格组成的网格以获得最短路径 的相关文章

随机推荐

  • 将占位符添加到我的 html 网站的 Google 自定义搜索引擎中的文本框

    我使用以下代码添加了谷歌自定义搜索引擎 function var cx 005899633628958982661 wekn1lpckzg var gcse document createElement script gcse type t
  • 图像未出现在 Tkinter 上

    我有一个问题Tkinter因为我想添加图像作为框架的背景 但是 我尝试了很多方法但没有显示出来 我在代码的开头 在解决这个问题后我将继续前进 这是我的代码 import Tkinter from Tkinter import sc Tk s
  • 使用Intel的PIN工具来计算程序中缓存命中/未命中的次数

    我一直在尝试编写一个 pintool 来检测给定程序中的缓存命中和未命中情况 我发现有INS IsMemoryRead Write等调用来判断指令是否是LD ST 有没有办法确定指令是否命中或未命中缓存 如果是这样 是否还可以获得从缓存 内
  • 从左到右两排的光滑旋转木马

    我需要制作一个从左到右顺序的两行轮播 也有响应 With slider slick rows 2 slidesToShow 3 responsive breakpoint 768 settings slidesToShow 1 我收到这个订
  • Java 中何时使用原始类型和何时使用引用类型

    在这种情况下你应该使用原始类型 int 或引用类型 Integer This question https stackoverflow com questions 2508918 激发了我的好奇心 在什么情况下你应该使用原始 类型 int
  • 从鼠标位置获取地图纬度经度

    我正在尝试将鼠标在谷歌地图上的位置转换为 LatLng 对象 我看到很多关于通过谷歌地图 点击 事件等获取位置的帖子 如下所示 google maps event addListener map click function event m
  • 当REST客户端和服务器在同一台服务器上时如何避免网络调用

    我有一个 Web 应用程序 其中两个主要组件是网站 在 Groovy 和 Grails 中实现 和后端 RESTful Web 服务 使用 JAX RS Jersey 和 Spring 实现 这两个都将在 Glassfish 中运行 该网站
  • C++调用基类的模板函数

    下面是两个案例 情况 1 Base gt BaseIndirect gt DerivedIndirect 情况 2 基础 gt 派生 在案例 2 中 我可以使用 3 个符号调用基类的模板函数 在情况 1 中 我可以仅使用其中一种符号来调用基
  • Powershell脚本命令持久化

    我开始学习 Powershell 并编写一个模块 psm1 来存储我的函数 然后我将这段代码插入到模块中 以便在修改模块时重新加载模块 function reload Remove Module init Import Module F S
  • 保存具有自定义前向功能的 Bert 模型并将其置于 Huggingface 上

    我创建了自己的 BertClassifier 模型 从预训练开始 然后添加由不同层组成的我自己的分类头 微调后 我想使用 model save pretrained 保存模型 但是当我打印它并从预训练上传时 我看不到我的分类器头 代码如下
  • 如何获得凸包中均匀分布的点?

    给定一组点 points np random randn n 3d points 我想均匀地填充由凸包定义的体积 其中它们位于一个列表 np array of shapenx3 的 3d 点 我可以得到凸包 hull scipy spati
  • SwiftUI 转义闭包捕获变异的“self”参数

    我有一个可以通过两种方式打开的视图 一个包含提供给它的数据 另一个包含对 Firestore 文档的文档引用 我创建了两个构造函数 在第一个构造函数中我提供数据 在另一个构造函数中我提供文档参考 然后我使用此引用进行网络调用 但出现错误 E
  • VBA 代码中的 Excel 公式

    所以 在 Sheet1 中 我有一些名称的基础 它看起来像这样 在 Sheet2 中 我正在使用 Sheet1 中的这些名称 我这样做的方式是在 A 列中输入代码值 在 B 列中输入名称 在 C 列中输入姓氏 看起来像这样 我已经用公式完成
  • 命令行显示文件的属性?

    我想编写一个显示默认 Windows 属性表的应用程序 我找不到太多关于以正常编程方式执行此操作的信息 所以我想也许可以使用命令行调用 有谁知道 Windows 命令行调用文件的属性 详细信息窗口吗 没有任何运气去寻找它 这个窗口在这里 看
  • 如何添加 Maven 执行任务以在“mvn test”上执行

    我的 pom 中有以下 exec 任务
  • cordova 使用什么 js“引擎”?

    Cordova 使用什么 JS 引擎 它是特定于平台的还是跨所有平台的一个标准 意味着 iOS 的 safari 和 Android 的 chrome 以及 Windows 可能的 IE 标准 或者跨所有平台的 Cordova JS 引擎
  • PHP 中的循环数组

    我创建了一个由部分和问题组成的数组 如何循环浏览各个部分并显示每个部分的嵌套问题 这是我创建数组的方式 db db open query SELECT FROM assessment selections WHERE assessment
  • 多个共享对象使用相同的原型导致错误:文件已存在于数据库中

    与protobuf3相关的错误 我有一个项目 它有一个 C 可执行核心和几个称为插件的共享对象 so dll 当核心启动时 它将使用 dlopen 加载这些插件 核心和插件使用protobuf作为通信协议 因此它们必须编译生成的 pb cc
  • 在R闪亮中,如何在UI端使用在SERVER端计算的值?

    在我的 R闪亮应用程序中 我想调整我的高度d3heatmap 见包装d3热图 https cran r project org web packages d3heatmap index html 作为我的数据框行数的函数 有一个论点heig
  • 优化 WPF 中由单元格组成的网格以获得最短路径

    我目前正在尝试在 WPF 中制作一个由 Cell 对象组成的网格 我需要将单元格绑定到对象 该对象需要位于二维数组中 我需要它很大 可扩展 并改变单元格的颜色并将数据存储在对象中 我已经实现了 但是绘制网格似乎很慢 100x100 网格需要