在WPF网格中显示带索引的一维数组
本文关键字:索引 一维数组 显示 WPF 网格 | 更新日期: 2023-09-27 18:19:49
我有一个双值的一维数组。
使用WPF,在具有数组索引(第一列)和数组值(第二列)两列的网格中显示值的最佳方式是什么?
第一列是只读的,但第二列应该是可编辑的。
事实上,数据代表了一个基于时间的值序列,在固定频率下,例如20kHz。我可以将每个样本的时间计算为(start_time+index/frequency)。
用索引、时间和数据三列显示数据的最佳方式是什么?
我在这里找到了的基本想法
我定义了两个转换器,一个用于索引
public class IndexConverter : IValueConverter
{
public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
{
ListViewItem item = (ListViewItem) value;
ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
int index = listView.ItemContainerGenerator.IndexFromContainer(item);
return index.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
一个用于时间
public class SampleTimeConverter : IValueConverter
{
private double StartTime = 0.0 ;
private double SamplingRate = 20000.0 ;
public object Convert(object value, Type TargetType, object parameter, CultureInfo culture)
{
ListViewItem item = (ListViewItem) value;
ListView listView = ItemsControl.ItemsControlFromItemContainer(item) as ListView;
int index = listView.ItemContainerGenerator.IndexFromContainer(item);
double sampleTime = StartTime + index / SamplingRate ;
return String.Format ( "{0:F5}", sampleTime ) ;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
稍后,我将把StartTime和SamplingRate作为属性传入。
对于这个测试,我定义了一个具有属性public double[] MyData
的ViewModel。
在XAML中,我定义了一个ListView,如下所示:
<Window.Resources>
<local:IndexConverter x:Key="IndexConverter"/>
<local:SampleTimeConverter x:Key="SampleTimeConverter"/>
</Window.Resources>
<Grid>
<ListView Name="listviewNames" ItemsSource="{Binding MyData}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Index" Width="100"
DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Converter={StaticResource IndexConverter}}" />
<GridViewColumn Header="Time" Width="200"
DisplayMemberBinding="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Converter={StaticResource SampleTimeConverter}}" />
<GridViewColumn Header="Value" Width="auto" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=OneWay}" Width="200" BorderThickness="0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Grid>
这或多或少是在做我想做的事。
我还没有测试编辑后的值是否会写回数组。事实上,我很确定绑定模式=单向是错误的,但我认为我会找到解决方案。
我唯一怀疑的是,为每个值创建一个文本框(可能有数千个)是否会导致性能问题。
我的第一个解决方案非常适合显示只读数据。
根据我目前的理解,编辑一个双值数组是不可能的。要编辑一个值,必须使用setter和getter方法将其定义为一个属性。
我并不是真的想这么做,因为这意味着为数组中的每个元素创建一个单独的对象。然而,如果我这样做,剩下的就很容易了,而且我可以不用第一个解决方案中使用的值转换器。
我为网格中的每个项目定义了一个类:
public class DataItem
{
int _index ;
double _sampleTime ;
ViewModel _vm ;
public DataItem ( int Index, double SampleTime, ViewModel vm )
{
_index = Index ;
_sampleTime = SampleTime ;
_vm = vm ;
}
public int Index
{
get { return _index ; }
}
public double SampleTime
{
get { return _sampleTime ; }
}
public double Value
{
get { return _vm.MyData[_index] ; }
set { _vm.MyData[_index] = value ; }
}
}
我在一个简单的循环中创建视图模型中的对象。为了简单起见,我将索引和时间作为参数传递。我传入对ViewModel的引用,以便数组值的setter可以将编辑后的值存储回原始数组中。
_items = new List<DataItem>() ;
for ( int i = 0 ; i < darray.Length ; i++ )
{
_items.Add ( new DataItem ( i, startTime + samplingRate * i, this ) ) ;
}
我把这段代码放在ViewModel的构造函数中,这对我来说已经足够好了。ViewModel有一个返回集合的Items属性。
在XAML中,我使用了DataGrid,因为它支持直接编辑。
<DataGrid x:Name="ValuesGrid"
ItemsSource="{Binding Items}"
AutoGenerateColumns="False"
CanUserReorderColumns="False"
CanUserResizeRows="False"
CanUserAddRows="False"
HorizontalGridLinesBrush="DarkGray"
VerticalGridLinesBrush="DarkGray"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Index" Binding="{Binding Index}" Width="10*" IsReadOnly="True"/>
<DataGridTextColumn Header="Time" Binding="{Binding SampleTime}" Width="20*" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Value}" Width="20*" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
最后,我想重复一遍。这个问题的答案是"你做不到"。双向绑定到double数组是不可能的。必须使用值的setter和getter方法将每个项包装在对象中。