WPF ItemsControl联合UniformGrid绑定集合,动态修改数据模板

例如:
扫雷程序:在第一次绑定数据模板后,后面要根据翻开的格子的属性,进行判断,来决定翻开后要展示插旗、空白格或者数字格

绑定数据的实体类

这里必须要把需要动态绑定的属性加上OnPropertyChanged事件,否则无法触发

public class NumData : INotifyPropertyChanged
{
    private bool _isLei;
    private bool _isOpen;
    private bool _isSign;
    public int Num { get; set; }
    public bool IsLei
    {
        get { return _isLei; }
        set { _isLei = value; OnPropertyChanged(nameof(IsLei)); }
    }
    public int Row { get; set; }
    public int Col { get; set; }
    public bool IsOpen
    {
        get { return _isOpen; }
        set { _isOpen = value; OnPropertyChanged(nameof(IsOpen)); }
    }
    public bool IsSign
    {
        get { return _isSign; }
        set { _isSign = value;OnPropertyChanged(nameof(IsSign)); }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

窗体设置上下文,并设置响应式集合数据

public partial class GamePage : UserControl, INotifyPropertyChanged
{
    public GamePage()
    {
        InitializeComponent();
    }

    public GamePage(int sizeRow, int sizeCol, int boomNum)
    {
        InitializeComponent();
        SizeRow = sizeRow;
        SizeCol = sizeCol;
        DataContext = this; // 设置上下文
        GenerateData(); // 生成初始数据
    }

    private bool GenerateData()
    {
        try
        {
            // 生成数据的逻辑
            NumDatas = new List<NumDatas>();
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }    

    #region 响应式数据定义
    private List<NumData> _numDatas;
    public List<NumData> NumDatas
    {
        get { return _numDatas; }
        set
        {
            _numDatas = value;
            OnPropertyChanged(nameof(NumDatas));
        }
    }

    private int _sizeRow;
    public int SizeRow
    {
        get { return _sizeRow; }
        set
        {
            _sizeRow = value;
            OnPropertyChanged(nameof(SizeRow));
        }
    }

    private int _sizeCol;
    public int SizeCol
    {
        get { return _sizeCol; }
        set
        {
            _sizeCol = value;
            OnPropertyChanged(nameof(SizeCol));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

创建数据模板

我这里是子窗体,所以是UserControl.Resources
创建了3个数据模板,并且声明了一个数据模板选择器

<UserControl.Resources>
    <DataTemplate x:Key="TemplateA">
        <TextBox Content={Binding Num}/>
    </DataTemplate>

    <DataTemplate x:Key="TemplateB">
        <Border BorderThickness="1" BorderBrush="DarkGray">
            <Button Background="#f7d3ba"></Button>
        </Border>
    </DataTemplate>

    <DataTemplate x:Key="TemplateC">
        <Border BorderThickness="1" BorderBrush="DarkGray"/>
    </DataTemplate>
    
    <!-- 定义 TemplateSelector -->
    <local:ItemTemplateSelector x:Key="ItemTemplateSelector"
                                TemplateA="{StaticResource TemplateA}"
                                TemplateB="{StaticResource TemplateB}"
                                TemplateC="{StaticResource TemplateC}"/>    
</UserControl.Resources>

下面介绍两种可以在运行中动态修改数据模版的方法

一、使用DataTrigger

MultiDataTrigger可以多条件选择,与DataTrigger作用是一样的
使用这种方法时,不需要声明上面的模板选择器ItemTemplateSelector

编辑XAML

<Grid>
    <ItemsControl ItemsSource="{Binding NumDatas, Mode=TwoWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Rows="{Binding SizeRow}" Columns="{Binding SizeCol}" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <!--默认的模板-->
                <ContentControl x:Name="cc" Content="{Binding}" ContentTemplate="{StaticResource TemplateA}"/>
                <DataTemplate.Triggers>
                                 
                    <!--条件1:IsSign为True-->
                    <DataTrigger Binding="{Binding IsSign}" Value="True">
                        <Setter TargetName="cc" Property="ContentTemplate" Value="{StaticResource TemplateB}"/>
                    </DataTrigger> 
                    
                    <!--条件2:当IsLei为True,且IsOpen为True-->                        
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding IsLei}" Value="True"/>
                            <Condition Binding="{Binding IsOpen}" Value="True"/>
                        </MultiDataTrigger.Conditions>
                        <Setter TargetName="cc" Property="ContentTemplate" Value="{StaticResource TemplateC}"/>
                    </MultiDataTrigger>
                    
                </DataTemplate.Triggers>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        
    </ItemsControl>
</Grid>

二、使用DataTemplateSelector

创建模板选择器

public class ItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate TemplateA { get; set; }
    public DataTemplate TemplateB { get; set; }
    public DataTemplate TemplateC { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is NumData numData)
        {
            PropertyChangedEventHandler lamda = null;
            lamda = (o, args) =>
            {
                if(args.PropertyName == "IsSign" || args.PropertyName == "IsOpen")
                {
                    numData.PropertyChanged -= lamda;
                    var cp = (ContentPresenter)container;
                    cp.ContentTemplateSelector = null;
                    cp.ContentTemplateSelector = this;
                }
            };
            numData.PropertyChanged += lamda;

            if (numData.IsSign)
            {
                return TemplateB;
            }
            else if (numData.IsLei && numData.IsOpen)
            {
                return TemplateC;
            }
            else
            {
                return TemplateA;
            }
        }
        return base.SelectTemplate(item, container);
    }
}

绑定模板选择器

对应上面创建数据模板里面的选择器

<Grid>
    <ItemsControl ItemsSource="{Binding NumDatas, Mode=TwoWay}"
            ItemTemplateSelector="{StaticResource ItemTemplateSelector}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Rows="{Binding SizeRow}" Columns="{Binding SizeCol}" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>        
    </ItemsControl>
</Grid>

使用这两种方法,都在程序运行的时候,修改集合对象属性值之后,动态更换数据模板

posted @ 2025-04-16 17:02  LuoLh  阅读(121)  评论(0)    收藏  举报