C# 基于列表内容显示/隐藏DataGrid列
我正在编写一个C# 基于列表内容显示/隐藏DataGrid列,c#,wpf,data-binding,datagrid,show-hide,C#,Wpf,Data Binding,Datagrid,Show Hide,我正在编写一个WPF应用程序来显示大型系统中生成的日志文件,这将是其中的一部分。日志文件是.CSV文件,并且具有预定义的列数。但是,这些文件中隐藏着“子类型”。让我详细说明一下 为简洁起见,假设我的文件有4列,即: LineNum、Data1、Data2、Data3 但问题是。我们的Type1日志文件将包含与LineNum、Data1和Data2相关的数据,而Type2日志文件将包含与LineNum、Data1和Data3相关的数据。请记住,这只是一个例子,我实际上有超过4列,大约有20种不同的
WPF
应用程序来显示大型系统中生成的日志文件,这将是其中的一部分。日志文件是.CSV
文件,并且具有预定义的列数。但是,这些文件中隐藏着“子类型”。让我详细说明一下
为简洁起见,假设我的文件有4列,即:
LineNum、Data1、Data2、Data3
但问题是。我们的Type1
日志文件将包含与LineNum、Data1和Data2
相关的数据,而Type2
日志文件将包含与LineNum、Data1和Data3
相关的数据。请记住,这只是一个例子,我实际上有超过4列,大约有20种不同的组合。以下是每种类型日志的两个示例
类型1
1、数据1A、数据2A、
2、数据1b、数据2b、
3、数据1C、数据2C、
类型2
1、数据1a、数据3a、
2,数据1b,,数据3b
3、数据1C、数据3C、
正如我之前所说,我的实际文件有大量的列(70+),但每种类型的数据都只涉及少数列。我完全无法控制这一点,因为这是一个已经存在的更大系统的一部分。现在我想让我的应用程序读取这些文件,但只显示其中包含数据的列
幸运的是,在更大的系统中,有一组“系统”文件,指示哪些类型的文件在哪些列中包含数据。因此,Type1
的sys
文件如下所示:
1,1,1,0
1
表示除第3列(Data3
)外的所有列都将包含数据。类型2的sys
文件如下所示:
1,1,0,1
表示除数据2之外的所有数据都可用
现在,用户通过文件选择对话框选择要显示的文件类型。当他们这样做时,我希望应用程序找出它是哪种类型,并相应地只显示适用的列。下面是一个示例应用程序,演示了该应用程序
我使用的两个模型类:
public class LogDataSet
{
public List<LogData> LogData { get; set; }
public List<bool> DisplayColumns { get; set; }
}
public class LogData
{
public int LineNum { get; set; }
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
}
我的简单的XAML
如下:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DataGrid Grid.Row="0" Grid.ColumnSpan="2"
Name="DgLogs" Margin="8" AutoGenerateColumns="False" IsReadOnly="True"
ItemsSource="{Binding LogDataSet.LogData}">
<DataGrid.Columns>
<DataGridTextColumn Header="Line #" Binding="{Binding LineNum}"/>
<DataGridTextColumn Header="Data1" Binding="{Binding Data1}"/>
<DataGridTextColumn Header="Data2" Binding="{Binding Data2}"/>
<DataGridTextColumn Header="Data3" Binding="{Binding Data3}"/>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="BtnType1" Content="Type 1"
Width="100" Height="28" Margin="4, 4, 4, 8" Grid.Column="0" Grid.Row="1"
Click="BtnType1_Click"/>
<Button x:Name="BtnType2" Content="Type 2"
Width="100" Height="28" Margin="4, 4, 4, 8" Grid.Column="1" Grid.Row="1"
Click="BtnType2_Click"/>
</Grid>
这显然将显示所有4列,其中列中的空单元格不包含每种文件类型的数据。但是,基于我的bool
列表DisplayColumns
,我该如何隐藏这些列呢?我看到很多SO帖子展示了如何基于另一个控件等执行类似的操作,但没有展示如何绑定到列表的索引
编辑
我也愿意接受其他创造性的解决方案,我不必阅读sys
文件和使用DisplayColumns
列表。这是我采用的方法,但只要完成了工作,我就可以走另一条路。如果您只想隐藏这些列,可以在网格初始化后轻松完成
<DataGrid Name="DgLogs" Initialized="DgLogs_OnInitialized">
编辑
您还可以使用绑定和转换器查找要隐藏的列:
<DataGrid Name="DgLogs" Margin="8" AutoGenerateColumns="False" IsReadOnly="True" Initialized="DgLogs_OnInitialized">
<DataGrid.Resources>
<myProject:MyHeaderToVisibilityConverter x:Key="MyHeaderToVisibilityConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Line #" Binding="{Binding LineNum}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
<DataGridTextColumn Header="Data1" Binding="{Binding Data1}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
<DataGridTextColumn Header="Data2" Binding="{Binding Data2}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
<DataGridTextColumn Header="Data3" Binding="{Binding Data3}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
</DataGrid.Columns>
</DataGrid>
您可以在视图中为LogDataSet
属性处理PropertyChanged
事件:
public MainWindow()
{
InitializeComponent();
PropertyChanged += MainWindow_PropertyChanged;
DataContext = this;
}
private void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(LogDataSet))
{
for (int i = 0; i < DgLogs.Columns.Count; i++)
{
DgLogs.Columns[i].Visibility = LogDataSet.DisplayColumns[i] ? Visibility.Visible : Visibility.Collapsed;
}
}
}
这并不能完全解决我的问题。正如我所说的,有不同类型的文件,只有当用户选择文件时,我才知道文件的类型。我想使用我拥有的bool
列表,或者一个不需要硬编码的解决方案。我忘了在我的应用程序中使用MVVM
模式,这是我的缺点,所以我不想从ViewModel
访问数据网格,我也不想在视图中隐藏这个列(我无法访问我的视图中的日志数据集
)。错误。如果您使用的是MVVM,则应该在视图中隐藏列。其他一切都会破坏该模式。视图模型不知道视图中任何列的任何信息。它公开DisplayColumns
集合,但隐藏列则取决于视图。因此,您应该在视图中处理PropertyChanged
事件。
<DataGrid Name="DgLogs" Margin="8" AutoGenerateColumns="False" IsReadOnly="True" Initialized="DgLogs_OnInitialized">
<DataGrid.Resources>
<myProject:MyHeaderToVisibilityConverter x:Key="MyHeaderToVisibilityConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Line #" Binding="{Binding LineNum}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
<DataGridTextColumn Header="Data1" Binding="{Binding Data1}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
<DataGridTextColumn Header="Data2" Binding="{Binding Data2}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
<DataGridTextColumn Header="Data3" Binding="{Binding Data3}" Visibility="{Binding Path=Header, RelativeSource={RelativeSource Self}, Converter={StaticResource MyHeaderToVisibilityConverter}}"/>
</DataGrid.Columns>
</DataGrid>
public class MyHeaderToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//Find your settings for each header here.
//and compare below with the results
switch (value?.ToString())
{
case "Line #":
return Visibility.Visible;
case "Data1":
return Visibility.Collapsed;
default:
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public MainWindow()
{
InitializeComponent();
PropertyChanged += MainWindow_PropertyChanged;
DataContext = this;
}
private void MainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(LogDataSet))
{
for (int i = 0; i < DgLogs.Columns.Count; i++)
{
DgLogs.Columns[i].Visibility = LogDataSet.DisplayColumns[i] ? Visibility.Visible : Visibility.Collapsed;
}
}
}
private void BtnType1_Click(object sender, RoutedEventArgs e)
{
var type1logs = new List<LogData>
{
new LogData() { LineNum = 1, Data1 = "Data1A", Data2 = "Data2A" },
new LogData() { LineNum = 2, Data1 = "Data1B", Data2 = "Data2B" },
new LogData() { LineNum = 3, Data1 = "Data1C", Data2 = "Data2C" }
};
LogDataSet = new LogDataSet() { LogData = type1logs, DisplayColumns = ReadSysFiles("Type1.sys") };
}
private void BtnType2_Click(object sender, RoutedEventArgs e)
{
var type2logs = new List<LogData>
{
new LogData() { LineNum = 1, Data1 = "Data1A", Data3 = "Data3A" },
new LogData() { LineNum = 2, Data1 = "Data1B", Data3 = "Data3B" },
new LogData() { LineNum = 3, Data1 = "Data1C", Data3 = "Data3C" }
};
LogDataSet = new LogDataSet() { LogData = type2logs, DisplayColumns = ReadSysFiles("Type2.sys") };
}