IMultiValueConverter 在 DataGridTemplateColumn 中使用时从绑定值接收空字符串
IMultiValueConverter receives empty string from bound value when used in a DataGridTemplateColumn
我在绑定到 DataGrid 的 Observable Collection 中嵌套了一堆 object。 IMultiValueConverter 用于从两个属性收集信息;这在我在 DataGridTextColumn 中这样做时有效,但在 DataGridTemplateColumn 中失败。这是一个复杂的情况,所以我将进一步分解并 post 我的代码的简化版本。
各个列表项的嵌套如下:
User_Ext class 继承了用户 class,它有一个 User_Rank 属性 class 又有一个 属性用户 class。不幸的是,这种嵌套对于程序的设置方式来说是必需的。
还有一个单独的 Rank object 列表,它被绑定为 DataGridTemplateColumn 中 ComboBox 的选项,这将切换 ObservableCollection 中项目的 Rank。
Rank 有一个布尔值 属性 Require_License,User 有一个字符串 属性 License。这个想法是突出显示单元格,使用 IMultiValueConverter,如果许可证为空且 Require_License 为真。
我在此处的示例代码中包含了 DataGridTextColumn 和 DataGridTemplateColumn 以更轻松地演示正在发生的事情。
对于绑定到 License 的 DataGridTextColumn,只要我编辑 Rank 单元格的 ComboBox 选择或 License 文本的内容,转换器就会触发,所有信息都会保留。
对于绑定到许可证的 DataGridTemplateColumn,转换器仅在我更改 ComboBox 选择时触发,但在我编辑许可证文本时不会触发。最重要的是,当转换器捕捉到 ComboBox 更改时,许可证的值是一个空字符串(不是 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-07-25
我对它进行了更多研究,发现我对 DataGridCheckboxColumn 也有同样的问题。现在,我不太了解控件的内部功能,但这是我观察到的。
-IMultiValueConverter确实看到了单元格的初始值。
- 使用带有文本框的 DataGridTemplateColumn 时,绑定到 DataGrid 的 ItemsSource UserImport 会在 DataGrid 中进行更改时更新。但是,当绑定的许可证更改时, IMultiValueConverter 不会触发。它会在绑定 user_Rank.rank 更改(在 DataGridComboBoxColumn 中)时执行,但即便如此,许可证更改也不会反映出来。
-如果我尝试使用 DataGridCheckBoxColumn,也是如此。
-如果我单击列 header,导致出现排序列,IMultiValueConverter 将在排序时获取许可证的值,但之后不会更新。
-如果我在其中使用带有 DatePicker 的 DataGridTemplateColumn,我会遇到与其他情况相同的问题:IMultiValueConverter 不会拾取更改...除非它拾取更改。如果我疯狂地点击文本区域内的日期选择器按钮,在日期选择器中选择一个日期,点击日期选择器按钮右侧的小space,然后点击远离框,我发现 有时 IMultiValueConverter 会触发。有时是当我在 DatePicker 中单击一个日期时,有时是当我单击按钮旁边的 space 时,有时是当我单击另一个单元格时 after 单击该单元格space 按钮旁边。
所以,我在单元格中更新了一个值,在绑定 object 中更新了一个值,但不知何故没有被 IMultiValueConverter 拾取,除非在某些情况下。就好像有第三个地方存储数据。我想知道(同样,没有控件的内部知识)当您单击离开时,某些单元格内容是否仅更新单元格内的控件而不更新单元格本身。
单元格内的控件是否有可能 "Value" 测量 sep从单元格开始,直到它 "Updates" 单元格的 "Value"?如果是这种情况,并且控件正在更新绑定 object 而不更新单元格,并且 IMultiValueConverter 正在查看单元格而不是绑定 object 或单元格内的控件......也许那是我的问题吗?
求大神指点我哪里错了,然后解释一下这个现象。 :)
编辑
我找到了一个解决方案 post.
这是因为您的许可证 属性 在更改时没有引发 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 的重新实现 类。
我找到了解决办法。
虽然我不确定为什么它能够正确找到 user_Rank.rank 而不是 License,因为它们绑定到同一个对象,它似乎在寻找 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。
我还没有完全解开这个谜团,但我想出了一些东西,这样我就可以继续我的计划了。如果有人有更聪明的答案,请随时为我们列出。 :)
我在绑定到 DataGrid 的 Observable Collection 中嵌套了一堆 object。 IMultiValueConverter 用于从两个属性收集信息;这在我在 DataGridTextColumn 中这样做时有效,但在 DataGridTemplateColumn 中失败。这是一个复杂的情况,所以我将进一步分解并 post 我的代码的简化版本。
各个列表项的嵌套如下: User_Ext class 继承了用户 class,它有一个 User_Rank 属性 class 又有一个 属性用户 class。不幸的是,这种嵌套对于程序的设置方式来说是必需的。
还有一个单独的 Rank object 列表,它被绑定为 DataGridTemplateColumn 中 ComboBox 的选项,这将切换 ObservableCollection 中项目的 Rank。
Rank 有一个布尔值 属性 Require_License,User 有一个字符串 属性 License。这个想法是突出显示单元格,使用 IMultiValueConverter,如果许可证为空且 Require_License 为真。
我在此处的示例代码中包含了 DataGridTextColumn 和 DataGridTemplateColumn 以更轻松地演示正在发生的事情。
对于绑定到 License 的 DataGridTextColumn,只要我编辑 Rank 单元格的 ComboBox 选择或 License 文本的内容,转换器就会触发,所有信息都会保留。
对于绑定到许可证的 DataGridTemplateColumn,转换器仅在我更改 ComboBox 选择时触发,但在我编辑许可证文本时不会触发。最重要的是,当转换器捕捉到 ComboBox 更改时,许可证的值是一个空字符串(不是 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-07-25
我对它进行了更多研究,发现我对 DataGridCheckboxColumn 也有同样的问题。现在,我不太了解控件的内部功能,但这是我观察到的。
-IMultiValueConverter确实看到了单元格的初始值。
- 使用带有文本框的 DataGridTemplateColumn 时,绑定到 DataGrid 的 ItemsSource UserImport 会在 DataGrid 中进行更改时更新。但是,当绑定的许可证更改时, IMultiValueConverter 不会触发。它会在绑定 user_Rank.rank 更改(在 DataGridComboBoxColumn 中)时执行,但即便如此,许可证更改也不会反映出来。
-如果我尝试使用 DataGridCheckBoxColumn,也是如此。
-如果我单击列 header,导致出现排序列,IMultiValueConverter 将在排序时获取许可证的值,但之后不会更新。
-如果我在其中使用带有 DatePicker 的 DataGridTemplateColumn,我会遇到与其他情况相同的问题:IMultiValueConverter 不会拾取更改...除非它拾取更改。如果我疯狂地点击文本区域内的日期选择器按钮,在日期选择器中选择一个日期,点击日期选择器按钮右侧的小space,然后点击远离框,我发现 有时 IMultiValueConverter 会触发。有时是当我在 DatePicker 中单击一个日期时,有时是当我单击按钮旁边的 space 时,有时是当我单击另一个单元格时 after 单击该单元格space 按钮旁边。
所以,我在单元格中更新了一个值,在绑定 object 中更新了一个值,但不知何故没有被 IMultiValueConverter 拾取,除非在某些情况下。就好像有第三个地方存储数据。我想知道(同样,没有控件的内部知识)当您单击离开时,某些单元格内容是否仅更新单元格内的控件而不更新单元格本身。
单元格内的控件是否有可能 "Value" 测量 sep从单元格开始,直到它 "Updates" 单元格的 "Value"?如果是这种情况,并且控件正在更新绑定 object 而不更新单元格,并且 IMultiValueConverter 正在查看单元格而不是绑定 object 或单元格内的控件......也许那是我的问题吗?
求大神指点我哪里错了,然后解释一下这个现象。 :)
编辑 我找到了一个解决方案 post.
这是因为您的许可证 属性 在更改时没有引发 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 的重新实现 类。
我找到了解决办法。
虽然我不确定为什么它能够正确找到 user_Rank.rank 而不是 License,因为它们绑定到同一个对象,它似乎在寻找 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。
我还没有完全解开这个谜团,但我想出了一些东西,这样我就可以继续我的计划了。如果有人有更聪明的答案,请随时为我们列出。 :)