使用時我具有被綁定到DataGrid在可觀察到的集合對象的一個嵌套一束從限值接收空字符串。 IMultiValueConverter用於從兩個屬性收集信息;當我在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正在查看單元格,而不是單元格中的綁定對象或控件......也許這會是我的問題?
請別人告訴我我是多麼的錯,接下來是對這種現象的解釋。 :)
編輯 我找到了我要發佈的解決方案。
試過,但仍然是同樣的問題。我不得不改變行<< if(handler!= null)handler(this,「License」); >> to << if(handler!= null)handler(this,new PropertyChangedEventArgs(「License」)); >>因爲它給了我一個錯誤,否則。我認爲ObservableCollection可以處理很多屬性更改通知,因爲大多數其他屬性更改會在不實現INotifyPropertyChanged的情況下繼續進行,並且假定它在DataGridTextColumn中工作,但不在DataGridTemplateColumn中工作。 – Pierre
@Pierre,如果您已經做出更改以確保PropertyChanged事件在正確拼寫屬性名稱時正確解鎖,則無論何時更改許可證,我都無法在此處找到該錯誤。你的XAML是完美的。 – hoodaticus
@Pierre,ObservableCollection僅在項目被插入,刪除或替換時通知UI。它不會跟蹤它包含的對象的屬性。只有PropertyChanged可以做到這一點。我知道這是因爲我從頭開始寫了很多我自己的免費線程ObservableCollection替代品。你不相信你的問題的唯一正確答案。 – hoodaticus