创建不同宽度单元格的滚动网格
本文关键字:滚动 网格 单元格 创建 | 更新日期: 2023-09-27 18:20:11
我是c#和WinPhone7开发的新手,如果有人能这么友善,我需要一些指导,所以请原谅我的无知。
我遇到的问题是,我需要为电视指南创建一个网格视图,其中左侧有一个上下滚动的频道徽标垂直列表,右侧有一个水平和垂直滚动的网格。水平滚动不会移动固定在屏幕上的频道徽标,但垂直滚动网格也会滚动徽标,正如您所期望的那样。
我应该继续尝试使用XAML和Silverlight来完成这项工作,还是只通过XNA来完成?
我问这个问题是因为我已经通过Silverlight尝试了几种不同的方法来解决我遇到的两个主要问题:
性能
当我使用ASync请求从我们的API接收数据时,我创建了一个后台工作线程,该线程解析JSON,并使用Dispatch.Invoke在网格"画布"视图上创建程序单元。由于这种情况,没有逐渐的反馈,整件事都在等待,直到一切完成,然后突然出现网格。我希望单元格在不阻塞UI的情况下逐个频道或逐个单元格显示,这样滚动仍然可以很好地工作,但这似乎不会发生。
我遇到了一些问题,因为使用UIElements的任何工作都是在UI线程上完成的,而WinPhone7是主线程(我相信),这包括解析XAML或创建/修改UIElements,即使它们没有添加到屏幕上或可见的任何内容中。这意味着我无法通过预先创建或重新使用元素来改进事物。
我试着在工作线程中做尽可能多的工作,只将UI工作的一小部分发送到UI线程,以最大限度地减少阻塞,但这似乎没有帮助。
内存
很明显,我无法在7天内为每一个可能有数百个(我们支持650多个频道)的电视节目事件创建"节目单元",因为手机很快就会耗尽内存;所以我想创建一个虚拟网格,在其中创建单元格并将其加载到画布视图中,仅用于当前看到的视口。
我有两个问题:
- 执行任何UI工作的UI阻止如上所述停止任何滚动,因此如果不阻止UI,就无法在后台创建要滚动到视图中的新单元格
- 滚动视图没有发送滚动事件,我已经尝试过绑定到滚动视图中的滚动条来获得偏移值,但这并不能很好地工作,因为它只是时断时续地更新,所以如果你进行了大量滚动,我想在出现暂停或OnIdle之前,什么都不会发送
是我搞砸了,我应该坚持下去吗?还是我正在做一些不能做的事情,我应该尝试一种不同的策略,比如通过XNA来做?
如有任何建议,我们将不胜感激。
编辑:更多信息和一些示例代码
我有一个包含开始时间和标题的节目类,一个有名称和标志等的频道类,还有一系列节目。
当我检索API数据时,我会创建一个通道对象并将其添加到通道数组中,然后将程序添加到通道程序数组中。一旦一个频道的所有节目都添加到数组中,我就会将其发布到ChannelProgrammesComplete事件监听器中,以便更新UI。
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible">
<Canvas x:Name="ProgGrid"
Height="55" Width="393"
VerticalAlignment="Top" HorizontalAlignment="Left">
</Canvas>
</ScrollViewer>
public void ChannelProgrammesComplete( object sender, EventArgs e )
{
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += ( doWorkSender, args ) =>
{
Dispatch( (Channel)sender );
};
bw.RunWorkerAsync();
}
private void Dispatch( BackgroundWorker bw, object param )
{
Channel channel = (Channel)param;
int progCount = 0;
foreach( Programme programme in channel.Programmes )
{
double left = ( ( programme.StartSecsFromToday / 60 ) * PixelsPerMinute ); // turn it into seconds
if( progCount == 0 && left < 0 )
{
// If first prog starts before 6am, shrink the cell so it starts at the 6am start point
programme.UIWidth = ( ( programme.Duration - ( ( programme.StartSecsFromToday / 60 ) * -1 ) ) * PixelsPerMinute ) - _cellPadding;
left = 0;
}
else
{
programme.UIWidth = ( programme.Duration * PixelsPerMinute ) - _cellPadding; // Multiply by zoom level which is 3 for now, and take off the amount we use for right margin grid separator
}
Debug.Assert( programme.UIWidth > 0 );
programme.UITop = channel.SortIndex * ( _rowHeight + _cellPadding );
programme.UILeft = left;
programme.UIHeight = _rowHeight;
object[] invokeArgs = new object[ 1 ];
invokeArgs[ 0 ] = programme;
// Do as much work as possible in the thread before dispatching to the UI thread for the simple UI work
Dispatcher.BeginInvoke( new InvokeProgrammeCellDelegate( AddProgrammeCellDelegate ), invokeArgs );
}
}
public delegate void InvokeProgrammeCellDelegate( Programme prog );
public void AddProgrammeCellDelegate( Programme prog )
{
Rectangle progCell = new Rectangle();
progCell.Fill = new SolidColorBrush( Color.FromArgb( 0xFF, (byte)( 0x13 ), (byte)( 0x45 ), (byte)( 0x70 ) ) );
progCell.Height = prog.UIHeight;
progCell.Width = prog.UIWidth;
progCell.SetValue( Canvas.TopProperty, prog.UITop );
progCell.SetValue( Canvas.LeftProperty, prog.UILeft );
ProgGrid.Children.Add( progCell );
ProgGrid.Width = Math.Max( ProgGrid.Width, prog.UIWidth + prog.UILeft );
}
首先,使用大的Canvas
应该只是最后的手段——在大多数情况下,Grid
或StackPanel
的组合(有时在其他StackPanel
中)和带有Margin
的项目会更快(但处理起来会稍微困难一些)。
为了保持应用程序的响应,你可以
- 一次只加载小块数据(例如,两个页面。这将大大减少同时呈现的数据量)。然后,您可以立即加载下一个区块,也可以在用户滚动到列表末尾时加载
- 加快结果处理速度。如果您在插入每个项目后等待100ms(或者在每个频道后1秒,如果您想一次加载频道),则对响应性的影响应该几乎消失,同时对加载时间没有太大的影响。上述时间仅为估计值,如果它们太大/太小,请根据您的需求进行调整,以提供流畅的用户体验