简体   繁体   English

如何使用条件更新ItemsControl显示的所有项目?

[英]How to update all items shown by ItemsControl using a condition?

In a UWP Project I have a UI which has an ItemsControl bound to a set of Team objects. 在UWP项目中,我有一个UI,该UI具有绑定到一组Team对象的ItemsControl。 There is a separate GameController object that has a CurrentTeam property which changes as the Game progresses. 有一个单独的GameController对象,该对象具有CurrentTeam属性,该属性会随着游戏的进行而变化。 I want to be able to have a visual cue in the ItemTemplate for the Team that is the CurrentTeam. 我希望能够在CurrentTemplate的团队的ItemTemplate中具有视觉提示。 An example would be the Current Team's name gets underlined say. 一个例子是当前团队的名字下划线。 The Team objects do not have a reference to the GameController. Team对象没有对GameController的引用。

One way is to put a flag on each Team, say IsCurrentTeam and bind to that in the ItemTemplate. 一种方法是在每个Team上放置一个标志,例如IsCurrentTeam并绑定到ItemTemplate中的标志。 I don't particularly like this approach as it means when the CurrentTeam changes I've got to loop around all the Teams except the current one, to update their flags. 我并不特别喜欢这种方法,因为这意味着当CurrentTeam更改时,我必须循环遍除当前团队之外的所有团队,以更新其标志。

In WPF I think there might have been a solution using an ObjectDataProvider as it offers the ability to bind to a method, but since I'm in UWP this option is not available. 在WPF中,我认为可能存在使用ObjectDataProvider的解决方案,因为它提供了绑定到方法的功能,但是由于我在UWP中,因此该选项不可用。

Does anyone know of a better approach to do this? 有谁知道这样做的更好方法?

Ok, I've prepared an example that shows how this achievable. 好的,我准备了一个示例,说明如何实现此目的。 To work around limitations in UWP it uses a few techniques such as 'data context anchoring' and attached properties. 为了解决UWP中的限制,它使用了一些技术,例如“数据上下文锚定”和附加属性。

Here's my support classes, I assume they're somewhat similar to yours: 这是我的支持课程,我认为它们与您的有些相似:

public class GameControllerViewModel : INotifyPropertyChanged
{
    private Team _currentTeam;

    public event PropertyChangedEventHandler PropertyChanged;

    public GameControllerViewModel(IEnumerable<Team> teams)
    {
        Teams = teams;
    }

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public Team CurrentTeam
    {
        get { return _currentTeam; }
        set
        {
            if (value != _currentTeam)
            {
                _currentTeam = value;

                OnPropertyChanged();
            }
        }
    }

    public IEnumerable<Team> Teams { get; private set; }
}

public class Team
{
    public string Name { get; set; }
}

And the code behind of the page: 以及页面后面的代码:

public sealed partial class GamesPage : Page
{
    public GamesPage()
    {
        this.InitializeComponent();

        this.DataContext = new GameControllerViewModel(
            new[]
            {
                new Team { Name = "Team A" },
                new Team { Name = "Team B" },
                new Team { Name = "Team C" },
                new Team { Name = "Team D" }
            }
        );
    }
}

As you can see, the constructor of the page instantiates a GameControllerViewModel with four teams and sets it as the data context of the page. 如您所见,页面的构造函数实例化具有四个团队的GameControllerViewModel并将其设置为页面的数据上下文。

The page XAML is as follows: XAML页面如下:

<Page
    x:Class="UniversalScratchApp.GamesPage" x:Name="View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UniversalScratchApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <local:BoolToFontWeightConverter x:Key="BoolToFontWeightConverter"/>
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Current Team:" Margin="4" VerticalAlignment="Center"/>
        <ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding Teams}" SelectedItem="{Binding CurrentTeam, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" Margin="4">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
        <ItemsControl Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Teams}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" local:TeamProperties.CurrentTeam="{Binding ElementName=View, Path=DataContext.CurrentTeam}" local:TeamProperties.Team="{Binding}" FontWeight="{Binding Path=(local:TeamProperties.IsCurrentTeam), RelativeSource={RelativeSource Mode=Self}, Mode=OneWay, Converter={StaticResource BoolToFontWeightConverter}}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Page>

