I took the standard Item GridView template and modified it a bit to fit my needs. I actually have changed very little of the template code.
I have a single group, and I have a lot of items in it (92 items). The listview does render some of them, but it only renders 12 of them. Why is that? How can I override that and make it display all of the items?
Here's a screenshot of me broken into the debugger as I'm setting the DefaultViewModel:
I add items to my listview like so (as I parse XML from a service):
DataSource.AddItem(new DataItem(... title, name, etc, DataSource.getGroup("gallery")));
Then in my DataSource class (this is exactly the same one as the sample, I just renamed it), I added this method:
public static void AddItem(DataItem item)
{
item.Group.Items.Add(item);
}
Here's what the XAML that renders this looks like (it's the same as the GridView template:
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
Content="{Binding Title}"
Click="Header_Click"
Style="{StaticResource TextButtonStyle}"/>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Orientation="Vertical" Margin="0,0,80,0"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
I'd really appreciate any help.
Grid application template limits amount of items displayed in each group to 12 for reasons explained in the comment below:
public class SampleDataGroup : SampleDataCommon
{
...
public IEnumerable<SampleDataItem> TopItems
{
// Provides a subset of the full items collection to bind to from a GroupedItemsPage
// for two reasons: GridView will not virtualize large items collections, and it
// improves the user experience when browsing through groups with large numbers of
// items.
//
// A maximum of 12 items are displayed because it results in filled grid columns
// whether there are 1, 2, 3, 4, or 6 rows displayed
get { return this._items.Take(12); }
}
}
Related
I have a grid view that is populated with lot of items...each item has a Image in it. And when user scrolls down, some items become unrealized by virtualization. And, when user scrolls back to show them, those items start to update data, image source. The problem is that updating image source blocks the UI, and items in my grid are not showing until the image is loaded. This looks bad, and very choppy scrolling...
Here is the example what virtualization is doing in gridview:
My question is, how to set number of items that virtualization will make realized?
Use the CacheLength property of the virtualizing panel.
<ListView>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel CacheLength="0" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
If cache length is in multiples of view port size. So if 3 items are visible in the window and cache length is 1, then you'll have 3 realized items before and 3 realized items after, (3+3+3 = 9). If cache length is 2, then 6+6+3 = 15, etc.
This works with GridView as well:
<GridView>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid CacheLength="0" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
I have a string array like
string[] strngData = new string[] {"12","11","23","34"};
This string array length may be up to 4000. I need create a textbox for each string and loading all the text boxes inside a stackpanel within scrollviewer.
As the number of strings increases and number of textboxes increases resulting, the time to render the controls to UI is taking more time. for displaying 4000+ strings it is taking around 18+ secs.
Is there a way to improve the rendering time?
n place of stackpanel you may use ListView (which has inbuild deferred UI loading) with custom ItemTemplate and ItemsPanelTemplate:
<ListView Name="x" ItemsSource="..." HorizontalContentAlignment="Stretch" VirtualizingPanel.IsVirtualizing="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel HorizontalAlignment="Stretch" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=/}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
There are some key moments: you have to use VirtualizingStackPanel in ItemsPanel template in place of usual StackPanel; TextBox.Text binding path have to be equal to /(working with current item); I also test the example for ObservaleCollection so using of List may disallow you to edit items position, count, etc.
I have an ItemsControl that displays tiles in a Windows 8 manner with 2 columns and 4 rows. Each tile is clickable and triggers a command that will load the selected item in another view.
My problem starts here: my bound IList<> can contain more than 8 elements at the time, but must display no more than 8 tiles.
What I am trying to achieve is to create a different type of tile (linked to another command) that will appear (ex: using a Converter) only when my IList<> is bigger than 8. Please check the drawing below to understand my aim.
So far I could limit the number of elements retrieved in the IList<> container to 7 whenever it is bigger than 8, but adding the "special" 8th element is still a mystery for me.
I have used CompositeCollection to solve the problem this enables multiple collections and items to be displayed as a single list. more on CompositeCollection
here is a sample
xaml
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type sys:Int32}">
<Border Margin="4"
Background="LightSkyBlue">
<TextBlock Text="{Binding}" FontSize="15"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:String}">
<Border Margin="4"
Background="MediumPurple">
<TextBlock Text="{Binding}" FontWeight="SemiBold"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
note that I have defined data templates for two different types i.e. int and string which will help me render the same accordingly
public ViewModel()
{
IEnumerable<int> originalData = Enumerable.Range(1, 12);
Items = new CompositeCollection();
Items.Add(new CollectionContainer() { Collection = originalData.Take(originalData.Count() > 8 ? 7 : 8) });
if (originalData.Count() > 8)
Items.Add(originalData.Count() - 7 + " more");
}
public CompositeCollection Items { get; set; }
whole idea is to limit the number of elements in the primary collection and add an extra element to the collection of different type eg original list is int and extra is a string
so item control will render all the elements in the collection and we can control the appearance based on the data type
you may also make use of Attached Properties or Converters to simplify this or perform more sophisticated functions.
result
Something is strange with my semantic zoom. I have two sections in it:
And when I set the the ZoomOut, the grouping is okay, here is the image:
But for example if I choose the second option, semantic zoom does not navigate me to the item clicked.
Here are the most important parts of my program.
<!-- from resources -->
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="False">
...
<!-- Horizontal scrolling grid used in most view states -->
<SemanticZoom x:Name="semanticZoomControl" Grid.Row="1" >
<SemanticZoom.ZoomedInView>
<ListView x:Name="itemGridView" SelectionMode="None" IsItemClickEnabled="False"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollMode="Auto"
Margin="0,-3,0,0"
Padding="116,0,40,46"
ItemTemplateSelector="{StaticResource StartItemSelector}"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemContainerStyle="{StaticResource ListViewItemStyleFlat}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView x:Name="groupGridView" CanDragItems="False"
CanReorderItems="False" SelectionMode="None" IsItemClickEnabled="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollMode="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled"
ItemContainerStyle="{StaticResource ListViewItemStyleSimple}"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplateSelector="{StaticResource ZoomedOutSelector}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="330"
HorizontalAlignment="Left" VerticalAlignment="Top" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
What could be the reason which is happening this?
If you feel more confortable, you can download the project from SkyDrive: http://sdrv.ms/Ma0LmE
You need to set the ItemsSource of the Zoomed Out GridView in the codebehind like
groupGridView.ItemsSource = groupedItemsViewSource.View.CollectionGroups;
You'll most likely need to update your templates for that Grid, to append a "Group." before your bindings.
Your ItemTemplateSelector will also stop working at it will be passed a DependencyObject rather than your bound group. You can cast the object to ICollectionViewGroup which has a Group property that you can cast to your model object etc.
It's all a pain in the ass but I can't find a better way at the moment.
My case was somewhat different but I decided to share it here, hope someone will find it useful. In my app I had to have two different datasources for the zoomed in/out views of the semantic zoom. This, of course, broke the link between the two, so I couldn't do what #Nigel suggested above and what would work in the general case. Instead, I had to handle scrolling myself.
So, I added an event handler for the view change event:
<SemanticZoom ViewChangeStarted="OnSemanticZoomViewChangeStarted">
...
</SemanticZoom>
Then I defined it in the codebehind:
private void OnSemanticZoomViewChangeStarted(object sender, SemanticZoomViewChangedEventArgs e)
{
// only interested in zoomed out->zoomed in transitions
if (e.IsSourceZoomedInView)
{
return;
}
// get the selected group
MyItemGroup selectedGroup = e.SourceItem.Item as MyItemGroup;
// identify the selected group in the zoomed in data source (here I do it by its name, YMMV)
ObservableCollection<MyItemGroup> myItemGroups = this.DefaultViewModel["GroupedItems"] as ObservableCollection<MyItemGroup>;
MyItemGroup myGroup = myItemGroups.First<MyItemGroup>((g) => { return g.Name == selectedGroup.Name; });
// workaround: need to reset the scroll position first, otherwise ScrollIntoView won't work
SemanticZoomLocation zoomloc = new SemanticZoomLocation();
zoomloc.Bounds = new Windows.Foundation.Rect(0, 0, 1, 1);
zoomloc.Item = myItemGroups[0];
zoomedInGridView.MakeVisible(zoomloc);
// now we can scroll to the selected group in the zoomed in view
zoomedInGridView.ScrollIntoView(myGroup, ScrollIntoViewAlignment.Leading);
}
As you can see, the hack is that the gridview first needs to be rewound for the ScrollIntoView to work properly. I suppose this is just another of Microsoft's "by design" decisions... :P
I need my application to render an image only when it becomes visible to the user. I tried attaching. I've tried the following (f#):
image.IsVisibleChanged.Add(fun e ->
if image.IsVisible & mtvCapture.Capture <> null then
mtvCapture.BeginCapture()
)
But this just loads, doesn't lazy load. How does IsVisible work, will this only be true when the users scrolls the image element into view?
Also tried modifying the binding source like so:
public ImageSource ImageElementSource
{
get
{
if (Capture == null)
{
BeginCapture();
return loadingImageSource;
}
CaptureToWpfImage();
return imageElement.Source;
}
}
How can I have BeginCapture() be called only when image is scrolled into view?
Sounds like you need something that supports Virtualization. This only creates the visible elements at load time. All other elements are created lazy when they get visible.
Example using VirtualizingStackPanel for a ListBox
<ListBox Name="c_imageListBox">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImagePath}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>