Resizable window with ScrollViewer - c#

I have a wpf c# application.
In my window I want to start with a certain height when it opens. But I also want to allow the user to be able to resize the window.
My window has a dockpanel with lastChildFill set to true. It has on the left a tree to pick a section you would like to see it's details, in the middle a seperator and on the right the section's details (you need last child fill so that this will get all the space left).
The code looks like this:
<StackPanel>
<DockPanel LastChildFill="True" Background="#344755">
<TextBlock Text="Header" Background="#344755" Foreground="White"/>
</DockPanel>
<DockPanel LastChildFill="True" Margin="5">
<telerik:RadTreeView x:Name="treeV">
<telerik:RadTreeView.Items>
<telerik:RadTreeViewItem Header="General" />
<telerik:RadTreeViewItem Header="General2" />
<telerik:RadTreeViewItem Header="General3" />
<telerik:RadTreeViewItem Header="General4" />
</telerik:RadTreeView.Items>
</telerik:RadTreeView>
<Separator/>
<Grid >
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl>
<DockPanel LastChildFill="True" Margin="5">
<Grid >
<TextBlock Text="Something"></TextBlock>
<CheckBox HorizontalAlignment="Right"/>
</Grid>
</DockPanel>
// And many more dock panels
</ItemsControl>
</ScrollViewer>
</Grid >
</DockPanel>
</StackPanel>
This is my problem: If I don't give the second dock panel height than the window automatically grows bigger and the scroll never shows. If I do give height, then the scroll does show but if the user resizes the window the dock panel doesn't resize and so doesn't the seperator.
Any ideas?

You need to Rethink your layout
for one thing you don't need to have a dock panel to fill the available area
here is a quick description of the most common layouts
StackPanel Creates a List of items in a top down or left to right
arrangement
Grid Attaches item to a grid define by sized rows and columns (uniform grid does the same but with rows and colums set to equally fill the available space)
Dockpanel attaches items to its edges using the order they are added
and the Dock property, with by default the last item set to fill the
middle
Canvas position items at fixed distances from the edges
So given controls w,x,y,z if you want w to be on the left z on the right and x & Y to share the centre
<DockPanel>
<w DockPanel.Dock="Left" />
<z DockPanel.Dock="Right" />
<UniformGrid Columns="2">
<x/>
<y/>
</UniformGrid>
</DockPanel>
if you wanted w to cover the first 1/4 of the screen and x,y & z to be stacked vertically on the remaining 3/4 and scrollable you would do
<Grid>
<Grid.ColumnDefintions>
<ColumnDefintion \>
<ColumnDefintion Width="3*" \>
<Grid.ColumnDefintions>
<w/>
<ScrollViewer>
<StackPanel>
<x/>
<x/>
<x/>
</StackPanel>
</ScrollViewer>
</Grid>
if you only have 1 item inside a layout container then that container is not required
A quick fiddle with you above code and it simplfies to
<DockPanel Background="#344755">
<TextBlock DockPanel.Dock="Top" Text="Header" Foreground="White"/>
<TreeView x:Name="treeV">
<TreeViewItem Header="General" />
<TreeViewItem Header="General2" />
<TreeViewItem Header="General3" />
<TreeViewItem Header="General4" />
</TreeView>
<Separator/>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Right">Something</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
or if you really don't have a collection to bind the items control to
<DockPanel Background="#344755">
<TextBlock DockPanel.Dock="Top" Text="Header" Foreground="White"/>
<TreeView x:Name="treeV">
<TreeViewItem Header="General" />
<TreeViewItem Header="General2" />
<TreeViewItem Header="General3" />
<TreeViewItem Header="General4" />
</TreeView>
<Separator/>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<CheckBox HorizontalAlignment="Right">Something</CheckBox>
<CheckBox HorizontalAlignment="Right">Something</CheckBox>
<CheckBox HorizontalAlignment="Right">Something</CheckBox>
<CheckBox HorizontalAlignment="Right">Something</CheckBox>
<CheckBox HorizontalAlignment="Right">Something</CheckBox>
...
</StackPanel>
</ScrollViewer>
</DockPanel>

Related

Resize WPF TextBlock with WrapWithOverflow

