web-dev-qa-db-ja.com

DataGridの列幅が自動更新されない

<DataGridTextColumn Binding="{Binding Name}" Width="*"/>
<DataGridTextColumn Binding="{Binding Change}" Width="Auto"/>

Changeの値が更新されると、その列は新しい値に合わせて更新されません。そのため、列は小さすぎるままであり、値はクリップされます。
何か案は?

26
user626528

DataGridは、データが長くなるにつれて列のサイズを拡大して適合させますが、データの長さが減少しても列のサイズを自動的に縮小しません。あなたの例では、「変更」列を右揃えにし、「名前」列に残りのスペースを使用しています。

これで、「変更」プロパティが大きくなり、列の幅が増えるはずですが、「名前」列はそれに合わせて縮小することを拒否しているため、自分で更新を強制する必要があります。

次の手順でこれを行う必要があります(デモ用のサンプルアプリを含めました)。

1)DataGridTextColumn Bindings(*サイズの列を除くすべて)でNotifyTargetUpdated = Trueを設定します。
2)DataGridで、TargetUpdatedイベントにハンドラーを追加します。
3)TargetUpdatedイベントハンドラーで:
-a)DataGridの*サイズの列の幅を0に設定します。
-b)DataGridでUpdateLayout()メソッドを呼び出します。
-c)DataGridの*サイズの列の幅を新しいDataGridLength(1、DataGridLengthUnitType.Star)に設定します

XAMLの例:

<Window x:Class="DataGridTest.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <CollectionViewSource x:Key="MyObjectCollection" />
    </Window.Resources>
    <DockPanel>
        <Button DockPanel.Dock="Bottom" Content="Click to Make Item 1s Text Longer" Click="Button_Click" />
        <Grid>
            <DataGrid x:Name="dg" ItemsSource="{Binding Source={StaticResource MyObjectCollection}}" AutoGenerateColumns="False" TargetUpdated="dg_TargetUpdated">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding First}" Width="1*"/>
                    <DataGridTextColumn Binding="{Binding Last, NotifyOnTargetUpdated=True}"  Width="Auto" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>

    </DockPanel>
</Window>

後ろのコード例:

using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;

namespace DataGridTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ObservableCollection<MyObject> myObjectList = new ObservableCollection<MyObject>();

        public MainWindow()
        {
            InitializeComponent();
            (this.FindResource("MyObjectCollection") as CollectionViewSource).Source = this.myObjectList;
            this.myObjectList.Add(new MyObject() { First = "Bob", Last = "Jones" });
            this.myObjectList.Add(new MyObject() { First = "Jane", Last = "Doe" });
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.myObjectList[0].Last = "BillyOBrian";
        }

        private void dg_TargetUpdated(object sender, DataTransferEventArgs e)
        {
            dg.Columns[0].Width = 0;
            dg.UpdateLayout();
            dg.Columns[0].Width = new DataGridLength(1, DataGridLengthUnitType.Star);
        }
    }

    public class MyObject : INotifyPropertyChanged
    {
        private string firstName;
        public string First
        {
            get { return this.firstName; }
            set
            {
                if (this.firstName != value)
                {
                    this.firstName = value;
                    NotifyPropertyChanged("First");
                }
            }
        }

        private string lastName;
        public string Last
        {
            get { return this.lastName; }
            set
            {
                if (this.lastName != value)
                {
                    this.lastName = value;
                    NotifyPropertyChanged("Last");
                }
            }
        }

        public MyObject() { }

        #region -- INotifyPropertyChanged Contract --

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion INotifyPropertyChanged Contract
    }
}
55
Scott

私はリストビューで同様の問題を抱えていました。解決策は how-to-autosize-and-right-align-gridviewcolumn-data-in-wpf here Stackoverflowで見つかりました。

私の場合、このコードを、リストビューがバインドされている監視可能なコレクションのcollectionchangedイベントハンドラーに追加しています。

void listview_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
        // this is a listview control
        GridView view = this.View as GridView;
        foreach(GridViewColumn c in view.Columns) {
            if(double.IsNaN(c.Width)) {
                c.Width = c.ActualWidth;
            }
            c.Width = double.NaN;
        }
    }

それは私にとっては機能しますが、ユーザーが列に「点滅」することに気付く場合があります。

2
Zee

WPFは、必要に応じてデータグリッドの列幅をAutoに設定するだけでサイズを変更します。つまり、コンテンツを完全に表示することはできません。したがって、コンテンツの幅が縮小しても、コンテンツは完全に表示されるため、列のサイズは変更されません。

1つまたは2つのupdateLayout()がスローされるように、wpfに列の幅を強制的に再計算させる唯一の方法は、列の幅をすべて0にしてからautoに戻すことですが、これは非常に良いプログラミングではありません:-/

基本的に、コードビハインドでは:

_foreach (DataGridColumn c in dg.Columns)
    c.Width = 0;

// Update your DG's source here

foreach (DataGridColumn c in dg.Columns)
    c.Width = DataGridLength.Auto;
_

おそらく、どこかにdg.UpdateLayout()が1つまたは2つ必要です(おそらく、更新と設定をautoに戻した後)。

1

これを解決する1つの方法は、スタイル設定で列の幅プロパティを定義し、その設定をバインド先のオブジェクトのプロパティにバインドすることです。

<DataGridTextColumn Binding="{Binding Change}" ElementStyle="{StaticResource ChangeColumnStyle}"/>

ResourceDictionaryで:

<Style TargetType="{x:Type DataGridTextColumn }" x:Key="ChangeColumnStyle">
   <Setter Property="Width" Value="{Binding ColumnWidth}"
</Style>

ColumnWidthはオブジェクトのプロパティである必要があります。ここで、「Change」プロパティのセッターからこのプロパティを更新した場合(フォントなどを考慮に入れて、自己定義アルゴリズムを使用して)、次を呼び出します。

RaisePropertyChanged("ColumnWidth");

列幅が更新されます。

 public int Change
   {
      get { return m_change; }
      set
      {
         if (m_change != value)
         {
            m_change = value;
            ColumnWidth = WidthAlgo(numberOfCharacters);
            RaisePropertyChanged("Change");
            RaisePropertyChanged("ColumnWidth");
         }
      }
   }
0
Edwin de Koning