2017-07-21 67 views
0

使用时我具有被绑定到DataGrid在可观察到的集合对象的一个​​嵌套一束从限值接收空字符串。 IMul​​tiValueConverter用于从两个属性收集信息;当我在DataGridTextColumn中这样做,但在DataGridTemplateColumn中失败时,这是有效的。这是一个复杂的情况,所以我会进一步分解它并发布我的代码的简化版本。IMultiValueConverter在DataGridTemplateColumn

每个列表项的嵌套如下: User_Ext类继承具有User_Rank类的属性的User类,该类又具有User类的属性。不幸的是,这种嵌套对于程序设置的方式是必需的。

还有一个Rank对象的单独列表,绑定为DataGridTemplateColumn中ComboBox的选项,它将从ObservableCollection中的项目切换等级。

秩有一个布尔属性Require_License和用户有一个字符串属性许可证。如果许可证是空白并且Require_License是真的,那么这个想法是使用IMultiValueConverter的单元格的亮点。

我已经在我的示例代码既包括一个DataGridTextColumn和DataGridTemplateColumn这里更容易表现出发生了什么。

对于绑定到许可证的DataGridTextColumn,转换器会在我编辑Rank单元格的ComboBox选项或许可证文本的内容后立即触发,并且所有信息都会传送。

对于DataGridTemplateColumn势必许可,转换器仅火灾时,我改变了组合框的选择,而不是当我编辑的许可证文本。最重要的是,当转换器捕获组合框更改时,许可证的值是一个空字符串(不是UnsetValue)而不是单元格的内容,而第二个绑定值(Rank选择)是正确的。我还应该在这里提到,所做的任何更改都正确更新ObservableCollection中的项目,以便绑定的方面工作正常。

我已经就我和我在这里搜索得到,但我似乎无法找到解决这个问题。

如果有任何事情混乱或被遗忘,我很抱歉,但我不得不取消我的作品的识别标记,并希望尽可能包括,因为我不知道问题发生在哪里。但是,如果我的代码有助于将其复制到项目中并对其进行测试,那么我的代码就可以运行。如果我过于冗长,我也很抱歉;这是我的第一个问题,我不确定用多少措辞来描述这种情况。

至于为什么我不只是使用功能DataGridTextColumn,还有更多的东西需要放置到位,我将需要DataGridTemplateColumn的灵活性。

这是我的XAML:

<Window x:Class="Tool.Transfer" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:src="clr-namespace:Tool" 
     Title="MainWindow" Height="350" Width="525" 
     DataContext="{Binding RelativeSource={RelativeSource Self}}" 
     > 
    <Window.Resources> 
     <src:MatchMultiCellColourConverter x:Key="MatchMultiCellColourConverter"/> 
    </Window.Resources> 
    <Grid> 
     <DataGrid ItemsSource="{Binding UserImport, Mode=TwoWay}" AutoGenerateColumns="False"> 
      <DataGrid.Resources> 
       <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/> 
      </DataGrid.Resources> 
      <DataGrid.Columns> 
       <DataGridTextColumn Header="User" Binding="{Binding User_Code}"/> 
       <DataGridComboBoxColumn Header="Rank" DisplayMemberPath="Desc" SelectedValuePath="Code" SelectedItemBinding="{Binding user_Rank.rank}"> 
        <DataGridComboBoxColumn.ElementStyle> 
         <Style TargetType="{x:Type ComboBox}"> 
          <Setter Property="ItemsSource" Value="{Binding Path=TargetRanks, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/> 
          <Setter Property="DisplayMemberPath" Value="Desc"/> 
          <Setter Property="Background" Value="White"/> 
         </Style> 
        </DataGridComboBoxColumn.ElementStyle> 
        <DataGridComboBoxColumn.EditingElementStyle> 
         <Style TargetType="{x:Type ComboBox}"> 
          <Setter Property="ItemsSource" Value="{Binding Path=TargetRanks, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/> 
          <Setter Property="DisplayMemberPath" Value="Desc"/> 
         </Style> 
        </DataGridComboBoxColumn.EditingElementStyle> 
       </DataGridComboBoxColumn> 

       <DataGridTextColumn Header="TextColumn License" Binding="{Binding License}"> 
        <DataGridTextColumn.ElementStyle> 
         <Style TargetType="{x:Type TextBlock}"> 
          <Style.Setters> 
           <Setter Property="Background"> 
            <Setter.Value> 
             <MultiBinding Converter="{StaticResource MatchMultiCellColourConverter}"> 
              <Binding Path="License"/> 
              <Binding Path="user_Rank.rank"/> 
             </MultiBinding> 
            </Setter.Value> 
           </Setter> 
          </Style.Setters> 
         </Style> 
        </DataGridTextColumn.ElementStyle> 
       </DataGridTextColumn> 

       <DataGridTemplateColumn Header="TemplateColumn License"> 
        <DataGridTemplateColumn.CellTemplate> 
         <DataTemplate> 
          <TextBox Text="{Binding License, UpdateSourceTrigger=PropertyChanged}"> 
           <TextBox.Style> 
            <Style TargetType="{x:Type TextBox}"> 
             <Style.Setters> 
              <Setter Property="Background"> 
               <Setter.Value> 
                <MultiBinding Converter="{StaticResource MatchMultiCellColourConverter}"> 
                 <Binding Path="License"/> 
                 <Binding Path="user_Rank.rank"/> 
                </MultiBinding> 
               </Setter.Value> 
              </Setter> 
             </Style.Setters> 
            </Style> 
           </TextBox.Style> 
          </TextBox> 
         </DataTemplate> 
        </DataGridTemplateColumn.CellTemplate> 
       </DataGridTemplateColumn> 
      </DataGrid.Columns> 

     </DataGrid> 
    </Grid> 
