【问题标题】:Silverlight Relating Two DatagridsSilverlight 关联两个数据网格
【发布时间】:2009-12-01 14:39:48
【问题描述】:

我的问题:如何将 SelectedItem 从主数据网格绑定到辅助数据网格的 ItemsSource?

详细说明: 我的视图上有两个数据网格。第一个显示团队的集合,第二个显示为所选团队中的人员列表。

当我从网格中选择一个团队时,我可以看到 SelectedTeam 属性正在正确更新,但人员网格没有得到填充。

注意:我不能使用嵌套网格,也不能使用 SL 数据网格中提供的很酷的主从功能。

更新:用 ComboBox 替换父数据网格会产生完全不同的结果,并且效果很好。 为什么 ComboBox.SelectedItem 和 DataGrid.SelectedItem 的行为会如此不同?

谢谢,
标记


简单复制:

查看:

<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");
            }
        }

    }
}

更新

用组合框替换第一个数据网格,一切正常。为什么 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>

【问题讨论】:

  • 我们可以假设第二个网格绑定中的错误名称是错字吗?
  • 是的 - 很抱歉。参考文献已更新。

标签: silverlight silverlight-3.0 datagrid


【解决方案1】:

做了一些测试。

首先,我只是想确认 Binding 本身是否正常工作。当第二个 DataGrid 被替换为 ListBox 时,它的工作非常愉快。到目前为止,我已经确认第二个 DataGrid 的 ItemsSource 属性已被绑定引擎更改。

我还将第一个 DataGrid 换成了 ListBox,然后第二个 DataGrid 开始正常工作。

此外,如果您在第一个数据网格上连接 SelectionChanged 事件并使用代码直接分配给第二个数据网格,它就会开始工作。

我还删除了第一个 Grid 上的 SelectedItem 绑定,并从第二个 Grid 的 ItemsSource 属性上设置了一个 ElementToElement 绑定。还是不开心。

因此问题通过框架绑定引擎缩小到一个 DatGrid 上的 SelectedItem 到另一个 DatGrid 的 ItemsSource。

Reflector 提供了一个可能的线索。 Data 命名空间包含一个以 DependencyObject 为目标的 Extensions 静态类,它有一个由静态变量支持的 AreHandlersSuspended 方法。代码处理对 ItemsSource 属性的更改使用此方法,如果返回 true,则不执行任何操作。

我未经证实的怀疑是,在第一个 Grid 分配其 SelectedItem 属性的过程中,它已打开标志以避免无限循环。但是,由于此标志实际上是全局的,因此不会执行由于此 SelectedItem 分配而运行的任何其他合法代码。

有人获得了 SL4 并对其进行了精彩的测试吗?
有没有潜伏的 MSFTer 想调查一下?

如果 SL4 仍然存在,则需要将其作为错误报告给 Connect。

【讨论】:

  • +1 谢谢安东尼。我也有使用 ListBox 的示例,并且可能可以将其用于此特定场景,因为我的“父”网格非常简单。我会留下这个问题,希望 MS 的人可以看看这个。
  • 我希望拥有 Beta SL4 的人会来并给你的 repro 一个机会,我现在正忙于生成生产 SL3,无法对 Beta 感到厌烦,但没有意义如果它已经修复,请为此提出一个错误。我当然可以看到我自己的代码在某些时候需要修复这个问题。
  • 嗨 anthony,您可能想知道我找到了一个使用自定义 CommandBehaviour 的更优雅(尽管仍然比它应该更尴尬)的解决方案。已经在下面发布了详细信息。 HTH,马克
【解决方案2】:

更好的解决方案是使用添加 DataGridRowSelected 命令。这比我之前的鼠标单击示例更适合 MVVM 模式。

这是受到John Papa 的一些代码的启发,我创建了一个关于此http://thoughtjelly.blogspot.com/2009/12/binding-selecteditem-to-itemssource.html 的详细帖子。

[心满意足地坐下来点一支雪茄]
标记

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,并通过将它添加到我的代码隐藏来“修复”它。

    后面的代码:

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

    型号:

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

    【讨论】:

      【解决方案4】:

      我有一个变通办法。它涉及一些背后的代码,因此不会受到纯粹的 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

      希望这对其他人有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-14
        • 1970-01-01
        • 2011-05-12
        • 2011-07-25
        • 2011-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多