如何将ListViewItem的IsSelected绑定到Style中的属性

时间:2018-02-01 17:04:13

标签: c# wpf xaml data-binding

我正在尝试根据类的属性突出显示ListView中的一行。 ItemSource设置为ObservableCollection<FileInformation>我需要确定该行是否应突出显示的属性包含在FileInformation类 - bool IsPlaying中。

这是我的ListView xaml:

    <ListView Name="lvListView" Margin="0,0,0,35" AllowDrop="True" ItemsSource="{Binding FilesCollection}"> 
        <ia:Interaction.Triggers>
        <ia:EventTrigger EventName="Drop">
            <cmd:EventToCommand Command="{Binding ListViewFileDrop}" PassEventArgsToCommand="True"/>
        </ia:EventTrigger>
        <ia:EventTrigger EventName="MouseDoubleClick">
            <cmd:EventToCommand Command="{Binding ListViewDoubleClickCommand}" PassEventArgsToCommand="True"/>
        </ia:EventTrigger>
    </ia:Interaction.Triggers>

        <ListView.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Remove" Command="{Binding RemoveCommand}" CommandParameter="{x:Reference Name=lvListView}"/>

                <MenuItem Header="Add File"  Command="{Binding BrowseCommand}"/>

                <MenuItem Header="Clear All"  Command="{Binding ClearAllCommand}"/>
            </ContextMenu>
        </ListView.ContextMenu>

        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="IsSelected" Value="{Binding RelativeSource={RelativeSource Self}, Path=IsPlaying, Converter={converters:TestConverter}}"/>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="Yellow"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ListView.ItemContainerStyle>

    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Title" Width="350" DisplayMemberBinding="{Binding Title}"/>

                    <GridViewColumn Header="Time" Width="114" 
                                    DisplayMemberBinding="{Binding Time, Converter={converters:TimeSpanFormatConverter}}"/>
                </GridView.Columns>
        </GridView>
    </ListView.View>
  </ListView>

这是我正在努力的部分:

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Setter Property="IsSelected" Value="{Binding RelativeSource={RelativeSource Self}, Path=IsPlaying, Converter={converters:TestConverter}}"/>
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Background" Value="Yellow"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</ListView.ItemContainerStyle>

现在为了突出显示我需要点击它的行,我希望仅当属性IsPlaying设置为true时才以黄色突出显示。

这是财产声明:

public static readonly DependencyProperty IsPlayingProperty =
    DependencyProperty.Register(nameof(IsPlaying), typeof(bool), typeof(FileInformation),
        new PropertyMetadata(null));

private bool _isPlaying = false;
public bool IsPlaying
{
    get => (bool)GetValue(IsPlayingProperty);
    set
    {
        SetValue(IsPlayingProperty, value);
        OnPropertyChanged();
    }
}

public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}

1 个答案:

答案 0 :(得分:0)

看看这是否有帮助。看一下转换器XAML(在绑定和Window.Resources中)。

我添加了一个只读数据网格来查看原始数据和一个用于切换(true / false)特定FileInformation.IsPlaying的按钮。

MainWindow.xaml