</Window> 

而我的C#:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 

namespace Tool 
{ 
    public partial class Transfer 
    { 
     private ObservableCollection<User_Ext> _userImport = null; 
     public ObservableCollection<User_Ext> UserImport 
     { 
      get 
      { 
       if (_userImport == null) 
       { 
        _userImport = new ObservableCollection<User_Ext>(); 
       } 
       return _userImport; 
      } 
      set { _userImport = value; } 
     } 

     private ObservableCollection<Rank> _targetRanks = null; 
     public ObservableCollection<Rank> TargetRanks 
     { 
      get 
      { 
       if (_targetRanks == null) 
       { 
        _targetRanks = new ObservableCollection<Rank>(); 
       } 
       return _targetRanks; 
      } 
      set { _targetRanks = value; } 
     } 

     public Transfer() 
     { 
      Rank r1 = new Rank(); r1.Code = "R1"; r1.Desc = "Rank1"; r1.Require_License = false; 
      Rank r2 = new Rank(); r2.Code = "R2"; r2.Desc = "Rank2"; r2.Require_License = true; 

      User a = new User(); a.User_Code = "A"; a.License = ""; a.user_Rank = new User_Rank(); a.user_Rank.rank = r1; 
      User b = new User(); b.User_Code = "B"; b.License = ""; b.user_Rank = new User_Rank(); b.user_Rank.rank = r2; 


      TargetRanks.Add(r1); TargetRanks.Add(r2); 
      UserImport.Add(new User_Ext(a)); UserImport.Add(new User_Ext(b)); 

      InitializeComponent(); 
     } 
    } 

    public class MatchMultiCellColourConverter : IMultiValueConverter 
    { 
     #region IValueConverter Members 

     public object Convert(object[] value, Type targetRank, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (targetRank != typeof(Brush)) 
       throw new InvalidOperationException("The target must be a Brush"); 

      bool pass = false; 

      if (value[0] != DependencyProperty.UnsetValue && value[1] != DependencyProperty.UnsetValue) 
      { 
       String l = (String)value[0]; 

       Rank r = (Rank)value[1]; 
       pass = !((l ?? "") == "" && r.Require_License); 
      } 
      return pass ? Brushes.White : Brushes.Pink; 
     } 

     public object[] ConvertBack(object value, Type[] targetRank, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotSupportedException(); 
     } 

     #endregion 
    } 

    public class User_Ext : User, INotifyPropertyChanged 
    { 
     private bool _isComplete; 
     public bool IsComplete 
     { 
      get { return _isComplete; } 
      set 
      { 
       _isComplete = value; 
       NotifyPropertyChanged("IsComplete"); 
      } 
     } 

     public User_Ext(User u) : base(u) 
     { 
      IsComplete = false; 
     } 

     #region INotifyPropertyChanged 
     public event PropertyChangedEventHandler PropertyChanged; 

     private void NotifyPropertyChanged(String info) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new PropertyChangedEventArgs(info)); 
      } 
     } 
     #endregion 
    } 

    public class User 
    { 
     public string User_Code { get; set; } 
     public string License { get; set; } 
     public User_Rank user_Rank { get; set; } 

     public User() { } 

     public User(User u) 
     { 
      User_Code = u.User_Code; 
      License = u.License; 
      user_Rank = u.user_Rank; 
     } 
    } 

    public class User_Rank 
    { 
     public Rank rank { get; set; } 
    } 

    public class Rank 
    { 
     public string Code { get; set; } 
     public string Desc { get; set; } 
     public bool Require_License { get; set; } 
    } 

} 

编辑 2017年7月25日

我已经玩了更多,我我发现我和DataGridCheckboxColumn有同样的问题。现在,我不太了解控件的内部功能,但这是我观察到的。

- IMultiValueConverter确实可以看到单元的初始值。

- 当在DataGrid中使用带有TextBox的DataGridTemplateColumn时,在DataGrid中进行更改时绑定到DataGrid的ItemsSource,UserImport不会更新。但是,当绑定的许可证更改时,IMultiValueConverter不会触发。它在绑定的user_Rank.rank发生更改(在DataGridComboBoxColumn中)时执行,但即便如此,许可证更改也未反映出来。

- 如果我尝试使用DataGridCheckBoxColumn,情况也是如此。

- 如果我单击列标题,导致发生排序列,IMultiValueConverter将在排序时选取License的值,但之后不会更新。

