FindName 返回 null

5
我正在为学校编写一个简单的井字游戏。作业要求使用C++,但老师允许我挑战自己使用C#和WPF。我已经完成了所有的游戏逻辑并且几乎完成了表格,但是我遇到了一些困难。我目前使用Label指示哪个玩家可以走棋,并希望在玩家进行有效移动后更改它。根据Applications = Code + Markup,我应该能够使用Window类的FindName方法。然而,它一直返回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上搜索并发现我可以使用VisualTreeHelper类,但我不知道如何使用它。

有什么想法吗?

2个回答

6
FindName 是对调用控件的 XAML 命名空间进行操作。在您的情况下,由于该控件完全是在代码中创建的,因此该 XAML 命名空间为空 - 这就是为什么 FindName 失败的原因。详见此页面

除了最初加载和处理后的元素树之外添加的任何内容都必须调用定义 XAML 命名空间的类的适当 RegisterName 实现。否则,不能通过 FindName 等方法按名称引用添加的对象。仅设置 Name 属性(或 x:Name 属性)不会将该名称注册到任何 XAML 命名空间中。

解决问题的最简单方法是将 StatusDisplay 标签的引用作为私有成员存储在类中。或者,如果您想学习如何使用 VisualTreeHelper 类,则可以查看此页面底部的代码片段,该代码片段遍历可视树以查找匹配元素。

(编辑:当然,如果您不想存储标签的引用,调用 RegisterName 比使用 VisualTreeHelper 更省事。)

如果您计划深入使用 WPF/Silverlight,建议全文阅读第一个链接。这是有用的信息。


我选择在这个程序中使用私有成员。感谢您的解释,我会尽快阅读完整页面。 - Cristián Romo

2

您需要为窗口创建一个新的NameScope:

NameScope.SetNameScope(this, new NameScope());

然后你需要在窗口中注册你的标签名称:
RegisterName(statusDisplay.Name, statusDisplay);

所以似乎这就是使FindName()起作用的全部步骤。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接