2017-07-21 65 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