VerticalAlignment="Stretch" for label inside canvas doesn't work - wpf

How to use VerticalAlignment="Stretch" with a Label inside a Canvas? I'm trying to center the text "Cancel" in the button as in the code below. To use fixed height and width for the label isn't a desired option.
<Button Name="buttonCancel" Width="80" Height="40" IsCancel="True" Padding="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Canvas>
<Label Canvas.Top="0" Canvas.Left="0" Padding="0" FontSize="10">Esc</Label>
<Label VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">Cancel</Label>
</Canvas>
</Button>

Use a binding to the Canvas's ActualWidth:
<Canvas>
<Label Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualWidth}">...</Label>
</Canvas>
But as mentioned above, if you are interested in dynamic stretching layouts, the Canvas is not the ideal choice of control.

A Canvas does not perform any scaling layout of its contents; if you want to scale the contents, you could use Grid in this case, which will, by default, scale both Label elements to fill the Content space.

Assuming you need the canvas for other objects that are of a fixed nature, you could overlay the Canvas on a Grid, and then put the labels in the grid. You can put the labels before the canvas to make them background z-index (overwritten by canvas objects) or after the canvas to make them higher z-index (will overwrite canvas objects). For example:
<Button Name="buttonCancel" Width="80" Height="40" IsCancel="True" Padding="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Grid>
<Label Padding="0" FontSize="10">Esc</Label>
<Label VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">Cancel</Label>
<Canvas>
<!-- Your Canvas content here -->
</Canvas>
</Grid>
</Button>

Repeating my solution from the comments, since (a) you really don't want a Canvas and (b) it sounds like this solved your problems, so I'll make it an answer where it will be more visible to others.
Canvas is meant for fixed-pixel-size layouts, which is probably the least common case. You should replace your Canvas with a Grid as shown below, so that both Labels are laid out dynamically (and independently) within the available space:
<Grid>
<Label Padding="0" FontSize="10">Esc</Label>
<Label VerticalAlignment="Center" HorizontalAlignment="Center">Cancel</Label>
</Grid>

Related

Horizontal scrolling a row of ViewBox images in Xaml

I'm working on a UWP Windows 10 app with a XAML UI. One of my pages requires that images fill the height of the window (or screen in tablet mode) and uniformly scale as one long row of images from left to right (going off-screen). I've got this set up perfectly using ViewBoxes for the images inside of a StackPanel set to a Horizontal Orientation like so:
<StackPanel Orientation="Horizontal">
<Viewbox>
<Image Source="http://lorempixel.com/200/200/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/400/600/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/700/700/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/300/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/500/" />
</Viewbox>
</StackPanel>
The intention is for the images to flow off-screen with a horizontal scroll that allows the user to pan from left to right to see the gallery of images as one long row.
I've tried enabling HorizontalScrollMode on the parent StackPanel like so:
<StackPanel Orientation="Horizontal" ScrollViewer.HorizontalScrollMode="Enabled">
But this did not enable any scrolling at all.
I also tried to wrap everything inside of a ScrollViewer like so:
<ScrollViewer HorizontalScrollMode="Enabled">
<StackPanel Orientation="Horizontal">
<Viewbox>
<Image Source="http://lorempixel.com/200/200/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/400/600/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/700/700/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/300/" />
</Viewbox>
<Viewbox>
<Image Source="http://lorempixel.com/100/500/" />
</Viewbox>
</StackPanel>
</ScrollViewer>
But this completly breaks my ViewBox layout by shrinking all the images so they fit within a small portion of the screen and no longer fill the window/tablet height.
I've tried a number of other variations with similar results. Does anyone have some suggestions for solving this? Let me know if you need more info.
You do need a ScrollViewer to enable scrolling, although you might need to set a few properties to make it only scroll horizontally as mentioned in Windows 8 ListView with horizontal item flow
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.ZoomMode="Disabled"
Now the Viewbox is not the most controllable... control. You could try using the
SquareGrid panel from my toolkit instead of the Viewboxes. Maybe simplify it a bit. If that isn't enough - you could add some bindable properties that would update when the size of your window changes and bind the Width and Height of these images to these properties. Note that you can't use ActualWidth or ActualHeight because these don't raise change notifications on size changes.
<GridView x:Name="ImageGridView"
SelectedItem="{x:Bind ViewModel.SelectedLocation, Mode=TwoWay}"
Margin="10,0"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Disabled"
ScrollViewer.HorizontalScrollMode="Auto"
Grid.Row="4" Grid.ColumnSpan="5"
ItemsSource="{x:Bind ViewModel.CheckedLocations}"
ItemContainerStyle="{StaticResource PinsGridViewItemStyle}"
ItemTemplate="{StaticResource ImageOverlayGalleryFolderDataTemplate}" >
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid MaximumRowsOrColumns="1"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
This is my code to show 1 row of photos. You can adjust by the MaxiumRowsOrColumns. Also note that both HorizontalScrollBarVisibility and HorizontalScrollMode are present to custom as you want to.

