设置 DataGridTextColumn 宽度

Setting DataGridTextColumn Width

我有一个 MVVM WPF 应用程序。

我在 WPF 数据网格中有一个 DataGridTextColumn。我想将其宽度 属性 绑定到转换器并将其单元格值传递给它。对于这一列,有些情况下这一列的所有单元格都是空的,所以我也想将列宽设置为一个固定值,20(与其 MinWidth 相同)以防所有单元格都为空,否则为 50。问题是该转换器未被调用。

为了简化和关注有趣的部分我只post这里相关代码:

 <DataGrid  Grid.Row="1"                          
               AutoGenerateColumns="False" 
               ItemsSource="{Binding Path=MyListOfItems}" 
               VerticalAlignment="Stretch" IsReadOnly="True" 
               SelectionMode="Single" ColumnWidth="*" 
               >

<DataGridTextColumn 
      CellStyle="{StaticResource MyDataGridCellStyle}"
      Binding="{Binding Path=EntryDate, StringFormat=\{0:dd/MM/yyyy\}}" 
      Header="Entry Date" 
      Width="{Binding Path=EntryDate, Converter={StaticResource ColumnWidthConverter}}"
      HeaderStyle="{DynamicResource CenterGridHeaderStyle}">

</DataGridTextColumn> 

 </DataGrid>

转换器:

public class ColumnWidthConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string cellContent= (string)value;

        return (string.IsNullOrEmpty(cellContent.Trim()) ? 20 : 50);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我的最终目标是在所有单元格为空时将列宽设置为 20,否则将其宽度设置为 50。我认为使用转换器是个好主意,但从未调用过转换器。为什么?

更新: Finllay 我已经完成了@Andy 建议的操作:将 属性 从视图模型绑定到视图上的 datagridtextcolumn 宽度 属性。 属性 视图模型遍历所有列单元格,然后相应地设置宽度。见下文。我的问题是视图模型上的这个 属性 'EntryDateColumnWidth' 仅在应用程序启动时第一次触发,然后在调用 OnPropertyChanged("EntryDateColumnWidth") 时不会引发。

查看型号:

public class MyMainViewModel : ViewModelBase
{
  public DataGridLength EntryDateColumnWidth
  {
      get
      {
          bool isEmpty = this.MyListOfItems.TrueForAll(i => string.IsNullOrEmpty(i.EntryDate.ToString().Trim()));

          return (isEmpty ? 20 : new DataGridLength(0, DataGridLengthUnitType.Auto));
      }
  }
}

此外,在视图模型中,当我为数据网格设置项目列表时,我执行:

OnPropertyChanged("EntryDateColumnWidth");

这个 属性 returns 一个 DataGridLength 对象,因为当任何列单元格不为空时,我需要将宽度设置为自动。

注意: ViewModelBase 是一个实现 INotifyPropertyChanged 的​​抽象class。

查看:

<DataGrid  Grid.Row="1"                          
           AutoGenerateColumns="False" 
           ItemsSource="{Binding Path=MyListOfItems}" 
           VerticalAlignment="Stretch" IsReadOnly="True" 
           SelectionMode="Single" ColumnWidth="*">

<DataGrid.Resources>
   <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

<DataGridTextColumn 
      CellStyle="{StaticResource MyDataGridCellStyle}"
      Binding="{Binding Path=EntryDate, StringFormat=\{0:dd/MM/yyyy\}}" 
      Header="Entry Date" 
      Width="{Binding Data.EntryDateColumnWidth, Source={StaticResource proxy}}"
      HeaderStyle="{DynamicResource CenterGridHeaderStyle}">

</DataGridTextColumn> 

</DataGrid>

Class 绑定代理:

namespace MyApp.Classes
{
    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }
}

更新 2:

依赖对象class:

namespace My.WPF.App.Classes
{
    public class BridgeDO: DependencyObject
    {
        public DataGridLength DataComandaColWidth
        {
            get { return (DataGridLength)GetValue(DataComandaColWidthProperty); }
            set { SetValue(DataComandaColWidthProperty, value); }
        }

        public static readonly DependencyProperty EntryDateColWidthProperty =
            DependencyProperty.Register("EntryDateColWidth", 
                                        typeof(DataGridLength), 
                                        typeof(BridgeDO),                                         
                                        new PropertyMetadata(new DataGridLength(1, DataGridLengthUnitType.Auto)));
    }
}

资源字典中的实例(DictionaryDO.xaml):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:My.WPF.App.Classes">
    <local:BridgeDO x:Key="DO"/>
</ResourceDictionary>

合并到资源字典中(app.xaml) :

<Application x:Class="My.WPF.Apps.MyApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"
    xmlns:local="clr-namespace:My.WPF.Apps.MyApp"
    StartupUri="Main.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionaries/DictionaryDO.xaml"/>
            </ResourceDictionary.MergedDictionaries>

            <!-- Styles -->
        </ResourceDictionary>
    </Application.Resources>
</Application>

Window :

