Reputation: 11
I need a way to make a button overlap a textblock when resizing horizontally to a smaller size.This works fine for the first example, but when i put the same code in a scrollviewer, the horizontal scrollbar kicks in from the moment the textblock and button meet. How can i avoid this so that the button overlaps the same way as without the scrollviewer? Ideally scrolling should start from the moment that the textblock reaches its minimum width.
<StackPanel>
<!--works-->
<Grid Height="30" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf" HorizontalAlignment="Left"></TextBlock>
<Button Grid.Column="1" MinWidth="20">te</Button>
</Grid>
<!--doesn't work-->
<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
<Grid Height="30" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf" HorizontalAlignment="Left"></TextBlock>
<Button Grid.Column="1" MinWidth="20">te</Button>
</Grid>
</ScrollViewer>
</StackPanel>
Update In fact i can reduce the problem to a textblock in a scrollviewer. I would like that it would only become possible for the horizontal scrollbar to scroll when the min width of the textblock is reached.
<ScrollViewer HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Disabled">
<TextBlock Background="Yellow"
MinWidth="50">reallylooooooooooooooooooooong
text</TextBlock>
</ScrollViewer>
Update I found a solution here https://social.msdn.microsoft.com/Forums/en-US/26e03045-4b57-4960-8d33-d0295855f4e3/minwidth-with-scrollviewer-to-then-take-over?forum=wpf The converter substracts the width of the button from the actual width of the grid.Now it starts scrolling the moment that the minimum width of the textblock is reached.
I don't know if a custom scrollviewer for any content could be smart enough to behave the same way?
<ScrollViewer HorizontalScrollBarVisibility="Visible"
VerticalScrollBarVisibility="Disabled"
>
<Grid Height="30" x:Name="grid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"
HorizontalAlignment="Left"
MinWidth="150"
Width="{Binding ElementName=grid, Path=ActualWidth,
Converter={x:Static conv:WidthConverter.Instance}}"
></TextBlock>
<Button Grid.Column="1" MinWidth="20">te</Button>
</Grid>
</ScrollViewer>
Upvotes: 1
Views: 451
Reputation: 28968
Wrapping the complete Grid
into a ScrollViewer
results in the ScrollViewer
to make all its contained elements visible i.e. scrollable. In your case, it will make the TextBlock
and the Button
scrollable.
To allow the Button
to clip the TextBlock
i.e. the TextBlock.Text
to virtually exceed the actual width of the TextBlock
, you can wrap the TextBlock
alone into a ScrollViewer
.
This will result in the TextBlock
being able to hide or clip text that exceeds the available rendering width, while the user can still view it entirely by scrolling.
The following example will make the scroll bars appear as soon text has become hidden from the user, allowing him to still read all the text:
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf" />
</ScrollViewer>
<Button Grid.Column="1"
HorizontalAlignment="Right"
MinWidth="20">te</Button>
</Grid>
Your requirement sounds like you want to hide certain amount of text from the user before he is allowed to scroll through it, enabling him to read all the text.
From a user experience point of view, I highly recommend against such a behavior.
However, to achieve this, simply modify the above example to add a negative right Padding
to the ScrollViewer
:
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled"
Padding="0,0,-150,0>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf" />
</ScrollViewer>
<Button Grid.Column="1"
HorizontalAlignment="Right"
MinWidth="20">te</Button>
</Grid>
The above solution will clip the exceeding text and will show a ScrollViewer
after a certain threshold (controlled via ScrollViewer.Padding
), but will not allow to scroll the entire hidden text (only the portion from the moment on the ScrollViewer
became visible).
To enable scrolling of the entire hidden text, you can define a simple Style
that resets the Padding
to 0
(in fact, it will toggle the Padding
), once the scroll bars are visible:
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition /n>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<ScrollViewer.Style>
<!-- Optionally, move this Style to a ResourceDictionary (e.g. App.xaml),
to make it apply to all ScrollViewer instance within the scope -->
<Style TargetType="ScrollViewer">
<Setter Property="Padding"
Value="0,0,-150,0" />
<Style.Triggers>
<Trigger Property="ComputedHorizontalScrollBarVisibility"
Value="Visible">
<Setter Property="Padding"
Value="0" />
</Trigger>
</Style.Triggers>
</Style>
</ScrollViewer.Style>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf" />
</ScrollViewer>
<Button Grid.Column="1"
MinWidth="20">te</Button>
</Grid>
Upvotes: 1
Reputation: 16119
You'll need to re-template the ScrollViewer control. Start by extracting the template by moving the cursor over the ScrollViewer control and from the Properties pane select Miscellaneous -> Template -> Convert To New Resource (from the drop-down). The ScrollContentPresenter in the new template that appears is what draws everything inside the "client area":
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanHorizontallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" CanVerticallyScroll="False" Grid.Column="0" Content="{TemplateBinding Content}" CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
So move your 2-column grid from outside the template to that point there, and move the ScrollContentPresenter to it's left column and your button to it's right. You should also set the button's column width to "Auto" so that it gets all the space it needs. Final ControlTemplate should look like this:
<ControlTemplate x:Key="ScrollViewerTemplate1" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanHorizontallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" CanVerticallyScroll="False" Grid.Column="0" Content="{TemplateBinding Content}" CanContentScroll="{TemplateBinding CanContentScroll}" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<Button Grid.Column="1" MinWidth="20">te</Button>
</Grid>
</Grid>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
And your XAML should look like this:
<StackPanel>
<ScrollViewer Template="{DynamicResource ScrollViewerTemplate1}" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Disabled">
<Grid Height="30" >
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf" HorizontalAlignment="Left"></TextBlock>
</Grid>
</ScrollViewer>
</StackPanel>
Result:
Upvotes: 1
Reputation: 1175
If you want to show multiple elements in a panel, governed by a scrollviewer you may use an ItemsControl
inside the ScrollViewer
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" >
<ItemsControl >
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"
HorizontalAlignment="Left"></TextBlock>
<Button Grid.Column="1"
MinWidth="20">te</Button>
</Grid>
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="testsdfqqsdfqsdqsdfqsdfqsdfqsdfqsdfsdqsdf"
HorizontalAlignment="Left"></TextBlock>
<Button Grid.Column="1"
MinWidth="20">te</Button>
</Grid>
</ItemsControl>
</ScrollViewer>
Upvotes: 1