In the DataTemplate of the ItemsControl you can see that I bind to a three custom attached properties; 在ItemsControl的DataTemplate中,您可以看到我绑定到了三个自定义附加属性。 TeamProperties.CurrentTeam , TeamProperties.Team and TeamProperties.IsCurrentTeam . TeamProperties.CurrentTeamTeamProperties.TeamTeamProperties.IsCurrentTeam The attached properties are defined in the following class: 附加的属性在以下类中定义:

[Bindable]
public static class TeamProperties
{
    private static void TeamPropertiesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        Team team = GetTeam(sender);
        Team currentTeam = GetCurrentTeam(sender);

        if (team != null && currentTeam != null)
        {
            SetIsCurrentTeam(sender, team.Equals(currentTeam));
        }
    }

    public static readonly DependencyProperty CurrentTeamProperty = DependencyProperty.RegisterAttached("CurrentTeam", typeof(Team), typeof(TeamProperties), new PropertyMetadata(null, TeamPropertiesChanged));

    public static Team GetCurrentTeam(DependencyObject obj)
    {
        return (Team)obj.GetValue(CurrentTeamProperty);
    }

    public static void SetCurrentTeam(DependencyObject obj, Team value)
    {
        obj.SetValue(CurrentTeamProperty, value);
    }

    public static readonly DependencyProperty TeamProperty = DependencyProperty.RegisterAttached("Team", typeof(Team), typeof(TeamProperties), new PropertyMetadata(null, TeamPropertiesChanged));

    public static Team GetTeam(DependencyObject obj)
    {
        return (Team)obj.GetValue(TeamProperty);
    }

    public static void SetTeam(DependencyObject obj, Team value)
    {
        obj.SetValue(TeamProperty, value);
    }

    public static readonly DependencyProperty IsCurrentTeamProperty = DependencyProperty.RegisterAttached("IsCurrentTeam", typeof(bool), typeof(TeamProperties), new PropertyMetadata(false));

    public static bool GetIsCurrentTeam(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsCurrentTeamProperty);
    }

    public static void SetIsCurrentTeam(DependencyObject obj, bool value)
    {
        obj.SetValue(IsCurrentTeamProperty, value);
    }
}

To explain, the CurrentTeam and Team properties are set on the dependency object (the textblock) by the bindings. 为了说明,通过绑定在依赖对象(文本块)上设置CurrentTeamTeam属性。 While the Team property can use the current datacontext, the CurrentTeam property must be bound to the 'outer' DataContext. 尽管Team属性可以使用当前数据上下文,但是CurrentTeam属性必须绑定到“外部” DataContext。 It does this by specifying an x:Name="View" on the Page and using that to 'anchor' the datacontext so it can then be accessed by bindings using the ElementName=View part of the binding. 它通过在页面上指定x:Name="View"并将其“锚定”数据上下文来执行此操作,以便随后可以使用绑定的ElementName=View部分通过绑定对其进行访问。

So, whenever either of these properties change, the IsCurrentTeam property is set on the same dependency object by the TeamPropertiesChanged callback. 因此,只要这些属性中的任何一个发生更改, TeamPropertiesChanged回调就在同一依赖项对象上设置IsCurrentTeam属性。 The IsCurrentTeam property then is bound to the FontWeight property (as it was easier than underlining) with the BoolToFontWeightConverter shown here: 然后,将IsCurrentTeam属性绑定到FontWeight属性(因为它比下划线要容易),并在此处显示BoolToFontWeightConverter:

public class BoolToFontWeightConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        if (value is bool)
        {
            return ((bool)value) ? FontWeights.ExtraBold : FontWeights.Normal;
        }
        else
        {
            return DependencyProperty.UnsetValue;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotSupportedException();
    }
}

Now, when a team is selected in the top combobox (a proxy for whatever mechanism you use to change teams) the appropriate team in the ItemsControl will be displayed in bold. 现在,当在顶部组合框中选择了一个团队(用于更改团队的机制的代理)时, ItemsControl的相应团队将以粗体显示。

Works nicely for me. 对我来说很好。 Hope it helps. 希望能帮助到你。

In WPF my preferred option would be a DataTrigger, setting the underline property when the DataContext {Binding} of an item equals the contents of CurrentTeam on the parent (use an elementname to refer to that). 在WPF中,我的首选选项是DataTrigger,当项的DataContext {Binding}等于父项上CurrentTeam的内容时设置下划线属性(使用元素名称来引用)。

As UWP doesn't support Trigger, you can use a DataTriggerBehaviour like in this post . 由于UWP不支持触发器,您可以使用DataTriggerBehaviour这个职位

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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