I have a textblock on a window which is contained in a scroll viewer. For the textlblock I have set the text wrapping to "WrapWithOverflow". In addition I bound the textblock width property to the actual width of the scroll viewer.
My desired behavior is, that I can resize my window and the textblock should wrap the content. The window should only show the scrollbars when other controls (e.g. the buttons in the example xaml get cut)
Xaml:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<DockPanel Margin="5">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="First Button" Margin="0,0,10,0"/>
<Button Content="Second Button"/>
</StackPanel>
<DockPanel>
<TextBlock VerticalAlignment="Center" IsHitTestVisible="False" TextAlignment="Center" TextWrapping="WrapWithOverflow"
Width="{Binding RelativeSource={RelativeSource AncestorType=ScrollViewer}, Path=ActualWidth}" MaxWidth="260">
Just a small line<LineBreak />
This is the long line which will wrap during resize</TextBlock>
</DockPanel>
</DockPanel>
</ScrollViewer>
But I see the scroll bars even befor the buttons get cut. I guess this is because of the margin in the dockpanel which is required in my case.
I guess this is because of the margin in the dockpanel
Correct.
...which is required in my case.
Why? You should move the Margin to the StackPanel:
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="5">
...
...or the buttons:
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="First Button" Margin="5,5,5,5"/>
<Button Content="Second Button" Margin="5,5,0,5"/>
</StackPanel>
...
This is necessary as the margins are included in the ActualWidth that you bind to.

Making a Scrollviewer fill the available space in a DockPanel

