这是我第一次在这里发布问题,所以提前道歉,如果我在我的问题中遗漏了什么,或者我没有提供足够的细节。
首先,我发现了这篇文章中描述的一个非常相似的问题,它非常清楚地描述了我想要实现的目标:Autoresize Listview Columns on content update 但是信誉限制不允许我发表评论,因此我正在创建这个问题。
关于我的具体问题:
我是第一次构建一个 WPF 应用程序,并试图围绕 MVVM 模式。在我的视图中,我有一个带有网格视图的 listView。listview 的 ItemSource 绑定到我的 ViewModel 中的 ICollectionView,根据用户选择的植物类别,它的 CollectionViewSource 使用不同的 ObservableCollections 更新。可观察集合根据用户输入的搜索查询从本地数据库中获取数据。
我的问题是,用户第一次输入搜索输入时,列表中会填充结果,并且每个 gridview 列的宽度都会调整以适合每列中最长的字符串。
但是,当用户输入另一个搜索查询并且我更新了 observable 集合时,gridview 列的宽度不会调整以适应新数据,因此宽度将保持第一个搜索查询结果设置的宽度。
我希望能够动态地设置每列的宽度以适应该列中最长的元素,但无法在不破坏 MVVM 模式的情况下正确地做到这一点。
前面提到的帖子中提出的解决方案建议编辑我的 xaml.cs 文件,据我了解,这不应该在 MVVM 中完成,因为视图的代码隐藏文件不应该包含任何逻辑。据我了解,正确的方法是在视图模型上公开属性,然后通过 xaml 在视图中绑定到这些属性。
然后,我天真的方法是在 ViewModel 中公开一个 Width 属性,但是,由于我的视图中有几列,我需要为每个列公开宽度属性,并通过遍历新获取的每个对象来重新计算它们的宽度结果并找到其中最长的字符串,这似乎既乏味又是错误的方法。
所以我的问题基本上是:当 viewmodel 中的 ItemSource 在不让 viewmodel 知道视图的情况下更新时,重新计算视图中 gridviewcolumns 的宽度的最佳方法是什么?
我应该在我的视图模型中公开宽度的属性,还是我可以在视图模型中引发一个事件并在我的视图中订阅它?对此的任何帮助或提示将不胜感激!
这是我的列表视图的 xaml 代码:
<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>
和视图模型:
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();
}
}