scrollviewer the property 'content' is set more than once - wpf

Here my code:
<Grid Style="{StaticResource LayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer
Grid.RowSpan="2" Style="{StaticResource HorizontalScrollViewerStyle}"
HorizontalScrollBarVisibility="Visible" >
<!-- The elements you want to be horizontally scrollable goes here -->
<!-- Horizontal scrolling grid used in most view states -->
<GridView
Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Padding="100,136,116,46"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"/>
<Button Hello /> <!-- From here come the Error -->
</ScrollViewer>
</Grid>
When I add any thing after my GridView, it give me that error (the property content is set more than once).

The answer is right there in the error message. A ScrollViewer can only have one child (you have two, a GridView and a Button). If you want to add multiple things, you will have to wrap them in a panel that allows multiple children (e.g. Grid).

Related

Listbox content not dynamically re-sizing when Listbox size changes

I've seen quite a few people asking this question and i feel like my question is slightly different. I have a listbox that holds a series of identical custom made user controls. These controls are added to the listbox at runtime. Now i currently have my listbox items resizing themselves properly upon first creation and insertion into the control.
Here is the strange part. If I resize the listbox the controls that have been visible previously are not resized to the new listbox width. In other words if i add 12 controls and the box only shows 4 (the rest are hidden by a scrollbar) then if i resize the box the first 4 controls will still be the original width and if i scroll then the other 8 will be the correct size. Also if i manipulate the list items in any way they resize themselves to the proper width automatically. SEE EDIT2
I've tried attaching to the sizeChanged event and issuing the following on both the listbox and the items but it has had no effect. I think i need to find some way of resetting the layout information for the listbox items but i can't find the command.
item.InvalidateArrange();
item.InvalidateMeasure();
Layers.UpdateLayout();
item.UpdateLayout();
I think this has something to do with the items i'm adding because even if i detach the items from the lisbox and then attach them they remain the wrong width.
Here is my listbox code:
<ListBox x:Name="Layers" VerticalContentAlignment="Top" Margin="0,17,0,0" BorderThickness="0,1,0,0" HorizontalContentAlignment="Stretch" SizeChanged="Layers_SizeChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Here is the code for my items
<UserControl x:Class="ListOverlayItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:Data"
HorizontalContentAlignment="Stretch">
<UserControl.Resources>
<data:Translator x:Key="translatorString" />
</UserControl.Resources>
<Border BorderBrush="Silver" BorderThickness="0,0,0,0" Name="border1" HorizontalAlignment="Stretch">
<Grid x:Name="layout" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<!-- Column 0 -->
<ScrollBar Name="arrangeIcon" FlowDirection="LeftToRight" Maximum="10" SmallChange="1" Value="5" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2"/>
<!-- Column 1 -->
<Slider Name="OverlayOpacity" Height="25" Grid.Column="1" Margin="6,0,0,0" Grid.Row="1" Maximum="1" LargeChange="0.1" ValueChanged="OverlayOpacity_ValueChanged"/>
<TextBlock x:Name="OverlayName" Text="{Binding Path=LocationName}" Foreground="#FFF08D2D" Margin="10,2,0,0" Grid.Column="1" FontSize="12" Height="18" VerticalAlignment="Top" />
<!-- Column 3 -->
<Button Name="SettingsButton" Grid.Column="3" Content="{Binding TRK_OV_Settings, Source={StaticResource translatorString}}" VerticalAlignment="Center" Click="SettingsButton_Click" />
<CheckBox x:Name="OverlayEnabled" FlowDirection="LeftToRight" Grid.Column="2" DataContext="{Binding}" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.RowSpan="2" Checked="OverlayEnabled_Checked" Unchecked="OverlayEnabled_Unchecked" />
<TextBlock Name="percentage" Text="100%" FontSize="9" TextAlignment="Right" Grid.Column="2" Grid.Row="1" VerticalAlignment="Center" Margin="2,6,26,6" MinWidth="30"/>
</Grid>
</Border>
Again it seems like my UserControl is correctly able to scale itself its just not getting the command to do so when the parent container is resized.
EDIT: Whoops, removed the WPF tag because it was incorrectly added. I think i've got it down to slider being the culprit. If i set the slider to a fixed size instead of 'stretch' then the item correctly scales just fine. So the question now is how do i force the slider to resize itself.
EDIT2: I know what is causing the issue but i don't know how to solve it. What is happening is the slider that i have in my user control will NOT resize along with the rest of the control unless i change the value of the slider during the resize. The instant i change its value even a fraction it resizes itself automatically. How can i force it to resize?
I determined that the slider was causing the issue and i tried many ways to force the slider to re-draw when it needed to but i was unsuccessful. My final solution was to remove the slider entirely. Works fine now.

