Привязка ComboBox не вызывается для каждой строки в DataGrid

Я пытаюсь создать выпадающий список внутри DataGridTemplateColumn, но он должен содержать разные значения в зависимости от строки. Вот мой код:

<dg:DataGridTemplateColumn x:Name ="NameColumn" Header="Player Name">
    <dg:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox
                SelectedValue="0"
                DisplayMemberPath="FullName"
                SelectedValuePath="Id"
                ItemsSource="{Binding AllPlayers, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
        </DataTemplate>
    </dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>

AllPlayers будет возвращать другой список после каждого вызова.

public List<Player> AllPlayers
{
    get 
    {
        counter = counter + 1;
        Debug.Print("getting all players " + counter);

        List<Player> lst = new List<Player>();

        for (int i=0; i < 5; i++) 
        {
            Player p = new Player();
            p.Id = counter + i;
            p.FullName = "Name " + counter + i;
            lst.Add(p);
        }

        return lst;
    }
}

По какой-то причине функция AllPlayers вызывается для первых 39 строк, а затем данные берутся из ранее созданных списков. Я вижу это из отладочной информации (она перестает печатать после 39 звонков). А также списки в выпадающих списках не являются уникальными. Я не понимаю логику такого поведения. Мне нужно, чтобы AllPlayers вызывались для каждой строки.

3 ответа

Я не использовал счетчик для подсчета индексов, а просто для целей отладки, чтобы подсчитать количество раз, когда была вызвана функция, и использовать его для создания уникального списка для каждого выпадающего списка. Мой оригинальный код имеет тот же подход, который вы предложили. Вот конвертер:

Public Function Convert(ByVal value() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
    Dim playerReportRow As MainAdminDS.PlayerReportRow = value(0).Row
    'Dim sourceList As MainAdminDS.PRSourceDataTable = SmallReportForm.GetSmallReportForm().PRSource
    Dim sourceList As MainAdminDS.PRSourceDataTable = value(1)

    Dim sourceListView As New List(Of MainAdminDS.PRSourceRow)

    Dim rand As New Random
    For i As Integer = 0 To sourceList.Count - 1
        If (sourceList(i).PRSource_Id = playerReportRow.PlayerReport_Source Or rand.Next(0, 2) = 0) Then
            sourceListView.Add(sourceList(i))
        End If
    Next

    Return sourceListView
End Function

Я снова создаю уникальный список для целей отладки. Это тоже не работает!!!

Я нашел решение, добавив новые поля в DataLayer типа Object, и они не назначены никаким полям. Эти поля содержат список для выпадающих списков, и я инициализирую эти списки индивидуально для каждого объекта. Это прекрасно работает. Но меня все еще удивляет, почему предыдущий подход не сработал. Я чувствую, что это просто ошибка в WPF.

Покажите свою привязку к сетке. Я бы сделал Players публичной собственностью коллекции, которую вы привязали к сетке. В ctor для списка 39+ соберите AllPlayers там. Предположим, ваш список из 39+ состоит из команд и имеет свойства Имя, Менеджер, Город, Игроки. Даже если вы получаете игроков, встроенных в шаблон, они не связаны напрямую с командой (без обхода визуального дерева).

Ваш подход неверен. Во-первых, вы не должны доверять порядку, в котором происходит виртуализация сетки данных. Следовательно, подход, основанный на счетчике, для загрузки разных списков происходит хаотично.

Когда строка сетки данных де-виртуализирована, ваш комбинированный список становится видимым, запрашивает источник элементов и получает его из Window.AllPlayers имущество. Но порядок counter будет прикручен на основе прокрутки. Если вы внезапно прокручиваете пропуск нескольких рядов или используете отложенную прокрутку counter всегда буду неправ. Если вы прокрутите вперед и назад counter будет ввернут (как я не вижу никакого кода, чтобы уменьшить счетчик)...

Итак, суть в том, пожалуйста, не используйте этот подход.

Теперь вы сказали, что не хотите загружать список из отдельного элемента. counter переменная, вероятно, относится к Index текущего ряда в таблице данных ItemsSource, Если это так, вы могли бы по крайней мере использовать мульти-конвертер для того же.

Combobox XAML:

    <ComboBox
        SelectedValue="0"
        DisplayMemberPath="FullName"
        SelectedValuePath="Id" >
        <ComboBox.ItemsSource>
            <MultiBinding Converter="{StaticResource RowWiseListConverter}">
                <!--The current row item-->
                <Binding BindsDirectlyToSource="True" /> 

                <!---The items source of the data grid.-->
                <Binding Path="ItemsSource"
                         RelativeSource="{RelativeSource
                                 AncestorType={x:Type DataGrid}}"/>
            </MultiBinding>
        </ComboBox.ItemsSource>
    </ComboBox>

Мульти конвертер код:

public class RowWiseListConverter : IMultiValueConverter
{
    public object Convert(
            object[] values,
            Type targetType,
            object parameter,
            CultureInfo culture)
    {
        var item = values[0];
        var list = values[1] as System.Collections.IEnumerable;

        if (item != null && list != null)
        {
            var counter = list.Cast<object>().ToList().IndexOf(item);

            List<Player> lst = new List<Player>();
            for (int i = 0; i < 5; i++)
            {
                Player p = new Player();
                p.Id = counter + i;
                p.FullName = "Name " + counter + i;
                lst.Add(p);
            }

            return lst; 
        }

        return null;
    }
    .....
}

Код только для иллюстрации и может не компилироваться.

Надеюсь это поможет.

Другие вопросы по тегам