Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
686 views
in Technique[技术] by (71.8m points)

c# - How can I set the width of a DataGridColumn to fit contents ("Auto"), but completely fill the available space for the DataGrid in MVVM?

I have a WPF DataGrid that contains some data. I would like to set the width of the columns such that the content fits in and never gets cropped (instead, a horizontal scroll bar should become visible). Additionally, I want the DataGrid to fill the whole place available (I am working with a DockPanel). I am using the following code (simplified):

<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>

This apparently does not work out of the box with Width="Auto" as it always looks something like this:

DataGrid: only the first part of the row is marked as "selected"

This obviously looks ugly. I would like to have the whole row selected, or, which would be much better, the columns to fill the whole width, but as one can see, this does not work.

If I use Width="*" instead, the content of the columns gets cropped which is even worse for me.

I found a similar question here, and a workaround was posted there. This may work, but I am working with the MVVM pattern, so the ItemsSource gets updated in the ViewModel and I cannot think about a way of doing it from there, because I cannot access the ActualWidth property of the DataGridColumn. Also, I would like to do it only in XAML if possible.

I would appreciate any help. Thanks!

Edit: As I still don't have a clue what to do about it, I start a small bounty. I would be very happy about a suggestion what one could do about my problem. Thanks again!

Edit 2: After saus' answer I thought about the options again. The problem is that I need to update the Width and the MinWidth properties also during the application is running, so not only after loading the window. I already tried to do something like

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

in some event that is fired when the underlying ItemsSource of the DataGrid is updating. However, this does not work, as the ActualWidth property does not seem to change after setting the Width on Auto. Is there an option to somehow "repaint" it in order to get the ActualWidth property updated? Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would suggest using the workaround that you linked to. It does work. In the codebehind of your view, add the following to the constructor after InitializeComponent():

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

and define SetMinWidths as:

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

I'm assuming that the reason you don't want to use this solution is that you believe that MVVM prohibits code in the codebehind. But since this is entirely view specific logic I believe that it is justified in this case. The whole "MVVM prohibits code in the codebehind" thing is a bit of a misconception.

But if you are bound by style guides, or you want this logic to be available for all datagrids in your app, you can make an attached behaviour that does the job like this:

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);
            }
        }
    }

and then for any datagrid that you want to apply this behaviour to, add the following:

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

And then your conscience, as well as your codebehind, will be clear.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...