- 如果我在其中使用带有DatePicker的DataGridTemplateColumn,则具有与其他情况相同的问题:IMultiValueConverter不会接受更改...除了当它发生更改时。如果我疯狂点击,在文本区域内,在日期选择器按钮上,在日期选择器中选择一个日期,点击日期选择器按钮右侧的小空间,然后点击离开框,我发现那有时 IMultiValueConverter会触发。有时是在DatePicker中点击日期,有时候是点击按钮旁边的空间,有时候是当点击另一个单元点击了该按钮旁边的空间。

因此,我在单元格中更新了一个值,在绑定对象中进行了更新,但在某些情况下除了IMultiValueConverter没有被拾取之外。就好像数据存储的第三个点一样。我想知道(再次,没有控制的内在知识)当单击时,某些单元格内容是否只更新单元格内的控件,而不更新单元格本身。

单元格中的控件是否可能具有与单元格分开测量的“值”,直到它“更新”单元格的“值”?如果是这样,并且控件正在更新绑定对象而不更新单元格,并且IMultiValueConverter正在查看单元格,而不是单元格中的绑定对象或控件......也许这会是我的问题?

请别人告诉我我是多么的错,接下来是对这种现象的解释。 :)

编辑 我找到了我要发布的解决方案。

回答

0

我找到了解决方案。

尽管我不确定为什么它能够正确地找到user_Rank.rank而不是License,因为它们绑定到同一个对象,它似乎正在迷失试图查找许可证。

如果我看到它自己的内容,而不管怎样绑定到对象,它可以正确地将它携带到IMultiValueConverter。

我改变了DataGridTemplateColumn码略做:

<DataGridTemplateColumn Header="TemplateColumn License"> 
    <DataGridTemplateColumn.CellTemplate> 
     <DataTemplate> 
      <TextBox Text="{Binding License}"> 
       <TextBox.Style> 
        <Style TargetType="{x:Type TextBox}"> 
         <Style.Setters> 
          <Setter Property="Background"> 
           <Setter.Value> 
            <MultiBinding Converter="{StaticResource MatchMultiCellColourConverter}"> 
             <Binding Path="Text" RelativeSource="{RelativeSource Self}"/> 
             <Binding Path="user_Rank.rank"/> 
            </MultiBinding> 
           </Setter.Value> 
          </Setter> 
         </Style.Setters> 
        </Style> 
       </TextBox.Style> 
      </TextBox> 
     </DataTemplate> 
    </DataGridTemplateColumn.CellTemplate> 
</DataGridTemplateColumn> 

现在IMultiValueConverter每一个变化立即回升。

这可以应用于我提供的其他示例。对于DataGridCheckBoxColum,我用于该样式的TargetType是DataGridCell,所以我使用Path =“Content.IsChecked”来访问CheckBox。

我还没有完全解决这个谜,但我想出了一些东西,以便我可以继续我的程序。如果有人有更明智的答案,请随时为我们安排。 :)

0

这是因为您的许可证属性在更改时不会引发PropertyChanged事件。

更改此:

public class User 
{ 
    public string User_Code { get; set; } 
    public string License { get; set; } 
    public User_Rank user_Rank { get; set; } 

    public User() { } 

    public User(User u) 
    { 
     User_Code = u.User_Code; 
     License = u.License; 
     user_Rank = u.user_Rank; 
    } 
} 

要这样:

public class User : INotifyPropertyChanged 
{ 
    public string User_Code { get; set; } 
    string _license; 
    public string License 
    { 
     get { return _license; }; 
     set 
     { 
      _license = value; 
      var handler = PropertyChanged; 
      if (handler != null) 
       handler(this, new PropertyChangedEventArgs("License")); 
     } 
    } 
    public User_Rank user_Rank { get; set; } 

    public User() { } 

    public event PropertyChangedEventHandler PropertyChanged; 

    public User(User u) 
    { 
     User_Code = u.User_Code; 
     License = u.License; 
     user_Rank = u.user_Rank; 
    } 
} 

然后清理在派生类重新实现INotifyPropertyChanged的的。

+0

试过,但仍然是同样的问题。我不得不改变行<< if(handler!= null)handler(this,“License”); >> to << if(handler!= null)handler(this,new PropertyChangedEventArgs(“License”)); >>因为它给了我一个错误,否则。我认为ObservableCollection可以处理很多属性更改通知,因为大多数其他属性更改会在不实现INotifyPropertyChanged的情况下继续进行,并且假定它在DataGridTextColumn中工作,但不在DataGridTemplateColumn中工作。 – Pierre

+0

@Pierre,如果您已经做出更改以确保PropertyChanged事件在正确拼写属性名称时正确解锁,则无论何时更改许可证,我都无法在此处找到该错误。你的XAML是完美的。 – hoodaticus

+0

@Pierre,ObservableCollection仅在项目被插入,删除或替换时通知UI。它不会跟踪它包含的对象的属性。只有PropertyChanged可以做到这一点。我知道这是因为我从头开始写了很多我自己的免费线程ObservableCollection替代品。你不相信你的问题的唯一正确答案。 – hoodaticus