数据表格列宽度不会自动更新

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

Change的值更新时,其列不会更新以适应新值。因此,该列保持太小并且该值被裁剪。
有任何想法吗?


该列不会无限延伸,除非您在样式中定义。 - sajoshi
@sajoshi,如何定义? - user626528
如果您将Width更改为除Auto以外的任何值,问题将消失,但是如果您需要它,则Scott在下面的答案是正确的方法! 我循环遍历了所有自动列“dataGrid.Columns.Where(c => c.Width.IsAuto)”并执行了Scott所做的操作。 - Shahin Dohan
5个回答

71

DataGrid会在数据变长时增加列的大小,但是当数据长度减小时,它不会自动减小列的大小。在你的例子中,你将“Change”列右对齐,并使用其余的空间来放置“Name”列。

现在,当“Change”属性变得足够大以增加列的宽度时,“Name”列拒绝缩小以适应,因此您必须强制刷新。

以下步骤应该为您完成此操作(我包含了一个演示示例应用程序):

1)在DataGridTextColumn绑定中(除了您*大小的列),设置NotifyTargetUpdated=True
2)在您的DataGrid上,添加一个TargetUpdated事件处理程序。
3)在您的TargetUpdated事件处理程序中:
-- a) 将DataGrid的*大小列的宽度设置为0。
-- b) 在DataGrid上调用UpdateLayout()方法。
-- c) 将DataGrid的*大小列的宽度重新设置为new 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
    }
}

你是个好人,查理·布朗。这是我还没想出如何让我的WPF DataGrid变得更美观的最后一个小细节! - Will Rogers
我已经实现了这个功能,当列首次显示时似乎是有效的。但是,当我点击数据网格时,它会将列设置为非常小的大小,即使其中的数据更长。 - Logan B. Lehman
这对我有用,但如果我设置了ScrollViewer.CanContentScroll="True"(即虚拟化),程序将崩溃并显示异常:“Cannot call StartAt when content generation is in progress.” STR如下: 1:使用大量行填充数据网格 2:选择底部附近的一行。 3. 按住向上箭头键直到触发异常。 - Matt Becker
16
有没有人觉得微软只是半成品地推出了 WPF,只有通过集体努力(感谢 Scott 和其他人)才能让它正常工作?抱歉,我只是在发泄一下 :) - ebol2000
2
我成功地使用了 dg.Columns[0].Width = new DataGridLength(1, DataGridLengthUnitType.Auto); - Gerard
显示剩余4条评论

2
我之前在使用列表视图时遇到了类似的问题,后来在stackoverflow上找到了解决方案。具体方法可以参考这个链接:how-to-autosize-and-right-align-gridviewcolumn-data-in-wpf
对于我的情况,我是将以下代码添加到可观察集合的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;
        }
    }

在我的环境中它是可用的,但有时用户可能会注意到列上的“闪烁”。


1

如果需要,WPF将仅调整设置为自动的数据网格列宽度大小,即:无法完全显示内容。因此,当内容的宽度缩小时,列不会重新调整大小,因为内容仍然可以完全看到。

我唯一能想到的强制WPF重新计算列宽度的方法是在代码后台中将它们全部强制设置为0,然后再设置为自动,并加入一两个updateLayout(),但这并不是很好的编程方式 :-/

基本上,在您的代码后台:

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()(在更新和设置回自动之后)


0
你可以通过在样式设置中定义列的宽度属性,并将该设置绑定到你要绑定的对象的属性来解决这个问题。
<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”属性的setter中更新此属性(使用一些自定义算法,考虑字体等因素),并调用:

RaisePropertyChanged("ColumnWidth");

它应该更新您的列宽度。

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

1
但是这并不能解决问题,因为计算列宽是很复杂的,因为它基于应用的文本样式。 - Akash Kava

-2

你试过这个吗?

<DataGridTextColumn Binding="{Binding Path= Id}"  Header="ID" IsReadOnly="True" Width="1*" />

对我来说,这会创建超宽的列,而不会调整宽度。 - Welcor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接