3
votes

I have a DataGrid control, dgChangesMade, with ItemsSource = ObservableCollection<CheckinPath>.

The first column, dgPath, is a DataGridTemplateColumn, consisting of

  • CheckBox
  • TextBlock. A converter returns for the latter a relative path. The visibility of a datagrid row is bound to the IsVisible property.

Besides the DataGrid control, I also have a CheckBox control:

  • IsChecked event is bound to the boolean property IsChecked. The setter sets the IsVisible property of objects bound to the item source of the DataGrid. Therefore datagrid rows are collapsed/shown when the checkbox is (un)checked.
  • SourceUpdated event is bound to CheckBox_SourceUpdated

Like Scott pointed out in another thread:

The DataGrid will increase column sizes to fit as the data becomes longer, but it does not automatically decrease column sizes when the length of the data decreases

So in the CheckBox_SourceUpdated event I

  1. set column widths to 0
  2. force an update layout of the DataGrid
  3. set column widths to Auto

Unfortunately that didn't help. When (un)checking the checkbox control, the column widths increases when needed (see 2 in screenshot) but aren't automatically decreased according to the length of the data in the datagrid columns (see 3 in screenshot, 3 shows the same content as 1).

Any ideas?

Screenshot enter image description here

XAML

<DataGrid Name='dgChangesMade' Width='Auto' ItemsSource="{Binding Path=ChangesMade}">
    <DataGrid.Resources>
        <DataGridTemplateColumn Width='Auto' x:Key='dgPath' Header='Path' IsReadOnly='True' x:Shared='False'>
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <StackPanel Orientation='Horizontal' VerticalAlignment='Center'>
                    <CheckBox IsChecked='{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}'>
                    <TextBlock Text='{Binding Converter={StaticResource CheckinPathConverter}, ConverterParameter="Path"}' />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <Style x:Key='dgShowHideRow' TargetType='DataGridRow'>
            <Setter Property='Visibility' Value='{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter="Collapsed", Mode=TwoWay}' />
        </Style>

    <DataGrid.Columns>
        <StaticResource ResourceKey='dgPath' />
        <StaticResource ResourceKey='dgStatus' />
        <StaticResource ResourceKey='dgLock' />
    </DataGrid.Columns>
    <DataGrid.ItemContainerStyle>
        <StaticResource ResourceKey='dgShowHideRow' />
    </DataGrid.ItemContainerStyle>
</DataGrid>
<!-- Setter of this checkbox sets the IsVisible property of the datagrid bound objects -->
<CheckBox IsChecked='{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}' SourceUpdated='CheckBox_SourceUpdated'/>

Code behind

private void CheckBox_SourceUpdated(object sender, DataTransferEventArgs e)
{
    foreach (var c in dgChangesMade.Columns)
        c.Width = 0;

    dgChangesMade.UpdateLayout();
    foreach (var c in dgChangesMade.Columns)
        c.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}

Model

public class CheckinPath : ViewModelBase
{
    private bool _isVisible;
    public bool IsVisible
    {
        get { return _isVisible; }
        set
        {
            if (_isVisible != value)
            {
                _isVisible = value;
                RaisePropertyChanged(nameof(IsVisible));
            }
        }
    }  
}
1
You could try forcing the whole datagrid to re-read the data. Set ChangesMade to null and then back to the filtered collection. Might work. Might not, I'd have to give it a go myself and see what happened. Assuming the property raises propertychanged.Andy
Or call .refresh() on the collectionview of ChangesMade.Andy
@Andy I don't think clearing all data in a datagrid is good practice for filtering the datagrid. In the meantime I solved the issue myself.BertAR

1 Answers

0
votes

I solved the issue.

  1. Instead of binding the datagrid itemsource to an ObservableCollection, it is now bound to CollectionView. In this way rows in the datagrid can be shown/hidden by using the CollectionViewSource Filter property. The filtering takes place in the ViewModel.
  2. After the CollectionView has been filtered, The column widths of the datagrid is resetted. Because a datagrid is a control, the reset takes place in the code behind of the view (using Messenger from the MVVM Light Toolkit)

XAML

<DataGrid x:Name='dg'
            ItemsSource='{Binding ChangesMadeView}'
            AutoGenerateColumns='False'
            IsReadOnly='True' .../>
<CheckBox IsChecked='{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}' .../>

Window code behind

public partial class TestWindow : Window
{
    public static readonly Guid Token = new Guid();
    public TestWindow()
    {
        InitializeComponent();
        Messenger.Default.Register<NotificationMessage>(this, Token, ResetColumnWidth);
    }

    private void ResetColumnWidth(NotificationMessage notMessage)
    {
        if (notMessage.Notification == "ResetColumnWidth")
        {
            foreach (DataGridColumn c in dg.Columns)
            {
                c.Width = 0;
                c.Width = DataGridLength.Auto;
            }
        }
    }
}

ViewModel

private IList<CheckinPath> _changesMade = new List<CheckinPath>();
public ICollectionView ChangesMadeView { get; private set; }

public TestViewModel()
{
    LoadData(); //Loads data for _changesMade
    ChangesMadeView = CollectionViewSource.GetDefaultView(_changesMade);
}

private bool _isChecked;

public bool IsChecked
{
    get { return _isChecked; }
    set
    {
        if (_isChecked != value)
        {
            _isChecked = value;
            RaisePropertyChanged(nameof(IsChecked));
            ChangesMadeView.Filter = checkinPath => { //do logic for filtering. Returns true or false ;};
            Messenger.Default.Send(new NotificationMessage("ResetColumnWidth"), TestWindow.Token); //notify the code behind of the view
        };
    }
}