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

时间:2016-11-11 15:39:12

标签: c# wpf xaml uwp uwp-xaml

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

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

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

有没有人知道更好的方法呢?

2 个答案:

答案 0 :(得分:1)

好的,我已经准备了一个展示如何实现这一目标的例子。为了解决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.CurrentTeamTeamProperties.TeamTeamProperties.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);
    }
}

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

因此,每当这些属性中的任何一个发生更改时,IsCurrentTeam回调都会在同一个依赖项对象上设置TeamPropertiesChanged属性。然后使用此处显示的BoolToFontWeightConverter将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中的相应团队将以粗体显示。

很适合我。希望它有所帮助。

答案 1 :(得分:0)

在WPF中,我首选的选项是DataTrigger,当项的DataContext {Binding}等于父项的CurrentTeam的内容时设置下划线属性(使用元素名来引用它)。

由于UWP不支持触发器,您可以使用this post中的DataTriggerBehaviour