简体   繁体   中英

Can I access XAML elements in an array in the codebehind?

I've been looking around but I haven't been able to find anything on this. I am trying to get started making Windows 8.1 apps in C# with Visual Studio 2013 Pro. I want to be able to access multiple elements (particularly buttons or text blocks) in an array because this is more convenient for developing things like board games. For instance, if I were developing tic-tac-toe, I might use a series of buttons like this:

<Grid>
    <Button Name="Cell00"/>
    <Button Name="Cell01"/>
    <Button Name="Cell02"/>
    <Button Name="Cell10"/>
    <Button Name="Cell11"/>
    <Button Name="Cell12"/>
    <Button Name="Cell20"/>
    <Button Name="Cell21"/>
    <Button Name="Cell22"/>
<Grid/>

Now for the function that would check for a win, I would have to check all possible combinations like this is in the code behind:

private bool CheckForWin()
{
    if((Cell00 == Cell01) && (Cell01 == Cell02) && isNotBlank(Cell02)) return true;
    if((Cell10 == Cell11) && (Cell11 == Cell12) && isNotBlank(Cell12)) return true
    ...
    return false; //if none of the win conditions pass
}

This type of code would be extremely cumbersome. I would like to write it instead in a way that lets me check the array with for loops.

I realize that with tic-tac-toe, it is fairly easy to code it using brute force, but this was the first example that came to my head. Other games like Reversi or Go would not work well like this because of either the sheer size or the fact that pieces placed can change other cells than the one they were placed on.

Any help with this would be greatly appreciated.

This is not the correct way to use WPF. WPF is designed to use data binding....creating and manipulating UI elements directly is bad form. There are more posts/discussion/questions about this than you can imagine and I'll leave you to research them for yourself. In the mean time this is how you use WPF "properly":

First use NuGet to add MVVM lite to your project so that you get the ViewModelBase class and create a view model for a single cell:

public class Cell : ViewModelBase
{
    private string _Text;
    public string Text
    {
        get { return _Text; }
        set { _Text = value; RaisePropertyChanged(() => this.Text); }
    }
}

One level up you'll want a main model to encapsulate an array of these, this is where you will typically do all your game logic:

public class MainModel : ViewModelBase
{
    private ObservableCollection<Cell> _Cells;
    public ObservableCollection<Cell> Cells
    {
        get { return _Cells; }
        set { _Cells = value; RaisePropertyChanged(() => this.Cells); }
    }

    public MainModel()
    {
        this.Cells = new ObservableCollection<Cell>(
            Enumerable.Range(1, 100)
                .Select(i => new Cell { Text = i.ToString() })
        );
    }
}

Notice that all I'm doing at the moment is creating a 100-element collection of cells. This main view model becomes the one that you assign to your window's data context:

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new MainModel();
}

Now your XAML controls need to bind to this data. ItemsControl is used to render a collection of elements so use one of those and bind it to your array. You want them displayed in a 2D grid so replace the ItemsPanelTemplate with a WrapPanel. Finally add a DataTemplate for your Cell class so that a button gets drawn for each cell:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:Cell}">
        <Button Width="32" Height="32" Content="{Binding Text}"/>
    </DataTemplate>
</Window.Resources>

<ItemsControl ItemsSource="{Binding Cells}" Width="320" Height="320" HorizontalAlignment="Center" VerticalAlignment="Center">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel  />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

That's how you use WPF. Your logic is stored entirely in the view model and it's completely decoupled from the view. Here's what this particular code displays, it should be pretty self-evident how flexible this code is and easy to change:

在此处输入图片说明

That's very possible. Simply declare an array variable :

private Button[] _buttonArray;

populate the array once, maybe in constructor :

_buttonArray = new[] {Cell00, Cell01, .... , Cell22};

And all of the buttons are now accessible through _buttonArray .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM