2009-12-01 57 views
1

我的問題:如何將SelectedItem從主數據網格綁定到輔助數據網格的ItemsSource?有關Silverlight的兩個Datagrids

詳細內容: 我在我的視圖中有兩個datagrid。第一個顯示了一組球隊,第二個顯示了選定球隊中的人員名單。

當我從網格中選擇一個團隊時,我可以看到SelectedTeam屬性正在正確更新,但人員網格未獲取填充。

注意:我無法使用嵌套網格或SL數據網格中提供的冷卻主控細節功能。

更新:用ComboBox代替父DataGrid給出了完全不同的結果,並且完美地工作。 爲什麼ComboBox.SelectedItem和DataGrid.SelectedItem行爲如此不同?

謝謝,
馬克


簡單攝製:

VIEW:

<UserControl x:Class="NestedDataGrid.MainPage" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      mc:Ignorable="d" 
      xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"> 
    <StackPanel x:Name="LayoutRoot"> 
     <TextBlock Text="Teams:" /> 
     <data:DataGrid ItemsSource="{Binding Teams}" 
         SelectedItem="{Binding SelectedTeam, Mode=TwoWay}" 
         AutoGenerateColumns="False"> 
      <data:DataGrid.Columns> 
       <data:DataGridTextColumn Header="Id" Binding="{Binding TeamId}" /> 
       <data:DataGridTextColumn Header="Desc" Binding="{Binding TeamDesc}" /> 
      </data:DataGrid.Columns> 
     </data:DataGrid> 
     <TextBlock Text="Peeps:" /> 
     <data:DataGrid ItemsSource="{Binding SelectedTeam.People}" 
         AutoGenerateColumns="False"> 
      <data:DataGrid.Columns> 
       <data:DataGridTextColumn Header="Id" 
             Binding="{Binding PersonId}" /> 
       <data:DataGridTextColumn Header="Name" 
             Binding="{Binding Name}" /> 
      </data:DataGrid.Columns> 
     </data:DataGrid> 
    </StackPanel> 
</UserControl> 

CODE_BEHIND:

using System.Windows.Controls; 
namespace NestedDataGrid 
{ 
    public partial class MainPage : UserControl 
    { 
     public MainPage() 
     { 
      InitializeComponent(); 
      this.LayoutRoot.DataContext = new ViewModel(); 
     } 

    } 
} 

視圖模型:

using System.Collections.ObjectModel; 
namespace NestedDataGrid 
{ 
    public class ViewModel: ObjectBase 
    { 
     public ViewModel() 
     { 
      ObservableCollection<Person> RainbowPeeps = new ObservableCollection<Person>() 
      { 
       new Person(){ PersonId=1, Name="George"}, 
       new Person(){ PersonId=2, Name="Zippy"}, 
       new Person(){ PersonId=3, Name="Bungle"}, 
      }; 

      ObservableCollection<Person> Simpsons = new ObservableCollection<Person>() 
      { 
       new Person(){ PersonId=4, Name="Moe"}, 
       new Person(){ PersonId=5, Name="Barney"}, 
       new Person(){ PersonId=6, Name="Selma"}, 
      }; 

      ObservableCollection<Person> FamilyGuyKids = new ObservableCollection<Person>() 
      { 
       new Person(){ PersonId=7, Name="Stewie"}, 
       new Person(){ PersonId=8, Name="Meg"}, 
       new Person(){ PersonId=9, Name="Chris"}, 
      }; 


      Teams = new ObservableCollection<Team>() 
      { 
       new Team(){ TeamId=1, TeamDesc="Rainbow", People=RainbowPeeps}, 
       new Team(){ TeamId=2, TeamDesc="Simpsons", People=Simpsons}, 
       new Team(){ TeamId=3, TeamDesc="Family Guys", People=FamilyGuyKids }, 
      }; 
     } 


     private ObservableCollection<Team> _teams; 
     public ObservableCollection<Team> Teams 
     { 
      get { return _teams; } 
      set 
      { 
       SetValue(ref _teams, value, "Teams"); 
      } 
     } 


     private Team _selectedTeam; 
     public Team SelectedTeam 
     { 
      get { return _selectedTeam; } 
      set 
      { 
       SetValue(ref _selectedTeam, value, "SelectedTeam"); 
      } 
     } 


    } 
} 

相關的類:

using System; 
using System.ComponentModel; 

namespace NestedDataGrid 
{ 
    public abstract class ObjectBase : Object, INotifyPropertyChanged 
    { 
     public ObjectBase() 
     { } 

     public event PropertyChangedEventHandler PropertyChanged; 

