Silverlight, относящийся к двум датагридам - PullRequest
1 голос
/ 01 декабря 2009

Мой вопрос: как связать SelectedItem из первичной сетки данных с Источником Items для вторичной сетки данных?

Подробно: У меня есть два датагрид на мой взгляд. Первый показывает коллекцию команд, а второй показывает список людей в выбранной команде.

Когда я выбираю команду из сетки, я вижу, что свойство SelectedTeam корректно обновляется, но сетка Люди не заполняется.

Примечание: я не могу использовать вложенные сетки или интересные функции мастер-детали, представленные в сетке данных SL.

ОБНОВЛЕНИЕ: Замена родительской таблицы данных ComboBox дает совершенно другие результаты и работает отлично. Почему ComboBox.SelectedItem и DataGrid.SelectedItem ведут себя так по-разному?

Спасибо
Mark


Простое воспроизведение:

ВИД:

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

    }
}

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.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>

Ответы [ 4 ]

1 голос
/ 08 марта 2010

У меня была та же проблема, и я "исправил" ее, добавив это в свой код.

Код:

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

Модель:

    public void RefreshDetail()
    {
        RaisePropertyChanged("Detail");
    }
1 голос
/ 09 декабря 2009

Лучшим решением является использование команды add DataGridRowSelected. Это соответствует шаблону MVVM намного лучше, чем мой предыдущий пример щелчка мышью.

Это было вдохновлено кодом из Джона Папа , я создал подробный пост об этом http://thoughtjelly.blogspot.com/2009/12/binding-selecteditem-to-itemssource.html.

[Сидит довольный и зажигает сигару]
Mark

1 голос
/ 01 декабря 2009

Проведя несколько тестов.

Сначала я просто хотел подтвердить, что сама Binding работает. Это работает довольно счастливо, когда вторая DataGrid заменяется на ListBox. Я зашел так далеко, чтобы подтвердить, что второй DataGrid имеет свойство ItemsSource, измененное механизмом привязки.

Я также поменял первый DataGrid для ListBox, а затем второй DataGrid начал работать довольно счастливо.

Кроме того, если вы подключаете событие SelectionChanged к первой сетке данных и используете код для непосредственного присвоения второй сетке данных, оно начинает работать.

Я также удалил привязку SelectedItem в первой Grid и настроил привязку ElementToElement к нему из свойства ItemsSource второй Grid. Все еще нет радости.

Следовательно, проблема сводится к SelectedItem в одной DatGrid к ItemsSource другого через механизм привязки фреймворка.

Отражатель дает возможную подсказку. Пространство имен Data содержит статический класс Extensions, нацеленный на DependencyObject, в котором есть метод AreHandlersSuspended, поддерживаемый статической переменной. То, что обработка кода изменяет в свойстве ItemsSource, использует этот метод и ничего не делает, если возвращает true.

Мое неподтвержденное подозрение заключается в том, что в процессе первой Grid, присваивающей свое свойство SelectedItem, он включил флаг, чтобы избежать бесконечного цикла. Однако, поскольку этот флаг фактически является глобальным, любой другой легитимный код, выполняемый в результате этого назначения SelectedItem, не выполняется.

У кого-нибудь есть SL4 и интересное тестирование?
Любой скрывающийся MSFT хочет посмотреть?

Если SL4 все еще имеет это, это должно будет сообщить о соединении как ошибка.

0 голосов
/ 07 декабря 2009

У меня есть обходной путь. Он включает в себя немного кода, поэтому его не будут одобрять фанатики-пуристы из 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.

Надеюсь, это поможет кому-то еще.

...