【问题标题】:WPF - Game gridWPF - 游戏网格
【发布时间】:2012-01-06 12:55:56
【问题描述】:

我有 wpf 应用程序,一个数独游戏。我有一个游戏网格(9x9 方格)的模型和用户控件。因为我对绑定的了解有限,而且我没有足够的时间,所以我决定采用旧的方式,不进行数据绑定并手动同步模型和视图。但是它非常不干净并且出现了同步问题。

我决定转换为正确的数据绑定。

我想我的网格应该类似于 itemscontrol(列表框或组合框),但不是线性列表,而是将其项目布局到二维网格中。

我是绑定新手,我不知道如何实现我的目标。我有模型类,其中包含有关当前拼图的一些一般信息并包含单元格集合。每个单元格都有自己的属性,例如值、可能性、状态等。

我拥有整个网格的用户控制权和每个单独单元格的用户控制权。我需要网格绑定到我的模型的某些属性(例如,禁用、启用、数独类型)和每个单元格以绑定到我相应的单元格 - 值、背景等。

编辑:单元格在可观察的集合中。每个单元格都有 X 和 Y 属性。我认为他们应该以某种方式绑定到 Grid.Row 和 Grid.Column 属性。

你能指点我一些如何继续的方向吗?特别是创建itemscontrol以及如何绑定它?

谢谢。

【问题讨论】:

  • 我在答案中添加了一些代码示例,现在应该完全清楚了。

标签: c# .net wpf data-binding


【解决方案1】:

1) 它应该是可观察的吗? - 不,您可以使用例如 List<List<CellModel>>()
2) 如果您仍想使用 array[,] - 如果您想使用 ItemsControl,您可能需要某种转换器
3) 您可以将项目控件与 ItemTemplate 一起使用,这也将是一个项目控件

希望这会有所帮助

编辑 1:让我们从第 2 点创建一个转换器...

public class ArrayConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var val = value as CellModel[,];
        if (val == null) return null;
        return ToEnumerable(val);
    }

    private IEnumerable<IEnumerable<CellModel>> ToEnumerable(CellModel[,] array)
    {
        var count = array.GetLength(0);

        for (int i = 0; i < array.GetLength(0); ++i)
        {
            yield return GetLine(array, i);
        }
    }

    private IEnumerable<CellModel> GetLine(CellModel[,] array, int line)
    {
        var count = array.GetLength(1);
        for (int i = 0; i < count; ++i)
        {
            yield return array[line, i];
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

编辑 2:您的 xaml 可能看起来像(见第 3 点):

<Grid>

    <Grid.Resources>
        <Converters:ArrayConverter x:Key="ArrayConverter"/>
    </Grid.Resources>

    <ItemsControl
        ItemsSource="{Binding CellArray, Mode=OneWay, Converter={StaticResource ArrayConverter}}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <ItemsControl
                    ItemsSource="{Binding ., Mode=OneWay}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <!-- TODO: Add cell template here -->
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

编辑 3:添加行 &lt;StackPanel Orientation="Horizontal" IsItemsHost="True"/&gt; - 这将使您的行被水平渲染

编辑 4:您的视图模型现在可能是这样的:

public class GameViewModel : ViewModelBase
{
    public void Load()
    {
        var array = new CellModel[9, 9];
        for (int i = 0; i < 9; ++i)
        {
            for (int j = 0; j < 9; ++j)
            {
                array[i, j] = new CellModel()
                {
                    //TODO: init properties of the cell[i, j]
                };
            }
        }

        this.CellArray = array;
    }

    CellModel[,] _CellArray;
    public CellModel[,] CellArray
    {
        get
        {
            return _CellArray;
        }
        private set
        {
            if (_CellArray == value) return;
            _CellArray = value;
            NotifyPropertyChanged("CellArray");
        }
    }
}

如果还有什么不清楚的地方请告诉我

编辑 5:取决于您上次的编辑,让我们更改我们的 XAML:

  <ItemsControl
        ItemsSource="{Binding CellArray, Mode=OneWay}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid
                    Grid.Column="{Binding X}"
                    Grid.Row="{Binding Y}">
                    <!-- TODO: -->
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid IsItemsHost="True">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

【讨论】:

  • 谢谢,我决定使用可观察集合。我不知道 stackpanel 如何足够 - 我需要我的行和列大小相同,并在调整控件大小时调整大小。并且每三行/列的边框应该更厚(以创建 3x3 正方形的单元格)
  • 因为它更简单,实际上如果所有内容都绑定了,我不需要遍历索引。
  • 不,我的意思是,为什么是 ObservableCollection,而不仅仅是一个 List?仅当我们在运行时更改集合(例如添加或删除项目)时,我们才需要可观察的集合,但据我所知,在您的情况下,集合不会更改
  • 根据您最近的更改更改了 XAML 示例,请参阅我的回答中的“编辑 5”
  • 单元格的属性会改变,但单元格的数量不会改变。重点是我不需要二维 ollection 而只需要一维
【解决方案2】:

您不需要使用ObservableCollection 来绑定到集合,但是如果您希望自动实现 CollectionChange 通知,建议您这样做。

这意味着如果您更新您的收藏(例如,清除它并开始一个新游戏),它会自动告诉 UI 该收藏已更改,并且 UI 将使用新元素重新绘制自己。

您还可以使用 ObservableCollections 进行 Linq 语句来查找特定元素。例如,以下将返回第一个符合指定条件的单元格,如果没有找到项目,则返回 null

var cell = MyCollection.FirstOrDefault(
    cell => cell.Y == desiredRowIndex && cell.X == desiredColumnIndex);

【讨论】:

    猜你喜欢
    • 2022-11-14
    • 1970-01-01
    • 2017-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多