如何填补数据网格与空行 [英] How to fill up datagrid with empty rows

查看:133
本文介绍了如何填补数据网格与空行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个绑定到一个可观察的集合数据网格。

I have a datagrid that is bound to an observable collection.

我想实现这个帖子我的Datagrid,但也有其他的考虑:

I'd like to achieve a similar thing shown in this post with my Datagrid but there are additional considerations:

  1. 在DataGrid中,可以由用户调整。填充数据表中的一些固定的行数不为我的目的工作。
  2. 滚动行为应该正常工作。

基本上我试图让错误列表窗口,是一个类似于Visual Studio的内部。

Basically I'm trying to make an error list window that is similar to the one inside Visual Studio.

我想AP preciate的指引。

I'd appreciate any guidelines.

推荐答案

这是一个棘手的一个。我的想法是创建一个装饰器,将负责起草,你需要不同的线路。我不喜欢制造不必要的行对象。

This was a tricky one. My idea would be to create an adorner that will be responsible for drawing the different lines you need. I'm not fond of creating unnecessary row objects.

下面是一个开始的例子(还有一些小问题,它需要调整,但我认为这是一个良好的开端。)

Here is a starting example (there are still some glitches and it will need tweaking, but I think it's a good start.)

XAML

<Window x:Class="WpfApplication11.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WpfApplication11"
    Title="MainWindow" Height="350" Width="525">

<local:MyDataGrid HeadersVisibility="Column">
    <local:MyDataGrid.Columns>
        <DataGridTextColumn Header="Column 123" Binding="{Binding}" />
        <DataGridTextColumn Header="Column 2" Binding="{Binding}" />
        <DataGridTextColumn Header="Column 33333333333333333333333" Binding="{Binding}" />
    </local:MyDataGrid.Columns>
    <sys:String>Row</sys:String>
    <sys:String>Row</sys:String>
</local:MyDataGrid>

</Window>

控制code

public static class Visual_ExtensionMethods
{
    public static T FindDescendant<T>(this Visual @this, Predicate<T> predicate = null) where T : Visual
    {
        return @this.FindDescendant(v => v is T && (predicate == null || predicate((T)v))) as T;
    }

    public static Visual FindDescendant(this Visual @this, Predicate<Visual> predicate)
    {
        if (@this == null)
            return null;

        var frameworkElement = @this as FrameworkElement;
        if (frameworkElement != null)
        {
            frameworkElement.ApplyTemplate();
        }

        Visual child = null;
        for (int i = 0, count = VisualTreeHelper.GetChildrenCount(@this); i < count; i++)
        {
            child = VisualTreeHelper.GetChild(@this, i) as Visual;
            if (predicate(child))
                return child;

            child = child.FindDescendant(predicate);
            if (child != null)
                return child;

        }
        return child;
    }
}

public class GridAdorner : Adorner
{
    public GridAdorner(MyDataGrid dataGrid)
        : base(dataGrid)
    {
        dataGrid.LayoutUpdated += new EventHandler(dataGrid_LayoutUpdated);
    }

    void dataGrid_LayoutUpdated(object sender, EventArgs e)
    {
        InvalidateVisual();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        var myDataGrid = AdornedElement as MyDataGrid;
        if (myDataGrid == null)
            throw new InvalidOperationException();

        // Draw Horizontal lines
        var lastRowBottomOffset = myDataGrid.LastRowBottomOffset;
        var remainingSpace = myDataGrid.RenderSize.Height - lastRowBottomOffset;
        var placeHolderRowHeight = myDataGrid.PlaceHolderRowHeight;
        var lineNumber = (int)(Math.Floor(remainingSpace / placeHolderRowHeight));

        for (int i = 1; i <= lineNumber; i++)
        {
            Rect rectangle = new Rect(new Size(base.RenderSize.Width, 1)) { Y = lastRowBottomOffset + (i * placeHolderRowHeight) };
            drawingContext.DrawRectangle(Brushes.Black, null, rectangle);
        }

        // Draw vertical lines
        var reorderedColumns = myDataGrid.Columns.OrderBy(c => c.DisplayIndex);
        double verticalLineOffset = - myDataGrid.ScrollViewer.HorizontalOffset;
        foreach (var column in reorderedColumns)
        {
            verticalLineOffset += column.ActualWidth;

            Rect rectangle = new Rect(new Size(1, Math.Max(0, remainingSpace))) { X = verticalLineOffset, Y = lastRowBottomOffset };
            drawingContext.DrawRectangle(Brushes.Black, null, rectangle);
        }
    }
}

public class MyDataGrid : DataGrid
{
    public MyDataGrid()
    {
        Background = Brushes.White;
        Loaded += new RoutedEventHandler(MyDataGrid_Loaded);
        PlaceHolderRowHeight = 20.0D; // random value, can be changed
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
    }

    private static void MyDataGrid_Loaded(object sender, RoutedEventArgs e)
    {
        var dataGrid = sender as MyDataGrid;
        if (dataGrid == null)
            throw new InvalidOperationException();

        // Add the adorner that will be responsible for drawing grid lines
        var adornerLayer = AdornerLayer.GetAdornerLayer(dataGrid);
        if (adornerLayer != null)
        {
            adornerLayer.Add(new GridAdorner(dataGrid));
        }

        // Find DataGridRowsPresenter and set alignment to top to easily retrieve last row vertical offset
        dataGrid.DataGridRowsPresenter.VerticalAlignment = System.Windows.VerticalAlignment.Top;
    }

    public double PlaceHolderRowHeight
    {
        get;
        set;
    }

    public double LastRowBottomOffset
    {
        get
        {
            return DataGridColumnHeadersPresenter.RenderSize.Height + DataGridRowsPresenter.RenderSize.Height;
        }
    }

    public DataGridColumnHeadersPresenter DataGridColumnHeadersPresenter
    {
        get
        {
            if (dataGridColumnHeadersPresenter == null)
            {
                dataGridColumnHeadersPresenter = this.FindDescendant<DataGridColumnHeadersPresenter>();
                if (dataGridColumnHeadersPresenter == null)
                    throw new InvalidOperationException();
            }
            return dataGridColumnHeadersPresenter;
        }
    }

    public DataGridRowsPresenter DataGridRowsPresenter
    {
        get
        {
            if (dataGridRowsPresenter == null)
            {
                dataGridRowsPresenter = this.FindDescendant<DataGridRowsPresenter>();
                if (dataGridRowsPresenter == null)
                    throw new InvalidOperationException();
            }
            return dataGridRowsPresenter;
        }
    }

    public ScrollViewer ScrollViewer
    {
        get
        {
            if (scrollViewer == null)
            {
                scrollViewer = this.FindDescendant<ScrollViewer>();
                if (scrollViewer == null)
                    throw new InvalidOperationException();
            }
            return scrollViewer;
        }
    }

    private DataGridRowsPresenter dataGridRowsPresenter;
    private DataGridColumnHeadersPresenter dataGridColumnHeadersPresenter;
    private ScrollViewer scrollViewer;
}

这具体的一条code

This specific piece of code

void dataGrid_LayoutUpdated(object sender, EventArgs e)
{
    InvalidateVisual();
}

你真的不想要的。这是得到的OnRender最简单但最丑的方式,当它必须被调用。您应该只强制的OnRender被要求重新排序列与列大小改变。祝你好运!

you really don't want. It's the easiest but ugliest way to get OnRender being called when it has to. You should only force OnRender to be called on Column reordering and Column Size changed. Good luck !

这篇关于如何填补数据网格与空行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