0

我有一个 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>

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

但是列宽没有被更新......

4

2 回答 2

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>  

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

于 2018-04-16T19:06:55.793 回答
0

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

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

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

然后您可以遍历DataGrid's中的所有项目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. 如果它是 aDateTime或 aNullable<DateTime>你可以检查它是否等于default(DateTime)default(DateTime?)分别。

于 2018-04-13T14:43:37.600 回答