How to control button layout in Xaml

Im having trouble controlling the exact layout of a button control with XAML.
It seems that whatever i do the button is of a minimum width.
I have a simple button with only a textblock inside the button. But the button has a lot of margin and padding that i cant seem to get rid of (i know of negative margins and padding).
The things i want to know is:
1. Why in the world was it designed this way.
2. what are the groundrules for controlling the exact layout of a button?
My code is as follows:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"></StackPanel>
<Pivot Grid.Row="1">
<Pivot.Title>
<StackPanel Orientation="Horizontal" Margin="-15,-3,0,0" Background="red" Width="480">
<Button Background="Blue" x:Name="btnStudies" Click="btnMenuItem_Click" Width="20">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Green">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
<Button Background="Blue" Click="btnMenuItem_Click">
<TextBlock Text="Title" Foreground="White"></TextBlock>
</Button>
</StackPanel>
</Pivot.Title>
</Pivot>
</Grid>
I want five buttons in a row but these are already too wide for the screen (windows phone). Changing the width doesnt seem to have any effect (why is it there).
The textBlock control within the button the button is as wide as the text on it, but i dont seem to have any control on the width of the button. In HTML you only have padding or margin when you define it but in xaml it just seems to be there and for me its unclear how to undo that.
*****EDIT*****
After reading Rachel's reply i decided to start from the ground up.
Using the code below i still have no control over how wide the button is because it uses a certain amount of padding that i cant seem to remove. The button has a width of about 110 when i define a width lower than that it doesnt change. Margins and paddings of 0 have no effect at all (dont want to use negative values just yet because that doesnt seem very intuitive). So the code below is very simple but still the button takes up an amount of space that i dont have any control over. I cant imagine a reason why it was designed this way.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400" />
</Grid.ColumnDefinitions>
<StackPanel Width="300" Background="Red" HorizontalAlignment="Left">
<Button Background="Blue" HorizontalAlignment="Left" Width="100" Margin="0" Padding="0">
<TextBlock Text="Title" Width="Auto" HorizontalAlignment="Left" />
</Button>
</StackPanel>
</Grid>
The type and size of the parent panel containing the control affects the size/layout of the child controls.
In your case, you have a Grid as your parent panel, and a Grid defaults to taking up all available space. In addition, children placed inside the grid default to taking up all available space as well unless you specify otherwise.
So your <Pivot> is being assigned a width equal to Grid.Width, and Pivot.Title sounds like it's being assigned a width equal to Pivot.Width, and StackPanel is being assigned a width equal to Pivot.Title.Width... you get the picture.
To specify that a control should not take up all available space, specify a HorizontalAlignment or VerticalAlignment property to tell it what side of the parent panel to dock the item on.
For example
<Pivot Grid.Row="1" HorizontalAlignment="Left">
or
<StackPanel OWidth="480" HorizontalAlignment="Left" ...>
If you're new to WPF's layout system, I would recommend reading through the codeproject article WPF Layouts: A Quick Visual Start to quickly learn what the main layout panels are for WPF.