I somewhat of a WPF noob and am using a DockPanel to display the text content of an email in a TextBox in a ScrollViewer. The panel has a button bar and area for the email headers at the top, a button bar at the bottom, a panel at the right and the email itself should fill the remaining space dynamically:
[Banner at top, below which is a button bar and box with email headers. At the bottom is another full width button bar. The space between them is divided into a fixed-width panel at the right and a large text box for the email contents.]
http://rowlandsoftware.com/Screenshots/LongEmail.png
(The DockPanel sits inside a grid that provides the bit at the very top and is used when the email is not visible.)
The problem is that if the email is too short or too narrow, the textbox fails to fill the remaining width. In this screenshot, you can see some of the underlying stuff between the box and the panel:
[Between the text box and the panel is a column that is not filled. Part of what lies under it can be seen.]
http://rowlandsoftware.com/Screenshots/TooNarrow.png
The XAML is:
<DockPanel x:Name="EmailCanvas" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Visibility="Collapsed" Height="Auto">
<DockPanel x:Name="topButtonBar" DockPanel.Dock="Top" Height="50" Background="White">
<Button x:Name="btnReturn" DockPanel.Dock="Left" Content="Return to List" Click="btnReturn_Click" />
<Image Width="15" Source="Images/transparent.png" />
<Button x:Name="btnTextToggle" Content="Plain text" Click="btnTextToggle_Click" />
<Button x:Name="btnZoomOut" Content="Zoom Out" />
<Button x:Name="btnZoomIn" Content="Zoom In" />
<Image Width="150" DockPanel.Dock="Right" Source="Images/transparent.png" />
<Button x:Name="btnPrint" DockPanel.Dock="Right" Content="Print" Width="100"/>
<Image DockPanel.Dock="Right" Source="Images/transparent.png" />
</DockPanel>
<StackPanel x:Name="EmailHeaders" Orientation="Horizontal" DockPanel.Dock="Top" Width="Auto" Background="Linen">
<StackPanel Orientation="Vertical">
<TextBlock Text="Subject:" FontSize="16" />
<TextBlock Text="Time Sent:" FontSize="16"/>
<TextBlock Text="Other Recipients: " FontSize="16"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock x:Name="labSubject" Text="Subject:" FontSize="16" />
<TextBlock x:Name="labDateTime" Text="Sent:" FontSize="16"/>
<TextBlock x:Name="labSpare" Text="Other Recipients:" FontSize="14"/>
</StackPanel>
</StackPanel>
<DockPanel x:Name="bottomButtonBar" DockPanel.Dock="Bottom" Height="80" >
<Image Width="150" DockPanel.Dock="Left" Source="Images/transparent.png" />
<Button x:Name="btnDelete" DockPanel.Dock="Left" Content="Delete" />
<Image Width="150" Source="Images/transparent.png" />
<Button x:Name="btnSave" Content="Save" />
<Image Width="150" Source="Images/transparent.png" />
<Button x:Name="btnReply" Content="Reply" />
<Image Width="150" DockPanel.Dock="Right" Source="Images/transparent.png" />
</DockPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Vertical" Background="White" Width="150">
<Button x:Name="btnAttachments" Content="Attachment"/>
</StackPanel>
<ScrollViewer x:Name="eTextViewer" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<TextBox x:Name="eText" Background="White" HorizontalAlignment="Stretch" TextWrapping="Wrap" FontFamily="Verdana" FontSize="18" IsReadOnly="True" />
</ScrollViewer>
<!--Image Width="Auto" Height="Auto" DockPanel.Dock="Bottom" Source="Images/white.png" /-->
<WebBrowser x:Name="eHTML" Height="Auto" DockPanel.Dock="Top" Visibility="Collapsed" />
</DockPanel>
The box is extended only to fill the available height, not the width. The documentation for the DockPanel.LastChildFill Property says,
If you set the LastChildFill property to true, which is the default setting, the
last child element of a DockPanel always fills the remaining space, regardless
of any other dock value that you set on the last child element.
msdn.microsoft.com/en-us/library/system.windows.controls.dockpanel.lastchildfill%28v=vs.110%29.aspx
That does not appear to be the case: in my program it is only filling height, not width. LastFillChild=true is the default, but explicitly setting it to True or False doesn't make a blind bit of difference.
Interestingly, if you set DockPanel.Dock="Top" on the ScrollViewer, it fills the available width but not the height, leaving some of the underlying stuff showing between the text box and the bottom button bar. Again this is contrary to the documentation which says that the dock value set on the last child element is ignored.
Setting HorizontalAlignment="Stretch" to both the ScrollViewer and the TextBox and HorizontalContentAlignment="Stretch" to the ScrollViewer makes not a jot of difference.
So my question is, how can I ensure that the textbox fills the available space in the DockPanel even when the contents of the email are too short?
UPDATE: I have just noticed that if I remove the WebBrowser that is set initially to Visibility="Collapsed", then it works fine. I guess that its presence fools the DockPanel into regarding it as the LastChild even though it is Collapsed.
However, I need to toggle between displaying the email in the web browser and the text box, so removing it isn't an option. Both need to be treated as last child when they are visible. Any ideas?
UPDATE 2: So I wrap the scrollviewer and the web browser in another container, e.g. grid, which is the Last Child. Then I can toggle between the the two but they both fill the space.
Thanks guys. I wouldn't have got there without humiliating myself in public ;)
DockPanel will work, but beware of Collapsed items. Although the documentation says that a collapsed item has no effect on layout, with DockPanel it does if it is the LastChild.
The solution in my case was to add another container as the last child, and place the two items that I wanted to toggle between in that container. Then both will fill the remaining space in the panel.

ScrollView not working in Windows 10

I have a ViewModel with about 200 contacts and I am binding it to a ListView
<Grid>
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ListView x:Name="ContactListing" ItemsSource="{Binding ContactListing}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=DisplayName}" />
<Image Source="{Binding Thumbnail,Converter={StaticResource ResourceKey=contactConv}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Grid>
Would be great if someone could explain the details behind the ScrollViewers visibility
A ListView control already contains a ScrollViewer and implements scrolling based on the amount of items, there is no need to wrap this control in another ScrollViewer. You can double check this in the default ControlTemplate of ListView at line 6219 in the generic.xaml file containing all Windows 10 styles:
C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10240.0\Generic\generic.xaml
So you simply have to remove the ScrollViewer from your XAML fragment.
<Grid>
<ListView x:Name="ContactListing" ItemsSource="{Binding ContactListing}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=DisplayName}" />
<Image Source="{Binding Thumbnail,Converter={StaticResource ResourceKey=contactConv}}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Note: As you're using a ListView, I suppose you want all your items stacked above each other. If you want multiple columns to show your items, use a GridView instead.

