I have a WPF application in which the main window holds a DockPanel with two children. The top child is another DockPanel which holds the menu and is of a fixed size. The lower child is the main work area, which should fill the remaining space and be resizable along with the window. (Hence the DockPanel parent.) Draggable objects get placed in this work area and might appear anywhere inside it.
I'm trying to figure out how to make scroll bars appear if an object is dragged outside the visible area.
The approximate XAML structure currently goes
<Window>
<DockPanel>
<DockPanel with fixed-size content ... >
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid ClipToBounds="True" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"/>
</ScrollViewer>
</DockPanel>
</Window>
So far I've tried a Grid and a Canvas. Both have built-in scroll bars, but they won't appear unless dimensions are specified--but if I apply dimensions, then the panel won't automatically resize to fill the work area.
Then I tried surrounding the panel with a ScrollViewer. The unconstrained panel now successfully auto-resizes to fill the space, but the ScrollViewer has the same problem as the panel--it will only display scroll bars if it's constrained to hard dimensions.
I'm thinking that this would work if I could dynamically constrain the ScrollViewer. So far, I haven't found any reliable way to dynamically apply size values to the ScrollViewer.
Is there a way to create a Binding between the ScrollViewer dimensions and the ActualHeight and ActualWidth of the Grid? Or, is there a way I can define the ActualHeight/ActualWidth of the grid as a DynamicResource that can be applied to the ScrollViewer? Or is there some other panel or method or resource that can be used so that all three criteria (panel fills available space, panel auto-resizes with window, anything dragged outside visible area triggers scroll bars) are met?
Thanks in advance for any help.
The problem was that I did not have a DockPanel.Dock setting on the bottom child of the containing DockPanel. Relying on the DockPanel's LastChildFill wasn't enough to do the job. Once I set DockPanel.Dock = Bottom on the bottom child, the scroll bars started working.
Related
I have a 50x50 draggable grid inside another grid that I am able to move around with the cursor. (I forgot that I am using the DraggableExtender class
The problem is that I want the moveable grid to be relatively positioned inside it's container grid no matter how the container grid is resized. My solution right now is to dynamically change the HorizontalAlignment and VerticalAlignment of the moveable grid whenever it is moved, but this is hacky and doesn't work well.
Relative positioning in a grid is one of the easiest things to do in XAML, but not when you have draggable elements ;(
Any ideas?
EDIT for code and images:
My XAML:
<Grid Margin="10" ClipToBounds="True" Background="#FFB4B4B4">
<Grid Name="testGrid" MouseLeftButtonDown="testGrid_MouseLeftButtonDown" MouseLeftButtonUp="testGrid_MouseLeftButtonUp" RenderTransformOrigin="0.5,0.5" Height="100" Margin="50,0,0,0" Width="100" Background="#FFE6E6E6" local:DraggableExtenderGrid.CanDrag="true" HorizontalAlignment="Center"/>
</Grid>
and I use a DraggableExtender class (shown here) which I have edited to apply to a Grid instead of a Canvas (I was hoping for better results from a grid.. but both containers produce the same result, even with the original class on a canvas).
This is a picture of my 2 grids. I can move the smaller grid around inside it's parent grid, but I would like for it to maintain relative positioning once the window is resized. This is what it currently looks like when I resize the window. In this particular example, the grid would ideally remain slightly off-center horizontally and vertically in both pictures.
May be you should try placing the Grid inside a Canvas instead..
Take a look here
I'd like to enable a ScrollView for my ListBox. My ListBox is showing StackPanels.
Unfortunately I'm not able to set a specific height to my ListBox (this is the only solution I found where I can use my Scroller).
What do I have to do to get a ScrollBar for my ListBox? (And even if I minimize/maximize the window it should appear if necessary)...
Thanks
Well, the ListBox in WPF already contains a scroll, which you can force to be visible like this:
<ListBox
ScrollViewer.VerticalScrollBarVisibility="Visible">
</ListBox>
Most probably your problem comes from the fact, that your listbox resides in a control, which does not delimit it's height, so your listbox has a height that is exactly equal to the needed space (which may be more than your screen estate). Try to put it inside a Grid for example.
So to illustrate it with an example:
<StackPanel>
<ListBox>
<ListBox.Items>
<TextBlock>Test</TextBlock>
...
</ListBox.Items>
</ListBox>
</StackPanel>
will never show a scrollbar, as the stackpanel will have a height that will be always enough for the listbox to show all elements, even though it might not be visible on the screen.
But if you switch the StackPanel to a Grid in the above example you will have scrollbars when they are needed, as the Grid will constrain the height of the child control (the ListBox).
Hope this helps.
(new to WPF) I am looking over the WPF example:
<Window x:Class="Attempt_XAML.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Label HorizontalAlignment="Center">A Button Stack</Label>
<Button HorizontalAlignment="Left">Button 1</Button>
<Button HorizontalAlignment="Right">Button 2</Button>
<Button Background="#FFA29494">Button 3</Button>
<Button>Button 4</Button>
</StackPanel>
</Window>
Remark in MS notes that:
The default value is stretch for both HorizontalAlignment and
VerticalAlignment of content that is contained in a StackPanel.
However, result looks different from what I am expecting. Button and Label are not stretched out vertically, but only horizontally(i.e they don't fill the entire space of Window in both directions) Why ?
Button and Label are not stretched out vertically, but only horizontally(i.e they don't fill the entire space of Window in both directions) Why ?
To understand the reason, you really need at least a basic understanding of Panels. Any Panel such as a StackPanel uses a measure-arrange cycle to decide on a layout for its child elements:
in the "measure" cycle, it assigns a size to each child
in the "arrange" cycle, it positions each child in its view
The key feature of a StackPanel is that it has infinite space -- infinite horizontal space if its orientation is Horizontal, and infinite vertical space if Vertical. In other words, it does not actually pay attention to the size available to it (in the direction of its orientation), but claims an infinite space. So therefore, coming back to your example, even though the VerticalAlignment of the children may be Stretch, they cannot actually be stretched out to an infinite size.
If you need a panel that stretches out its children to fill the available space, then Grid is a good example (by default the Grid will assign an equal share of the total height to each child -- or you can use the star sizing to adjust the proportions). You could also consider creating your own custom Panel, if you need a specialized layout behavior.
Edit
To clarify "infinite space": what I mean is that the StackPanel tells its children that there is infinite space available. What do you do if you are a Button, Label, etc, and there is infinite space available? You probably just take up the minimum space you need, even if your VerticalAlignment is "Stretch", right? That's what happens. Contrast to a Grid, which tells the child controls that they have x (finite) amount of space -- in that case, a Button, Label, etc, will fill up all that space (if their VerticalAlignment is "Stretch").
To illustrate the above, take this custom control as an example:
public class TestControl : ContentControl
{
public string Description { get; set; }
protected override Size MeasureOverride(Size availableSize)
{
System.Diagnostics.Debug.WriteLine("Size available for '" + Description + "': " + availableSize.Height);
return base.MeasureOverride(availableSize);
}
}
This doesn't actually do anything, just reports how much space has been allocated to it. Now, place the test control in a Grid and a StackPanel, and compare:
<Grid Height="50">
<Grid.RowDefinition />
<Grid.RowDefinition />
<local:TestControl Description="in Grid" />
<StackPanel Grid.Row="1" Height="10">
<local:TestControl Description="in StackPanel" />
</StackPanel>
</Grid>
You'll see that the Grid assigns its CustomPanel (the first one above) a height of 25 (half its height). The StackPanel, though, assigns its CustomPanel a height of Infinity.
Although default value is stretch for both HorizontalAlignment and VerticalAlignment of Content that is contained in a StackPanel. But in which direction to stretch is controlled by Orientation Property.
If Orientation is set to Vertical then all the items of the stack with no defined width value are stretched only.
In the image here, each block with a number in it represents a laser. These blocks are laid out on a canvas inside a DockPanel. Also inside the DockPanel docked to the top is the red TextBlock that you can see is hiding behind the laser map canvas. Why is this happening? The TextBlock is docked to the top of the DockPanel and canvas has no dock setting, therefore it should fill the rest of space. Also of note: I had to put the DockPanel inside a ViewBox in order for the whole center screen space to scale properly on window resizes. Then I had to put that ViewBox inside a ScrollViewer to allow scroll bars to appear when needed.
Here is the XAML Code for the center screen (Note: Child of the Window is a DockPanel. Menu is docked to the top, left-hand button panel is docked to the left, right-hand button panel is docked to the right, the status bar is docked to the bottom and everything you see in the center screen is defined by the following XAML code)
<ScrollViewer
Name="centerScreenScrollViewer"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="{Binding IsScrollbarsVisible, Converter={StaticResource BoolToScrollbarVisConverter}, FallbackValue=Hidden}">
<Viewbox>
<DockPanel
LastChildFill="True">
<TextBlock
DockPanel.Dock="Top"
Name="tbkFullVisual"
Style="{StaticResource tbkStyleBlue}"
Foreground="Red"
IsEnabled="{Binding FullVisual}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
FontSize="24">
*** This Print Requires Full Visual Inspection! ***
</TextBlock>
<Canvas x:Name="mapCanvas">
<ContentPresenter Content="{Binding MapCanvas}"/>
</Canvas>
</DockPanel>
</Viewbox>
</ScrollViewer>
Any help in solving this issue will be greatly appreciated.
Regards,
Kyle
This has to do with the way that a ViewBox works, in particular with the Canvas element. The ViewBox is used to resize child elements, as I'm sure you're aware. There are 2 issues with the Canvas element:
The default Height and Width are 0, which means that the TextBlock will get all the space.
The Canvas element lets you draw outside of its own boundaries, so even if your canvas is tiny or not even visible, you would be allowed to render your grid of numbers.
The quickest solution is to set VerticalAlignment on the ViewBox:
<Viewbox VerticalAlignment="Top">
...
</Viewbox>
You could set a Height on the Canvas, but I think this is less ideal because you don't want to change this dynamically with window resize.
I have a usercontrol that consists of a label and a textbox. It's inside a scrollviewer.
I'm drawing an adorner on top of it and I need to adjust the size of the adorner to the visible size of the control.
How do I get the visible size of the control?
In the image below the green rectangle is the adorner. As you can see it's being drawn over the scrollbar on the right side.
Is it possible to get the size of the rendered part or will I have to manually go trough the visual tree and calculate it?
edit: more background info
I'm build a very limited form designer. Everything is happening in code. The adorner is used to display the current selected control.
I would put a Canvas in your ScrollViewer and place all of your user controls on the Canvas. If the Adorner is then painted on the Canvas you don't have to worry about it drawing over top your ScrollViewer.
You would also have the added benefit of the adorner disappearing under the ScrollViewer, rather than just ending at it, so your users know that the control extends beyond the ScrollViewer. This is how all of the designers I've made and seen made in WPF work.
If you are only worried about clipping the adorner, then you can include an AdornerDecorator in your content. Something like:
<ScrollViewer>
<AdornerDecorator>
<!-- Your content here -->
</AdornerDecorator>
</ScrollViewer>
Or you can include the AdornerDecorator directly in your UserControl.