     protected virtual void _OnPropertyChanged(string propertyName) 
     { 
      PropertyChangedEventHandler pceh = PropertyChanged; 
      if (pceh != null) 
      { 
       pceh(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 

     protected virtual bool SetValue<T>(ref T target, T value, string propertyName) 
     { 
      if (Object.Equals(target, value)) 
      { 
       return false; 
      } 

      target = value; 
      _OnPropertyChanged(propertyName); 

      return true; 
     } 

    } 


    public class Person: ObjectBase 
    { 
     private int _personId; 
     public int PersonId 
     { 
      get { return _personId; } 
      set 
      { 
       SetValue(ref _personId, value, "PersonId"); 
      } 
     } 

     private string _name; 
     public string Name 
     { 
      get { return _name; } 
      set 
      { 
       SetValue(ref _name, value, "Name"); 
      } 
     } 

    } 

    public class Team : ObjectBase 
    { 

     private int _teamId; 
     public int TeamId 
     { 
      get { return _teamId; } 
      set 
      { 
       SetValue(ref _teamId, value, "TeamId"); 
      } 
     } 

     private string _teamDesc; 
     public string TeamDesc 
     { 
      get { return _teamDesc; } 
      set 
      { 
       SetValue(ref _teamDesc, value, "TeamDesc"); 
      } 
     } 


     private ObservableCollection<Person> _people; 
     public ObservableCollection<Person> People 
     { 
      get { return _people; } 
      set 
      { 
       SetValue(ref _people, value, "People"); 
      } 
     } 

    } 
} 

UPDATE

與組合框的更換第一個DataGrid和eveything工程確定。爲什麼DataGrid.SelectedItem和ComboBox.SelectedItem行爲如此不同?

<StackPanel x:Name="LayoutRoot">   
    <TextBlock Text="Teams:" /> 
    <ComboBox SelectedItem="{Binding SelectedTeam, Mode=TwoWay}" 
        ItemsSource="{Binding Teams}"/> 
    <TextBlock Text="{Binding SelectedTeam}" />  
    <TextBlock Text="Peeps:" /> 
    <data:DataGrid ItemsSource="{Binding SelectedTeam.People}" />  
</StackPanel> 
+1

我們可以假設在第二格的綁定errorneous名稱是一個錯字? – AnthonyWJones 2009-12-01 15:06:16

+0

是的 - 對不起。參考文獻已更新。 – 2009-12-01 15:13:32

回答

0

我有一個解決方法。它涉及到一些代碼,所以不會被純粹的MVVM狂熱者青睞! ;-)

<StackPanel x:Name="LayoutRoot">   
    <TextBlock Text="Teams:" /> 
    <data:DataGrid x:Name="dgTeams" 
        SelectedItem="{Binding SelectedTeam, Mode=TwoWay}" 
        ItemsSource="{Binding Teams}" /> 
    <TextBlock Text="{Binding SelectedTeam}" /> 
    <TextBlock Text="Peeps:" /> 
    <data:DataGrid x:Name="dgPeeps" /> 
</StackPanel> 

代碼背後:

public partial class MainPage : UserControl 
{ 
    public MainPage() 
    { 
     InitializeComponent(); 
     this.LayoutRoot.DataContext = new ViewModel(); 

     dgTeams.MouseLeftButtonUp += new MouseButtonEventHandler(dgTeams_MouseLeftButtonUp) 
    } 

    void dgTeams_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
    { 
     DataGridRow row = DependencyObjectHelper.FindParentOfType<DataGridRow>(e.OriginalSource as DependencyObject); 

     ///get the data object of the row 
     if (row != null && row.DataContext is Team) 
     { 
      dgPeeps.ItemsSource = (row.DataContext as Team).People; 
     } 
    } 

} 

FindParentOfType方法在這裏詳細描述:http://thoughtjelly.blogspot.com/2009/09/walking-xaml-visualtree-to-find-parent.html

希望這可以幫助別人。

1

做了一些測試。

首先,我只想確認綁定本身是否正常工作。當第二個DataGrid換出ListBox時,它工作的非常開心。我到目前爲止已經確認第二個DataGrid的綁定引擎更改了它的ItemsSource屬性。

我也換出了ListBox的第一個DataGrid,然後第二個DataGrid開始工作得非常開心。

此外,如果您連接第一個數據網格上的SelectionChanged事件並使用代碼直接分配給第二個數據網格,它就開始工作。

我也刪除了第一個網格上的SelectedItem綁定,並從第二個網格的ItemsSource屬性上設置了一個ElementToElement綁定到它。仍然沒有快樂。

因此,通過框架綁定引擎將問題縮小到一個DatGrid上的SelectedItem到另一個Datagram的ItemsSource。

反射器提供了一個可能的線索。命名空間Data包含一個定位爲DependencyObject的擴展靜態類,它有一個由靜態變量支持的AreHandlersSuspended方法。代碼處理更改爲ItemsSource屬性的方法使用此方法,如果返回true,則不執行任何操作。

我未經證實的懷疑是,在第一個網格分配其SelectedItem屬性的過程中,它已打開標誌以避免無限循環。但是,由於此標誌實際上是全局的,因此此SelectedItem分配運行的任何其他合法代碼未被執行。

任何人都有SL4和花哨的測試呢?
任何MSFTers潛伏想要調查?

如果SL4仍然存在,這將需要報告連接作爲錯誤。

+0

+1謝謝安東尼。我也得到了使用ListBox的例子,並且可以將這個用於這個特定的場景,因爲我的「父」網格非常簡單。我會打開這個問題,希望MS的某個人能看看這個。 – 2009-12-02 08:35:59

+0

我希望有一個測試版SL4的人會出現,並給你的repro一個去吧,我太忙了生產生產SL3現在正在琢磨關於貝塔斯,但沒有一點提出這個錯誤,如果它已經固定。我當然可以看到我自己的代碼在某個時候需要修正這個問題。 – AnthonyWJones 2009-12-02 10:20:29

+0

嗨安東尼,以爲你可能想知道我發現使用自定義CommandBehaviour更優雅(儘管還比它應該更尷尬)。已在下面發佈詳情。 HTH,Mark – 2009-12-09 16:34:39

1

我有同樣的問題,並通過添加這對我的代碼隱藏「固定」它。

後面的代碼:

private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) 
    { 
     if (_model != null) 
     { 
      _model.RefreshDetail(); 
     } 
    } 

型號:

public void RefreshDetail() 
    { 
     RaisePropertyChanged("Detail"); 
    }