WPF: Aligning GRID contents with respect to a bitmap - wpf

I have to display few strings under a bitmap . At a time maximum number of strings that can displayed are 5 and not always all the 5 strings will be displayed. Also the length of the strings vary. Whatever be the case, I want to display these strings in a visually appealing manner under the bitmap. Like, if just one string, I want to position it centrally under the bitmap. If 2 strings, I want to space the strings nicely and center it under the bitmap and so.
I know only at run time the strings to display, number of strings ( min 1 and max 5) to display and also length of string.
I wrote the below XAML code, but I am unable to position the strings nicely for all my conditions. Bitmap1 is a circle, bitmap2 is left arrow and bitmap3 is right arrow.
Can someone help me here?
<Grid x:Name="Graphics" Grid.Column="1" Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="0.319*"/>
<RowDefinition Height="0.56*"/>
<RowDefinition Height="0.321*"/>
</Grid.RowDefinitions>
<Image Height="72" Source="/DataBinding;component/Bitmap1.bmp" Stretch="Fill" Width="108" Grid.Row="1" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.408*"/>
<ColumnDefinition Width="0.15*"/>
<ColumnDefinition Width="0.408*"/>
</Grid.ColumnDefinitions>
<Image x:Name="Next" Height="48" Width="48" Grid.Column="2" Source="/DataBinding;component/Bitmap3.bmp" HorizontalAlignment="Left" />
<Image x:Name="Prev" Width="48" Height="48" Grid.Column="0" Source="/DataBinding;component/Bitmap2.bmp" HorizontalAlignment="Right"/>
<Grid HorizontalAlignment="Center" ShowGridLines="True" Width="Auto" Grid.ColumnSpan="3" Margin="38,69,41,-40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Visibility="Visible" Text="String1" Padding="10" Grid.Column="0" FontSize="14.667" TextAlignment="Center" Foreground="White" />
<TextBlock Visibility="Visible" Text="String2" Padding="10" Grid.Column="1" FontSize="14.667" TextAlignment="Center" Foreground="White" />
<TextBlock Visibility="Visible" Text="String3" Padding="10" Grid.Column="2" FontSize="14.667" TextAlignment="Center" Foreground="White" />
<TextBlock Visibility="Visible" Text="String4" Padding="10" Grid.Column="3" FontSize="14.667" TextAlignment="Center" Foreground="White" />
<TextBlock Visibility="Visible" Text="String5" Padding="10" Grid.Column="4" FontSize="14.667" TextAlignment="Center" Foreground="White" />
</Grid>
</Grid>
</Grid>

What you want to do is create a custom panel by creating a custom control based on panel. Override ArrangeOverride and then place things exactly where you want them as if you have a canvas to work with, because your panel is like a canvas when you override ArrangeOverride. Grids are a custom panel themselves :-)

<StackPanel Name="stack1" Orientation="Horizontal" Width="Auto" Background="Red" >
<TextBlock FontSize="14.667" Width="Auto" TextAlignment="Center">
<Run x:Name="String1" Text="String1" />
<Run x:Name="String2" Text="String2" />
<Run x:Name="String3" Text="String3" />
<Run x:Name="String4" Text="String4" />
<Run x:Name="String5" Text="String5" />
</TextBlock>
</StackPanel>
</Grid>
</Grid>

Related

WPF Grid Column not aligning image correctly

The final column of the below grid view is not displaying in the center vertically, though the first image is and they use the same lookup method to find the image source (A resource dictionary object locator class I have). The final image (CurrencyImg) has the bottom of the image aligned with the middle of the row, so it stretches out of view upwards and doesn't fill the lower half of the row. Confused!
<Grid Name="grdCustomer" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="220" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="18"/>
</Grid.RowDefinitions>
<Image Source="{y:ImageStaticResource {Binding IconString}}" Margin="0,0,0,0" VerticalAlignment="Center" ></Image>
<TextBlock Grid.Column="1" Text="{Binding CustomerDesc}" VerticalAlignment="Center" />
<TextBlock Name="tbTxnCount" Grid.Column="2" Text="{Binding TxnCount}" VerticalAlignment="Center" />
<TextBlock Name="tbAmount" Style="{StaticResource myCustStyleColor}" Grid.Column="3" Text="{Binding Amount}" HorizontalAlignment="Right" VerticalAlignment="Center" />
<TextBlock Name="tbCurrency" Grid.Column="4" Text="{Binding Currency}" HorizontalAlignment="Right" VerticalAlignment="Center" />
<Image Name="imgCurrency" Grid.Column="5" Margin="0,0,0,0" Source="{y:ImageStaticResource {Binding CurrencyImg}}" VerticalAlignment="Center" />
</Grid>