How to add a buttom that is always visible after a scrollviewer?

I have a ScrollViewer which includes a lot of content (datagrids, stackpanels, textboxes, labels, etc...), and outside of it I want to add a button (PRINT), and it is important that the button is not part of the ScrollViewer. My goal is that the top 90% of my screen is the scrollviewer and the bottom 10% is a "frozen panel" that always shows the PRINT button, and this should remain true when maximized and minimized.
After having a lo of problems with 'the property content is set more then once' I realized I need to add both my ScrollViewer and the Button inside another container, so far the only one that seems to work is GRID - but honestly after you read this if you have anything else to recommend I am open to suggestions, I only used GRID because it seemed to almost give me what I wanted.
This is my code right now:
[Code]
<Window DataContext="{Binding PrintView, Source={StaticResource Locator}}" Width="900">
<Grid Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer Name="PrintView" Grid.Row="0" Height="Auto">
<StackPanel>
... a LOT of stuff ...
</StackPanel>
</ScrollViewer>
<Button Content="Print"
Margin="0,20,0,20"
Height="50"
Width="150"
FontSize="24"
FontWeight="Bold"
Grid.Row="1"
/>
</Grid>
</Window>
[Code]
When done like this my ScrollViewer doesn't have a Scrollbar so I see the first page but I cannot scroll down, also there is no PRINT button seen
One interesting test was to change the following:
<ScrollViewer Name="Apercu" Grid.Row="0" Height="600">
Now I see my scrollbar again (and I can scroll) and my PRINT button is beneth it and always visible (this is almost perfect) but when I maximumize my window the ScrollViewer stays 600 of height and as such well it doesn't acctually maximize (everything below the PRINT button is just white).
Any ideas? Is there another way I could specify my HEIGHTS or is there a different control I should be using (not GRID)?
Thanks,
Found it ...
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
And remove height from ScollViewer

DevExpress LayoutSplitter and DockLayoutManager drag and drop issue

I am having some some problems using the LayoutSplitter and DockLayoutManger from DevExpress.
dxdo:DockLayoutManager>
<dxdo:DockLayoutManager.LayoutRoot>
<dxdo:LayoutGroup Orientation="Vertical" Height="*">
<dxdo:LayoutGroup Orientation="Vertical" Height="*">
<dxdo:DocumentPanel>
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{Binding SomeViewModel}" ContentTemplateSelector="{StaticResource templateSelector}"/>
</Grid>
</dxdo:DocumentPanel>
</dxdo:LayoutGroup>
<dxdo:LayoutGroup Height="Auto" Orientation="Vertical">
<dxdo:LayoutSplitter/>
</dxdo:LayoutGroup>
<dxdo:LayoutGroup>
<dxdo:LayoutPanel Height="Auto" AllowDock="True">
<ItemsControl ItemsSource="{Binding SomeViewModel.SomeCollection}" ItemTemplateSelector="{StaticResource templateSelector}">
</ItemsControl>
</dxdo:LayoutPanel>
</dxdo:LayoutGroup>
</dxdo:LayoutGroup>
</dxdo:DockLayoutManager.LayoutRoot>
</dxdo:DockLayoutManager>
The LayoutSplitter isn't moving up and down. It just remains static.
With the DockLayoutManager, the 3rd nested LayoutGroup, when I remove a component from it, I can't drop the component back in. A hint would be great thanks!
The LayoutSplitter control is needed to split layout groups or layout control items. It can't be used with LayoutPanels or DocumentPanels. Moreover it is not needed to use splitters with dock items because these items resizing are automatically provided by the DockLayoutManager. You should also use the DocumentPanel as child item for the DocumentGroup instead of placing that document directly to the LayoutGroup.Items collection. Or use the LayoutPanel instead of the DocumentPanel.
So, you markup should looks like this:
<dxdo:DockLayoutManager>
<dxdo:LayoutGroup Orientation="Vertical" >
<dxdo:DocumentGroup>
<dxdo:DocumentPanel Caption="Document">
<!-- Document content -->
</dxdo:DocumentPanel>
</dxdo:DocumentGroup>
<dxdo:LayoutGroup>
<dxdo:LayoutPanel Caption="Panel">
<!-- Panel content -->
</dxdo:LayoutPanel>
</dxdo:LayoutGroup>
</dxdo:LayoutGroup>
</dxdo:DockLayoutManager>
You can read more in these articles:
Dock Layout Manager Fundamentals
Layout Groups
Dock Items
Layout Items
Document Groups and Panels

