如何设置 DataGridColumn 的宽度以适应内容(“自动”),但完全填充 MVVM 中 DataGrid 的可用空间?

Posted

技术标签:

【中文标题】如何设置 DataGridColumn 的宽度以适应内容(“自动”),但完全填充 MVVM 中 DataGrid 的可用空间?【英文标题】:How can I set the width of a DataGridColumn to fit contents ("Auto"), but completely fill the available space for the DataGrid in MVVM? 【发布时间】:2011-06-27 05:22:27 【问题描述】:

我有一个包含一些数据的 WPF DataGrid。我想设置列的宽度,以便内容适合并且永远不会被裁剪(相反,水平滚动条应该变得可见)。此外,我希望DataGrid 能够填满整个可用位置(我正在使用DockPanel)。我正在使用以下代码(简化):

<DataGrid ItemsSource="Binding Table">
    <DataGrid.Columns>
        <DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1" Binding="Binding Property1" />
        <DataGridTextColumn MinWidth="200" Width="Auto" Header="Column 2" Binding="Binding Property2" />
    </DataGrid.Columns>
</DataGrid>

这显然不适用于 Width="Auto",因为它总是看起来像这样:

这显然看起来很难看。我希望选择整行,或者更好的是选择列来填充整个宽度,但正如大家所见,这不起作用。

如果我改用Width="*",列的内容会被裁剪,这对我来说更糟。

我找到了similar question here,并在那里发布了解决方法。这可能有效,但我正在使用 MVVM 模式,所以 ItemsSource 在 ViewModel 中得到更新,我想不出从那里做的方法,因为我无法访问 ActualWidth 属性DataGridColumn 的。另外,如果可能的话,我想只在 XAML 中进行。

如果有任何帮助,我将不胜感激。谢谢!

编辑:由于我仍然不知道该怎么做,我开始了一个小赏金。我会很高兴有人提出可以解决我的问题的建议。再次感谢!

编辑 2:在 saus 的回答之后,我再次考虑了这些选项。问题是我需要在应用程序运行期间更新WidthMinWidth 属性,所以不仅在加载窗口之后。我已经尝试过做类似的事情

column.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
column.MinWidth = column.ActualWidth;
column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

DataGrid 的底层ItemsSource 更新时触发的某些事件。但是,这不起作用,因为在 Auto 上设置 Width 后,ActualWidth 属性似乎没有改变。是否可以选择以某种方式“重新绘制”它以更新 ActualWidth 属性?谢谢!

【问题讨论】:

我不确定我是否了解您想在这里做什么。如果列的宽度比DataGrid 更宽,您想要一个垂直滚动条并且不希望文本换行?你的意思是水平滚动条吗?否则,这对我来说毫无意义,但也许我在这里很慢:) 感谢您的提示!当然我的意思是水平滚动条,否则它没有任何意义;-) 【参考方案1】:

我建议使用您链接到的解决方法。它确实有效。在视图的代码隐藏中,在 InitializeComponent() 之后将以下内容添加到构造函数中:

Griddy.Loaded += SetMinWidths; // I named my datagrid Griddy

并将 SetMinWidths 定义为:

public void SetMinWidths(object source, EventArgs e )
        
            foreach (var column in Griddy.Columns)
            
                column.MinWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            
        

我假设您不想使用此解决方案的原因是您认为 MVVM 禁止代码隐藏中的代码。但由于这完全是视图特定的逻辑,我相信在这种情况下是合理的。整个“MVVM 禁止代码隐藏中的代码”这件事有点误解。

但是,如果您受样式指南的约束,或者您希望此逻辑可用于您应用中的所有数据网格,您可以创建一个附加的行为来完成这项工作:

public class SetMinWidthToAutoAttachedBehaviour 
    
        public static bool GetSetMinWidthToAuto(DependencyObject obj)
        
            return (bool)obj.GetValue(SetMinWidthToAutoProperty);
        

        public static void SetSetMinWidthToAuto(DependencyObject obj, bool value)
        
            obj.SetValue(SetMinWidthToAutoProperty, value);
        

        // Using a DependencyProperty as the backing store for SetMinWidthToAuto.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SetMinWidthToAutoProperty =
            DependencyProperty.RegisterAttached("SetMinWidthToAuto", typeof(bool), typeof(SetMinWidthToAutoAttachedBehaviour), new UIPropertyMetadata(false, WireUpLoadedEvent));

        public static void WireUpLoadedEvent(DependencyObject d, DependencyPropertyChangedEventArgs e)
        
            var grid = (DataGrid)d;

            var doIt = (bool)e.NewValue;

            if (doIt)
            
                grid.Loaded += SetMinWidths;
            
        

        public static void SetMinWidths(object source, EventArgs e)
        
            var grid = (DataGrid)source;

            foreach (var column in grid.Columns)
            
                column.MinWidth = column.ActualWidth;
                column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
            
        
    