How to cut TextBox width to prevent out of the Grid?

I have the next layout
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Width="60" Height="60" />
<StackPanel Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal">
<TextBlock Text="Title should be long" HorizontalAlignment="Left" />
<Ellipse Fill="White" Stroke="White" Width="7" Height="7" />
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Message" />
<TextBlock Grid.Row="1" Grid.Column="2" Text="Info" />
</Grid>
I have an issue in the StackPanel which hosts a Title and Ellipse, the goal is the Online marker by the ellipse whitch should be placed at the end off the title. But it shouldn't out of a view part.
I have tried to put TextBox and Ellipse into cells of the Grid unfortunatly it doesn't help.
<Grid Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2">
<Grid.ColumnsDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnsDefinitions>
<TextBlock Grid.Column="0" Text="Title should be long" HorizontalAlignment="Left" />
<Ellipse Grid.Column="1" Fill="White" Stroke="White" Width="7" Height="7" />
</Grid>
In my mind it should render correct, but the ellipse is out of view port again.
This is a Expression Blend layout scrinshots, the same layout is rendering in runtime.
The Grid bounds:
The TextBox bounds:
The Ellipse bounds:
So the TextBox and Ellipse is out of the grid :(
Update: I need the next behaviour of layout
1) Short title, the ellipse attached to the title end
2) Long title, the ellipse attached to the right side of container
I tried your code and it renders fine (in other terms it renders in the viewport. See red arrow). Please find attached a screenshot of the results. (I added the showgridlines just to illustrates the rows and cols)
//--- Changed testing and fixed code for intended effect ---//
Code changes in XAML: Swapped the width values for the columndefinitions. Added Textwrapping to textblock in order to see entire text. (You could opt for texttrimming instead depending on your aesthetics.)
<Grid Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Title should be really really really really really long" HorizontalAlignment="Left" TextWrapping="Wrap" />
<Ellipse Grid.Column="1" Fill="White" Stroke="White" Width="7" Height="7"/>
</Grid>
Outcome:

Have to replicate this layout in XAML, what controls would you use?

I'm just beginning on figuring out what works best for layout/data display in XAML (WPF), and don't have the intuition yet of what controls, etc work well in certain situations.
Nothing needs to be sorted or filtered, columns and rows are static
Would using a datagrid or some combination of stackpanels/listboxes be best?
I see the "tricky" parts as being adding the "skew adjusted" sub-heading and the separating lines between some of the columns, what would be some suggestions to add these?
I would actually use the standard Grid (not the DataGrid). It seems ankward at the beginning but after you learn how to use it, you're gonna use it everywhere! :)
Here is an example to display your data (its not complete but you see the point):
<Grid x:Name="grdData" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
<ColumnDefinition Width="Auto" MinWidth="50" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" FontSize="14" Padding="10">Annualized Statistics</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="1">1Yr</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="2">2Yr</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="3">3Yr</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="4">4Yr</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="5">5Yr</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="6">1st Half</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="7">2nd Half</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="8">Incept</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">Return</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1">(4.81)</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2">(2.25)</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="3">1.01</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="4">4.30</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="5">(0.61)</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="6">(18.75)</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="7">5.06</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="8">(7.48)</TextBlock>
<Rectangle Grid.Column="5" Grid.Row="1" Grid.RowSpan="5" Fill="Black" Width="1" HorizontalAlignment="Right" />
<Rectangle Grid.Column="7" Grid.Row="1" Grid.RowSpan="5" Fill="Black" Width="1" HorizontalAlignment="Right" />
</Grid>

