2010-02-06 4 views
5

Я пишу простую игру для игры в стиле tic tac для школы. Назначение выполняется на C++, но учитель дал мне разрешение использовать C# и WPF как вызов. Я закончил всю логику игры, и форма была в основном завершена, но я столкнулся с стеной. В настоящее время я использую Label, чтобы указать, кто его превращает, и я хочу изменить его, когда игрок совершает действительный ход. Согласно Applications = Code + Markup, я должен использовать метод FindName класса Window. Однако он продолжает возвращаться null. Вот код:FindName return 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 и нашел I should be able to use VisualTreeHelper class, но я не понимаю, как его использовать.

Любые идеи?

ответ

6

FindName работает на XAML-названии вызывающего элемента управления. В вашем случае, поскольку элемент управления создается полностью внутри кода, это пространство имен XAML пуст - и именно поэтому FindName терпит неудачу. См this page:

Любые дополнения к дереву элементов после первоначальной загрузки и обработки необходимо вызвать соответствующую реализацию RegisterName для класса, который определяет XAML NameScope. В противном случае добавленный объект нельзя ссылаться по имени с помощью таких методов, как FindName. Простое задание свойства Name (или x: Name Attribute) не регистрирует это имя в любом пространстве имен XAML.

Самый простой способ исправить вашу проблему - сохранить ссылку на ярлык StatusDisplay в классе как частный член. Или, если вы хотите узнать, как использовать класс VisualTreeHelper, есть фрагмент кода at the bottom of this page, который просматривает визуальное дерево, чтобы найти соответствующий элемент.

(Отредактировано: Конечно, это меньше работы для вызова RegisterName, чем использовать VisualTreeHelper, если вы не хотите, чтобы сохранить ссылку на этикетке.)

Я рекомендую прочитать первую ссылку в его полноту, если вы планируете использовать WPF/Silverlight на любой глубине. Полезная информация.

+0

Я решил, для этой программы, пойти с частным членом. Спасибо за объяснение, я прочитаю полную страницу, как только смогу. –

2

Вы должны создать новый NameScope для окна:

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

Тогда вы регистрируете название ярлыка с окном:

RegisterName(statusDisplay.Name, statusDisplay); 

Так что это, кажется, все, что вам нужно сделать сделать FindName() работы.