代码之家  ›  专栏  ›  技术社区  ›  Willy

设置DataGridTextColumn宽度

  •  1
  • Willy  · 技术社区  · 8 年前

    我有一个MVVM WPF应用程序。

    我在WPF datagrid中有一个DataGridTextColumn。我想将其width属性绑定到转换器,并将其单元格值传递给它。对于此列,有时此列的所有单元格都为空,因此我还想将列宽设置为固定值,如果所有单元格都为空,则设置为20(与其最小宽度相同),否则设置为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。我认为使用转换器是个好主意,但从未调用过转换器。为什么?

    使现代化 : 最后,我做了@Andy建议的事情:将视图模型中的属性绑定到视图中的datagridtextcolumn width属性。视图模型上的此属性迭代所有列单元格,然后相应地设置宽度。见下文。我的问题是,视图模型上的这个属性“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));
          }
      }
    }
    

    此外,在视图模型中,当我为datagrid设置了项目列表时,我会执行:

    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>
    

    类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 回复  |  直到 8 年前
        1
  •  1
  •   Andy    8 年前

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

    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>
    

    在应用程序中合并该资源字典。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));
            }
        }
    }
    

    在我的窗口中使用:

            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>  
    

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

        2
  •  0
  •   mm8    8 年前

    我认为使用转换器是个好主意,但从未调用过转换器。为什么?

    因为 DataGridTextColumn 继承编号 DataContext 无法绑定到 EntryDate 像那样的财产。

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

    然后可以遍历 DataGrid ItemsSource 并检查其 入职日期 属性,例如:

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

    注意:在这个特定的示例中,我假设 入职日期 确实是 string .如果是 DateTime 或a Nullable<DateTime> 你可以检查它是否等于 default(DateTime) default(DateTime?) 分别地