WPF layout for autosize textblock and icon floating on the right - how?

I am trying to get a layout where an icon floats on the right end of a textblock; the textblock grows/shrinks to content. I cannot make this happen without the textblock running outside the grid. For example:
<Grid x:Name="LayoutRoot" Width="500" HorizontalAlignment="Left" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="0" >
<TextBlock.Text>longer keeps going and going testgrand you going and then t
</TextBlock.Text>
</TextBlock>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="1"/>
</Grid>
Seems like the natural approach and works fine when the text is shorter than the column/grid, except the textbox and column will grow indefinitely and not honor the bounds of the grid.
The inverse, with the icon on the left, works fine with a simpler layout, and the textblock doesn’t grow indefinitely. This is achieved with this markup:
<Grid Grid.Row="1" Width="500" HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="0"/>
<TextBlock x:Name="textBlock2" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="1" HorizontalAlignment="Left">
<TextBlock.Text>longer testgrow the textblock and it will just keep growing but it will stop when it gets too </TextBlock.Text>
</TextBlock>
</Grid>
Any help appreciated. If a grid won’t work, is there an alternate layout where I can get the icon floating on the right of the text, and the textblock will trim text when it’s too long?
Also:
No, using * size columns doesn't work because the columns are fixed, and the icon won't float at the end of the text
A DockPanel doesn't work either, or at least I or others I've asked haven't been able to. The best it can do is to have the icon half-cut-off outside the dockpanel's right side.
Can you get what you want by setting MaxWidth on the TextBlock? If you add MaxWidth="460" to your first example:
<Grid x:Name="LayoutRoot" Width="500" HorizontalAlignment="Left" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock MaxWidth="460" x:Name="textBlock" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="0" >
<TextBlock.Text>longer keeps going and going testgrand you going and then t</TextBlock.Text>
</TextBlock>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="1"/>
</Grid>
Then the TextBlock will grow horizontally and always have the rectangle immediately on its right. It won't be wider than 460, so the TextBlock plus the Rectangle shouldn't be wider than 500. If you need the Grid to resize dynamically then you can bind TextBlock.MaxWidth to Grid.ActualWidth with a converter that subtracts the width of the Rectangle.
Edit:
Actually, it should be even simpler than that. Use star sizing on the columns, but set MaxWidth instead of Width on the Grid. That way, the grid itself will get smaller when the text is smaller so that the rectangle is always at the edge of the text.
<Grid x:Name="LayoutRoot" MaxWidth="500" HorizontalAlignment="Left" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock" VerticalAlignment="Top" Height="25" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="0" >
<TextBlock.Text>longer keeps going and going testgrand you going and then t</TextBlock.Text>
</TextBlock>
<Rectangle Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" Grid.Column="1"/>
</Grid>
Someone internally suggested this answer, which works:
<WrapPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<AccessText TextTrimming="CharacterEllipsis" Grid.Column="0" Margin="0,0,4,0" Text="type more typingon the long hi longer than what if you keep tyingin and get to the end and that's why it changed because you were in the middle" />
<Border Grid.Column="1" Width="10" Height="10" Background="Red" />
</Grid>
</WrapPanel>
The wrappanel seems to provide the necessary magic. I haven't tried Quartermeister's but will save it for future reference!
Our final layout is more complicated and looks like this (it's the header bar for an expander):
<WrapPanel Orientation="Vertical">
<Grid x:Name="HeaderSite" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19" />
<ColumnDefinition Width="16" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" /> <!-- 7/14: fix from list: wrap the whole thing in a wrappanel. Allows for one * col. -->
<ColumnDefinition Width="19" />
</Grid.ColumnDefinitions>
<ToggleButton x:Name="buttonExpanderToggleButton"
Height="20" VerticalAlignment="Top"
/>
<Image x:Name="imageActivityIcon" Grid.Column="1"
Height="16" Width="16"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0"/>
<AccessText x:Name="textActivityID"
Grid.Column="2"
VerticalAlignment="Top" Margin="5,2,0,0"
TextTrimming="CharacterEllipsis"
FontSize="12" HorizontalAlignment="Left" Text="MA77777"/>
<AccessText x:Name="textActivityHeader"
Grid.Column="3"
VerticalAlignment="Top" Margin="0,2,0,0"
TextTrimming="CharacterEllipsis"
FontSize="12" HorizontalAlignment="Left" Text="Title title title title aand Title title title title a little and if you type more what happens as you keep typing "/>
<AccessText x:Name="textActivityStatus"
FontWeight="Normal"
FontStyle="Italic"
Grid.Column="4"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Top" Margin="0,2,8,0"
FontSize="12" HorizontalAlignment="Left" Text="(On Hold)"/>
<Image x:Name="imageLink"
Stretch="None" VerticalAlignment="Top" HorizontalAlignment="Left" Grid.Column="5"/>
</Grid>
</WrapPanel>
This works fine too even with the other auto sized columns. The key seems to be the wrappanel and the one * sized column. If you set them all to auto it doesn't work.
I hope this and Quartermeister's answer helps somebody, because this drove me #$%#$% crazy.
The below code will result in the following output, is that what you are looking for???
longer keeps going and going... [red rectangle]
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="longer keeps going and going testgrand you going and then t" TextTrimming="CharacterEllipsis"/>
<Rectangle Grid.Column="1" Fill="#FFDE3030" Stroke="Black" VerticalAlignment="Top" Height="41" Width="41" />
</Grid>
I had a somewhat similar problem; I wanted to show some content with an externally-sized border area but containing two TextBlocks, where the first is auto-sized and the second is fixed-sized, and the second floats left as the first gets smaller but stops at the right edge (so the first block's text is clipped instead of the second becoming invisible).
Distilling the previous answers, it appears that the key bit of magic is simply to use HorizontalAlignment="Left" with the first column set to star-sized.
<Border BorderThickness="1" BorderBrush="Black">
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Value}" />
<TextBlock Grid.Column="1" Text="⏫" Margin="4,0,0,0" Foreground="Blue" />
</Grid>
</Border>
It appears that the way this works is that (a bit counter-intuitively) the Border stays full width (as set by its parent layout), while the Grid will size to its content -- except that it will not get wider than the containing Border. This keeps the second TextBlock visible.

WPF: Horizontal Alignment

Probably I'm just missing something obvious, but I can't get the image in my DataTemplate to align to the right in the Grid, so that when the window is stretched, the image is "pulled" to the right as well:
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="Minimal.Client.Person">
<Border BorderBrush="Purple" BorderThickness="2" CornerRadius="2" Padding="5" Margin="5">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200"/>
<ColumnDefinition Width="Auto" MaxWidth="200"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0" Orientation="Horizontal" >
<TextBlock FontFamily="Verdana" FontSize="16" FontWeight="Bold" Text="{Binding LastName}" />
<TextBlock FontFamily="Verdana" FontSize="16" Text=", " />
<TextBlock FontFamily="Verdana" FontSize="16" Text="{Binding FirstName}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Vertical" HorizontalAlignment="Right">
<Border BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Picture}" Width="180" Height="150" />
</Border>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
Any suggestions?
I think the problem is that you have set a MaxWidth of 200 for the second column (where the Image is contained). Therefore, the column will not be any wider than 200 pixels and the two columns will not use the complete available space. If you insert another column in between the two columns and make this one star-sized, the Image will be right-aligned:
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" MaxWidth="200"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column ="0" Orientation="Horizontal" >
<TextBlock FontFamily="Verdana" FontSize="16" FontWeight="Bold" Text="{Binding LastName}" />
<TextBlock FontFamily="Verdana" FontSize="16" Text=", " />
<TextBlock FontFamily="Verdana" FontSize="16" Text="{Binding FirstName}" />
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Vertical" HorizontalAlignment="Right">
<Border BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Picture}" Width="180" Height="150" />
</Border>
</StackPanel>
</Grid>
This way, it works for me. However, you should be careful when using StackPanels. They always take as much space as they need. And if they are not given that much space, part of the content will simply be hidden.
gehho.
Try taking out the "MaxWidth" from your second column definition, and setting the Width to "*".
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
This keeeps the explicit settings from positioning your second column to the left.

Resources