Как связать простой массив? - PullRequest
3 голосов
/ 25 июля 2011

Я делаю игру в стиле скрэббл в WPF C #.На данный момент у меня есть Алгоритм ИИ, работающий с «Модельным игровым полем», переменной строкой [15,15].Однако в последние несколько дней я застрял в создании графического интерфейса для отображения этого массива в виде GameBoard.

На данный момент у меня есть следующий код XAML:

Мое "MainWindow"который содержит:

Кнопка:

<Button Click="Next_TurnClicked" Name="btnNext_Turn">Next Turn</Button>

A UserControl: это игровая доска (GameBoard также является еще одним UserControl) и стойка игрока

<clr:Main_Control></clr:Main_Control>

ТогдаВнутри моего UserControl у меня есть:

    <DockPanel Style ="{StaticResource GradientPanel}">
    <Border Style ="{StaticResource ControlBorder}" DockPanel.Dock="Bottom">
        <ItemsControl VerticalAlignment="Center" HorizontalAlignment="Center">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Height ="Auto" Name="ListBox" Orientation="Horizontal" HorizontalAlignment="Center">
                    </StackPanel>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <clr:Rack_Cell_Sender x:Name="Player_Tile_1" ></clr:Rack_Cell_Sender>
            <clr:Rack_Cell_Sender x:Name="Player_Tile_2" ></clr:Rack_Cell_Sender>
            <clr:Rack_Cell_Sender x:Name="Player_Tile_3" ></clr:Rack_Cell_Sender>
            <clr:Rack_Cell_Sender x:Name="Player_Tile_4" ></clr:Rack_Cell_Sender>
            <clr:Rack_Cell_Sender x:Name="Player_Tile_5" ></clr:Rack_Cell_Sender>
        </ItemsControl>
    </Border>
    <Viewbox Stretch="Uniform">
        <Border Margin="5" Padding="10" Background="#77FFFFFF" BorderBrush="DimGray" BorderThickness="3">
            <Border BorderThickness="0.5" BorderBrush="Black">
                <clr:GameBoard>
                </clr:GameBoard>
            </Border>
        </Border>
    </Viewbox>
</DockPanel>

clr: GameBoard - это ItemsControl с его ItemsPanelTemplate в качестве UniformGrid

        <Style TargetType="ItemsControl">
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <UniformGrid IsItemsHost="True" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Rows="15" Columns="15" />
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Words:Cell>
                    </Words:Cell>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>
<ItemsControl Name="BoardControl" ItemsSource="{DynamicResource CellCollectionData}">
</ItemsControl>

UI Structure

Итак, мой вопрос:

  1. Как мне получить мой массив [,] в ItemsControl?Я понятия не имею, как DataBind, я прочитал несколько учебных пособий, но я только начинаю понимать, что такое DataContext.
  2. Как я буду обновлять доску после каждого хода?Я не хочу, чтобы доска обновлялась до нажатия btnNext_Turn.
  3. Как обновить ModelBoard после ввода пользователем нового слова в пользовательский интерфейс и нажатия btn_Next_Turn?

Я являюсьновичок в программировании, и это мой первый настоящий проект на C # и WPF.Мой учитель не знает ни WPF, ни C #, поэтому вы, ребята из сообщества StackoverFlow, в последние несколько недель оказали большую помощь, особенно помогли мне с SQL.

Пожалуйста, любая помощь будет высоко оценена!

ОБНОВЛЕНИЕ:

Спасибо Эрно за быстрый ответ!Да, я получил ошибку для EventHandler, поэтому я заменил ее на PropertyChangedEventHandler, и это остановило ошибки.

public partial class GameBoard : UserControl
{
    TileCollection RefreshTiles = new TileCollection();
    public GameBoard()
    {
        InitializeComponent();
    }
    public void getBoard()
    {
        string[,] ArrayToAdd = InnerModel.ModelBoard;
        for (int i = 0; i < 15; i++)
        {
            for (int j = 0; j < 15; j++)
            {
                Tile AddTile = new Tile();
                AddTile.Charater = ArrayToAdd[i, j];
                AddTile.X = i;
                AddTile.Y = j;
                RefreshTiles.Add(AddTile);
            }
        }
    }
}

Поэтому, когда я запускаю отладку, я вижу заполненную коллекцию RefreshTiles.Однако, как мне связать коллекцию с ItemsControl?

Должен ли я установить для DataContext элемента UserControl значение RefreshTiles?

this.DataContext = RefreshTiles

, затем в XAML

<ItemsControl Name ="BoardControl" ItemsSource="{Binding}">

Обновление:

Так что, если яустановить связывание в MainWindow это работает отлично, однако это не работает, когда я пытаюсь связать из Usercontrol?Я установил точку останова в «RefreshArray» и вижу, что она заполняется, однако UserControl не обновляется?

