【问题标题】:WPF XAML Change multiple ellipses colors with data bindingWPF XAML 使用数据绑定更改多个椭圆的颜色
【发布时间】:2014-01-08 18:23:20
【问题描述】:

我目前正在创建一个 connect-4 游戏以学习 WPF 和 XAML。我制作了 UI,但遇到了一个问题。

您可以在下面看到有关游戏板的 XAML 代码摘录:

<Grid DockPanel.Dock="Bottom" Background="#FF1506A4" MouseLeftButtonUp="Grid_MouseLeftButtonUp_1">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            ... 5 more rows
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            ... 6 more columns
        </Grid.ColumnDefinitions>
        <Ellipse Grid.Row="0" Grid.Column="0" Fill="White" Margin="8"/>
        ... 41 more ellipses
</Grid>

棋盘存储在 GameState 类中的 Token 数组(一个 Empty、Red 和 Yellow 的枚举)中。

椭圆的颜色是使用 SolidBrushColor 类提供的。

我的问题是我不知道如何根据游戏模型改变椭圆的颜色。

我想我应该使用数据绑定,但我必须在绑定数据之前将颜色从 Token 类型转换为 SolidBrushColor 类型。我认为它可以使用一些 DataObjectProvider 对象来实现,但是为这样一个简单的任务创建 42 个 DataObjectProvider 对象似乎过于复杂......

那么根据最佳实践,正确的解决方案是什么?

【问题讨论】:

  • 这里提供了这么多帮助,却没有人接受甚至赞成?我认为upvote is a more proper way to say thank you 在这个网站
  • 很抱歉,我因为工作面试而缺席了几天。我很快就会回到代码上来。
  • 这就是原因。没关系,慢慢来。我上面的评论是以防万一..

标签: c# wpf xaml xaml-binding


【解决方案1】:

您需要在后端使用某种 ViewModel,然后利用 DataBinding。

假设以下(人为的)ViewModel 结构代表一个四连接板。

BoardViewModel.cs

public class BoardViewModel
{
    public BoardViewModel()
    {
        var rand = new Random();
        Squares = Enumerable
            .Range(1, 42)
            .Select(a => new SquareViewModel() { Token = rand.Next(-1, 2) })
            .ToList();
    }

    public List<SquareViewModel> Squares { get; set; }
}

SquareViewModel.cs

public class SquareViewModel : INotifyPropertyChanged
{
    private int _Token;
    public int Token
    {
        get
        {
            return _Token;
        }
        set
        {
            if (_Token.Equals(value)) return;

            _Token = value;
            RaisePropertyChanged("Token");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string property)
    {
        var handlers = PropertyChanged;
        if (handlers != null)
        {
            var args = new PropertyChangedEventArgs(property);
            handlers(this, args);
        }
    }
}

那么你就可以使用下面的 XAML 来代表你的板子了。

MainWindow.xaml

<Window 
    x:Class="ConnectFour.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    mc:Ignorable="d" 
    xmlns:ConnectFour="clr-namespace:ConnectFour"
    Title="MainWindow" Height="350" Width="525"
    d:DataContext="{d:DesignInstance Type={x:Type ConnectFour:BoardViewModel}, IsDesignTimeCreatable=True}">
    <ItemsControl
        ItemsSource="{Binding Squares}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Ellipse
                    Stroke="Magenta">
                    <Ellipse.Style>
                        <Style TargetType="Ellipse">
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding Token}" Value="0">
                                    <Setter Property="Fill" Value="Black" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding Token}" Value="1">
                                    <Setter Property="Fill" Value="Red" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Ellipse.Style>
                </Ellipse>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid IsItemsHost="True" Columns="6" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Window>

需要注意的重要事项是:

  • 根据 Square 的 Token 属性值设置椭圆颜色的 DataTrigger。
  • 使用带有 UniformGrid 支持的 ItemsPanel。
  • INotifyPropertyChanged 在 SquareViewModel 中的实现,这样当 Token 的值发生变化时,View 就代表了这一点。
  • d:DataContext 属性的使用,它将表单的设计时 DataContext 设置为 BoardViewModel 的实例(将自身初始化为随机标记)。

在运行时,您会希望将板视图的 DataContext 设置为 BoardViewModel 的真实实例(或任何您的 ViewModel 被调用),但如何更改标记颜色的基本概念已经存在.

【讨论】:

  • 如果我理解正确,这种模式是侵入性的,对吧?有没有办法拥有一个独立于框架的模型? ModelView 可以从 GameState 中检索数据吗?
  • 在 MVVM 中,你有 3 层。视图、视图模型和模型。模型包含逻辑,视图模型知道如何将模型信息转换为视图需要的格式,视图位于顶部。在您的特定情况下,您可以轻松拥有一个 GameStateViewModel,它将 GameState 信息转换为 View 所需的格式。
【解决方案2】:

如果Token 是具有颜色枚举属性的类,您可以在Token 类中添加另一个字符串类型的属性。在属性的getter中根据颜色枚举属性返回一个有效的颜色名称字符串。这样您就可以将每个Ellipse's Fill 属性绑定到相应的Token 的颜色字符串属性而无需构建您自己的转换器。 WPF 已经内置了字符串到颜色的转换器,这就是为什么您可以在 XAML 中指定颜色字符串并在渲染时获得正确的颜色画笔。

<Ellipse Grid.Row="0" Grid.Column="0" Margin="8"
         Fill="{Binding TokenList[0].ColorStringProperty}" />

请注意,上述解决方案是一种更好的做法,然后在 XAML 中为每个 Ellipse 分配名称,然后从代码中更改颜色。它适用于您现有的代码,无需彻底修改。但最佳实践将指导您在更大程度上利用数据绑定(如@Iain 的回答中所指出的),并实现 MVVM 设计模式。更长的路要走,但你会发现这是值得的。

最后,您还需要在Token 类或任何其他将UI 绑定到类对象属性的类中实现INotifyPropertyChanged(您还可以在@Heena 的答案中查看INotifyPropertyChanged 的示例) .

【讨论】:

  • 感谢您提及 MVVM 模式。看来我需要学习如何使用它。
【解决方案3】:

是的,您应该使用数据绑定。您应该将Fill 颜色绑定到您的模型,并使用ValueConverter 在枚举和颜色之间进行转换。或者,在您的 ViewModel 上有一个 Color 属性并直接绑定到该属性。

我认为使用Grid 是错误的方法,您应该将您的板建模为列表令牌(实际上是列表列表,用于行和列),然后使用ItemsControlItemsTemplate创建董事会。这意味着 XAML 的重复性要少得多。

当您尝试学习 WPF 和 XAML 时,请尝试使用谷歌搜索以上内容,看看您能学到多远。

【讨论】:

    猜你喜欢
    • 2022-01-13
    • 1970-01-01
    • 2021-10-11
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    相关资源
    最近更新 更多