设置DataGridTextColumn宽度

时间:2018-04-13 14:15:27

标签: wpf .net-3.5 converter ivalueconverter datagridtextcolumn

我有一个MVVM WPF应用程序。

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

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

 <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 width属性。视图模型上的此属性迭代所有列单元格,然后相应地设置宽度。见下文。我的问题是这个属性&quot; EntryDateColumnWidth&#39;视图模型仅在启动应用程序时第一次触发,然后在调用OnPropertyChanged(&#34; EntryDateColumnWidth&#34;)时,它不会被引发。

查看模型

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

此属性返回DataGridLength对象,因为当任何列单元格不为空时,我需要将width设置为auto。

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

查看

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

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

依赖对象类

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

但是列宽未更新......

2 个答案:

答案 0 :(得分:1)

好的,这说明了我所描述的原则,而且它有点快 将依赖项对象定义为新类。

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>

快速且脏的viewmodel,这将在实例化后将列宽更改为自动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));
        }
    }
}

在我的窗口中使用它:

        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>  

当我运行时,我从一个狭窄的列开始。坐在那里观看一会儿,它会变为自动宽度和加宽。

答案 1 :(得分:0)

  

我认为使用转换器这将是一个好主意但转换器永远不会被调用。为什么呢?

因为DataGridTextColumn没有继承DataContext并且无法绑定到EntryDate属性。

  

我的最终目标是在所有单元格为空时将列宽设置为20,否则将其宽度设置为50。

然后,您可以遍历DataGrid的{​​{1}}中的所有项目并检查其ItemsSource属性的值,例如:

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。如果是stringDateTime,您可以分别检查它是否等于Nullable<DateTime>default(DateTime)