1

Я программно создаю поле состоящее из прямоугольников. В итоге на экране отображается только первый прямоугольник. Код:

            for (int i = 0; i < Height; i++)
            {
                rd.Add(new RowDefinition());
            }
            for (int j = 0; j < Width; j++)
            {
                cd.Add(new ColumnDefinition());
            }
        for (int j = 0; j &lt; Width*Height; j++) {
            Rectangle rect = new Rectangle();
            rect.Fill = new SolidColorBrush(Color.FromRgb(173, 216, 230));
            rect.Opacity = 1;
            rect.Margin = new System.Windows.Thickness(4);
            rect.Width = 20;
            rect.Width = 20;
            MWindow.MainField.Children.Add(rect);
        }

  • Забудьте про программное добавление в WPF проекте! WPF проектировался так, чтобы весь интерфейс был в XAML, а код в C#, а вы сейчас все в одну кучу суете, делая кучу костылей как сейчас, так и в будущем. Вопрос только зачем...? Не понятно.. – EvgeniyZ Dec 17 '21 at 16:12
  • Хочу иметь возможность изменять размеры поля. А также адаптивность – AndrewwSS Dec 17 '21 at 16:13
  • Не задавайте размеры и будет вам адапривность. Вот пример. Хотите конкретный размер, конкретному элементу - также, привяжите к свойству, не делайте контрол в C# коде! Вообще забудьте про контролы в c#, в WPF весь интерфейс делается спокойно на XAML с его триггерами, расширениями разметки и кучей чего еще. – EvgeniyZ Dec 17 '21 at 16:19

1 Answers1

2

Покажу пример, пошагово, с использованием MVVM, без сторонних библиотек, только хардкор

Создаю класс вьюмодели, реализую интерфейс INotifyPropertyChanged

public class MainViewModel : INotifyPropertyChanged
{
    private int width;
    private int height;
public event PropertyChangedEventHandler? PropertyChanged;

private void OnPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public int Width
{
    get =&gt; width; 
    set
    {
        width = value;
        OnPropertyChanged(nameof(Width));
        OnPropertyChanged(nameof(Items));
    }
}

public int Height
{
    get =&gt; height; 
    set
    {
        height = value;
        OnPropertyChanged(nameof(Height));
        OnPropertyChanged(nameof(Items));
    }
}

public IEnumerable&lt;int&gt; Items =&gt; Enumerable.Range(1, Width * Height);

}

Подключаю ее к окну

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

Делаю разметку интерфейса для создания адаптивных прямоугольников, для развлечения добавил изменение количества этих прямоугольников прямо в интерфейсе.

<Window x:Class="WpfApp5.MainWindow"
        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="clr-namespace:WpfApp5"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        d:DataContext="{d:DesignInstance local:MainViewModel}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="Width:" Margin="5" FontWeight="Bold"/>
            <TextBox Width="100" Margin="5" MaxLength="2" Text="{Binding Width, UpdateSourceTrigger=PropertyChanged}"/>
            <TextBlock Text="Height:" Margin="5" FontWeight="Bold"/>
            <TextBox Width="100" Margin="5" MaxLength="2" Text="{Binding Height, UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
        <ItemsControl ItemsSource="{Binding Items}" Grid.Row="1" >
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderThickness="4" BorderBrush="#add8e6" Margin="3">
                        <TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20"/>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Rows="{Binding Height}" Columns="{Binding Width}"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

Вот и всё, если вы имеете дело с коллекциями, надо подружиться с соответствующими контролами, и самый базовый и простой из них - ItemsControl. Ну и панели разные Grid, StackPanel, DockPanel, WrapPanel, UniformGrid так же помогут удобно верстать интерфейс.

введите сюда описание изображения

Я понимаю, что XAML выглядит с первого взгляда страшно, но его освоение того стоит. Про MVVM даже говорить ничего не буду, так как уже много раз говорил. :)

Итого я написал пример и этот ответ за 15 минут. При этом это так же легко дорабатывается, чтобы внутрь прямоугольников засунуть то что нужно. Просто заменив текстблок на что-то другое, а во вьюмодели просто поместить какую-то коллекцию с данными, текстом, картинками, без разницы.

Вот еще пара примеров работы с интерфейсом.

aepot
  • 49,560
  • Сделал по вашему примеру . Но у меня почему-то бордеры оказались только вверху и внизу. Когда убираю конкретные размеры, картинка вообще ломается – AndrewwSS Dec 17 '21 at 19:03
  • @АндрейФедоров если бы вы сделали в точности как показано, выглядело бы в точности как показано :) Заведите новый проект, экспериментируйте на нем. – aepot Dec 17 '21 at 20:52
  • Завел, но все же спавнятся только 45 квадратиков ``` public List<List> Items { get; set; } = new List<List>();
        public ViewModel()
        {
            int W = 70;
            int H = 45;
    
            for (int i = 0; i < H; i++)
            {
                List<Cell> Row = new List<Cell>();
    
                for (int j = 0; j < W; j++)
                    Row.Add(new Cell(CellState.Empty));
    
                Items.Add(Row);
            }
    
            Width = Items[0].Count;
            Height = Items.Count;
    
    
        }```
    
    – AndrewwSS Dec 19 '21 at 18:45
  • Вы не показали где именно и когда присваиваете значения свойствам. По-этому я присвоил после инициализации – AndrewwSS Dec 19 '21 at 18:48
  • @АндрейФедоров не вижу в этом еле-читаемом коде никаких проблем. Если не реализован INotifyPropertyChanged интерфейс для свойства, то присваивание возможно только в конструкторе. Иначе привязка не будет работать. Если реализован, то можно когда угодно и где угодно. – aepot Dec 19 '21 at 18:50
  • Интерфейс реализован. Все также как у вас. Получается что значение высоты определяет количество элементов отображаемых на экране – AndrewwSS Dec 19 '21 at 18:51
  • @АндрейФедоров { get; set; } не вижу здесь вызова OnPropertyChanged, вы еще раз посмотрите во все показанные примеры, я там как минимум один раз разжевал всю суть INPC. Но в любом случае, если это происходит в конструкторе, то должно работать даже без INPC. Возможно проблема в XAML. – aepot Dec 19 '21 at 18:53
  • INPC реализован в сетере как и у вас в коде. XAML идентичен вашему – AndrewwSS Dec 19 '21 at 18:55
  • @АндрейФедоров и зачем вам список списков? Это неудобно. Сделайте линейный массив. Выше ссылка на Шахматы в ответе, посмотрите реализацию класса Board, и как я к ней привязался. – aepot Dec 19 '21 at 18:56
  • @АндрейФедоров возмите на заметку: списки нужны там, где размер будущего списка неизвестен. Во всех остальных случаях используйте массивы. И работает быстрее, и выглядит понятнее. – aepot Dec 19 '21 at 18:57
  • Для того чтобы иметь возможность обращаться к нему как к матрице. – AndrewwSS Dec 19 '21 at 18:58
  • 1
    Поменял на одномерный, заработало. – AndrewwSS Dec 19 '21 at 19:02
  • @АндрейФедоров я же там показываю класс Board и объясняю, в чем прикол, почитайте хотя-бы текст, что я написал. Вы пытаетесь решить проблему, решение которой я уже вам дал. – aepot Dec 19 '21 at 19:21