Связывание объектов с сеткой данных WPF с неизвестными столбцами во время компиляции - PullRequest
0 голосов
/ 08 ноября 2018

Примечание: проблема идентична Привязать объекты с общим списком к сетке данных wpf (без ответа), которая запрашивает решение с использованием CellTemplates и не включает никаких обходных путей. Я открыт для любого решения, и у меня есть рабочее (но не идеальное) решение.

Настройка состоит в том, что у меня есть список объектов (лиц), каждый из которых содержит список объектов DataObjects.

class Person : List<DataObject>
{
    public string id { get; set; }
}
class DataObject
{
    public string columnName { get; set;}
    public string value { get; set;}
}

Имена столбцов основаны на пользовательском вводе, но у каждого персонажа одинаковые имена столбцов в списке объектов данных (т. Е. Все они имеют столбец firstName, столбец lastName, столбец dateOfBirth, все с разными значениями).

Я хотел бы показать эти значения в формате DataGrid, чтобы значения могли редактироваться пользователем.

В настоящее время я использую Grid (editGrid) и добавляю к нему дочерние TextBlocks для каждого заголовка столбца, а затем перебираю элементы, чтобы добавить TextBox для каждой ячейки. Это работает для небольших чисел, но когда у меня тысячи людей, программа отстает из-за огромного количества создаваемых TextBoxes и TextBlocks.

List<People> items;
List<string> columnHeaders;
Grid editGrid;

// Generate column headers
editGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
var columns = items.SelectMany(o => o.Select(a => a.columnName)).Distinct(StringComparer.OrdinalIgnoreCase);
foreach (string text in columns)
{
    TextBlock headerText = new TextBlock();
    headerText.Text = text;
    Grid.SetColumn(headerText, editGrid.ColumnDefinitions.Count());
    editGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });

    columnHeaders.Add(text);
    editGrid.Children.Add(headerText);
 }

// Create rows
foreach (var item in items)
{
    foreach (var dataObject in item)
    {
        var columnNum = columnHeaders.IndexOf(dataObject.columnName);
        if (columnNum != -1)
        {
            TextBox valueBox = new TextBox();
            Binding bind = new Binding();
            bind.Source = dataObject;
            bind.Mode = BindingMode.TwoWay;
            bind.Path = new PropertyPath("value");
            BindingOperations.SetBinding(valueBox , TextBox.TextProperty, bind);

            Grid.SetColumn(valueBox, columnNum);
            Grid.SetRow(valueBox, editGrid.RowDefinitions.Count);
            editGrid.Children.Add(valueBox);
        }
    }
    editGrid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
}

1 Ответ

0 голосов
/ 09 ноября 2018

Рассматривали ли вы создание эффекта DataGrid с помощью ItemsControl? Что-то вроде:

<ItemsControl ItemsSource="{Binding}" Name="IC1"  VirtualizingStackPanel.IsVirtualizing="True" ScrollViewer.CanContentScroll="True">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Template>
        <ControlTemplate>
            <Border
        BorderThickness="{TemplateBinding Border.BorderThickness}"
        Padding="{TemplateBinding Control.Padding}"
        BorderBrush="{TemplateBinding Border.BorderBrush}"
        Background="{TemplateBinding Panel.Background}"
        SnapsToDevicePixels="True">
                <ScrollViewer
                Padding="{TemplateBinding Control.Padding}"
                Focusable="False">
                    <ItemsPresenter
                    SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                </ScrollViewer>
            </Border>
        </ControlTemplate>
    </ItemsControl.Template>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*" />
                    <ColumnDefinition Width="9*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock Text="ID" Grid.Column="0" Grid.Row="0"/>
                <TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding Path=id}"/>
                <ItemsControl Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" ItemsSource="{Binding}" >
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock Text="{Binding Path=columnName}" Width="100" />
                                <TextBox Text="{Binding Path=value}" Width="100" />
                            </StackPanel>                                    
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Я заполнил некоторые тестовые данные (извините, но в VB):

Property PersonList As New ObservableCollection(Of Person)

Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
    For x As Integer = 1 To 500
        Dim P1 As New Person With {.id = "SE" & x}
        P1.Add(New DataObject With {.columnName = "First Name", .value = "Simon"})
        P1.Add(New DataObject With {.columnName = "Last Name", .value = "Evans"})
        P1.Add(New DataObject With {.columnName = "DOB", .value = "03/03/1980"})
        Dim P2 As New Person With {.id = "RE" & x}
        P2.Add(New DataObject With {.columnName = "First Name", .value = "Ruth"})
        P2.Add(New DataObject With {.columnName = "Last Name", .value = "Evans"})
        P2.Add(New DataObject With {.columnName = "DOB", .value = "11/02/1979"})
        PersonList.Add(P1)
        PersonList.Add(P2)
    Next
    IC1.DataContext = PersonList
End Sub

Это 1000 строк, но поскольку в элементе управления используется виртуализация, лагов нет.

EDIT

Не знаю, является ли лучший способ, но я бы предложил добавить свойство Int width в ваш класс DataObject и привязать к нему ширину TextBlocks & TextBoxes во втором ItemsControl.

Затем, используя приведенный ниже фрагмент кода, вычислите максимальную требуемую ширину (слава WPF эквивалентен TextRenderer ):

public static Size MeasureTextSize(string text, FontFamily fontFamily, FontStyle fontStyle, FontWeight fontWeight, FontStretch fontStretch, double fontSize)
{
    FormattedText ft = new FormattedText(text,
                                         CultureInfo.CurrentCulture,
                                         FlowDirection.LeftToRight,
                                         new Typeface(fontFamily, fontStyle, fontWeight, fontStretch),
                                         fontSize,
                                         Brushes.Black);
    return new Size(ft.Width, ft.Height);
}

(Я использовал детали шрифта для окна, но вы, конечно, можете указать другие детали, если вы вводите текстовые поля.)

Затем я написал это немного, чтобы просмотреть данные и установить ширину (извините, обратно в VB):

    If PersonList.Count > 0 Then
        Dim MaxLengths(PersonList(0).Count - 1) As Integer
        For i As Integer = 0 To MaxLengths.Count - 1
            MaxLengths(i) = 70 'Set Minimum width to accomodate Headers
        Next
        For Each P As Person In PersonList
            For i As Integer = 0 To P.Count - 1
                Dim BoxSize As Size = MeasureTextSize(P(i).value, FontFamily, FontStyle, FontWeight, FontStretch, FontSize)
                If BoxSize.Width > MaxLengths(i) Then MaxLengths(i) = BoxSize.Width + 6 'to allow for padding
            Next
        Next
        For Each P As Person In PersonList
            For i As Integer = 0 To P.Count - 1
                P(i).width = MaxLengths(i)
            Next
        Next
    End If
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...