2

This is my first time posting a question on here, so apologies in advance, if I have missed something in my question, of if I do not provide enough detail.

First of all, I have found a very similar problem described in this post, which pretty clearly describes what I'm trying to achieve: Autoresize Listview Columns on content update however reputation restrictions do not allow me to comment, and thus I'm creating this question.

On to my specific problem:

I'm building a WPF application for the first time and trying to wrap my head around the MVVM pattern. In my View I have a listView with a gridview. The ItemSource of the listview is bound to an ICollectionView in my ViewModel, which has its' CollectionViewSource updated with different ObservableCollections depending on which category of plants the user chooses. The observable collections fetch their data from a local database depending on a search query input from the user.

My problem is, that the first time the user enters a search input, the list is populated with the results and the width of each of the gridviewcolumns are adjusted to fit the longest string in each column.

However, when the user enters another search query, and I update the observable collection, the width of the gridviewcolumns are not adjusted to fit the new data, so the width will remain what it was set to by the results of the first search query.

I would like to be able to dynamically set the width of each column to fit the longest element in that column, but can not wrap my head around how to do this correctly without breaking the MVVM pattern.

The solutions proposed in the previous mentioned post suggest editing my xaml.cs file, which to my understanding should not be done in MVVM, since the codebehind file for the view should not contain any logic. From my understanding the correct approach is to expose properties on the viewmodel and then bind to these in the view through xaml.

My naive approach has then been to expose a Width property in my ViewModel, however, since I have several columns in my view, I would need to expose width properties for each and recalculate the width of these by iterating through every object in the newly fetched results and find the longest string among them, which seems both tedious and as a wrong approach.

So my question is basically: What is the best way to recalculate the width of the gridviewcolumns in the view when the ItemSource in the viewmodel is updated without having the viewmodel know about the view?

Should I expose properties for the width in my viewmodel, or can I somehow raise an event in the viewmodel and subscribe to that in my view? Any help or tips on this would be greatly appreciated!

Here is the xaml code for my listview:

 <ListView x:Name="lvPlantCollection" ItemsSource="{Binding ActiveView}" SelectedItem="{Binding SelectedPlant}" Grid.Row="0">
                <ListView.View>
                    <GridView>
                        <GridViewColumn DisplayMemberBinding="{Binding Name}">
                            <GridViewColumnHeader Content="Navn" Tag="Name" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding MotherPlant.Name}">
                            <GridViewColumnHeader Content="Mor" Tag="MotherPlantName" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding FatherPlant.Name}">
                            <GridViewColumnHeader Content="Far" Tag="FatherPlantName" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding CaNumber}">
                            <GridViewColumnHeader Content="Ca nummer" Tag="CaNumber" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding BreedersReference}">
                            <GridViewColumnHeader Content="Breeders ref" Tag="BreedersReference" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Elite}">
                            <GridViewColumnHeader Content="Elite" Tag="Elite" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                        <GridViewColumn Header="DNA">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <ItemsControl ItemsSource="{Binding DNA}">
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <TextBlock x:Name="MutationNameTxtBlock" Text="{Binding Name}" Visibility="Collapsed"></TextBlock> <!--Negated logic, set to collapsed as standard, and then only show if value if mutationscore is actually M-->
                                                <DataTemplate.Triggers>
                                                    <DataTrigger Binding="{Binding WildType}" Value="M">
                                                        <Setter TargetName="MutationNameTxtBlock" Property="Visibility" Value="Visible"></Setter>
                                                    </DataTrigger>
                                                </DataTemplate.Triggers>
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>
                                    </ItemsControl>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Comment}">
                            <GridViewColumnHeader Content="Kommentar" Tag="Comment" Command="{Binding SortActiveViewByColumnCommand}" CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag }"></GridViewColumnHeader>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>

And the ViewModel:


public ParentMenuViewModel(DataStore ds)
{            
            IBarleyDatabaseDAO db = BarleyDatabaseMysqlDAO.Instance;
            this.ds = ds;

            DHPlants = ds.DHPlants;
            DonorPlants = ds.DonorPlants;
            BreedersReferencePlants = ds.BreedersReferencePlants;
            ElitePlants = ds.ElitePlants;
            DMIPlants = ds.DMIPlants;

            _activeView = CollectionViewSource.GetDefaultView(new List<Plant>());            
}


 ICollectionView _activeView;
        public ICollectionView ActiveView
        {
            get
            {
                return _activeView;
            }
            set
            {
                if (_activeView != value)
                {
                    _activeView = value;
                    _activeView.SortDescriptions.Clear();
                    _activeView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
                    OnPropertyChanged("ActiveView");
                }
            }
        }

 ObservableCollection<Plant> _activeList;
        public ObservableCollection<Plant> ActiveList
        {
            get { return _activeList; }
            set
            {
                if (_activeList != value)
                {
                    _activeList = value;
                    ActiveView = CollectionViewSource.GetDefaultView(_activeList);

                    OnPropertyChanged("ActiveList");
                }
            }
        }


        public void UpdatePlants(string selectedPlantType)
        {
            if (!string.IsNullOrEmpty(Filter))
            {
                switch (selectedPlantType)
                {
                    case "CA":
                        ds.UpdatePlantList(PlantType.DH, Filter, Page * 100);
                        ActiveList = DHPlants;
                        break;

                    case "Donor":
                        ds.UpdatePlantList(PlantType.DONOR, Filter, Page * 100);
                        ActiveList = DonorPlants;
                        break;

                    case "DMI":
                        ds.UpdatePlantList(PlantType.DMI, Filter, Page * 100);
                        ActiveList = DMIPlants;                        
                        break;

                    case "Breeders Reference":
                                                ds.UpdatePlantList(PlantType.BREEDERS_REFERENCE_INTERNAL, Filter, Page * 100); 

                        ActiveList = BreedersReferencePlants;
                        break;

                    case "Elite":
                        ds.UpdatePlantList(PlantType.ELITE_INTERNAL, Filter, Page * 100); 
                        ActiveList = ElitePlants;
                        break;

                    default:
                        return;
                }
            }
            else
            {
                ds.ClearPlantCollecetions();
            }

        }
  • Have you tried just setting the GridViewColumn width to auto? As in ``. – RyanHx Oct 15 '19 at 14:20
  • 1
    Hi RyanHx, thanks for the suggestion! I have, sorry for not mentioning this. The problem still persists when doing this. The columns get the correct width the first time the listbox is populated, but when the user inputs a new search query, the columns do not update their width to fit with the new results. – GittinJiggyWithIt Oct 15 '19 at 15:36

0 Answers0