So, I've been working on a legend for our map using a WPF treeview to display the groups and layers.
I've gotten it working and displaying just fine, but when I scroll the treeview with the mousewheel, the control starts flickering and the vertical scrollbar for the tree keeps resizing up and down.
The treeview layout is like this:
Group
Layer
Layer sub items
Layer
Layer sub items
Layer
Layer sub items
Group
Layer
etc...
The Group and Layer nodes are tree view items, but the layer sub items are contained within an items control. The layer sub items are not meant to be expanded/contracted, or selected and thus must remain static under the layer node, thus the items control seemed like a sensible choice.
When I scroll with the mouse wheel all the way to the top or bottom of the tree view, the scrollbar starts flicking and resizing, the last few elements of the items control flickers in and out of view (when it shouldn't be in view at all), and sometimes, the tree view will actually scroll back and forth.
If I remove the items control, everything works as it's supposed to. And when I add it back in, it messes up.
Also, if I grab the scroller thumb with the mouse and drag it, everything works fine. No jumping around.
Here's the resource XAML for the control:
<views:DynamicLegendNodeTemplateSelector x:Key="LegendTemplateSelector">
<views:DynamicLegendNodeTemplateSelector.GroupTemplate>
<HierarchicalDataTemplate DataType="{x:Type legend:IDynamicMapLegendGroup}">
<HierarchicalDataTemplate.ItemsSource>
<MultiBinding Converter="{StaticResource LegendNode}">
<Binding Path="Groups"/>
<Binding Path="LegendLayers"/>
</MultiBinding>
</HierarchicalDataTemplate.ItemsSource>
<Grid>
<StackPanel Orientation="Horizontal">
<CheckBox Focusable="False" IsChecked="{Binding IsVisible}" VerticalAlignment="Center">
<TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center"/>
</CheckBox>
</StackPanel>
</Grid>
</HierarchicalDataTemplate>
</views:DynamicLegendNodeTemplateSelector.GroupTemplate>
<views:DynamicLegendNodeTemplateSelector.LayerTemplate>
<HierarchicalDataTemplate DataType="{x:Type legend:IDynamicMapLayerLegendItem}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Focusable="False" IsChecked="{Binding IsVisible}" VerticalAlignment="Center">
<TextBlock Text="{Binding LayerCaption}" VerticalAlignment="Center"/>
</CheckBox>
<ItemsControl Grid.Row="1"
Margin="16,0,0,0"
BorderThickness="0"
Background="Transparent"
ItemsSource="{Binding LegendItems, IsAsync=True}"
HorizontalAlignment="Left"
HorizontalContentAlignment="Left"
MouseWheel="ItemControls_MouseWheel"
ScrollViewer.CanContentScroll="False"
MouseUp="ItemsControl_MouseUp">
<ItemsControl.Template>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="20" Height="20" Stretch="UniformToFill" Source="{Binding Symbol}"/>
<Label Grid.Column="1" Content="{Binding Label}"/>
</Grid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</HierarchicalDataTemplate>
</views:DynamicLegendNodeTemplateSelector.LayerTemplate>
</views:DynamicLegendNodeTemplateSelector>
<Style x:Key="TreeItemStyle" TargetType="TreeViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<EventSetter Event="MouseUp" Handler="TreeViewItem_MouseUp"></EventSetter>
</Style>
And here's the treeview:
<TreeView x:Name="LegendHierarchy"
MinWidth="200"
ItemsSource="{Binding LegendItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:DynamicArcGisRuntimeMapLegendView}}}"
ItemContainerStyle="{StaticResource TreeItemStyle}"
ItemTemplateSelector="{StaticResource LegendTemplateSelector}" />
This code is using .NET 4.5 in Visual Studio 2015 if that matters.
Regardless, does anyone know what might be causing the problem?
Thanks
So, it goes to show that an attempt at a good nights sleep is helpful.
Apparently, all I had to do was set
VirtualizingPanel.VirtualizationMode="Recycling"
On the treeview control and it starting working.
Here's the full tree view XAML:
<TreeView x:Name="LegendHierarchy"
MinWidth="200"
ItemsSource="{Binding LegendItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:DynamicArcGisRuntimeMapLegendView}}}"
ItemContainerStyle="{StaticResource TreeItemStyle}"
ItemTemplateSelector="{StaticResource LegendTemplateSelector}" UseLayoutRounding="True" ScrollViewer.CanContentScroll="True" HorizontalContentAlignment="Stretch"
VirtualizingPanel.VirtualizationMode="Recycling"/>
I hope this helps others.
Related
I have a view Model that is bind with ItemsControl. Inside that ItemsControl I have a stack panel. Now What I want is that selected item can be changed with arrow keys. Like in the attached picture I have 1st item selected and when I press down 4th item should be selected. The problem is that Items per row depends on screen resolution so On some screen there are 4 items per row and in some there are three. Secondly when I move down to the point where the page ends scroll should move down. How can I achieve this?.
Here is my xaml:
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Auto" Width="Auto" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalAlignment="Top" Margin="0,10,10,0">
<ItemsControl Name="productVariants">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel MouseLeftButtonDown="ProductVariantClicked" Tag="{Binding VariantCBX}" Margin="8" MaxHeight="160" MaxWidth="200" MinWidth="200" MinHeight="160">
<Border Name="ItemBorder" CornerRadius="6" BorderBrush="Gray" Background="White" BorderThickness="2" DockPanel.Dock="Top">
<StackPanel Margin="0">
<TextBlock Name="ProductVariantOption" Text="{Binding VariantOption}" HorizontalAlignment="Center" FontWeight="Bold" FontSize="20"/>
<Image Source="{Binding ProductVariantLogoPath}" Height="80" Width="180" />
<TextBlock Text="{Binding VendorName}" HorizontalAlignment="Center" FontSize="15" FontWeight="Bold" />
<TextBlock Text="{Binding SellingPrice}" HorizontalAlignment="Center" FontSize="20" FontWeight="Bold" Foreground="Red" />
</StackPanel>
</Border>
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True" >
<Setter TargetName="ItemBorder" Property="BorderBrush" Value="Yellow"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Like I said, I'm not aware of any clean way to do it. I might start by looking at the ActualWidth of your items (e.g. your "ItemBorder" Border element). If you know the ActualWidth of your item (plus any Horizontal Margin), and the ActualWidth of your ItemsControl, you can figure out how many elements are in one row at that moment. You would need to re-calculate this on-demand -- you could either recalculate this when a scroll is performed (because the sizes may have changed), or you could keep it up-to-date by re-calculating on layout change.
To find the ActualWidths, you have a couple of options. One is you could traverse through the visual descendants at scroll-time until you find the element that you care about. Another is you could subscribe to a"Loaded" event on and remember the actual width at that time, assuming that the widths don't change over time.
I'm having issues with scrolling through ListViews in my Windows Phone 8.1 App. Short lists scroll just fine, scrolling smoothly however as soon Virtualization kicks in the entire ListView "wobbles" to the left slightly, but noticeable enough to be annoying.
I've tried remove all the transitions to no effect as well as having items load incrementally to no success. Setting the item panel to a StackPanel (removing virtualization) fixes the issue but is not preferable.
My listviews are binding to a property in the DefaultViewModel that comes with the Basic Page Template.
What am I doing wrong and what are causing my ListViews to exhibit this behavior?
XAML:
<ListView x:Name="searchResultsList" IsItemClickEnabled="True" ItemClick="ListView_ItemClick" ItemsSource="{Binding searchResults}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,0,0,20" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Width="80" Height="80">
<Image Source="{Binding Image}" />
</Border>
<StackPanel Grid.Column="2">
<TextBlock Text="{Binding PodcastTitle}" TextWrapping="WrapWholeWords" FontSize="{StaticResource TextStyleExtraLargeFontSize}" />
<TextBlock Text="{Binding LastUpdated, Converter={StaticResource dateConverter}}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
<TextBlock Text="{Binding PodcastArtist}" TextWrapping="WrapWholeWords" Style="{ThemeResource ListViewItemContentTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So this seems to be an OS issue, as evidenced in this thread on the MS forums: http://social.msdn.microsoft.com/Forums/en-US/9a363d33-5760-4d38-9c81-84259c4edcbe/listview-jiggles-horizontally-when-large-item-about-to-scroll-in-or-out-in-windows-phone-81-preview?forum=WindowsPhonePreviewSDK&prof=required.
The issue does indeed lie in virtualization, with items that have no fixed width. Using star as the width or making the horizontal alignment stretch won't work so the only solution that takes account orientation and resolution was to bind the width to the ListView's container's ActualWidth property:
<Grid x:name="contentRoot" Margin="19,9.5,19,0">
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width={Binding ActualWidth, ElementName=contentRoot} />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
The first element in the listview is not displayed because the ActiualWidth of the Grid is 0 in the first second, when loaded the page. This is the solution, that's working for me:
<Grid x:Name="contentRoot" Margin="20">
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<Grid MinWidth="{Binding ActualWidth, ElementName=contentRoot}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
This is a really annoying bug. I can't understand why this is not fixed since years now.
Imho stretching items in a vertical scrolling listview is a very basic feature and should work 100%.
A possible workaround is also this snipped, which should also be aware of size changes :
public class StrechItemsListView : ListView
{
public StrechItemsListView()
{
SizeChanged += StrechItemsListView_SizeChanged;
}
private void StrechItemsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ItemsPanelRoot != null)
{
ItemsPanelRoot.Width = e.NewSize.Width;
}
}
}
Changing the xaml only to a custom listview type is less work and cleaner then edit every datatemplate etc. Just my 2 cents.
My Practice seems to work. At least in WP8.1.
Just set the ItemsPanelTemplate in the <ListView></ListView> Block explicitly, but not use
Style="{StaticResource ListViewStyle1}" or something else.
Sample Code:
<ListView ItemsSource="{Binding RadioList}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollMode="Auto">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="10,3,10,0">
<TextBlock Text="{Binding RadioName}" FontSize="15" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" Width="{Binding PhoneWidth}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
The HorizontalContentAlignment and Width of VirtualizingStackPanel settings are used to center the content in the ListView .You can move these settings freely.I don't know why, but it does work.
This was fixed on Windows 10 for Windows 8.1 apps
I have a situation where I want to see a queue of items related to another list. I want to arrange the parent items vertically and the child items horizontally. So Far I have the following:
Parent:
<ListBox x:Name="listResources" ItemsSource="{Binding Resources}" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:ResourceControl x:Name="resources" thisResource="{Binding Path=.}" Margin="2"></local:ResourceControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My Child items:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="3">
</StackPanel>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<StackPanel Grid.Column="0">
<TextBlock FontSize="20" x:Name="labelResourceName" Text="{Binding ResourceName}"></TextBlock>
</StackPanel>
<ListBox Grid.Column="1" x:Name="listOperations" ItemsSource="{Binding Operations}" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:OperationControl x:Name="operations" thisOperation="{Binding Path=.}" Margin="2" ></local:OperationControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
This works relatively well and gives me my child items listed horizontally as I want them to be. The problem is that I need to be able to scroll the child items individually. As it is written currently I have the ability to scroll horizontally, but all of the child items scroll together.
I have attempted to wrap the parent listbox inside a but that did not seem to do it either.
How can I get the items inside of the listbox to scroll individually instead of all together as a group?
I found the answer to this issue. The problem was that I had not defined a width for the listboxes. Once I added widths to the controls the scrolling worked properly.
I have the following XAML code and the only element available in the C# code behind are the Grid and the FlipView. How can I make the ScrollViewer, Image or the Viewbox visibile in the code?
XAML:
<Grid x:Name="gridViewPages">
<FlipView x:Name="FlipView1" Loaded="FlipView1_Loaded" Style="{StaticResource FlipViewPreviewIndicator}" Tapped="FlipView1_Tapped">
<FlipView.ItemTemplate>
<DataTemplate>
<ScrollViewer x:Name="pagesScrollViewer" ZoomMode="Enabled"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
MinZoomFactor="1.0"
MaxZoomFactor="3.0"
Margin="0"
Width="1500" DoubleTapped="PagesScrollViewer_DoubleTapped">
<Viewbox x:Name="pagesViewbox">
<Image Source="{Binding}"
Height="730"
x:Name="pageImage" Stretch="Uniform" Loaded="MainImage_Loaded"/>
</Viewbox>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
The flipview is customized and contains also a listview defined in which is not visible in the code too...:
<Page.Resources>
...
<ListView x:Name="pagesPreview" HorizontalAlignment="Center" Height="100" VerticalAlignment="Bottom" Width="Auto"
ItemsSource="{TemplateBinding ItemsSource}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"
Background="AliceBlue"
Opacity="1"
SelectionChanged="pagesPreview_SelectionChanged"
Visibility="Visible">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Source="{Binding}" Stretch="UniformToFill"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
</Page.Resources>
See the basic concept of the flipview is to have multiple pages to have a flip effect. So whatever the Data template contains is repeated multiple times. So if you want the x:Name to come up then you wont have any success.
There are two ways As per my knowledge :
VisualTreeHelper -> for a better detail of it go through this link
Get elements from data template
you can manually iterate over the flipview elements and track down the children of the flipview. Try that in debug mode you'll get a fair idea of what comes after what. Just keep track of the element at which selected index position you want modified
Thanks.
I'm currently working on a data driven editing tool that was written in WPF using MVVM. The primary display is a scrollable list of view models, some (not all) of which have inner lists of their own child view models (not scrollable). The problem is that one of the view model types is an array type that includes functionality to add a new child item and we want to make it so that if you use that, it then scrolls the overall list to that new item. Is there a reasonable way to do this using MVVM?
To give you an idea of how this UI is currently set up, this is the overall display:
<Grid ScrollViewer.VerticalScrollBarVisibility="Auto">
<Label Content="{Binding Path=DisplayName}" Height="28" HorizontalAlignment="Left" Margin="4,4,0,0" VerticalAlignment="Top" />
<ItemsControl IsTabStop="False" ItemsSource="{Binding Path=VMEntries}" Margin="12,25,12,12" ItemTemplateSelector="{StaticResource EntryTemplateSelector}" ScrollViewer.VerticalScrollBarVisibility="Auto">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer x:Name="ScrollViewer">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Grid>
and this is the data template for the array entry that we're working with:
<DataTemplate x:Key="Array">
<Grid Margin="2,7,0,0">
<Label Content="{Binding Path=DisplayName}" ToolTip="{Binding Path=Tooltip}"/>
<Button Content="Add" HorizontalAlignment="Right" VerticalAlignment="Top" Height="24" Width="24" Command="{Binding Path=AddCommand}"/>
<ItemsControl HorizontalAlignment="Stretch" IsTabStop="False" ItemsSource="{Binding Path=SubEntries, NotifyOnSourceUpdated=True}" Margin="10,24,0,0" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Name="RemoveButton" Grid.Column="0" Margin="0,5,0,0" Content="Del" HorizontalAlignment="Right" VerticalAlignment="Top" Height="24" Width="24" Command="{Binding Path=RemoveCommand}" CommandParameter="{Binding Path=.}"/>
<ContentControl Grid.Column="1" Content="{Binding Path=.}" ContentTemplateSelector="{StaticResource EntryTemplateSelector}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=RemoveHandler}" Value="{x:Null}">
<Setter Property="Visibility" TargetName="RemoveButton" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
The MVVM ideal is not adding code behind, but for some complicated things the easier way is to add it. For several cases if you want add complicated behaviors on your application and keeping the MVVM, an alternative is use the Behaviors (c# codes that allows be used and binds from XAML). Also you could define behaviors using AttachedProperties and register to the PropertyChanged event. And another alternative is to create a UserControl and add the code behind to it.
In your particular case, the inner collection must raise some event when add items to it, and then in your outer collection execute something like this list.ScrollIntoView(itemToScroll);. Hope this could give some tips to go on.