<Window x:Name="MainWindow" x:Class="My.WPF.Apps.MyApp.wMain"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Window.Resources>
   <!-- Resources -->
</Window.Resources>

<DataGrid  Grid.Row="1"                          
           AutoGenerateColumns="False" 
           ItemsSource="{Binding Path=MyListOfItems}" 
           VerticalAlignment="Stretch" IsReadOnly="True" 
           SelectionMode="Single" ColumnWidth="*">

<DataGrid.Resources>
   <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

<DataGridTextColumn 
      CellStyle="{StaticResource MyDataGridCellStyle}"
      Binding="{Binding Path=EntryDate, StringFormat=\{0:dd/MM/yyyy\}}" 
      Header="Entry Date" 
      Width="{Binding EntryDateColWidth, Source={StaticResource DO}}"
      HeaderStyle="{DynamicResource CenterGridHeaderStyle}">

</DataGridTextColumn> 

</DataGrid>

</Window>

查看型号:

public class myMainViewModel : ViewModelBase 
{
   private BridgeDO _do;
   public myMainViewModel(IView view)
   {
      _view = view;
      _do = Application.Current.Resources["DO"] as BridgeDO;            
   }


   private void BackgroundWorker_DoWork()
   {
      // Do some stuff
      SetColumnWidth();
   }


   private void SetColumnWidth()
   {
      _view.GetWindow().Dispatcher.Invoke(new Action(delegate
       {
          bool isEmpty = this.MyListOfItems.TrueForAll(e => !e.EntryDate.HasValue);
          _do.SetValue(BridgeDO.EntryDateColWidthProperty, isEmpty ? new DataGridLength(22.0) : new DataGridLength(1, DataGridLengthUnitType.Auto));

            }), DispatcherPriority.Render);
   }
}

但列宽未更新...

I thought that using a converter it will be a good idea but converter is never called. Why?

因为 DataGridTextColumn 没有继承 DataContext 并且不能像那样绑定到 EntryDate 属性。

My final goal is to set column width to 20 when all its cells are empty, otherwise set its width to 50.

然后您可以遍历 DataGridItemsSource 中的所有项目并检查其 EntryDate 属性 的值,例如:

dgg.Loaded += (s, e) => 
{
    bool isEmpty = true;
    foreach(var item in dgg.Items.OfType<Item>())
    {
        if (!string.IsNullOrEmpty(item.EntryDate))
        {
            isEmpty = false;
            break;
        }
    }

    //set the Width of the column (at index 0 in this sample)
    dgg.Columns[0].Width = isEmpty? 20 : 500;
};

注意:在这个特定示例中,我假设 EntryDate 确实是 string。如果它是 DateTimeNullable<DateTime>,您可以分别检查它是否等于 default(DateTime)default(DateTime?)

好的,这演示了我所描述的原理,它有点快 n 脏。
将依赖对象定义为新的 class.

using System.Windows;
using System.Windows.Controls;

namespace wpf_12
{
    public class BridgeDO : DependencyObject
    {
        public DataGridLength ColWidth
        {
            get { return (DataGridLength)GetValue(ColWidthProperty); }
            set { SetValue(ColWidthProperty, value); }
        }
        public static readonly DependencyProperty ColWidthProperty =
            DependencyProperty.Register("ColWidth", typeof(DataGridLength), typeof(BridgeDO), new PropertyMetadata(new DataGridLength(20.0)));
    }
}

在资源字典中创建实例。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:wpf_12">
    <local:BridgeDO x:Key="DO"/>
</ResourceDictionary>

合并 app.xaml 中的资源字典:

<Application x:Class="wpf_12.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:wpf_12"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

快速而肮脏的视图模型,这将在实例化 6 秒后将列宽更改为自动。

using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace wpf_12
{
    public class MainWIndowViewModel
    {
        public ObservableCollection<object> Items { get; set; } = new ObservableCollection<object>
        {   new { Name="Billy Bob", ID=1},
            new { Name="Peter Parker", ID=2},
            new { Name="Sherlock Holmes", ID=2}
        };

        public MainWIndowViewModel()
        {
            ChangeWidth();
        }
        private async void ChangeWidth()
        {
            await Task.Delay(6000);
            var _do = Application.Current.Resources["DO"] as BridgeDO;
            _do.SetCurrentValue(BridgeDO.ColWidthProperty, new DataGridLength(1, DataGridLengthUnitType.Auto));
        }
    }
}

在我的 window 中使用它:

        Name="Window"
    >
    <Window.DataContext>
        <local:MainWIndowViewModel/>
    </Window.DataContext>

    <Window.Resources>

    </Window.Resources>
    <Grid>
        <DataGrid ItemsSource="{Binding Items}"
                  AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name}" Width="{Binding ColWidth, Source={StaticResource DO}}"/>
                <DataGridTextColumn Binding="{Binding ID}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>  

当我 运行 开始时,我会从一个较窄的专栏开始。坐在那里看一会儿,它变成自动宽度并变宽。