Why ZIndex is not considered in arrange override of a stackpanel

Here is what I tried to to:
Added a stackpanel to my window (Orientation: Horizontal)
Added a set of buttons to it
Set the first button's ZIndex to be higher than the second one
Increased the width of the first button
What I expected:
I expected the first button to be on top of the second button (atlest overlay)
StackPanel's width should not change unless the width of the first button is no more sufficient
What is happening actually:
First button's width increases and the second button moves towards the right accordingly. They stay on the same plane
StackPanel's width increases with increase in the first button's width
Here is my question:
I know that stackpanel has not considered ZIndex while arranging the items within itself, BUT WHY?? Shouldn't it consider the ZIndex of its children while arranging them???
The Stackpanel 'stacks' its children based on their widths, i.e. if you increase the width of an item (or increase its margin), the stackpanel will simply expand to accomodate this. If you want to force items within a stackpanel to overlap, you will have to change their location after the layout has been computed. You can perform this using a RenderTransform. See the example below:
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Button Content="One" Canvas.ZIndex="10">
<Button.RenderTransform>
<TranslateTransform X="10"/>
</Button.RenderTransform>
</Button>
<Button Content="One"/>
<Button Content="One"/>
<Button Content="One"/>
</StackPanel>
And yes, the ZIndex is respected. This is an attached proepry of Canvas, however, it seems to be used by the rendering engine directly rather than by Canvas, hence it works in the above code.
I tried to find some relevant info about how to set the z index of wpf layout elements and panels. Using a Canvas comes with a different set of positioning issues which I simply hadn't the time to investigate. Here is a simple solution using the Panel.ZIndex property in xaml.
<Grid>
<Border Width="100" Height="100" Margin="0,0,50,50" Panel.ZIndex="1" Background="Navy" Opacity="0.3"
VerticalAlignment="Top" HorizontalAlignment="Left">
</Border>
<Border Width="100" Height="100" Margin="50,50,0,0" Background="Fuchsia" Opacity="0.3">
</Border>
The resulting two square border elements will overlap. One can use stackpanels instead of borders and use this logic to overlap anything easily.
Here is the same code adapted to the button problem:
<Grid>
<StackPanel Panel.ZIndex="10" Margin="20,20,0,0" HorizontalAlignment="Left">
<Button Content="One" Width="50" Height="40">
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="50,0,0,0" >
<Button Content="Two" Width="50" Height="40"/>
<Button Content="Three" Width="50" Height="40"/>
<Button Content="Four" Width="50" Height="40"/>
</StackPanel>
</Grid>

How to add a border to the Text inside a TextBlock in WPF?

I am kinda new to WPF, dont know if the question is weird. I wanted to know if its possible to add a border around the text inside a textBlock.
EDIT:
As per suggestion I have tried both but without much success:
<Border BorderBrush="#FF0B232F" BorderThickness="2">
<TextBlock HorizontalAlignment="Left" Text="TextBlock" TextWrapping="Wrap" FontSize="36" FontWeight="Bold" Foreground="#FF88BCCD" OpacityMask="Black"/>
</Border>
and
<Label BorderBrush="#FF0B232F" BorderThickness="2,2,2,2" Content="TextBlock" FontSize="36" FontWeight="Bold" Foreground="#FF88BCCD" />
Am I doing something wrong here?
In such cases I use Label or TextBlock placed in Border.
Both your approaches are correct, however, if you have the textblock/label inside a grid (or any other container) declared as you have, its contents will stretch.
Try setting the VerticalAlignment and/or HorizontalAlignment to an appropriate setting (Top/Left, Center)..
something like this:
<Border BorderBrush="#FF0B232F" BorderThickness="2" VerticalAlignment="Top">
<TextBlock HorizontalAlignment="Left" Text="TextBlock" TextWrapping="Wrap" FontSize="36" FontWeight="Bold" Foreground="#FF88BCCD" OpacityMask="Black"/>
</Border>
Assuming that you are asking for a full size TextBlock with a border overlay within the bounds of the TextBlock you could wrap it in a Grid and draw the borders over the top of the TextBlock like this...
<Grid HorizontalAlignment="Left">
<TextBlock Text="TextBlock" TextWrapping="Wrap" FontSize="36" FontWeight="Bold" Foreground="#FF88BCCD" OpacityMask="Black" />
<Border BorderBrush="#FF0B232F" BorderThickness="2" />
</Grid>
Because we haven't specified the grid row and column on the TextBlock and Border objects the border overlays on top of the TextBlock.
if you just want to have a border around your textblock or any other control use :
<Border>
<TextBlock></TextBlock>
</Border>
you set border properties like color ,cornerradius ,thickness,...

