2014-05-03 13 views
7

Trong bản demo MVVM Josh Smith của ông là sử dụng một ListView với một phong cách ListViewItem như thế này:Tại sao DataGridRow IsSelected ràng buộc không làm việc trên DataGrid

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

Nó liên kết với các tài sản IsSelected độc đáo. Phong cách này được áp dụng cho các ListView như vậy:

<ListView 
    ItemContainerStyle="{StaticResource CustomerItemStyle}" 
    ItemsSource="{Binding}" 
    > 

My Version

Tôi đã cố gắng ràng buộc IsSelected theo cách tương tự với một DataGrid thông qua DataGridRow. Tuy nhiên nó gây ra vấn đề khi các mục được chọn thông qua việc thu thập các mục ViewModels, trong đó thuộc tính IsSelected được định nghĩa.

Vì sử dụng liên kết hai chiều, tôi có thể nghĩ rằng các mục có thể được chọn thông qua giao diện người dùng và tập hợp các mục ViewModels.

Giả sử tôi chọn các mục thông qua giao diện người dùng, điều này hoạt động tốt. Tôi có thể chọn một mục số và sau đó sử dụng [shift] để chọn một phạm vi, sau đó sử dụng [ctrl] chọn một số mục khác. Việc bỏ chọn các mục cũng hoạt động chính xác.

Tuy nhiên, tôi chọn một nhóm thông qua bộ sưu tập. Giả sử khi nhấp vào nút (như tôi làm trong mã bên dưới), một loạt các mục được chọn. Khi tôi di chuyển xuống DataGrid sau đó một số được chọn vì chúng phải là một số thì không. Nếu tôi chọn một mục thông qua giao diện người dùng thì chỉ một số mục được bỏ chọn và một số mục vẫn được chọn, tất cả đều hơi sôi nổi. Ngay cả nút Chọn tất cả ở trên cùng bên trái cũng không hoạt động hoàn toàn đúng.

Tất cả mã bên dưới, ở dưới cùng là chế độ xem khóa có kiểu DataGridRow với liên kết IsSelected.

Đây là lớp người sử dụng tôi:

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

     } 
    } 
} 

Đây là UserViewModel, nơi IsSelected cư trú:

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

     } 
    } 
} 

Đây là AllUsersViewModel tôi:

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

Đây là kho lưu trữ sử dụng nơi tôi tạo người dùng:

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

Và cuối cùng ở đây là quan điểm cho tất cả người dùng:

<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,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,1,2" Background="Transparent"> 
          </Border> 
          <Border x:Name="DGR_Border" 
            BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="1,2,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,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,2" > 
      <Grid DockPanel.Dock="Bottom" Margin="0,2,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> 
+0

bạn có dự án mẫu với điều này không? – AsitK

+0

những gì được hiển thị là toàn bộ dự án – Hank

Trả lời

2

Hank

Tắt ảo hóa, chắc chắn sẽ khắc phục sự cố của bạn với chi phí hiệu suất.

bạn có thể vô hiệu hóa việc tái chế VirtualizingStackPanel thành @nit được đề xuất, nhưng điều đó sẽ không giải quyết được vấn đề hoàn toàn.

Cách tiếp cận khác là sử dụng các hành vi được đính kèm.

có một giải pháp làm việc được kiểm tra bởi @Samuel Jack (sử dụng nó trước đây), nó dành cho hộp danh sách và MVVM và nó hoạt động rất tốt. các mục đã chọn có thể được liên kết ở chế độ hai chiều.

http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

Vì nó là cho ListBox, Tôi đã tải lên một giải pháp sửa đổi cho DataGrid, Đó là một DataGrid cơ bản với các chức năng mong muốn.

https://www.sugarsync.com/pf/D6837746_80955217_331798

Thưởng thức

+1

Đây là một ví dụ tốt về lý do tại sao tôi luôn downvote câu trả lời của người dùng mới, những người chỉ đăng liên kết. Tôi thực sự muốn thấy giải pháp của bạn khi tôi đang đối mặt với một vấn đề liên quan. Nhưng câu trả lời của bạn bây giờ là vô giá trị vì mã liên kết đã biến mất. –

1

Tôi nhớ có vấn đề này với DataGrid của tôi khi di chuyển. Lý do tại sao nó gây ra vấn đề lạ là do ảo hóa. Thử đặt ảo hóa thành False trong DataGrid của bạn.

<DataGrid EnableRowVirtualization="False"> 
+0

Tôi có thể có hàng nghìn bản ghi, do đó, ảo hóa nhiều như một bận tâm vì nó là, tôi vẫn muốn giữ nó. – Hank

0

Bạn sẽ cần phải nắm bắt sự kiện Cuộn và sau đó đính kèm với trình tạo bộ chứa mục để tạo mục được tạo trong khi cuộn. Khi mục được tải, bạn có thể làm mới các ràng buộc (bằng cách sử dụng BingingExpression) cho mục để nó sẽ phản ánh trạng thái của đối tượng của bạn tức là IsSelected

0

Đây là vấn đề với RecyclingVirtualizingStackpanel.Đặt VirtualizingStackPanel.VirtualizationMode="Standard" trên Datagrid sẽ giải quyết vấn đề này.

0

Hank, vấn đề của bạn là Grid sẽ chỉ ràng buộc có thể nhìn thấy Rows của DataGrid và không làm mới là Binding càng sớm càng Row khác được nhìn thấy được.

Vấn đề này là do Data Virtualization.

Hãy cố gắng để thiết lập Virtualization để Standard

VirtualizingStackPanel.VirtualizationMode="Standard" 

Tôi nghĩ rằng đây sẽ giải quyết vấn đề của bạn.

Các vấn đề liên quan