<Style x:Key="CustomerItemStyle" TargetType="{x:Type ListViewItem}"> <Setter Property="IsSelected" Value="{Binding Path=IsSelected,Mode=TwoWay}" /> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="ItemsControl.AlternationIndex" Value="1" /> <Condition Property="IsSelected" Value="False" /> <Condition Property="IsMouSEOver" Value="False" /> </MultiTrigger.Conditions> <Setter Property="Background" Value="#EEEEEEEE" /> </MultiTrigger> </Style.Triggers> </Style>
它很好地绑定了IsSelected属性.样式应用于ListView,如下所示:
<ListView ItemContainerStyle="{StaticResource CustomerItemStyle}" ItemsSource="{Binding}" >
我的版本
我尝试通过DataGridRow以类似的方式与DataGrid绑定IsSelected.但是,当通过项目viewmodel的集合选择项目时会导致问题,其中定义了IsSelected属性.
因为它使用双向绑定,我会认为可以通过UI和项目viewmodel的集合来选择项目.
假设我通过UI选择项目,这很好用.我可以选择单个项目,然后使用[shift]选择范围,然后使用[ctrl]选择更多项目.取消选择项也可以正常工作.
但是我通过集合选择了一堆.让我们说点击按钮(就像我在下面的代码中所做的那样),选择了一堆项目.当我向下滚动DataGrid时,会选择一些,因为它们应该是一些不是.如果我通过UI选择一个项目,那么只有一些项目被取消选择而一些项目仍然被选中,这一切都有点时髦.即使左上角的全选按钮也不是很正确.
码
所有代码都在下面,底部是视图,关键部分是带有IsSelected绑定的DataGridRow样式.
这是我的用户类:
using System.ComponentModel; namespace WpfAppDataGrid.Model { public class User : INotifyPropertyChanged { public static User CreateNewUser() { return new User(); } public User() { } public int User_ID { get; set; } public string Username { get; set; } public string Name { get; set; } public string Job_Title { get; set; } public string Department { get; set; } public string Company { get; set; } public string Phone_Office { get; set; } public string Phone_Mobile { get; set; } public string Email { get; set; } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this,new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } }
这是Userviewmodel,其中IsSelected驻留在:
using System; using System.ComponentModel; using WpfAppDataGrid.DataAccess; using WpfAppDataGrid.Model; namespace WpfAppDataGrid.viewmodel { class Userviewmodel : INotifyPropertyChanged { readonly User _user; readonly UserRepository _userRepository; bool _isSelected; public Userviewmodel(User user,UserRepository userRepository) { if (user == null) throw new ArgumentNullException("user"); if (userRepository == null) throw new ArgumentNullException("userRepository"); _user = user; _userRepository = userRepository; } public Userviewmodel() { } public int User_ID { get { return _user.User_ID; } set { if (value == _user.User_ID) return; _user.User_ID = value; RaisePropertyChanged("User_ID"); } } public string Username { get { return _user.Username; } set { if (value == _user.Username) return; _user.Username = value; RaisePropertyChanged("Username"); } } public string Name { get { return _user.Name; } set { if (value == _user.Name) return; _user.Name = value; RaisePropertyChanged("Name"); } } public string Job_Title { get { return _user.Job_Title; } set { if (value == _user.Job_Title) return; _user.Job_Title = value; RaisePropertyChanged("Job_Title"); } } public string Department { get { return _user.Department; } set { if (value == _user.Department) return; _user.Department = value; RaisePropertyChanged("Department"); } } public string Company { get { return _user.Company; } set { if (value == _user.Company) return; _user.Company = value; RaisePropertyChanged("Company"); } } public string Phone_Office { get { return _user.Phone_Office; } set { if (value == _user.Phone_Office) return; _user.Phone_Office = value; RaisePropertyChanged("Phone_Office"); } } public string Phone_Mobile { get { return _user.Phone_Mobile; } set { if (value == _user.Phone_Mobile) return; _user.Phone_Mobile = value; RaisePropertyChanged("Phone_Mobile"); } } public string Email { get { return _user.Email; } set { if (value == _user.Email) return; _user.Email = value; RaisePropertyChanged("Email"); } } /// <summary> /// Gets/sets whether this customer is selected in the UI. /// </summary> public bool IsSelected { get { return _isSelected; } set { if (value == _isSelected) return; _isSelected = value; RaisePropertyChanged("IsSelected"); } } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this,new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } }
这是我的AllUsersviewmodel:
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data; using System.Windows.Input; using WpfAppDataGrid.DataAccess; namespace WpfAppDataGrid.viewmodel { class AllUsersviewmodel : INotifyPropertyChanged { readonly UserRepository _userRepository; public AllUsersviewmodel() { _userRepository = new UserRepository(); _userRepository.LoadUsers(); CreateAllUsers(); } void CreateAllUsers() { List<Userviewmodel> all = (from usr in _userRepository.GetUsers() select new Userviewmodel(usr,_userRepository)).ToList(); foreach (Userviewmodel uvm in all) { uvm.PropertyChanged += this.OnUserviewmodelPropertyChanged; } this.UserCollection = new ObservableCollection<Userviewmodel>(all); this.UserCollection.CollectionChanged += this.OnCollectionChanged; } private ObservableCollection<Userviewmodel> userCollection; public ObservableCollection<Userviewmodel> UserCollection { get { return userCollection; } set { userCollection = value; RaisePropertyChanged("UserCollection"); } } RelayCommand selectItemsCommand; public ICommand SelectItemsCommand { get { if (selectItemsCommand == null) selectItemsCommand = new RelayCommand(SelectItemsCommandExecute,CanSelectItemsCommand); return selectItemsCommand; } } private void SelectItemsCommandExecute(object parameter) { for (int i = 4; i <= 49; i++) { UserCollection[i].IsSelected = true; } } private bool CanSelectItemsCommand(object parameter) { return true; } void OnCollectionChanged(object sender,NotifyCollectionChangedEventArgs e) { if (e.NewItems != null && e.NewItems.Count != 0) foreach (Userviewmodel userVM in e.NewItems) userVM.PropertyChanged += this.OnUserviewmodelPropertyChanged; if (e.OldItems != null && e.OldItems.Count != 0) foreach (Userviewmodel userVM in e.OldItems) userVM.PropertyChanged -= this.OnUserviewmodelPropertyChanged; } void OnUserviewmodelPropertyChanged(object sender,PropertyChangedEventArgs e) { string IsSelected = "IsSelected"; if (e.PropertyName == IsSelected) this.RaisePropertyChanged("TotalSelectedUsers"); } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this,new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } public class RelayCommand : ICommand { readonly Action<object> _execute; readonly Predicate<object> _canExecute; /// <summary> /// Creates a new command that can always execute. /// </summary> /// <param name="execute">The execution logic.</param> public RelayCommand(Action<object> execute) : this(execute,null) { } /// <summary> /// Creates a new command. /// </summary> /// <param name="execute">The execution logic.</param> /// <param name="canExecute">The execution status logic.</param> public RelayCommand(Action<object> execute,Predicate<object> canExecute) { if (execute == null) throw new ArgumentNullException("execute"); _execute = execute; _canExecute = canExecute; } [DebuggerStepThrough] public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(parameter); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { _execute(parameter); } } }
using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using WpfAppDataGrid.Model; namespace WpfAppDataGrid.DataAccess { public class UserRepository { ObservableCollection<User> _users = new ObservableCollection<User>(); public UserRepository() { } public ObservableCollection<User> GetUsers() { return _users; } public void LoadUsers() { int i = 0; while (i < 1000) { i++; var user = new User(); user.User_ID = i; user.Username = RandomString(8,true); user.Name = user.Username + " " + RandomString(8,true); user.Job_Title = RandomString(8,true); user.Department = RandomString(8,true); user.Company = RandomString(10,true); user.Phone_Office = "07 " + RandomNumber(5200,6700) + " " + RandomNumber(1000,9999); user.Phone_Mobile = "04 " + RandomNumber(2800,4500) + " " + RandomNumber(1000,9999); user.Email = user.Username + "@gmail.com"; _users.Add(user); } } private static Random randomSeed = new Random(); public static string RandomString(int size,bool lowerCase) { StringBuilder RandStr = new StringBuilder(size); int Start = (lowerCase) ? 97 : 65; for (int i = 0; i < size; i++) RandStr.Append((char)(26 * randomSeed.NextDouble() + Start)); return RandStr.ToString(); } private int RandomNumber(int min,int max) { return randomSeed.Next(min,max); } } }
最后这里是所有用户的视图:
<Window x:Class="WpfAppDataGrid.View.AllUsersView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewmodel="clr-namespace:WpfAppDataGrid.viewmodel" Title="AllUsersView" Height="450" Width="820"> <Window.DataContext> <viewmodel:AllUsersviewmodel /> </Window.DataContext> <Window.Resources> <Style x:Key="UserRowStyle" TargetType="{x:Type DataGridRow}"> <Setter Property="IsSelected" Value="{Binding Path=IsSelected,Mode=TwoWay}" /> <Setter Property="BorderBrush" Value="DarkGray" /> <Setter Property="BorderThickness" Value="0,1,0"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="Black"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridRow}"> <Grid> <Border x:Name="DGR_BackingBorder" BorderBrush="Orange" BorderThickness="1,2,2" Background="Transparent"> </Border> <Border x:Name="DGR_Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,2" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"> <SelectiveScrollingGrid> <SelectiveScrollingGrid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </SelectiveScrollingGrid.ColumnDefinitions> <SelectiveScrollingGrid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </SelectiveScrollingGrid.RowDefinitions> <DataGridCellsPresenter x:Name="DGR_CellsPresenter" Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> <DataGridDetailsPresenter x:Name="DGR_DetailsPresenter" Grid.Column="1" Grid.Row="1" SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen,ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical},Converter={x:Static DataGrid.RowDetailsScrollingConverter},RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Visibility="{TemplateBinding DetailsVisibility}" /> <DataGridRowHeader Foreground="White" Grid.RowSpan="2" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical" Visibility="{Binding HeadersVisibility,ConverterParameter={x:Static DataGridHeadersVisibility.Row},Converter={x:Static DataGrid.HeadersVisibilityConverter},RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> </SelectiveScrollingGrid> </Border> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouSEOver" Value="True"> <Setter TargetName="DGR_Border" Property="BorderBrush" Value="Transparent" /> <Setter TargetName="DGR_Border" Property="BorderThickness" Value="1,2" /> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter TargetName="DGR_Border" Property="BorderBrush" Value="DarkOrange"/> <Setter TargetName="DGR_Border" Property="Background" Value="Orange"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type DataGridCell}" > <Setter Property="Background" Value="Transparent"/> <Setter Property="BorderBrush" Value="Transparent"/> <Setter Property="BorderThickness" Value="0" /> <Setter Property="Foreground" Value="Black" /> </Style> </Window.Resources> <Grid Name="gridUsers" Background="Transparent"> <DockPanel Background="Transparent" Margin="2,10,2" > <Grid DockPanel.Dock="Bottom" Margin="0,4,2"> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center"> <Button Content="Select rows 5 to 50" Command="{Binding SelectItemsCommand}"/> <TextBlock Text=" Total: " /> <ContentPresenter Content="{Binding ElementName=GenericDataGrid,Path=ItemsSource.Count}" ContentStringFormat="0" /> </StackPanel> </Grid> <DataGrid Name="GenericDataGrid" Background="Transparent" RowStyle="{StaticResource UserRowStyle}" BorderThickness="0" CanUserReorderColumns="True" AutoGenerateColumns="False" ItemsSource="{Binding UserCollection}" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTextColumn Header="ID" CanUserReorder="True" IsReadOnly="True" Binding="{Binding Path=User_ID,NotifyOnTargetUpdated=True}" /> <DataGridTextColumn Header="Name" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Name}"/> <DataGridTextColumn Header="Username" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Username}"/> <DataGridTextColumn Header="Job Title" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Job_Title}"/> <DataGridTextColumn Header="Department" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Department}"/> <DataGridTextColumn Header="Company" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Company}"/> <DataGridTextColumn Header="Phone" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Phone_Office}"/> <DataGridTextColumn Header="Mobile" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Phone_Mobile}"/> <DataGridTextColumn Header="eMail" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Email}"/> </DataGrid.Columns> </DataGrid> </DockPanel> </Grid> </Window>
解决方法
您可以禁用VirtualizeStackPanel的回收,如@nit建议的那样,但这不能完全解决问题.
另一种方法是使用附加行为.
@Samuel Jack有一个经过测试的工作解决方案(之前使用过),它适用于列表框和MVVM,效果很好.
所选项目可以双向模式绑定.
http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html
就像ListBox一样,我为DataGrid上传了一个修改过的解决方案,它是一个具有所需功能的基本DataGrid.
https://www.sugarsync.com/pf/D6837746_80955217_331798
请享用