How to measure a canvas that has auto height and width

I'm a beginner in silverlight so i hope i can get an answer that brings me some more light in the measure process of silverlight.
I found an interessting flap out control from silverlight slide control
and now I try to use it in my project. So that the slide out is working proper, I have to place the user control on a canvas. The user control then uses for itself the height of its content. I just wanna change that behavior so that the height is set to the available space from the parent canvas.
You see the uxBorder where the height is set. How can I measure the actual height and set it to the border?
I tried it with Height={Binding ElementName=notificationCanvas, Path=ActualHeight} but this dependency property has no callback, so the actualHeight is never set.
What I want to achieve is a usercontrol like the tweetboard per example on Jesse Liberty's blog
Sorry for my English writing, I hope you understand my question.
<Canvas x:Name="notificationCanvas" Background="Red">
<SlideEffectEx:SimpleSlideControl GripWidth="20" GripTitle="Task" GripHeight="100">
<Border x:Name="uxBorder"
BorderThickness="2"
CornerRadius="5"
BorderBrush="DarkGray"
Background="DarkGray"
Padding="5" Width="300"
Height="700"
>
<StackPanel>
<TextBlock Text="Tasks"></TextBlock>
<Button x:Name="btn1" Margin="5" Content="{Binding ElementName=MainBorder, Path=Height}"></Button>
<Button x:Name="btn2" Margin="5" Content="Second Button"></Button>
<Button x:Name="btn3" Margin="5" Content="Third Button"></Button>
<Button x:Name="btn1_Copy" Margin="5" Content="First Button"/>
<Button x:Name="btn1_Copy1" Margin="5" Content="First Button"/>
<Button x:Name="btn1_Copy2" Margin="5" Content="First Button"/>
<Button x:Name="btn1_Copy3" Margin="5" Content="First Button"/>
<Button x:Name="btn1_Copy4" Margin="5" Content="First Button"/>
<Button x:Name="btn1_Copy5" Margin="5" Content="First Button"/>
<Button x:Name="btn1_Copy6" Margin="5" Content="First Button"/>
</StackPanel>
</Border>
</SlideEffectEx:SimpleSlideControl>
Hey - a couple of things:
I'm guessing it's just a typo, but: "{Binding ElementName=MainBorder, Path=Height}" should be "{Binding ElementName=uxBorder, Path=Height}" (or ActualHeight)
The SlideEffect appears to set a clip geometry, which is going to affect how "big" it looks
RectangleGeometry clipRect = new RectangleGeometry();
clipRect.Rect = new Rect(0, 0, panel.ActualWidth, host.Height);
host.Clip = clipRect;
If you comment out the above lines from the slide control, you should see a noticeable change in how big the panel looks.
I think that the ActualWidth or ActualHeight properties are not settable only gettable.
For that you won't be able to use binding, maybe if you create the element after the page has been loaded, since the Canvas Actual Size on the moment of the creation will be unknown.
Why don't you try creating a Property for this, with two way binding, and sign the Canvas event on layoutUpdated and update the value of the property, therefore updating all dependent objects.
I Don't know if this event is the right one, by check if it help's you.

Resources