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

我想用我的 Datagrid 实现 this post 中显示的类似内容,但还有其他注意事项:

  • Datagrid 可以由用户调整大小。用一些固定数量的行填充数据表对我的目的不起作用。
  • 滚动行为应该正常工作。

  • 基本上我正在尝试制作一个类似于 Visual Studio 中的错误列表窗口。

    我很感激任何指导方针。

    最佳答案

    这是一个棘手的问题。我的想法是创建一个装饰器,负责绘制您需要的不同线条。我不喜欢创建不必要的行对象。

    这是一个开始的例子(仍然有一些小故障,需要调整,但我认为这是一个好的开始。)

    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>
    

    控制代码
    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;
    }
    

    这段特定的代码
    void dataGrid_LayoutUpdated(object sender, EventArgs e)
    {
        InvalidateVisual();
    }
    

    你真的不想。这是在需要时调用 OnRender 的最简单但最丑陋的方法。您应该只在列重新排序和列大小更改时强制调用 OnRender。祝你好运 !

    关于c# - 如何用空行填充数据网格,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12890890/

    10-13 03:11
    查看更多