public partial class UserControl1 : UserControl
{
    public char GetIteration
    {
        get { return MainWindow.Iteration; }
        set { MainWindow.Iteration = value; }
    }
    CellCollection NewCells = new CellCollection();
    public UserControl1()
    {
        InitializeComponent();
        this.DataContext = NewCells;
        PopulateCells();
    }
    private void PopulateCells()
    {
        for (int i = 0; i < 15; i++)
        {
            for (int j = 0; j < 15; j++)
            {
                Cell NewCell = new Cell();
                NewCell.Character = "A";
                NewCell.Pos_x = i;
                NewCell.Pos_y = j;
                NewCells.Add(NewCell);
            }
        }
    }
    public void RefreshArray()
    {
        NewCells.Clear();
        for (int i = 0; i < 15; i++)
        {
            for (int j = 0; j < 15; j++)
            {
                Cell ReCell = new Cell();
                ReCell.Character = GetIteration.ToString();
                ReCell.Pos_x = i;
                ReCell.Pos_y = j;
                NewCells.Add(ReCell);
            }
        }
        this.DataContext = NewCells;
    }
}

public partial class MainWindow : Window
{
    UserControl1 Control = new UserControl1();
    public static char Iteration = new char();
    public MainWindow()
    {
        InitializeComponent();
    }


    private void Next_Click(object sender, RoutedEventArgs e)
    {
        Iteration = 'B';
        Control.RefreshArray();
    }
}

Это не работает, в то время как приведенное ниже работает

public partial class MainWindow : Window
{
    char Iteration = new char();
    CellCollection NewCells = new CellCollection();
    public MainWindow()
    {
        InitializeComponent();
        PopulateCells();
        this.DataContext = NewCells;
        Iteration++;
    }
    private void PopulateCells()
    {
        for (int i = 0; i < 15; i++)
        {
            for (int j = 0; j < 15; j++)
            {
                Cell NewCell = new Cell();
                NewCell.Character = "A";
                NewCell.Pos_x = i;
                NewCell.Pos_y = j;
                NewCells.Add(NewCell);
            }
        }
    }
    private void RefreshArray()
    {
        NewCells.Clear();
        for (int i = 0; i < 15; i++)
        {
            for (int j = 0; j < 15; j++)
            {
                Cell ReCell = new Cell();
                ReCell.Character = Iteration;
                ReCell.Pos_x = i;
                ReCell.Pos_y = j;
                NewCells.Add(ReCell);
            }
        }
    }

    private void Next_Click(object sender, RoutedEventArgs e)
    {
        RefreshArray();
    }
}

Ответы [ 2 ]

3 голосов
/ 25 июля 2011

На этот вопрос нет короткого ответа, поэтому я дам общую схему, чтобы задать дополнительные вопросы, чтобы получить более подробную информацию, где это необходимо:

Чтобы использовать привязку данных, убедитесь, что элементы в коллекции реализуют INotifyPropertyChanged. В настоящее время вы используете массив строк. Строка не реализует INotifyPropertyChanged.

Также убедитесь, что коллекция реализует INotifyCollectionChanged. В настоящее время вы используете двумерный массив. Массив не реализует этот интерфейс.

Решением было бы создание класса с именем Tile, который реализует INotifyPropertyChanged и сохраняет символ в виде строки и дополнительно сохраняет его положение на доске в свойстве X и Y:

public class Tile : INotifyPropertyChanged
{
    private string character;
    public string Character
    {
        get
        {
            return character;
        }
        set
        {
            if(character != value)
            {
                character = value;
                OnPropertyChanged("Character");
            }
        }
    }

