4

監視可能なコレクションにバインドされているデータグリッドがあります。

Datagridを使用して、この投稿に示されているのと同様のことを実現したいのですが、追加の考慮事項があります。

  1. データグリッドは、ユーザーがサイズを変更できます。データテーブルに一定数の行を入力しても、私の目的では機能しません。
  2. スクロール動作は正しく機能するはずです。

基本的に、VisualStudio内のものと同様のエラーリストウィンドウを作成しようとしています。

ガイドラインをいただければ幸いです。

4

3 に答える 3

11

これはトリッキーなものでした。私のアイデアは、必要なさまざまな線を描く責任がある装飾者を作成することです。不要な行オブジェクトを作成するのは好きではありません。

これが最初の例です(まだいくつかの不具合があり、微調整が必​​要ですが、良いスタートだと思います)。

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が呼び出されるように強制する必要があります。幸運を !

于 2012-10-17T13:40:33.823 に答える
1

2 つの GridView を含む DockPanel を使用して UserControl を作成します。最初の GridView は "上" にドッキングされ、2 番目の GridView (空白行) は残りのスペースを使用します (最初の GridView の行数に応じて残っている場合)。 )。また、両方の GridView でスクロールを実装するには、ScrollViewer が必要です。

DataGrid を使用することもできますが、DataGrid は列に INotifyPropertyChanged を実装していないため (ただし、GridView は実装している)、列幅を共通のデータ ソースにバインドする必要があります。

これを実装する方法の例を以下のコードに示します (より良い GridLines を取得するには、追加のスタイル設定が必要になることに注意してください) ObjectList に新しいオブジェクトを追加するとどうなるかをテストします。UserControl でさまざまな固定サイズでテストします。空白はスクロールバーなしで「魔法」によって消去されますが、十分なスペースが残っている場合は表示されます。


<Window.Resources>
<x:Array x:Key="ObjectList" Type="{x:Type local:MyDataStructure}">
    <local:MyDataStructure Description="John" Value="13" />
    <local:MyDataStructure Description="Tom" Value="12" />
    <local:MyDataStructure Description="John" Value="13" />
    <local:MyDataStructure Description="Tom" Value="12" />
    <local:MyDataStructure Description="John" Value="13" />
    <local:MyDataStructure Description="Tom" Value="12" />
</x:Array>

<x:Array x:Key="Blanks" Type="{x:Type local:MyDataStructure}">
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
    <local:MyDataStructure Description="" Value="{x:Null}" />
</x:Array>

<GridViewColumnCollection x:Key="columns">
    <GridViewColumn Header="Description" DisplayMemberBinding="{Binding Description}" Width="100" />
    <GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}" Width="50" />
</GridViewColumnCollection>

<DataTemplate x:Key="RowTemplate">
    <Border BorderBrush="Gray" BorderThickness="1">
    <GridViewRowPresenter Content="{Binding}" Columns="{StaticResource columns}" />
    </Border>
</DataTemplate>
</Window.Resources>

<DockPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto" DockPanel.Dock="Top">
    <ItemsControl DockPanel.Dock="Top">
    <GridViewHeaderRowPresenter Columns="{StaticResource columns}" DockPanel.Dock="Top" />
    <ItemsControl ItemsSource="{StaticResource ObjectList}" ItemTemplate="{StaticResource RowTemplate}" DockPanel.Dock="Top"></ItemsControl>
    </ItemsControl>
</ScrollViewer>
<ItemsControl ItemsSource="{StaticResource Blanks}" ItemTemplate="{StaticResource RowTemplate}"></ItemsControl>
</DockPanel>

  public class MyDataStructure
  {
    public string Description { get; set; }

    public int? Value { get; set; }
  }
于 2012-10-22T09:03:09.987 に答える
-2

コレクション内の空のオブジェクトで埋めてから、グリッドに追加するときにそれらを上書きします。最新の「空の」行がどこにあるかを判断する何らかのループ チェックを実行します。空でないオブジェクトをそれぞれ取得し、並べ替えを実行してから再挿入することもできます。

カスタム モジュールを使用する必要はありません。古き良きロジックと for/foreach ループを使用するだけです。

于 2012-10-18T20:58:24.990 に答える