【问题标题】:Setting DataGridTextColumn Width设置 DataGridTextColumn 宽度
【发布时间】:2018-09-23 22:26:44
【问题描述】:

我有一个 MVVM WPF 应用程序。

我在 WPF 数据网格中有一个 DataGridTextColumn。我想将其宽度属性绑定到转换器并将其单元格值传递给它。对于这一列,在某些情况下,该列的所有单元格都是空的,所以我还想将列宽设置为固定值,如果所有单元格都是空的,则为 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 宽度属性。视图模型上的这个属性遍历所有列单元格,然后相应地设置宽度。见下文。我的问题是视图模型上的这个属性 '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");

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

注意: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>

类绑定代理:

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

但是列宽没有更新...

【问题讨论】:

  • entrydate 在哪里,它到底是什么?您的专栏正在绑定 DataCommanda。根据您的描述,您必须遍历所有这些并检查它们是否为空。您意识到“输入日期”的标题不适合 20 宽的列吗?
  • 这和你的其他问题不是同一个问题吗?将属性添加到您的视图模型并使用带有 trueforall 的 linq 来查看所有特定列是否为空。然后设置你的属性。使用数据触发器设置宽度或将其设置为双倍并直接绑定宽度。
  • @Andy 这是一个错误,请参阅我的更新。 EntryDate 来自绑定到 DataGrid 的 Itemsource。我的意思是尽管所有列单元格都是空的,但列宽与其标题标题一样宽。
  • 我明白这一点。您需要迭代整个集合并检查每个条目日期。正如我所解释的。忘记转换器。在您的视图模型中执行此操作。如果您没有视图模型,请添加一个视图模型,并将绑定到的任何 itemssource 放在那里。然后使用linq检查是否所有entrydates都为null并设置一个属性。
  • @Andy 我已经完成了您在视图模型中使用属性的建议。我现在的问题是这个属性永远不会被解雇。查看我的最新更新。

标签: wpf .net-3.5 converter ivalueconverter datagridtextcolumn


【解决方案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>

快速而肮脏的视图模型,这将在实例化后 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>  

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

【讨论】:

  • 非常感谢!我正在尝试你的代码。如果我从视图模型构造函数更新依赖属性,它就可以工作。但是,如果我从我需要的点设置它,在后台工作人员中,当它在填充 datagrid itemsource 后完成其工作时,设置属性(不抛出异常),但 datagrid 列宽不会用新值更新......我的代码有问题...我想知道是什么...
  • 另外,如果最初我在 BridgeDO 类中将 DataGridLength(20.0) 设置为默认值,当应用程序启动时,列宽设置为 20.0,但在填充数据网格后,不遵守此宽度,它是自动的设置为自动,尽管稍后在代码中我再次设置为默认的 20.0.0。任何想法发生了什么?
  • 听起来你正在重写一些东西,对象或绑定。您是否通过不使用 setcurrentvalue 来覆盖绑定?还是在资源中覆盖实例?在后台线程上更改 dp?我发布的代码绝对有效。数据加载并且列很窄。 6 秒后,它变宽了。
  • 谢谢你的想法。明天我会检查我是否重写了一些东西,但我完全按照你的例子。唯一的区别是我使用的是 SetValue 而不是 SetCurrentValue。 SetCurrentValue 仅在 .NET Framework 4 及更高版本后可用。我使用的是 NET 3.5,因此此方法不存在。在 .NET 3.5 中,我使用的是 SetValue。我想这与 SetCurrentValue 相同。我在字典中创建了一次实例,就像在您的示例中一样,然后我将字典合并到 app.xaml。关于后台工作人员,是的,我使用其中的 SetValue,但首先使用 Dispatcher.CheckAccess。
  • 关于后台工作人员:我有两个数据网格。 Dg1 和 Dg2。 Dg2 是我要为其其中一列设置列宽的地方。最初我将数据加载到 Dg1 中。然后当在 Dg1 中选择一行时,我将数据加载到 Dg2 中。我使用后台工作程序加载 Dg2。 Dg2 Itemsource 属性绑定到视图模型属性。后台工作人员将数据加载到此视图模型属性中,一旦我将数据加载到其中,我调用 SetValue 来更新 Dg2 列宽。
【解决方案2】:

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

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

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

然后您可以遍历DataGrid 的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 确实是string。如果是DateTime 或Nullable&lt;DateTime&gt;,您可以分别检查它是否等于default(DateTime) 或default(DateTime?)。

【讨论】:

  • EntryDate 绑定到 width 属性然后传递给转换器来自绑定到 datagrid 的 ItemsSource。请参阅上面的更新。它不是视图模型中的属性。
  • 我知道。你想说什么?该列根本没有 DataContext,因此绑定不起作用。
  • 我的意思是,这个列的DataContext与ItemSource相同,因为在这种情况下EntryDate来自MyListOfItems设置的datagrid的ItemSource属性,所以wpf知道datacontext。
  • 再一次:不。你不能那样绑定。绑定是 DataGrid 在创建单元格时使用的 DataGridColumn 的一个属性。如果你不喜欢答案,问一个问题有什么意义?显然你不知道 DataGrid 是如何工作的。
  • 之所以问这个问题是因为我想知道为什么对转换器的调用不起作用。那么,好的,那么如何将单元格值传递给转换器?此外,除此之外,我也尝试过您的解决方案,但是当数据网格内容更改时,不会引发加载事件。
猜你喜欢
  • 2016-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-28
  • 2017-10-27
  • 2012-07-15
  • 2015-12-18
  • 1970-01-01
相关资源
最近更新 更多