    private int x; // repeat for y and Y
    public int X
    {
        get
        {
            return x;
        }
        set
        {
            if(x != value)
            {
                x = value;
                OnPropertyChanged("X");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        var p = PropertyChanged;
        if(p != null)
        {
            p(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Создайте класс плиток следующим образом:

public class Tiles : ObservableCollection<Tile>
{
}

и замените двумерный массив этой коллекцией.

Один из способов привязки элемента управления к коллекции требует, чтобы вы установили DataContext элемента управления к экземпляру коллекции и указали свойство ItemsSource = "{Binding}"

Используйте свойства символов, X и Y в шаблоне элемента, чтобы отобразить текст и расположить плитку.

Таким образом, привязки будут автоматически обновлять представление при манипулировании коллекцией плиток при добавлении или удалении плиток, и доска также будет обновляться при изменении плитки (позиции или содержимого)

2 голосов
/ 25 июля 2011

Подход Эмо хорош;Я бы предложил немного другую тактику.

Сначала отложите вашу существующую программу.Вы вернетесь к нему позже.

Далее, реализуйте прототип WPF-проекта.В этом проекте создайте класс, который предоставляет свойства Letter, Row и Column, и другой класс, который предоставляет коллекцию этих объектов.Напишите метод, который заполняет эту коллекцию тестовыми данными.

В вашем главном окне введите ItemsControl, чтобы представить эту коллекцию.Для этого элемента управления нужны четыре вещи:

  1. Его ItemsPanel должен содержать шаблон для панели, которую он собирается использовать для упорядочения элементов, которые он содержит.В этом случае вы будете использовать Grid со строками и столбцами предварительно определенного размера.

  2. Его ItemContainerStyle должен содержать сеттеры, которые сообщают объектам ContentPresenter, чтоtemplate генерирует, к какой строке и столбцу сетки они принадлежат.

  3. Его ItemTemplate должен содержать шаблон, который сообщает ему, какие элементы управления он должен поместить в ContentPresenter s.

  4. Его ItemsSource должен быть привязан к коллекции объектов.

Минимальная версия выглядит следующим образом:

<ItemsControl ItemsSource="{Binding}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="10"/>
          <RowDefinition Height="10"/>
          <RowDefinition Height="10"/>
          <RowDefinition Height="10"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="10"/>
          <ColumnDefinition Width="10"/>
          <ColumnDefinition Width="10"/>
          <ColumnDefinition Width="10"/>
        </Grid.ColumnDefinitions>
      </Grid>      
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
      <Setter Property="Grid.Row" Value="{Binding Row}"/>
      <Setter Property="Grid.Column" Value="{Binding Column}"/>
    </Style>
  </ItemsControl.ItemContainerStyle>
  <ItemsControl.ItemTemplate>
    <DataTemplate TargetType="{x:Type MyClass}">
      <TextBlock Text="{Binding Letter}"/>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Установите DataContext в главном окне на заполненный экземпляр вашей коллекции, и вы должны увидеть, как оно раскладывает буквы в сетке 4x4.

Как это работает: Установив ItemsSource в {Binding}, вы говорите ItemsControl, чтобы получить предметы из DataContext.(Элементы управления наследуют DataContext от своего родителя, поэтому установка его в окне делает его доступным для ItemsControl).

Когда WPF отображает ItemsControl, он создает панель с использованием ItemsPanelTemplateи наполняет его предметами.Для этого он проходит через элементы в ItemsSource и для каждого генерирует элемент управления ContentPresenter.

Он заполняет свойство Content этого элемента управления, используя шаблон, найденный в свойстве ItemTemplate.

Он устанавливает свойства ContentPresenter, используя стиль, найденный в ItemContainerStyleимущество.В этом случае стиль устанавливает присоединенные свойства Grid.Row и Grid.Column, которые сообщают Grid, куда их помещать, когда он рисует их на экране.

Таким образом, реальные объекты, которые создаются, выглядяткак это:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="10"/>
    <RowDefinition Height="10"/>
    <RowDefinition Height="10"/>
    <RowDefinition Height="10"/>
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="10"/>
    <ColumnDefinition Width="10"/>
    <ColumnDefinition Width="10"/>
    <ColumnDefinition Width="10"/>
  </Grid.ColumnDefinitions>
  <ContentPresenter Grid.Row="0" Grid.Column="0">
    <ContentPresenter.Content>
      <TextBlock Text="A"/>
    </ContentPresenter.Content>
  </ContentPresenter>
  <ContentPresenter Grid.Row="1" Grid.Column="1">
    <ContentPresenter.Content>
      <TextBlock Text="A"/>
    </ContentPresenter.Content>
  </ContentPresenter>
</Grid>      

(Фактически XAML не создается, но приведенный выше XAML является довольно хорошим представлением объектов, которые это делают.)

Когда у вас есть эта работа, у вас теперь естькуча относительно простых задач для решения:

  1. Как сделать так, чтобы это выглядело так, как вы хотите, чтобы оно выглядело?Ответ на этот вопрос будет заключаться в создании более сложной ItemTemplate.

  2. Как синхронизировать коллекцию, которая представлена ​​в массиве в вашем приложении?Это зависит (во многом) от того, как разработано ваше приложение;Один из подходов заключается в том, чтобы обернуть коллекцию в класс, заставить класс создать экземпляр вашей внутренней объектной модели и заставить внутреннюю объектную модель вызывать событие каждый раз, когда ее коллекция изменяется, так что класс, содержащий коллекцию объектовкоторые представлены в пользовательском интерфейсе, знают, как создать новый интерфейсный объект и добавить его в свою коллекцию.

  3. Как пользователь выбирает ячейку в сетке, чтобы поместить плитку в?Ответ на этот вопрос, вероятно, будет заключаться в создании объектов для всех ячеек, а не только тех, которые содержат буквы, реализации команды, выполняемой при нажатии пользователем на ячейку, и изменении ItemTemplateчтобы он мог выполнить эту команду, когда пользователь нажимает на нее.

  4. Если содержимое ячейки изменяется, как пользовательский интерфейс узнает об этом?Это потребует реализации INotifyPropertyChanged в вашем объектном классе пользовательского интерфейса и повышения PropertyChanged при изменении Letter.

Самая важная вещь в подходе, который я рекомендую здесь, если вы не заметили, это то, что вы можете фактически заставить UI работать почти полностью независимо от того, что вы делаете в бэкэнде.Пользовательский интерфейс и серверная часть связаны только в том первом классе, который вы создали, со свойствами Row, Column и Letter.

Это, кстати, довольнохороший пример шаблона Model / View / ViewModel, о котором вы, вероятно, слышали, если вы заинтересованы в WPF.Код, который вы написали, является моделью.Окно это вид.И этот класс со свойствами Row, Column и Letter является моделью представления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...