然后对于要应用此行为的任何数据网格,添加以下内容:

<DataGrid ItemsSource="Binding Table" local:SetMinWidthToAutoAttachedBehaviour.SetMinWidthToAuto="true">

然后你的良心以及你的代码隐藏就会变得清晰。

【讨论】:

嗨,非常感谢!实际上,我不认为完全查看特定内容不应该在代码隐藏中。问题在于 ViewModel 中 DataGrid 的内容发生了变化,因此 View 不知道何时必须再次调用 SetMinWidths。也许一旦我的NotifyPropertyChanged 方法被调用,DataGrid 就会发生变化?我认为附加行为也存在同样的问题,但我可能会找到解决该问题的方法。很快就会回来提供更多信息。再次感谢! 查看我的帖子,我编辑了它:不幸的是,我没有让它像那样工作。原因见上文。 我们是否需要取消订阅该事件以避免内存泄漏? 对我来说,这种模式将所有列缩小到最小尺寸,因此正好相反。 此解决方案的另一个缺陷是它绝对无法编辑列的宽度。【参考方案2】:

在模板列内使用带有文本换行的 TextBlock:

<DataGrid ItemsSource="Binding Table">      
    <DataGrid.Columns>          
        <DataGridTextColumn MinWidth="100" Width="Auto" Header="Column 1"
                            Binding="Binding Property1" />          
        <DataGridTemplateColumn Width="*" Header="Column 2">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="Binding Property2" TextWrapping="Wrap" />  
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>  
</DataGrid>

【讨论】:

感谢您的建议,但我不想换行。如上所述,我宁愿在文本太宽时立即获得垂直滚动条。 模板列的意义在于,您可以在 DataTemplate 中使用适合您需要的任何控件,例如 我不想在特定列中有一个滚动条,而是一个用于整个数据网格的滚动条,如果我将列的宽度设置为“自动”并且空间没有够了...如果每个单元格中都有滚动条,那看起来会很糟糕...【参考方案3】:

这个怎么样:

对于列的 MinWidth,设置与 dataGrid 的 ScrollContent 的 Width 的绑定,并使用一个转换器将此宽度除以列数

您需要为转换器提供一些代码,但这会保持您的 MVVM 结构完整。

可能是一个很长的尝试,但没有时间尝试。

编辑:我对此进行了更多思考,并且有一个 pb:如果你有一个大柱子和几个小柱子,它就不会很好地工作。我假设所有列的宽度都相同,这显然不是这里的情况。

不过,您可能想以这种方式进行探索。在 Widths 上使用带转换器的绑定是我能想到的唯一可行的方法:因为在计算列宽时基本上需要考虑两个条件,所以没有简单的方法可以做到这一点。

【讨论】:

好的,谢谢你的想法,我会考虑的。但是,我确实有一个列比其他列宽得多,因此它不适用于第一个建议。我认为你是对的,我需要对列的宽度使用绑定(这也是我的想法),但是为每列找到正确的 minwidth/width 仍然是一个挑战^^ 肯定会的。 ^^ 如果我可以添加一条建议:如果可以避免,请不要将 MinWidth 属性与 Width 属性结合使用。它会导致严重的头痛。例如:将 minWidth 设置为 100,然后将宽度设置为 80,控件的宽度将是 80:如果您通过程序设置,宽度将接管 minWidth...【参考方案4】:

我将Event 添加到DataGrid 中,根据DataGrid 的实际宽度重新定义它们的宽度:

 private static void OnLoaded(object sender, RoutedEventArgs e)
    
        DataGrid dg = sender as DataGrid;
        foreach (var column in dg.Columns)
        
            column.Width = new DataGridLength(dg.ActualWidth / dg.Columns.Count, DataGridLengthUnitType.Pixel);
        
    

行:

column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

完全没有意义。 DataGridLengthUnitType.Star 的定义将列的宽度缩小到最小宽度,并使它们的宽度静态且不可编辑。

【讨论】:

【参考方案5】:

强制最后一列占用datagrid中的所有剩余空间

grid.Columns[grid.Columns.Count -1].Width = new 
                  DataGridLength(250, DataGridLengthUnitType.Star);

【讨论】:

【参考方案6】:

将其用于您希望适合其内容的任何列: 宽度 = "SizeToCells"

【讨论】:

以上是关于如何设置 DataGridColumn 的宽度以适应内容(“自动”),但完全填充 MVVM 中 DataGrid 的可用空间?的主要内容,如果未能解决你的问题,请参考以下文章

如何同时输入 , 和 .然后将其格式化为 .用于所有文化中的 DataGridColumn?

动态设置dataGridColumn的ItemRenderer

使用转换器的DataGridColumn可见性

如何绑定 DataGridColumn.Visibility?

Flex 3:如何在其 ItemRenderer 中获取 DataGridColumn 的 dataField?

分别验证 DataGridColumn 单元格