如何使用条件更新ItemsControl显示的所有项目?(How to update all items shown by ItemsControl using a condition?)

编程入门 行业动态 更新时间:2024-10-25 12:23:00
如何使用条件更新ItemsControl显示的所有项目?(How to update all items shown by ItemsControl using a condition?)

在UWP项目中,我有一个UI,其中ItemsControl绑定到一组Team对象。 有一个单独的GameController对象,其CurrentTeam属性随着游戏的进展而变化。 我希望能够在ProjectTemplate中为CurrentTeam团队提供视觉提示。 一个例子是当前团队的名字被强调说。 Team对象没有对GameController的引用。

一种方法是在每个团队上放置一个标志,比如说IsCurrentTeam并绑定到ItemTemplate中的那个。 我并不特别喜欢这种方法,因为它意味着当CurrentTeam发生变化时,我必须绕过除当前团队之外的所有团队,以更新他们的标志。

在WPF中我认为可能有一个使用ObjectDataProvider的解决方案,因为它提供了绑定到方法的能力,但由于我在UWP中,因此该选项不可用。

有谁知道更好的方法来做到这一点?

非常感谢您的帮助。

In a UWP Project I have a UI which has an ItemsControl bound to a set of Team objects. There is a separate GameController object that has a CurrentTeam property which changes as the Game progresses. I want to be able to have a visual cue in the ItemTemplate for the Team that is the CurrentTeam. An example would be the Current Team's name gets underlined say. The Team objects do not have a reference to the GameController.

One way is to put a flag on each Team, say IsCurrentTeam and bind to that in the 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.

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.

Does anyone know of a better approach to do this?

最满意答案

好的,我已经准备了一个例子来说明这是如何实现的。 为了解决UWP中的限制,它使用了一些技术,例如“数据上下文锚定”和附加属性。

这是我的支持类,我认为它们与你的类似:

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; } }

以及页面背后的代码:

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" } } ); } }

如您所见,页面的构造函数实例化了一个包含四个团队的GameControllerViewModel,并将其设置为页面的数据上下文。

页面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>

在ItemsControl的DataTemplate中,您可以看到我绑定到三个自定义附加属性; TeamProperties.CurrentTeam , TeamProperties.Team和TeamProperties.IsCurrentTeam 。 附加属性在以下类中定义:

[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); } }

为了解释, CurrentTeam和Team属性是通过绑定在依赖项对象(文本块)上设置的。 虽然Team属性可以使用当前的datacontext,但CurrentTeam属性必须绑定到“外部”DataContext。 它通过在页面上指定x:Name="View"并使用它来“锚定”datacontext来实现这一点,然后可以使用绑定的ElementName=View部分通过绑定来访问它。

因此,每当这些属性中的任何一个发生更改时, TeamPropertiesChanged回调都会在同一依赖项对象上设置IsCurrentTeam属性。 然后使用IsCurrentTeam显示的IsCurrentTeam将IsCurrentTeam属性绑定到FontWeight属性(因为它比下划线更容易):

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(); } }

现在,当在顶部组合框中选择一个团队(用于更改团队的任何机制的代理)时, ItemsControl的相应团队将以粗体显示。

适合我。 希望能帮助到你。

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.

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.

The page XAML is as follows:

<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; TeamProperties.CurrentTeam, TeamProperties.Team and TeamProperties.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. While the Team property can use the current datacontext, the CurrentTeam property must be bound to the 'outer' 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.

So, whenever either of these properties change, the IsCurrentTeam property is set on the same dependency object by the TeamPropertiesChanged callback. The IsCurrentTeam property then is bound to the FontWeight property (as it was easier than underlining) with the BoolToFontWeightConverter shown here:

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.

Works nicely for me. Hope it helps.

更多推荐

CurrentTeam,Team,方法,电脑培训,计算机培训,IT培训"/> <meta name="descripti

本文发布于:2023-08-07 08:02:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1462599.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:如何使用   条件   项目   shown   condition

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!