我在WPF C#中制作类似拼字游戏的游戏。截至目前,我已经让AI算法在“模型游戏板”上运行,这是一个变量字符串[15,15]。然而,在过去的几天里,我一直在制作一个GUI来将这个数组显示为GameBoard。
截至目前,我已获得以下XAML代码:
我的“MainWindow”包含:
按钮:
<Button Click="Next_TurnClicked" Name="btnNext_Turn">Next Turn</Button>
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>
所以我的问题是:
我是编码的初学者,这是我在C#和WPF中的第一个真正的项目。我的老师既不知道WPF也不知道C#,所以在过去的几周里,StackoverFlow社区的人们都给了我很大的帮助,尤其是帮助我解决SQL问题。
拜托,非常感谢任何帮助!
更新:
感谢Erno的快速回复!是的,我收到了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集合。但是,如何将Collection绑定到ItemsControl?
我是否将UserControl的DataContext设置为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();
}
}
答案 0 :(得分:3)
这个问题没有简短的答案,所以我会给大纲随时提出更多问题,以便在需要时获得更多详细信息:
要使用数据绑定,请确保集合中的项目实现INotifyPropertyChanged。目前您正在使用字符串数组。 String不实现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));
}
}
}
像这样创建一个类Tiles:
public class Tiles : ObservableCollection<Tile>
{
}
并用此集合替换二维数组。
将项控件绑定到集合的一种方法是,您需要将items控件的DataContext设置为集合的实例并指定属性ItemsSource =“{Binding}”
使用itemtemplate中的Character,X和Y属性来显示文本并定位图块。
这样,当您在添加或删除切片时操作Tiles集合时,绑定将自动更新视图,并且当切片更改(位置或内容)时也会更新板
答案 1 :(得分:2)
首先,将现有程序放在一边。你稍后会回来的。
接下来,实现原型WPF项目。在此项目中,创建一个公开Letter
,Row
和Column
属性的类,以及另一个公开这些对象集合的类。编写一个用测试数据填充此集合的方法。
在主窗口中,实现ItemsControl
以显示此集合。这种控制需要四件事:
其ItemsPanel
必须包含其将用于排列其包含的项目的面板的模板。在这种情况下,您将使用具有预定义大小的行和列的Grid
。
其ItemContainerStyle
必须包含设置器,告诉ContentPresenter
对象模板生成它们所属网格的哪一行和哪一列。
其ItemTemplate
必须包含一个模板,告诉它应该在ContentPresenter
中添加哪些控件。
必须将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很好地表示了对象。)
一旦你有了这个工作,你现在有一堆相对简单的问题需要解决:
你如何让它看起来更像你想要的样子?对此的答案将涉及制作更精细的ItemTemplate
。
如何在应用程序中同步此呈现的集合?这取决于(很多)应用程序的设计方式;一种方法是将集合包装在一个类中,让类创建一个后端对象模型的实例,并让后端对象模型在每次集合更改时引发一个事件,以便包含对象集合的类在UI中呈现的内容知道创建一个新的前端对象并将其添加到其集合中。
用户如何选择网格中的单元格以放置图块?对此的答案可能涉及为所有单元创建对象,而不仅仅是包含字母的对象,实现在用户单击单元格时执行的命令,以及更改{{ 1}}以便当用户点击它时它可以执行此命令。
如果单元格的内容发生变化,UI会如何找到它?这需要在您的UI对象类中实现ItemTemplate
,并在INotifyPropertyChanged
更改时提升PropertyChanged
。
我在这里推荐的方法最重要的一点是,如果您没有注意到,那么您实际上可以让UI几乎完全独立于您在后端所做的工作。 UI和后端仅在您创建的第一个类中耦合在一起,即具有Letter
,Row
和Column
属性的类。
顺便说一下,如果您对WPF感兴趣,这可能是您可能听说过的Model / View / ViewModel模式的一个很好的例子。你写的代码是模型。窗口是视图。具有Letter
,Row
和Column
属性的类是视图模型。