c# - VirtualizingTilePanel background scrolling -
i'm trying create view looks bookshelf items sitting on shelf virtualization. able achieve adding virtualizingtilepanel
class listview
. class using dan crevier's blog here: http://blogs.msdn.com/b/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part-4-the-goods.aspx
i adding virtualizingtilepanel
, background listview
this:
<listview.itemspanel> <itemspaneltemplate> <local:virtualizingtilepanel> <local:virtualizingtilepanel.background> <imagebrush imagesource="..\images\bookshelf.png" alignmentx="left" alignmenty="top" tilemode="tile" stretch="none" viewportunits="absolute" viewport="0,0,319,203" /> </local:virtualizingtilepanel.background> </local:virtualizingtilepanel> </itemspaneltemplate> </listview.itemspanel>
the problem have background fills original visible area. once start scrolling down background moves (like want to) background comes view white.
how can background fill entire scrollable area?
hope makes sense, can add pictures if needed.
edit
my virtualizingtilepanel
class:
class virtualizingtilepanel : virtualizingpanel, iscrollinfo { public virtualizingtilepanel() { // use in iscrollinfo implementation this.rendertransform = _trans; } // dependency property controls size of child elements public static readonly dependencyproperty childsizeproperty = dependencyproperty.registerattached("childsize", typeof(double), typeof(virtualizingtilepanel), new frameworkpropertymetadata(168.0d, frameworkpropertymetadataoptions.affectsmeasure | frameworkpropertymetadataoptions.affectsarrange)); // accessor child size dependency property public double childsize { { return (double)getvalue(childsizeproperty); } set { setvalue(childsizeproperty, value); } } /// <summary> /// measure children /// </summary> /// <param name="availablesize">size available</param> /// <returns>size desired</returns> iitemcontainergenerator generator; protected override size measureoverride(size availablesize) { updatescrollinfo(availablesize); // figure out range that's visible based on layout algorithm int firstvisibleitemindex, lastvisibleitemindex; getvisiblerange(out firstvisibleitemindex, out lastvisibleitemindex); // need access internalchildren before generator work around bug uielementcollection children = this.internalchildren; generator = this.itemcontainergenerator; // generator position of first visible data item generatorposition startpos = generator.generatorpositionfromindex(firstvisibleitemindex); // index we'd insert child position. if item realized // (position.offset == 0), it's position.index, otherwise have add 1 // insert after corresponding child int childindex = (startpos.offset == 0) ? startpos.index : startpos.index + 1; using (generator.startat(startpos, generatordirection.forward, true)) { (int itemindex = firstvisibleitemindex; itemindex <= lastvisibleitemindex; ++itemindex, ++childindex) { bool newlyrealized; // or create child uielement child = generator.generatenext(out newlyrealized) uielement; if (newlyrealized) { // figure out if need insert child @ end or somewhere in middle if (childindex >= children.count) { base.addinternalchild(child); } else { base.insertinternalchild(childindex, child); } generator.prepareitemcontainer(child); } else { // child has been created, let's sure it's in right spot debug.assert(child == children[childindex], "wrong child generated"); } // measurements depend on layout algorithm child.measure(getchildsize()); } } // note: deferred idle time efficiency cleanupitems(firstvisibleitemindex, lastvisibleitemindex); return availablesize; } /// <summary> /// arrange children /// </summary> /// <param name="finalsize">size available</param> /// <returns>size used</returns> protected override size arrangeoverride(size finalsize) { iitemcontainergenerator generator = this.itemcontainergenerator; updatescrollinfo(finalsize); (int = 0; < this.children.count; i++) { uielement child = this.children[i]; // map child offset item offset int itemindex = generator.indexfromgeneratorposition(new generatorposition(i, 0)); arrangechild(itemindex, child, finalsize); } return finalsize; } /// <summary> /// revirtualize items no longer visible /// </summary> /// <param name="mindesiredgenerated">first item index should visible</param> /// <param name="maxdesiredgenerated">last item index should visible</param> private void cleanupitems(int mindesiredgenerated, int maxdesiredgenerated) { uielementcollection children = this.internalchildren; iitemcontainergenerator generator = this.itemcontainergenerator; (int = children.count - 1; >= 0; i--) { generatorposition childgeneratorpos = new generatorposition(i, 0); int itemindex = generator.indexfromgeneratorposition(childgeneratorpos); if (itemindex < mindesiredgenerated || itemindex > maxdesiredgenerated) { generator.remove(childgeneratorpos, 1); removeinternalchildrange(i, 1); } } } /// <summary> /// when items removed, remove corresponding ui if necessary /// </summary> /// <param name="sender"></param> /// <param name="args"></param> protected override void onitemschanged(object sender, itemschangedeventargs args) { switch (args.action) { case notifycollectionchangedaction.remove: case notifycollectionchangedaction.replace: case notifycollectionchangedaction.move: removeinternalchildrange(args.position.index, args.itemuicount); break; } } #region layout specific code // i've isolated layout specific code region. if want other tiling, // you'll make changes int width = 100; int height = 203; /// <summary> /// calculate extent of view based on available size /// </summary> /// <param name="availablesize">available size</param> /// <param name="itemcount">number of data items</param> /// <returns></returns> private size calculateextent(size availablesize, int itemcount) { int childrenperrow = calculatechildrenperrow(availablesize); // see how big return new size(childrenperrow * width, height * math.ceiling((double)itemcount / childrenperrow)); } /// <summary> /// range of children visible /// </summary> /// <param name="firstvisibleitemindex">the item index of first visible item</param> /// <param name="lastvisibleitemindex">the item index of last visible item</param> private void getvisiblerange(out int firstvisibleitemindex, out int lastvisibleitemindex) { int childrenperrow = calculatechildrenperrow(_extent); firstvisibleitemindex = (int) math.floor(_offset.y / height) * childrenperrow; lastvisibleitemindex = (int) math.ceiling((_offset.y + _viewport.height) / height) * childrenperrow - 1; itemscontrol itemscontrol = itemscontrol.getitemsowner(this); int itemcount = itemscontrol.hasitems ? itemscontrol.items.count : 0; if (lastvisibleitemindex >= itemcount) lastvisibleitemindex = itemcount-1; } /// <summary> /// size of children. assume same /// </summary> /// <returns>the size</returns> private size getchildsize() { //return new size(this.childsize, this.childsize); return new size(width, height); } /// <summary> /// position child /// </summary> /// <param name="itemindex">the data item index of child</param> /// <param name="child">the element position</param> /// <param name="finalsize">the size of panel</param> private void arrangechild(int itemindex, uielement child, size finalsize) { int childrenperrow = calculatechildrenperrow(finalsize); int row = itemindex / childrenperrow; int column = itemindex % childrenperrow; child.arrange(new rect(column * width, row * height, width, height)); } /// <summary> /// helper function tiling layout /// </summary> /// <param name="availablesize">size available</param> /// <returns></returns> private int calculatechildrenperrow(size availablesize) { // figure out how many children fit on each row int childrenperrow; if (availablesize.width == double.positiveinfinity) childrenperrow = this.children.count; else childrenperrow = math.max(1, (int)math.floor(availablesize.width / width)); return childrenperrow; } #endregion #region iscrollinfo members private size _extent = new size(0, 0); private size _viewport = new size(0, 0); private point _offset; private translatetransform _trans = new translatetransform(); private void updatescrollinfo(size availablesize) { // see how many items there itemscontrol itemscontrol = itemscontrol.getitemsowner(this); int itemcount = itemscontrol.hasitems ? itemscontrol.items.count : 0; size extent = calculateextent(availablesize, itemcount); // update extent if (extent != _extent) { _extent = extent; if (_owner != null) _owner.invalidatescrollinfo(); } // update viewport if (availablesize != _viewport) { _viewport = availablesize; if (_owner != null) _owner.invalidatescrollinfo(); } } private bool _canhscroll = false; public bool canhorizontallyscroll { { return _canhscroll; } set { _canhscroll = value; } } private bool _canvscroll = false; public bool canverticallyscroll { { return _canvscroll; } set { _canvscroll = value; } } public double extentheight { { return _extent.height; } } public double extentwidth { { return _extent.width; } } public double horizontaloffset { { return _offset.x; } } public double verticaloffset { { return _offset.y; } } public rect makevisible(visual visual, rect rectangle) { return new rect(); } public void mousewheeldown() { pagedown(); } public void mousewheelleft() { throw new invalidoperationexception(); } public void mousewheelright() { throw new invalidoperationexception(); } public void mousewheelup() { pageup(); } public void pagedown() { setverticaloffset(verticaloffset + _viewport.height * 0.1); } public void pageleft() { sethorizontaloffset(horizontaloffset - _viewport.width * 0.1); } public void pageright() { sethorizontaloffset(horizontaloffset + _viewport.width * 0.8); } public void pageup() { setverticaloffset(verticaloffset - _viewport.height * 0.1); } private scrollviewer _owner; public scrollviewer scrollowner { { return _owner; } set { _owner = value; } } public void sethorizontaloffset(double offset) { if (offset < 0 || _viewport.width >= _extent.width) { offset = 0; } else { if (offset + _viewport.width >= _extent.width) { offset = _extent.width - _viewport.width; } } _offset.x = offset; if (_owner != null) _owner.invalidatescrollinfo(); invalidatemeasure(); } public void setverticaloffset(double offset) { if (offset < 0 || _viewport.height >= _extent.height) { offset = 0; } else { if (offset + _viewport.height >= _extent.height) { offset = _extent.height - _viewport.height; } } _offset.y = offset; if (_owner != null) _owner.invalidatescrollinfo(); _trans.y = -offset; invalidatemeasure(); } public double viewportheight { { return _viewport.height; } } public double viewportwidth { { return _viewport.width; } } public void lineup() { setverticaloffset(this.verticaloffset - 10); } public void linedown() { setverticaloffset(this.verticaloffset + 10); } public void lineleft() { throw new invalidoperationexception(); } public void lineright() { throw new invalidoperationexception(); } #endregion #region helper data structures class itemabstraction { public itemabstraction(wrappanelabstraction panel, int index) { _panel = panel; _index = index; } wrappanelabstraction _panel; public readonly int _index; int _sectionindex = -1; public int sectionindex { { if (_sectionindex == -1) { return _index % _panel._averageitemspersection - 1; } return _sectionindex; } set { if (_sectionindex == -1) _sectionindex = value; } } int _section = -1; public int section { { if (_section == -1) { return _index / _panel._averageitemspersection; } return _section; } set { if (_section == -1) _section = value; } } } class wrappanelabstraction : ienumerable<itemabstraction> { public wrappanelabstraction(int itemcount) { list<itemabstraction> items = new list<itemabstraction>(itemcount); (int = 0; < itemcount; i++) { itemabstraction item = new itemabstraction(this, i); items.add(item); } items = new readonlycollection<itemabstraction>(items); _averageitemspersection = itemcount; _itemcount = itemcount; } public readonly int _itemcount; public int _averageitemspersection; private int _currentsetsection = -1; private int _currentsetitemindex = -1; private int _itemsincurrentsecction = 0; private object _syncroot = new object(); public int sectioncount { { int ret = _currentsetsection + 1; if (_currentsetitemindex + 1 < items.count) { int itemsleft = items.count - _currentsetitemindex; ret += itemsleft / _averageitemspersection + 1; } return ret; } } private readonlycollection<itemabstraction> items { get; set; } public void setitemsection(int index, int section) { lock (_syncroot) { if (section <= _currentsetsection + 1 && index == _currentsetitemindex + 1) { _currentsetitemindex++; items[index].section = section; if (section == _currentsetsection + 1) { _currentsetsection = section; if (section > 0) { _averageitemspersection = (index) / (section); } _itemsincurrentsecction = 1; } else _itemsincurrentsecction++; items[index].sectionindex = _itemsincurrentsecction - 1; } } } public itemabstraction this[int index] { { return items[index]; } } #region ienumerable<itemabstraction> members public ienumerator<itemabstraction> getenumerator() { return items.getenumerator(); } #endregion #region ienumerable members system.collections.ienumerator system.collections.ienumerable.getenumerator() { return getenumerator(); } #endregion } #endregion }
i had similar problem custom panel implemetning iscrollinfo
. in implementation background not moving @ (i think background moves because setting rendertransform
). ended manually updating viewport on assigned brush in sethorizontaloffset
/setverticaloffset
this:
public void sethorizontaloffset(double offset) { offsetx_ = offset; ((tilebrush)background).viewport = new rect(-offsetx_, -offsety_, width_, height_); ((tilebrush)background).viewportunits = brushmappingmode.absolute; }
you first need set viewboxunits = brushmappingmode.absolute
on brush item.
Comments
Post a Comment