I'm getting close to the understanding of the positioning of the System.Windows.Controls.UserControl objects:
By default, they don't have X or Y coordinates, relative to their container, but there is the possibility to add some, using so-called "Attached Properties".
A typical example of such attached properties are Canvas.Left and Canvas.Top, which mean that, in case the container of the UserControl is a Canvas, then the following happens with (supposingly) its upper left point (pseudo-code):
UserControl_UpperLeft_Point.X = Canvas.Left
UserControl_UpperLeft_Point.Y = Canvas.Top
Now what I'd like to know:
Is my idea correct? Is it indeed the upper left corner which is used?
What if I want to modify this behaviour, let's say into:
int left_Margin = 100;
int top_Margin = 200;
UserControl_UpperLeft_Point.X = Canvas.Left / 2 + left_Margin;
UserControl_UpperLeft_Point.Y = Canvas.Top * 2 + top_Margin;
What if I want to position my UserControl, based on the upper right corner or even the center?
Is my idea correct? Is it indeed the upper left corner which is used?
It is effectively the upper left corner in this case, but actually each of the sides are aligned. Furthermore, the control is aligned by the Canvas, it does not have internal X or Y coordinates that are set. They are given by the attached properties which are sepcific to the Canvas, no other panel. The Canvas internally calculates a rectangle where it draws the UserControl.
Canvas.Left
Gets or sets a value that represents the distance between the left side of an element and the left side of its parent Canvas.
Canvas.Top
Gets or sets a value that represents the distance between the top of an element and the top of its parent Canvas.
It is also important to note, that there is a priority for the attached properties.
If you specify them, the attached properties Canvas.Top or Canvas.Left take priority over Canvas.Bottom or Canvas.Right.
What if I want to modify this behaviour, let's say into: [...]
You would still have to assign the attached properties, but calculate the expression before.
Use a Binding or MultiBinding with a custom value converter that evaluates an expression that is bound or passed as converter parameter or with a specialized value converter to calculate a specific term.
Create your own specialized attached properties that internally assign the calculated value to the Canvas attached properties.
What if I want to position my UserControl, based on the upper right corner
Set the Canvas.Top and Canvas.Right attached properties instead.
<Canvas>
<local:MyUserControl Canvas.Top="0" Canvas.Right="0" Width="50" Height="80"/>
</Canvas>
[...] or even the center?
A Canvas is used for absolute positioning. If you want to center controls, a Grid might be the better choice. If you still want to use a Canvas and it is fixed size, just calculate the center coordinates yourself and set the attached properties accordingly. If it is resizeable and the position of the UserControl position needs to be responsive to that, you could do one of these.
Put a Grid around the Canvas and put the UserControl after it. It will appear on top of the Canvas and will be centered automatically, even on resizing.
Grid>
<Canvas>
<Rectangle Canvas.Top="80" Canvas.Left="20" Fill="Black" Width="50" Height="50"/>
<Rectangle Canvas.Top="300" Canvas.Left="230" Fill="Black" Width="100" Height="80"/>
</Canvas>
<local:MyUserControl Width="50" Height="80"/>
</Grid>
Implement a custom behavior or complicated bindings with converter using the bound ActualSize of the Canvas to set the attached properties, which I do not recommend.
I think for your last question it is more useful to ask why you need this behavior, what you want to achieve. Often there is a much simpler and more suitable solution.
If you want to position a control inside a container you should follow the WPF way.
Align it horizontal and vertical and set the margin, thats it.
<Grid>
<Grid.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5" />
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
<Setter Property="Background" Value="Red" />
<Setter Property="TextElement.Foreground" Value="White" />
<Setter Property="TextElement.FontWeight" Value="Bold" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextAlignment" Value="Center" />
</Style>
</Grid.Resources>
<!--#region top-->
<Border
Margin="20,20,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<TextBlock>
Top<LineBreak />
Left</TextBlock>
</Border>
<Border
Margin="0,20,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Top">
<TextBlock>
Top<LineBreak />
Center</TextBlock>
</Border>
<Border
Margin="0,20,20,0"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<TextBlock>
Top<LineBreak />
Right</TextBlock>
</Border>
<!--#endregion-->
<!--#region center-->
<Border
Margin="20,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBlock>
Center<LineBreak />
Left</TextBlock>
</Border>
<Border
Margin="0,0,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock>
Center<LineBreak />
Center</TextBlock>
</Border>
<Border
Margin="0,0,20,0"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<TextBlock>
Center<LineBreak />
Right</TextBlock>
</Border>
<!--#endregion-->
<!--#region bottom-->
<Border
Margin="0,0,20,20"
HorizontalAlignment="Right"
VerticalAlignment="Bottom">
<TextBlock>
Bottom<LineBreak />
Right</TextBlock>
</Border>
<Border
Margin="0,0,0,20"
HorizontalAlignment="Center"
VerticalAlignment="Bottom">
<TextBlock>
Bottom<LineBreak />
Center</TextBlock>
</Border>
<Border
Margin="20,0,0,20"
HorizontalAlignment="Left"
VerticalAlignment="Bottom">
<TextBlock>
Bottom<LineBreak />
Left</TextBlock>
</Border>
<!--#endregion-->
</Grid>
If you want to have the old style (knwon from WinForms you can align it HorizontalAlignemnt="Top" and VerticalAlignment="Left" and use the Left and Top property of Margin to set the old school Left and Top.
Related
I was wondering if it is possible to insert my own bitmaps for scrollbar styling. I would like to to use an image for up/down arrows, background and scroller. This is what I have to start with, but I just don't know how to access these mentioned properties:
<TextBox Name="SlideNotes" Foreground="White" FontSize="20" FontWeight="Bold" Background="Transparent" BorderThickness="0" TextWrapping="Wrap" AcceptsReturn="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Grid.Row="27" Grid.Column="11" Grid.ColumnSpan="46" Grid.RowSpan="5">
<TextBox.Resources>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background" Value="Red"/>
</Style>
</TextBox.Resources>
I would appreciate any help.
You can use ImageBrush to set an image to any property that uses Brushes (Background, Foreground, BorderBrush, Fill, etc.).
In your case, you'd use it like this:
<TextBox Name="SlideNotes" Foreground="White" FontSize="20" FontWeight="Bold" Background="Transparent" BorderThickness="0" TextWrapping="Wrap" AcceptsReturn="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Grid.Row="27" Grid.Column="11" Grid.ColumnSpan="46" Grid.RowSpan="5">
<TextBox.Resources>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="yourimage.jpg" Stretch="Fill" />
</Setter.Value>
</Setter>
</Style>
</TextBox.Resources>
</TextBox>
ImageBrush (and all other Brushes that inherit from TileBrush) has a set of properties that control how the image is shown. I've used Stretch="Fill" in my sample, which will make the image stretch to fill all the space available, but you could want it to behave differently.
For instance, this...
<ImageBrush ImageSource="yourimage.jpg" Stretch="None" TileMode="FlipXY" />
Will make your image repeat, with tiles alternatively flipped horizontally and/or vertically.
Toy around with Stretch and TileMode (or even ViewPort, ViewPortUnit, Viewbox and ViewboxUnit, if you feel brave enough) to get the effect you want.
EDIT - As with RepeatButton, the default ScrollBar template seems to pretty much ignore the Background property, most of the time, as well as other properties you could use for customization... This means you'll probably have to override the whole template to customize it to your liking.
Here's one ScrollBar template sample: ScrollBar Styles and Templates
And here's another for the RepeatButton: RepeatButton Styles and Templates
Looks like you're after a ControlTemplate. See here for a nice tutorial on how to style one.
To avoid having to style the entire ScrollViewer, you can instead style only the RepeatButton element, this will make things much easier to work with.
<Style TargetType="RepeatButton">
<Setter Property="Background" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
...
</Setter.Value>
</Setter>
</Style>
You may need to define this style in the Window Resources, instead of the TextBox resources due to the scope.
See the documentation for RepeatButton styles and templates.
I would like to create an WPF Button with no border, with background image and insert a center Text. So I have this:
<Style TargetType="Button" x:Key="TransparentButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="Transparent">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Button Style="{StaticResource TransparentButton}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="./Resources/Images/bottone_indietro.png" />
<Label content="Pippo"/>
</StackPanel>
</Button.Content>
</Button>
It works but Text "Pippo" is displayed at right of image. It possible to center the text???
Reguards...
If you want to centre align text over your image you could put both elements in a Grid instead of StackPanel
<Grid>
<Image Source="./Resources/Images/bottone_indietro.png" />
<Label Content="Pippo" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
StakcPanel will stack its children horizontally or vertically whilst Grid lets you put one element over other. From MSDN:
Child elements of a Grid are drawn in the order in which they appear in markup or code. As a consequence, layered order (also known as z-order) can be achieved when elements share the same coordinates
You can read more about WPF panels here
You can use the HorizontalAlignment property to centre your text, although it will always be to the right of your Image. You also would need to remove the Orientation setting on the StackPanel so that the text will appear underneath the Image... try this:
<Button Style="{StaticResource TransparentButton}">
<Button.Content>
<StackPanel>
<Image Source="./Resources/Images/bottone_indietro.png" />
<TextBlock Text="Pippo" HorizontalAlignment="Center" />
</StackPanel>
</Button.Content>
</Button>
Note that if you want your text to appear over the top of your Image, then you shouldn't use a StackPanel at all... in that case, you should replace it with a Grid instead.
I have a custom tooltip style that basically creates a nice black tooltip with an arrow pointing to the location of the item you hovered over.
The problem is that sometimes the tooltip will not always be placed in the correct location (i.e. near window edges) which means the tooltip arrow no longer points at the correct place... Is there anyway around this problem? Or can I create specific styles for each location placement?
<Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<StackPanel>
<Border CornerRadius="3" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="10,7" BorderThickness="0" Background="#e5323232">
<StackPanel>
<TextBlock FontFamily="Arial" FontSize="12" Text="{TemplateBinding Content}" Foreground="#f0f0f0" />
</StackPanel>
</Border>
<Path Margin="10,0,0,0" Fill="#e5323232" Data="M 0 0 L 6 6 L 12 0 Z"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
Maybe you could try this, I just set the Placement to Center and added a HorizontalOffset to match the arrow you created in the template.
However that wont center it vertically on the control, so you could make an IValueConverter and calculate the size of the control and divide by 2, or you could add a dummy element to your StackPanel that is the same size as the Border, and that should center the ToolTip without needing any code behind
<Style TargetType="{x:Type ToolTip}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Placement" Value="Center" />
<!--Offset to the arrow path-->
<Setter Property="HorizontalOffset" Value="15"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<StackPanel>
<Border x:Name="border" CornerRadius="3" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="10,7" BorderThickness="0" Background="#e5323232">
<StackPanel>
<TextBlock FontFamily="Arial" FontSize="12" Text="{TemplateBinding Content}" Foreground="#f0f0f0" />
</StackPanel>
</Border>
<Path Margin="10,0,0,0" Fill="#e5323232" Data="M 0 0 L 6 6 L 12 0 Z"/>
<!--Dummy rectangle same height as tool tip, so it centers on the control-->
<Rectangle Height="{Binding ActualHeight, ElementName=border}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The simplest way of doing it is to use a UIElement that exists in the Control Tree as the PlacementTarget of the Tooltip. This will avoid the Silverlight automated positioning when you get near the window edges:
<StackPanel ToolTipService.ToolTip="{Binding Title, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
ToolTipService.Placement="Bottom"
ToolTipService.PlacementTarget="{Binding ElementName=LayoutRoot}">
<TextBlock Text="{Binding Title,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
In this case the tooltip will be positioned always at the Origin of the LayoutRoot element. If you have a fixed path size and the PlacementTarget is always at the same position relative to the control for which you want to show the tooltip then this works fine.
If you need to position the Tooltip relatively to the control that triggers the Tooltip than you have to make the Path Data dynamic and calculate the distance to create a new Path Data in the Tooltip control every time the Tooltip is opened.
For this case you have to handle the Tooltip.IsOpened event and implement this logic. If you're using the PlacementTarget than you always know the direction relatively to your control so this makes it easier to calculate the Path vertices.
Another way which works but it's way more complex is to implement your own Popup that shows up when you move the mouse over your control. You would need to implement a few calculations to get the position of the popup relative to the Control, which is exactly what the Tooltip control does for you. The advantage of this is that you have complete control over the positioning of the tooltip and its appearance.
I'm trying something like a windows 8 tiles and want to display tiles of varying width and/or height. WrapPanel makes each column equal width and height leaving blank spaces around the smaller items. Is there a way or a panel to display items much like a StackPanel where each items can have individual dimensions and wrap like a WrapPanel?
Edit:
This is my ItemsControl. I replaced the Tile DataTemplate with a simple Border and TextBlock. Width is auto and Tiles looks fine except WrapPanel create equal sized cells.
<ItemsControl ItemsSource="{Binding Path=Contents}" HorizontalContentAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#B1DBFC" Height="200px" BorderBrush="#404648" Margin="5">
<TextBlock VerticalAlignment="Center" Text="{Binding Path=Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can see from the image that the width of each column is the width of widest item.
If I set the width explicitly on the border, things get more ugly.
The behaviour you are looking for is the default behaviour of the WrapPanel as can be seen from the following sample.
<WrapPanel >
<WrapPanel.Resources>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Width"
Value="80" />
<Setter Property="Height"
Value="80" />
<Setter Property="Margin"
Value="3" />
<Setter Property="Fill"
Value="#4DB4DD" />
</Style>
</WrapPanel.Resources>
<Rectangle Width="150" />
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle Width="200"/>
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle Width="220"/>
<Rectangle />
<Rectangle />
</WrapPanel>
Which produces the following result:
As you can see, The width of each item is honoured.
Your problem is caused by setting the orientation of the WrapPanel to Vertical in your template. This is laying out the items from top-to-bottom rather than left-to-right which means that it is the Height property that you need to be setting (or you could revert back to horizontal layout as in my example).
Compare your output to my screenshot where the panel is set to horizontal orientation; each row is the size of the highest Rectangle. Don't believe me? Try setting one of the Rectangle's Height property to a larger value and you will observe that the row size will increase and the Rectangles no longer line up vertically.
Reading your comments I think the best way to get started is to do the following:
Have a WrapPanel that lays out its content horizontally.
Items should have uniform Height.
Constrain the Height of the WrapPanel to a certain size so that you don't get vertical scrollbars.
The height of your WrapPanel should be worked out using the following formula:
((Height of item + Top Margin + Bottom Margin) x Number of rows))
The width of each item also requires a bit of thought so that the panel lays the items out horizontally like the metro interface (lined up rather than staggered).
There are two tile sizes; the small one is 80px wide and the large one is 166px wide.
The width of the large tile is worked out like this:
(item width * 2) + (left margin + right margin)
This ensures the tiles line up correctly.
So now my XAML looks something like this:
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled">
<StackPanel Orientation="Horizontal"
Margin="10,0">
<StackPanel.Resources>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Width"
Value="80" />
<Setter Property="Height"
Value="80" />
<Setter Property="Margin"
Value="3" />
<Setter Property="Fill"
Value="#4DB4DD" />
</Style>
<Style TargetType="{x:Type WrapPanel}">
<Setter Property="Margin"
Value="0,0,25,0" />
<Setter Property="MaxWidth"
Value="516" />
</Style>
</StackPanel.Resources>
<WrapPanel Height="258">
<Rectangle Width="166" />
<Rectangle />
<Rectangle Width="166" />
<Rectangle />
<Rectangle Width="166" />
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle Width="166" />
<Rectangle />
</WrapPanel>
<WrapPanel Height="258">
<Rectangle />
<Rectangle Width="166" />
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle />
<Rectangle Width="166" />
<Rectangle />
</WrapPanel>
</StackPanel>
</ScrollViewer>
Which produces the following result:
This should give you enough information to start to re-factor this into a full control. If you do this then keep the following in mind:
The layout properties (small item size, large item size, gaps etc) should be calculated rather than hard coded so that if you want to change, for example, the margins, the layout will still work correctly (I.E. the tiles will still line up).
I would limit the number of 'tiles' that can be displayed (in the current example, if the tiles do not fit in the layout they are simply hidden which is probably not what you want).
I would like to create a style or template for rectangles. The properties are quite superficial: changed background color, radius.
In addition I would like to add text inside of the rectangle.
I've found alot of examples, but none fit my needs the best. Is it possible to create a template drawing the rectangle and text inside in a way I only need to call
<Rectangle template={StaticRessources myBox}/>
And the defined template is applied? So far I came, the text is not aligned inside the rectangle:
<ControlTemplate x:Key="greenBoxTemplate">
<Grid>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="25" Text="Hello World" TextWrapping="Wrap"/>
<Rectangle Height="100" HorizontalAlignment="Left" Margin="233,144,0,0" Name="BNU2" Style="{StaticResource greenBox}" Stroke="Black" VerticalAlignment="Top" Width="200"/>
</Grid>
</ControlTemplate>
For what it's worth, the template is applied to a button, but actually I want to apply it to rectangle which does not work.
What you need is a Decorator. There is one already that seems like to fit for you perfectly: Border
if you want to have a recurring border for elements with some predefined values you can create as Style like:
<Style TargetType="Border" x:Key="MyBorderStyle">
<Setter Property="Background" Value="Red"/>
<Setter Property="CornerRadius" Value="3px"/>
</Style>
and apply it like:
<Border Style="{StaticResource MyBorderStyle}">
<TextBlock>Hello World</TextBlock>
</Border>