我正在为学校写一个简单的tic tac toe游戏。作业是用C ++编写的,但老师允许我使用C#和WPF作为挑战。我已经完成了所有游戏逻辑并且表单大部分都已完成,但我遇到了一堵墙。我目前正在使用Label
来指示轮到谁了,我想在玩家进行有效移动时更改它。根据{{3}},我应该可以使用FindName
类的Window
方法。但是,它会一直返回null
。这是代码:
public TicTacToeGame()
{
Title = "TicTacToe";
SizeToContent = SizeToContent.WidthAndHeight;
ResizeMode = ResizeMode.NoResize;
UniformGrid playingField = new UniformGrid();
playingField.Width = 300;
playingField.Height = 300;
playingField.Margin = new Thickness(20);
Label statusDisplay = new Label();
statusDisplay.Content = "X goes first";
statusDisplay.FontSize = 24;
statusDisplay.Name = "StatusDisplay"; // This is the name of the control
statusDisplay.HorizontalAlignment = HorizontalAlignment.Center;
statusDisplay.Margin = new Thickness(20);
StackPanel layout = new StackPanel();
layout.Children.Add(playingField);
layout.Children.Add(statusDisplay);
Content = layout;
for (int i = 0; i < 9; i++)
{
Button currentButton = new Button();
currentButton.Name = "Space" + i.ToString();
currentButton.FontSize = 32;
currentButton.Click += OnPlayLocationClick;
playingField.Children.Add(currentButton);
}
game = new TicTacToe.GameCore();
}
void OnPlayLocationClick(object sender, RoutedEventArgs args)
{
Button clickedButton = args.Source as Button;
int iButtonNumber = Int32.Parse(clickedButton.Name.Substring(5,1));
int iXPosition = iButtonNumber % 3,
iYPosition = iButtonNumber / 3;
if (game.MoveIsValid(iXPosition, iYPosition) &&
game.Status() == TicTacToe.GameCore.GameStatus.StillGoing)
{
clickedButton.Content =
game.getCurrentPlayer() == TicTacToe.GameCore.Player.X ? "X" : "O";
game.MakeMoveAndChangeTurns(iXPosition, iYPosition);
// And this is where I'm getting it so I can use it.
Label statusDisplay = FindName("StatusDisplay") as Label;
statusDisplay.Content = "It is " +
(game.getCurrentPlayer() == TicTacToe.GameCore.Player.X ? "X" : "O") +
"'s turn";
}
}
这里发生了什么?我在两个地方都使用相同的名称,但FindName
找不到它。我尝试使用Snoop查看层次结构,但表单没有显示在可供选择的应用程序列表中。我在StackOverflow上搜索并发现了我Applications = Code + Markup,但我不明白如何使用它。
有什么想法吗?
答案 0 :(得分:6)
FindName
在调用控件的XAML名称范围内运行。在您的情况下,由于控件完全在代码中创建,因此XAML名称范围为空 - 这就是FindName
失败的原因。见this page:
在初始加载和处理之后对元素树的任何添加都必须为定义XAML名称范围的类调用RegisterName的相应实现。否则,添加的对象不能通过FindName等方法按名称引用。仅设置Name属性(或x:Name Attribute)不会将该名称注册到任何XAML名称范围。
解决问题的最简单方法是将类中的StatusDisplay标签的引用存储为私有成员。或者,如果您想学习如何使用VisualTreeHelper类,可以使用代码片段at the bottom of this page遍历可视树以查找匹配元素。
(编辑:当然,如果你不想存储对标签的引用,那么调用RegisterName
比使用VisualTreeHelper要少得多。)
如果您计划在任何深度使用WPF / Silverlight,我建议您完整阅读第一个链接。有用的信息。
答案 1 :(得分:2)
您必须为您的窗口创建一个新的NameScope:
NameScope.SetNameScope(this, new NameScope());
然后您在窗口中注册您的标签名称:
RegisterName(statusDisplay.Name, statusDisplay);
所以这似乎是让FindName()
工作所需要做的所有事情。