Expander, Grid & ListBox = No Virtualization

I have a ListBox, inside a Grid, inside an Expander. The ListBox is bound to an IList.
When I expand the Expander control for the first time, the ListBox processes all of the items in the IList (which can be thousands) instead of only processing the items that would be visible on the screen.
If however I fix the height of the ListBox control, it behaves as expected and only accesses those items in the IList that will be visible.
Effectively, the Virtualization is not working, though I believe that this is more related to the ListBox not being able to determine a height when the content items are being prepared.
The XAML is basically as follows (some stuff removed for simplification)...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Expander ExpandDirection="Right"
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Header="Documents"
IsExpanded="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
</Grid>
<ListBox Name="listBox"
Grid.Row="1"
ItemsSource="{Binding Path=Items}"
SelectedIndex="{Binding Path=SelectedIndex}"
SelectedItem="{Binding Path=SelectedItem}"
SelectionMode="Single"
Width="250">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Style="{StaticResource prompt}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}{1:00000}">
<Binding Path="..."
FallbackValue="0" />
<Binding Path="..." />
</MultiBinding>
</TextBlock.Text></TextBlock>
<TextBlock Grid.Column="1"
Style="{StaticResource prompt}">
<TextBlock.Text>
<Binding Path="ItemCount"
StringFormat="{}{0} Items"
FallbackValue="" />
</TextBlock.Text></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Expander>
<v:DocumentView x:Name="documentView"
Grid.Column="1"
Grid.Row="0"
DocumentID="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:BatchView}}, Path=ViewModel.SelectedItem.ID}"
IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:BatchView}}, Path=IsActive}" />
<StackPanel Grid.Column="1"
Grid.Row="1"
Style="{StaticResource buttonStackStyle}">
<Button Command="{Binding Path=PreviousCommand}"
Style="{StaticResource previousButtonStyle}" />
<Button Command="{Binding Path=NextCommand}"
Style="{StaticResource nextButtonStyle}" />
</StackPanel>
</Grid>
Can anybody suggest how I might set the Height of the ListBox to the ActualHeight of the Grid.Row parent? Alternatively, can anybody provide a better solution?
Thanks.
Short version: remove Grid.RowSpan from the expander.
Long version:
Background: (in really broad strokes)
When you define the height of RowDefinition three things can happen, depending what type of units you end up using:
Pixel - any UIElement that is placed in that row will have the defined row height passed to the Measure and Arrange methods of the element.
Auto - the grid will pass infinity as the height for Measure and then element.DesiredSize.Height for Arrange.
Star - the grid will consider the heights of all rows with units of pixels and auto; calculate the height that is left from its available height and divide it by the "total of stars" that were defined for all the rows - this is the height of one star; then, each row height is assigned depending on the multiplier for its star definition; this height is passed to the Measure and Arrange methods.
Same logic is applied to column definitions only regarding width instead of height.
So, star definition is "stopping" the element, pixel definition is also "stopping" but it can be outside of the rendered view and auto definition is "letting" the element to be what ever size it wants to be.
All this logic is recursive so you need to think in two directions (explanation below).
In your case
In one direction. The ListBox is in a star row so it'll be stopped. The parent grid is also stopped (since the template for expander uses DockPanel that is also a "stopping panel"). The expander is defined to begin in a star row but it spans to an auto row - this means that it will be allowed to grow in height 'till infinity. Oops...time to reverse.
Now the reverse direction. The expander is not stopped, the child grid is not stopped (since the grid assumes it has infinite height available), thus the list box is not stopped, the ScrollViewer in the template of list box is not stopped so it's ViewportHeight is infinite, for the VirtualizingStackPanel that arranges the items (and is a child of the scroll viewer) this means all items are in the view == render all elements.
For a WPF window with default template, you can always assume the window is stopping its child element. So if removing the row span definition has not resolved the issue, continue traversing up until you find another element that is not stopping its child height and change its definitions or change the panel to stop the height from growing to infinity (scroll viewers are notorious for creating these behaviours, especially the ones that are hidden in templates).

