开发者

Binding of ComboBox is not called for every row in DataGrid

I'm trying to create combobox inside DataGridTemplateColumn but it should contain different values depending on the row. Here is my code:

<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 will return different list after each call.

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

For some reason the AllPlayers func开发者_StackOverflowtion is called for the first 39 rows and then the data is taken from previously created lists. I can see that from debug info (it stops printing after 39 calls). And also lists in comboboxes are not unique. I do not understand the logic behind this behavior. I need AllPlayers be called for every row.


Show your binding to the grid. I would make Players a Public property of the collection you bind to the grid. In the ctor for the list of 39+ build the AllPlayers there. Lets assume your list of 39+ is teams and has properties Name, Manager, City, Players. Even if you get the players built in the template they are not directly associated with the team (without walking the visual tree).


Your approach is not correct.. firstly you should not trust the order in which the datagrid virtualization takes place. Hence the counter based approach to load different lists is happening erratically.

When a datagrid row is de-virtualized, your combobox becomes visible and demands the items source and gets it from the Window.AllPlayers property. But the order of counter will be screwed based on scrolling. If you suddenly scroll skipping a few range of rows or if you are using deferred scrolling the counter will be always wrong. If you scroll back and forth the counter will get screwed (as I dont see any code to decrement the counter)...

So bottom line is please dont use that approach.

Now you said that you dont want to load list from individual item. The counter variable probably refers to the Index of current row in the datagrid's ItemsSource. If so you could atleast use a multi-converter for the same.

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>

Multi converter code:

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

The code is only for illustrative purpose and may not compile.

Hope this helps.


I did not use counter to count indexes but just for debug purposes to count number of time the function was called and use it to create unique list for each combobox. My original code has the same approach you offered. Here is the converter:

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

Again I create unique list for debug purposes. This does not work either!!!

I found solution by adding new fields in DataLayer of type Object and they are not assigned to any fields. Those fields contains List for comboboxes and I initialize these list individually for each object.. It works great. But it is still puzzling me why the previous approach did not work. I have feeling that this is just bug in WPF.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