RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 632517
Accepted
Peter Lavreniuk
Peter Lavreniuk
Asked:2020-02-26 02:48:50 +0000 UTC2020-02-26 02:48:50 +0000 UTC 2020-02-26 02:48:50 +0000 UTC

六角网格

  • 772

问题。你如何在 上实现六边形网格的可视化WPF,而这个网格的每个单元格都是一个单独的对象,假设它可以有不同的颜色。

只有画布浮现在脑海中。描述将固定单元格坐标的字段。我什至不知道如何绘制单元格本身。

在此处输入图像描述

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    VladD
    2020-02-27T03:54:07Z2020-02-27T03:54:07Z

    最简单的方法是使用ItemsControl/ Canvas,并使用 设置表单Path。

    我以这段代码为基础:Playing on WPF, porting WinForms code to MVVM。

    首先,VM部分。基本 VM 类是标准的:

    class VM : INotifyPropertyChanged
    {
        protected bool Set<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
                return false;
    
            field = value;
            NotifyPropertyChanged(propertyName);
            return true;
        }
    
        protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    一些辅助结构:

    struct FieldSize
    {
        public int Width { get; }
        public int Height { get; }
        public FieldSize(int w, int h) { Width = w; Height = h; }
    }
    
    struct FieldPosition
    {
        public int X { get; }
        public int Y { get; }
        public FieldPosition(int x, int y) { X = x; Y = y; }
    }
    

    当然,它们代表了场地的大小和在场地上的位置。

    现在虚拟机是一个单元格。这里的一切都很明显:

    class CellVM : VM
    {
        public CellVM(int row, int column)
        {
            Position = new FieldPosition(row, column);
            Activate = new RelayCommand(OnActivate);
        }
    
        public FieldPosition Position { get; }
    
        public ICommand Activate { get; }
    
        bool isActive = false;
        public bool IsActive
        {
            get { return isActive; }
            private set { Set(ref isActive, value); }
        }
    
        void OnActivate()
        {
            IsActive = !IsActive;
        }
    }
    

    唯一可以改变的字段是IsActive,所以改变它发送NotifyPropertyChanged。Activate单击时将调用该命令。

    现在全场。在改变字段的大小时,我们生成单元格。虽然单元格表示为列表,但如果您需要将它们作为二维数组,请将 save to array 添加到GenerateCells.

    class BoardVM : VM
    {
        FieldSize fieldSize;
        public FieldSize FieldSize
        {
            get { return fieldSize; }
            set { if (Set(ref fieldSize, value)) GenerateCells(); }
        }
    
        IEnumerable<CellVM> cells;
        public IEnumerable<CellVM> Cells
        {
            get { return cells; }
            private set { Set(ref cells, value); }
        }
    
        void GenerateCells()
        {
            var list = new List<CellVM>(FieldSize.Width * FieldSize.Height);
            for (int j = 0; j < FieldSize.Height; j++)
                for (int i = 0; i < FieldSize.Width; i++)
                    list.Add(new CellVM(i, j));
            Cells = list;
        }
    }
    

    这就是 VM 部分的全部内容,让我们转向 View。这里会有一些技巧。

    首先,我们需要转换器将坐标转换为屏幕上的位置。他们对偶数行和奇数行的计算略有不同。像元大小作为参数传递。

    class FieldPositionToCoordinateXConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter,
                              CultureInfo culture)
        {
            FieldPosition position = (FieldPosition)value;
            double cellSize = (double)parameter;
            if (position.Y % 2 == 0)
                return position.X * cellSize;
            else
                return (position.X + 0.5) * cellSize;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter,
                                  CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
    class FieldPositionToCoordinateYConverter : IValueConverter
    {
        static double diag = Math.Sqrt(3) / 2;
        public object Convert(object value, Type targetType, object parameter,
                              CultureInfo culture)
        {
            FieldPosition position = (FieldPosition)value;
            double cellSize = (double)parameter;
            return position.Y * cellSize * diag;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter,
                                  CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    现在 XAML。将单元格大小和转换器连接到窗口资源:

    <Window.Resources>
        <sys:Double x:Key="CellSize">25</sys:Double>
        <view:FieldPositionToCoordinateXConverter x:Key="XConv"/>
        <view:FieldPositionToCoordinateYConverter x:Key="YConv"/>
    </Window.Resources>
    

    接下来,内容本身。我没有自动调整窗口大小,为此您需要另一个转换器。

    单元格通过以下方式从集合中连接ItemsControl:

    <ItemsControl ItemsSource="{Binding Cells}">
    

    由于我们会“手动”安排坐标,因此我们需要Canvas作为载体:

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    

    现在,一个细胞。

        <ItemsControl.ItemTemplate>
            <DataTemplate>
    

    Grid让我们把它放在一个所需大小的正方形中:

                <Grid Width="{StaticResource CellSize}" Height="{StaticResource CellSize}">
    

    我们通过 定义内部部分Path,计算坐标如下:

                    <Path Data="M -1,-1
                                M 0,-1
                                L 0.86602540378443864676372317075294,-0.5
                                L 0.86602540378443864676372317075294,0.5
                                L 0,1
                                L -0.86602540378443864676372317075294,0.5
                                L -0.86602540378443864676372317075294,-0.5
                                L 0,-1
                                M 1,1"
                          Stretch="Uniform"
                          Stroke="Black"
                          StrokeThickness="0.5">
    

    接下来,根据 的值更改颜色IsActive。编写另一个转换器很丑陋,让我们使用触发器。DataTrigger仅在样式中可用,因此样式将负责设置背景:

                        <Path.Style>
                            <Style TargetType="Path">
                                <Setter Property="Fill" Value="LightGreen"/>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding IsActive}" Value="True">
                                        <Setter Property="Fill" Value="DarkGreen"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Path.Style>
    

    现在,我们需要组织点击。由于Path它不能自己做到这一点,因此有三种方法:要么使用程式化的 'th Button(你把它放在Path中ControlTemplate),要么定义InputBinding,或者包括 System.Windows.Interactivity.dll(来自通过安装的 Expression Blend SDK Visual Studio Installer,或来自 nuget System.Windows.Interactivity.WPF),并将命令挂在鼠标事件上。最简单的方法是第二种方法(@AndreyNOP 建议):

                        <Path.InputBindings>
                            <MouseBinding Gesture="LeftClick" Command="{Binding Activate}"/>
                        </Path.InputBindings>
    

    虽然我走的是第三条路(i:是前缀定义为xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity")。

                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="MouseLeftButtonUp">
                                <i:InvokeCommandAction Command="{Binding Activate}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
    

    一切都与细胞有关。

                    </Path>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    

    还需要什么?您需要将单元格放置在所需的坐标处。它本身Grid是DataTemplate装在一个容器中的,所以给它设置坐标是没有用的。因此有必要移动容器本身。这是这样做的:

        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Left"
                        Value="{Binding Position,
                                        Converter={StaticResource XConv},
                                        ConverterParameter={StaticResource CellSize}}"/>
                <Setter Property="Canvas.Top"
                        Value="{Binding Position,
                                        Converter={StaticResource YConv},
                                        ConverterParameter={StaticResource CellSize}}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    

    这是完整的窗口代码:

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        x:Class="HexGrid.View.MainWindow"
        xmlns:view="clr-namespace:HexGrid.View"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="Test" Height="290" Width="300">
        <Window.Resources>
            <sys:Double x:Key="CellSize">25</sys:Double>
            <view:FieldPositionToCoordinateXConverter x:Key="XConv"/>
            <view:FieldPositionToCoordinateYConverter x:Key="YConv"/>
        </Window.Resources>
        <Grid Margin="10">
            <ItemsControl ItemsSource="{Binding Cells}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas IsItemsHost="True"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Width="{StaticResource CellSize}"
                              Height="{StaticResource CellSize}">
                            <Path Data="M -1,-1
                                        M 0,-1
                                        L 0.86602540378443864676372317075294,-0.5
                                        L 0.86602540378443864676372317075294,0.5
                                        L 0,1
                                        L -0.86602540378443864676372317075294,0.5
                                        L -0.86602540378443864676372317075294,-0.5
                                        L 0,-1
                                        M 1,1"
                                  Stretch="Uniform"
                                  Stroke="Black"
                                  StrokeThickness="0.5">
                                <Path.Style>
                                    <Style TargetType="Path">
                                        <Setter Property="Fill" Value="LightGreen"/>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding IsActive}" Value="True">
                                                <Setter Property="Fill" Value="DarkGreen"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Path.Style>
                                <Path.InputBindings>
                                    <MouseBinding Gesture="LeftClick" Command="{Binding Activate}"/>
                                </Path.InputBindings>
                            </Path>
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="Canvas.Left"
                                Value="{Binding Position,
                                                Converter={StaticResource XConv},
                                                ConverterParameter={StaticResource CellSize}}"/>
                        <Setter Property="Canvas.Top"
                                Value="{Binding Position,
                                                Converter={StaticResource YConv},
                                                ConverterParameter={StaticResource CellSize}}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>
            </ItemsControl>
        </Grid>
    </Window>
    

    那么,标准的 App.xaml/App.xaml.cs:

    <Application x:Class="HexGrid.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    </Application>
    
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            new MainWindow()
            {
                DataContext = new BoardVM()
                {
                    FieldSize = new FieldSize(10, 10)
                }
            }.Show();
        }
    }
    

    结果:

    动画

    一切!

    • 27

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5