<Window x:Class="WpfApp13.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp13"
        xmlns:converters="clr-namespace:WpfApp13"
        mc:Ignorable="d"
        Title="MainWindow" Height="600" Width="525">
    <Window.Resources>
        <converters:TestConverter x:Key="TestConverter" />
        <converters:TimeSpanFormatConverter x:Key="TimeSpanFormatConverter" />
    </Window.Resources>
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!-- Since it looks like you want to play audio, I set SelectionMode to Single. Probably don't want to play multiple at once -->
        <ListView Name="lvListView" Margin="0,0,0,35" AllowDrop="True" ItemsSource="{Binding FilesCollection}" SelectionMode="Single">
            <!--<ia:Interaction.Triggers>
                <ia:EventTrigger EventName="Drop">
                    <cmd:EventToCommand Command="{Binding ListViewFileDrop}" PassEventArgsToCommand="True"/>
                </ia:EventTrigger>
                <ia:EventTrigger EventName="MouseDoubleClick">
                    <cmd:EventToCommand Command="{Binding ListViewDoubleClickCommand}" PassEventArgsToCommand="True"/>
                </ia:EventTrigger>
            </ia:Interaction.Triggers>-->

            <ListView.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Remove" Command="{Binding RemoveCommand}" CommandParameter="{x:Reference Name=lvListView}"/>

                    <MenuItem Header="Add File"  Command="{Binding BrowseCommand}"/>

                    <MenuItem Header="Clear All"  Command="{Binding ClearAllCommand}"/>
                </ContextMenu>
            </ListView.ContextMenu>

            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="Background"  Value="Beige"/>
                    <!-- The converter, TestConverter, isn't even needed but I'll leave it since you might be doing something else with it -->
                    <Setter Property="IsSelected" Value="{Binding Path=IsPlaying, Converter={StaticResource TestConverter}}"/>
                    <Style.Triggers>
                        <!--<Trigger Property="IsSelected" Value="True">-->
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="Yellow"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListView.ItemContainerStyle>

            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Title" Width="350" DisplayMemberBinding="{Binding Title}"/>

                        <GridViewColumn Header="Time" Width="114" 
                                    DisplayMemberBinding="{Binding Time, Converter={StaticResource TimeSpanFormatConverter}}"/>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>

        <DataGrid Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding FilesCollection}" IsReadOnly="True">
            <DataGrid.Columns>
                <DataGridCheckBoxColumn Binding="{Binding IsPlaying}" Header="IsPlaying" />
                <DataGridTextColumn Binding="{Binding Title}" Header="Title" />
                <DataGridTextColumn Binding="{Binding Time}" Header="Time" />
            </DataGrid.Columns>
        </DataGrid>

        <Button Content="Toggle File 5 IsPlaying" Grid.Row="2" Margin="10" Click="Button_Click" />
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace WpfApp13
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ((MainWindowViewModel)DataContext).PlayToggle(4); // I'm too lazy to use an ICommand...
        }
    }

    public class MainWindowViewModel : DependencyObject
    {
        public ObservableCollection<FileInformation> FilesCollection
        {
            get { return (ObservableCollection<FileInformation>)GetValue(FilesCollectionProperty); }
            set { SetValue(FilesCollectionProperty, value); }
        }
        public static readonly DependencyProperty FilesCollectionProperty = DependencyProperty.Register("FilesCollection", typeof(ObservableCollection<FileInformation>), typeof(MainWindowViewModel), new PropertyMetadata(null));

        public MainWindowViewModel()
        {
            FilesCollection = new ObservableCollection<FileInformation>() {
                new FileInformation() { Title = "File 1", IsPlaying = false, Time = new TimeSpan(0, 5, 1) },
                new FileInformation() { Title = "File 2", IsPlaying = false, Time = new TimeSpan(0, 5, 2) },
                new FileInformation() { Title = "File 3", IsPlaying = false, Time = new TimeSpan(0, 5, 3) },
                new FileInformation() { Title = "File 4", IsPlaying = false, Time = new TimeSpan(0, 5, 4) },
                new FileInformation() { Title = "File 5", IsPlaying = false, Time = new TimeSpan(0, 5, 5) },
                new FileInformation() { Title = "File 6", IsPlaying = false, Time = new TimeSpan(0, 5, 6) },
                new FileInformation() { Title = "File 7", IsPlaying = false, Time = new TimeSpan(0, 5, 7) },
                new FileInformation() { Title = "File 8", IsPlaying = false, Time = new TimeSpan(0, 5, 8) },
                new FileInformation() { Title = "File 9", IsPlaying = false, Time = new TimeSpan(0, 5, 9) },
            };
        }

        public void PlayToggle(int idx)
        {
            FilesCollection[idx].IsPlaying = !FilesCollection[idx].IsPlaying;
        }
    }

    public class FileInformation : DependencyObject
    {
        public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(nameof(IsPlaying), typeof(bool), typeof(FileInformation), new PropertyMetadata(null));
        //private bool _isPlaying = false;
        public bool IsPlaying
        {
            get => (bool)GetValue(IsPlayingProperty);
            set
            {
                SetValue(IsPlayingProperty, value);
                OnPropertyChanged();
            }
        }

        public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(FileInformation), new PropertyMetadata(null));
        public string Title
        {
            get => (string)GetValue(TitleProperty);
            set
            {
                SetValue(TitleProperty, value);
                OnPropertyChanged();
            }
        }

        public TimeSpan Time
        {
            get { return (TimeSpan)GetValue(TimeProperty); }
            set { SetValue(TimeProperty, value); }
        }
        public static readonly DependencyProperty TimeProperty = DependencyProperty.Register("Time", typeof(TimeSpan), typeof(FileInformation), new PropertyMetadata(null));


        public event PropertyChangedEventHandler PropertyChanged;
        //[NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }

    public class TestConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // value = IsPlaying
            if (value is bool b)
            {
                return b;
            }
            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value; // Needed because IsPlaying is twoway bound to IsSelected.
        }
    }

    public class TimeSpanFormatConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            // value = Time
            if (value is TimeSpan ts)
            {
                return $"{ts.Minutes} minutes {ts.Seconds} seconds";
            }
            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

如果您不希望IsSelected与IsPlaying相关联,但仍希望IsPlaying触发黄色背景,请尝试以下操作:

<ListView.ItemContainerStyle>
    <Style TargetType="ListViewItem">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsPlaying}" Value="True">
                <Setter Property="Background" Value="Yellow" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ListView.ItemContainerStyle>