WPF buttons same/recommended width

Suppose you have a window with multiple buttons such as Ok/Cancel or Yes/No/Cancel. All the buttons need to be the same width. Obviously this could be done by just guessing a number and hardwiring all of them to that number.
Is there a better way to do it, one that would take into account preferred/recommended sizes (just how wide should an Ok button be anyway? This is not a rhetorical question, I actually don't know the answer!), what's needed by the text of the longest caption, what happens if the font size is increased etc?
Another, perhaps simpler, way to do this is to use the SharedSizeGroup property on the ColumnDefinition and RowDefinition classes.
Columns (and Rows) in a WPF Grid can automatically resize to fit their contents - when SharedSizeGroup is used, columns with the same group name share their resizing logic.
The Xaml would look something like this ...
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
<ColumnDefinition SharedSizeGroup="Buttons" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Ok"
Margin="4" />
<Button Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Cancel"
Margin="4" />
<Button Grid.Column="3"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Content="Long Button Caption"
Margin="4" />
</Grid>
There are several ways to do this:
1) Use a Grid for layout. Each Button gets its own Column, which is Star-sized. That way, all columns are the same size:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Yes</Button>
<Button Grid.Column="1">No</Button>
<Button Grid.Column="2">Cancel</Button>
</Grid>
2) You can have one item as "master size" and bind the width of all others to this item's width.
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton" Width="100">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="Width"/>
</Button.Width>
No
</Button>
</StackPanel>
EDIT: In actual code, you probably will have Width="Auto". Since the other widths are based on the "master width", the button with the widest width (widest text) should be chosen.
Use a "master" control, like in Daniel's answer, but bind to the "ActualWidth" attribute instead of "Width":
<StackPanel Orientation="Horizontal">
<Button Name="MasterButton">Yes</Button>
<Button>
<Button.Width>
<Binding ElementName="MasterButton" Path="ActualWidth"/>
</Button.Width>
No
</Button>
</StackPanel>
This way, the value is taken from the master control at run time, after minimum and maximum width and all other layout calculations have been taken into account. Binding to "Width" binds to whatever you happen to put into the attribute at compile time, which may not be the width that is really used.
Also, the binding can be written shorter like
<Button Width="{Binding ElementName=MasterButton, Path=ActualWidth}"/>
According to the MS User Experience Interaction Guidelines for Windows 7 and Windows Vista (p61), standard dimensions for command buttons are 50x14 DLU actual size (75x23 pixels). The guidelines further suggest you "try to work with [these] default widths and heights." Obviously, if you need more width to fit a clear label, then take more width.
These answers are great if you have a fixed number or fixed layout for the buttons, but if like me there is a dynamic number of buttons coming from a binding and contained in a ItemsControl then this is not feasible. But there is a simple way and it still involves used the sharedsize property of Grid.
DataTemplate:
<DataTemplate x:Key="ODIF.Mapping">
<Button HorizontalContentAlignment="Left" Background="#FFEEEEEE" BorderBrush="#FFBDBDBD">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="PluginButtonsWidth"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsIconHeight"/>
<RowDefinition Height="Auto" SharedSizeGroup="PluginButtonsNameHeight"/>
</Grid.RowDefinitions>
<Image Width="32" Height="32" Source="{Binding PluginIcon}" RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Grid.Row="1" Text="{Binding PluginName}"/>
</Grid>
</Button>
</DataTemplate>
Parent container:
<ItemsControl ItemsSource="{Binding MappingPlugins, ElementName=page}" ItemTemplate="{StaticResource ODIF.Mapping}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Essentially the button's content can itself be a Gird which then you can place your labels and icons as needed in, but even though the buttons do not reside in the same grid (they each are their own) the grid can still share it size so long as you set the root container's (ItemsControl) property of Grid.IsSharedSizeScope to True.
This will force the content grid of each button to be the same exact size based on the largest one while not having to have the Buttons themselves in a predefined grid.
In the most general case, you want to create a
Style in your section, then apply this style as desired. Now when you change the style, all buttons change.
Or you can change the Content of the button so that it autosizes to the text.

Resources