Problems with flyout over itemscontrol in Windows Phone 8.1

I've got the following piece of XAML code:
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">
<ScrollViewer>
<ItemsControl ItemsSource="{Binding History}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Tapped="HistoryItemTapped">
<FlyoutBase.AttachedFlyout>
<Flyout>
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer>
<TextBlock Foreground="{ThemeResource PhoneMidBrush}" FontSize="{ThemeResource TextStyleExtraLargeFontSize}" HorizontalAlignment="Left" Text="{Binding Expression}" />
</ScrollViewer>
<ScrollViewer>
<TextBlock FontSize="{ThemeResource TextStyleExtraLargePlusFontSize}" HorizontalAlignment="Right" Text="{Binding Result}" />
</ScrollViewer>
</StackPanel>
</Flyout>
</FlyoutBase.AttachedFlyout>
<TextBlock Foreground="{ThemeResource PhoneMidBrush}" FontSize="{ThemeResource TextStyleExtraLargeFontSize}" HorizontalAlignment="Left" Text="{Binding Expression}"
TextTrimming="CharacterEllipsis"/>
<TextBlock FontSize="{ThemeResource TextStyleExtraLargePlusFontSize}" HorizontalAlignment="Right" Text="{Binding Result}"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
There are two problems I can't solve:
The flyout is shown upon tapping on one of items:
private void HistoryItemTapped(object sender, TappedRoutedEventArgs e)
{
FlyoutBase.GetAttachedFlyout((FrameworkElement)sender).ShowAt((FrameworkElement)sender);
}
However, no matter how I set up the flyout it always shows on the top of the screen, not over the tapped item. Why?
The flyout contains two TextBlocks on separate ScrollViewers. The text on one of them exceeds width of the flyout, bt the scrollviewer does not appear to be working (I cannot scroll horizontally the textblock). Why is that?
So let's knock these out real quick. Your #1 would be expected result, it's just going off the default FlyoutPlacementMode enumeration wherein generally your flyout is just meant to appear over the top in one of 5 spots, Top, Bottom, Left, Right, or Full(Center)
How to fix it, ditch the flyout control and just animate your own panel to do the same thing on a tap event or whatever, not hard at all.
Your #2, if I remember right the default on ScrollViewerfor something like the HorizontalScrollBarVisibility is False by default. So just add HorizontalScrollBarVisibility="Auto" to it.
Hope this helps, cheers

Why does the button size increases as the listbox size gets populated with data?

I'm a newbie to c#. I have designed a windows program with button and listbox. The listbox populates the log as the script runs the application. However, as the listbox gets populated with log, the button and label increases which makes the stack panel very ugly?
Any help/advice would be appreciated. Below is my xaml code.
<Grid>
<Border BorderBrush="Black" BorderThickness="2" Margin="0,11,0,5" CornerRadius="5" Background="White">
<StackPanel Orientation="Horizontal" Margin="7,10,7,2">
<StackPanel Orientation="Vertical">
<TextBlock Text="City" Margin="5" HorizontalAlignment="Center"></TextBlock>
<ComboBox Name="City" SelectedValue="Chicago" SelectedValuePath="Name">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name,Mode=OneWay}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<Button Name="btnEnter" Content="Enter" Width="200" Margin="4" Padding="6" Click="btnEnter_Click"></Button>
<Button Name="btnViewLog" Content="View Log" Width="100" Margin="4" Padding="6" Click="btnLog_Click"></Button>
<ListBox Name="lbLog" Visibility="Collapsed">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Description}" IsReadOnly="True" BorderThickness="0" Background="Transparent"></TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Thanks.
Ash
Without your Xaml, it is hard to diagnose the problem, so please try to post it with any further questions. It SOUNDS like it is a horizontal StackPanel, and the elements are all next to each other.
If the orientation of your StackPanel is Vertical, set the HorizontalAlignment on the button to Left, Right, or Center. If the StackPanel orientation is Horizontal, set the VerticalAlignment on the button to Top, Bottom, or Center.
I'm pretty certain both settings are "Stretch" by default, which means they will try to fill all the space that is available, rather than sizing to their content.

Categories