I have a TreeView control, and its template is set like this:
<TreeView.Template>
<ControlTemplate>
<Border BorderBrush="DeepSkyBlue" BorderThickness="1">
<ScrollViewer CanContentScroll="False"
Height="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=ActualHeight}">
<ItemsPresenter />
</ScrollViewer>
</Border>
</ControlTemplate>
</TreeView.Template>
The TreeView is filled dynamically:
There are two levels of the TreeViewItem nodes:
And the thumb is moving while in sub items are present only items below:
The Thumb moving:
But when I'm trying to add these items below:
...the thumb is not scrolling, but the ScrollBar area is active:
The template of elements is completely the same, but they differ only in the textblock and the image.
I'm stuck on this and can't understand what's wrong.
Which property of the ScrollViewer may be changing, which leads to this behavior. Here is the link to the full definition of the TreeView: the treeview definition on github.
Related
I need to create automation system-level test for control.
Control is set by style and have ListView with separate ListViewItem template.
And my goal is to get text from the header inside this ListViewItem.
Here is what I have in XAML (code simplified)
<Style>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Grid>
<ScrollViewer x:Name="ScrollViewer">
<ScrollViewer.TopHeader>
<StackPanel>
<ListView ItemsSource="{TemplateBinding SomeSource}"
ItemTemplate="{StaticResource MyTemplate}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</StackPanel>
</ScrollViewer.TopHeader>
</ScrollViewer>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And template for ListViewItem is something like:
<DataTemplate>
<Grid>
<Grid>
<TextBlock x:Name="TextName"
AutomationProperties.AutomationId="{Binding SomeId}"
Text="{x:Bind SomeText}"/>
</Grid>
</Grid>
</DataTemplate>
Of course ListViewItems are generating automatically in runtime. They are binded to according property and got needed template.
But in test I can't reach my TextBlock anyhow (ById, FindElementById and so on).
I can find elements by firstly finding ListView by class (FindElementByClassName), then ListViewItems by class, then TextBlock by class, but I think it is not the right way. Because in future structure of control could be changed and it will be harder to support tests.
Control have AutomationPeer as the Grids too.
So do you have any ideas why I can't get my simple TextBlock or even ListViewItems by their AutomationId?
Problem was resolved by creating custom Automation peer for conrtol. Control inherits ListView (wasn't clear in problem description). So there was problems with listview inside listview-inherited control by Automation id.
Also was helpfull adding Thread.Sleep() or Task.Delay() for a couple of seconds after element shown but before element finding by WinAppDriver.
Looks like test environment need more time to bind and find elements.
I have a TreeView that has 3 levels in it, driven by a HierarchicalDataTemplate. Here is a partial xaml listing:
<TreeView
Name="TreeView"
ItemsSource="{Binding GTOs}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<!-- This binding prevents the TreeView from slipping over to the right when an item is clicked. -->
<StackPanel MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ContentPresenter, AncestorLevel=1}}" />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:TreeGTOViewModel}" ItemsSource="{Binding Children}">
<TextBlock
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, Path=ActualWidth}"
MouseDown="TextBlock_MouseDown"
Tag="{Binding Uri}"
Text="{Binding Title}" />
</HierarchicalDataTemplate>
The Binding on the Width attribute of the TextBlock insures that each TreeViewItem assumes the full width of the TreeView container (and not just the width of the text box). Coupled with the Material Design styling, this produces nice shading effects when the mouse is hovered over a particular item in the tree.
There's only one problem. While I can get the Width to assume the full width of the TreeViewItem, I can't seem to do the same for the Height. This means that the user can click near the edge of the TreeView item and see all of the effects as if that item was clicked, but the MouseDown event does not fire.
This is the area that should be clickable (the dotted red line):
This is the area that is actually clickable (the outside border of the Text Box):
I've tried a number of different approaches, including this one:
VerticalAlignment="Stretch"
and this one:
Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}, Path=ActualHeight}"
but they either have no effect, or they break the TreeView in unexpected ways. I think part of the problem is the usual height weirdness of the StackPanel that is being used in the ItemsPanelTemplate.
How do I get the TextBox in each TreeViewItem to assume the height of the actual TreeViewItem?
Below is an image of the relevant part of the visual tree, from the TreeView down to the clickable Text Box. The Ripple element is a MaterialDesignThemes.Wpf.Ripple object.
I made a very simple MahApp test project with a treeview and one treeviewitem (no binding).
In the Live Visual Tree, the PART_Header had a VerticalAlignment value of Center. If I changed it to Stretch (using Live Property Explorer), the textblock filled the space vertically.
So, I used the Document Outline in VS to edit a copy of the treeview template. Inside of that, I saw <Setter Property="VerticalContentAlignment" Value="Center"> so I decided to set it to Stretch on my TreeView and it seemed to work.
<TreeView x:Name="TreeView"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
VerticalContentAlignment="Stretch">
Well, I solved this by going a different route. Instead of hooking the MouseDown event on the Text Block, I'm going to hook the SelectedItemChanged event on the Tree View.
<TreeView
ItemsSource="{Binding GTOs}"
SelectedItemChanged="TreeView_SelectedItemChanged">
The RoutedPropertyChangedEventArgs of the SelectedItemChanged event contains the ViewModel for that particular TreeViewItem, which yields the Uri property I need for navigation (I was previously binding this property to the TextBox's Tag property).
The new Event Handler looks like this:
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
dynamic viewModel = e.NewValue;
var uri = viewModel.Uri;
(DataContext as TreeViewModel).Navigate(uri);
}
Each level of the tree contains a different ViewModel type, but they all have a Uri property, so the dynamic binding provides the needed "don't care what type it is" behavior.
I can now remove the Tag binding and MouseDown event from the TextBlock.
<HierarchicalDataTemplate DataType="{x:Type local:TreeGTOViewModel}" ItemsSource="{Binding Children}">
<TextBlock
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type TreeView}}, Path=ActualWidth}"
Text="{Binding Title}" />
and the Tree View items now respond properly, no matter where they are clicked.
I use a Label with the ScrollViewer property:
<Label ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto">
Here is many many many text.
</Label>
But no scrollbar appears. Even when using Visible instead of Auto.
What is wrong here?
You need to put the ScrollViewer around the label, it's a seperate control:
<ScrollViewer Width="50" Height="30" HorizontalScrollBarVisibility="Visible">
<Label>
Here is many many many text.
</Label>
</ScrollViewer>
Now I am wondering, why the Label can have ScrollViewer properties
when they are not affecting the label at all?
The ScrollViewer properties are attached properties. Attached properties often have default behavior that is independent from the elements they are applied to (e.g. ToolTipService properties are handled by the service, the elements themselves don't need to know anything about it), so they can always be set anywhere.
It's similar to the BorderBrush property that is on every control. A control usually passes this value along to a Border element that is within its template. Most control templates contain a Border, but many don't contain a ScrollViewer. You could write a Label template that includes one, though:
<Window.Resources>
<Style TargetType="Label">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Label ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
Content="text" />
Notice that this works even though I haven't added TemplateBindings to the ScrollViewer. This is because ScrollViewer knows about its own attached properties and actually adds the bindings by default if it is inside a template.
As you can see from the template, a label is really nothing but a content presenter with RecognizesAccessKey set. This is so that users can use Alt keyboard shortcuts based on its text. Label also defines a Target property to define which element should be selected when the keyboard shortcut is pressed. Labels are meant to be short descriptive text next to input elements. In your case (displaying long text that needs scrolling), it is probably unnecessary. In fact, you can just place the text content directly within a ScrollViewer:
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
Here is many many many text.
</ScrollViewer>
ScrollViewer attached property is there to control ScrollViewer which was defined within the control's Template (first example of such control which comes to mind is ListView). There is no ScrollViewer in Label's template, therefore setting this property on label has no effect.
Instead, you should simply wrap your Label with ScrollViewer, as was shown by JMK.
You need to add the lable inside the ScrollViewer and provide some Height and Width
<ScrollViewer HorizontalScrollBarVisibility="Visible" Height="40" Width="40" >
<Label>
Content
</Label>
</ScrollViewer>
You have to put ScrollViewer around the Label.
like--
<ScrollViewer HorizontalScrollBarVisibility="Visible">
<Label/>
</ScrollViewer>
I have an (right-direction) expander which should hide the header when expanded, and display some text rotated to be vertical down the right hand side when it is collapsed.
I have managed to achieve this using the following code:
<Expander Grid.Column="1" ExpandDirection="Left"
IsExpanded="True" Name="rightHandExpander"
MaxWidth="{Binding RelativeSource={RelativeSource PreviousData}}">
<Expander.Header>
<TextBlock Text="Header text" RenderTransformOrigin="0,0"
Visibility="{Binding IsExpanded, RelativeSource={RelativeSource
AncestorType={x:Type Expander}, Mode=FindAncestor},
Converter={StaticResource boolToVisibilityConverter}}">
<TextBlock.LayoutTransform>
<TransformGroup>
<RotateTransform CenterX="25" CenterY="25" Angle="90" />
</TransformGroup>
</TextBlock.LayoutTransform>
</TextBlock>
</Expander.Header>
<!-- controls here -->
</Expander>
However when the expander is collapsed, the header takes up far too much space- the text is rotated, but the header's width appears to remain as if it has not.
Using VisualTreeHelper in the code-behind I discovered that the expander's header, border and dockpanel were too large, but the inner toggle button was the correct size. I tried setting the header, border and dockpanel's width to the correct size but they did not change.
Can anyone help?
Edit:
The expander is contained in a grid with two columns with a grid splitter and another expander in the first column. The other expander does the same thing but collapsing to the left hand side- this expander seems to work correctly, however.
I believe setting 'HorizontalAlignment' to 'Right' may fix the behaviour you are getting, as the default behaviour of most controls in a grid will be to fill all available space in their cell. It is kind of strange your left one works though, unless it is not directly in a grid, or has HorizontalAlignment set to Left?
Is there a way to zoom a viewbox content, excpet for one control?
I have a viewbox with a grid, and in this grid I have some controls and I'm trying to zoom all the controls in the viewbox except one, is it possible?
Many thanks,
Paulo
You can use a grid to add layers to your layout. That way, you can zoom one set of items, and leave another set un-zoomed.
<Grid>
<Viewbox>
<!-- Controls to zoom -->
</Viewbox>
<!-- Control to exclude from zoom -->
</Grid>
The order of the view box and the other controls in XAML will depend upon which layer appears on top.
If that doesn't quite do what you want, leave a comment and I'll revisit the answer.
EDIT You want the unzoomed control to be positioned relative to the (0,0) of the Viewbox. That will happen in this situation because both children of the grid are in cell (0,0) which means that their top-left corners are aligned. Can you give an example of what you have in XAML, and what's wrong with it (perhaps edit your original question)?
Here's some XAML to try:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Background="Green">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox>
<Rectangle Fill="Yellow" Width="10" Height="10" />
</Viewbox>
<TextBlock>I am positioned at (0,0)</TextBlock>
<TextBlock Margin="50,50">I am positioned at (50,50)</TextBlock>
</Grid>
</Page>
That gives a layout like this:
http://img20.imageshack.us/img20/2045/layout1m.png
But note that when the height is reduced, the grid becomes wider than the view box, and so the content is layed out like this:
http://img20.imageshack.us/img20/9397/layout2i.png
I guess that's not what you want. In that case, you use a canvas and go for something like this:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Background="Green">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox>
<Rectangle Fill="Yellow" Width="10" Height="10" />
</Viewbox>
<Canvas>
<TextBlock>I am positioned at (0,0)</TextBlock>
<TextBlock Margin="50,50">I am positioned at (50,50)</TextBlock>
</Canvas>
</Grid>
</Page>
Which looks like this:
http://img20.imageshack.us/img20/6743/layout3i.png