Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 基于列表内容显示/隐藏DataGrid列_C#_Wpf_Data Binding_Datagrid_Show Hide - Fatal编程技术网

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