C# storyboard draw grid from bottom to top - c#

I have a grid (which also could be a rectangle) which I want to draw from it's bottom to the top. It's a long lineair bar.
<Grid Grid.Row="2" Background="AliceBlue" x:Name="SkillBar11">
<TextBlock Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,40" FontSize="16">IV</TextBlock>
</Grid>
and I can make it draw from the middle to top and bottom with this code in C#:
Storyboard s = new Storyboard();
DoubleAnimation doubleAni = new DoubleAnimation();
doubleAni.To = SkillBar11.ActualHeight;
doubleAni.From = 0;
doubleAni.Duration = new Duration(TimeSpan.FromSeconds(1));
Storyboard.SetTarget(doubleAni, SkillBar11);
doubleAni.EnableDependentAnimation = true;
s.Children.Add(doubleAni);
Storyboard.SetTargetProperty(doubleAni, "Height");
s.Begin();
But I can't get it to be drawn from the bottom to the top, instead of the middle to bottom and top. Can someone help me out? :)

I have found the anwser. You can orientate how the bar grows by setting a Horizontal or Verticalalignment in the grid properties. This way it will start at the position where you aligned it!

Related

UI performance issue when I try to change the width of the ItemsControl in WPF

I'm making a dynamic lyrics player using the ItemsControl from the answer to this question.
I use two ItemsControls that are stacked on top of each other and both are placed in different Canvas (to get the actual width of the text currently being displayed), Canvas are placed in the Grid to ensure that the Canvas will appear in the correct position in the window, like this:
<Grid>
<Canvas x:Name="MainBackLineCanvas" Grid.Column="0" Grid.Row="0">
<ItemsControl x:Name="MainBackLine" ItemsSource="{Binding}">
...
</ItemsControl>
</<Canvas>
<Canvas x:Name="MainColorLineCanvas" Grid.Column="0" Grid.Row="0">
<ItemsControl x:Name="MainColorLine" ItemsSource="{Binding}">
...
</ItemsControl>
</Canvas>
</Grid>
The effect of this window is as follows:
The MainBackLine(the blue text) will be fully displayed at the beginning, the width of the MainColorLine(the yellow text) is set to 0 at the beginning, and the width will increase as the song time increases.
I used a DispatcherTimer to change its width. My code is as follows:
DispatcherTimer TextFlashTimer = new DispatcherTimer();
TextFlashTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
TextFlashTimer.Tick += TextFlash;
private void TextFlash(object? sender, EventArgs e)
{
//playTimePercent is the percentage of the playback progress of the current sentence, which is a Double variable
MainColorLine.Width = ((playTimePercent > 0 && playTimePercent <1) ? playTimePercent : 1) * MainBackLine.ActualWidth;
}
When I run this code, the width of the MainColorLine will change very slowly and not smoothly. I've tried changing the DispatcherTimer's Interval, or using DoEvents, but nothing works.
Is there any way to make it smoother please.
Try to use a DoubleAnimation to animate the width, e.g.:
double width = ((playTimePercent > 0 && playTimePercent < 1) ? playTimePercent : 1) * MainBackLine.ActualWidth;
DoubleAnimation doubleAnimation = new DoubleAnimation(width, new Duration(TimeSpan.FromMilliseconds(100)));
MainColorLine.BeginAnimation(FrameworkElement.WidthProperty, doubleAnimation);

WPF Rectangle Label Blurry

I'm using the code below to draw text over a rectangle in a WPF canvas but it seems to stretch/squash the text and sometimes the back colour does not fill the entire box.
I'm looking for a way to make sure the box is always filled and the text is clear. Probably some form of dynamic font sizing?
Thanks.
Rectangle elip = new Rectangle();
elip.Height = 6;
elip.Width = 6;
Brush brush = new SolidColorBrush(Color.FromRgb(n.Value.R,
n.Value.G, n.Value.B));
Label TB = new Label();
TB.HorizontalAlignment = HorizontalAlignment.Stretch;
TB.Margin = new Thickness(0, 0, 0, 0);
TB.Background = brush;
TB.FontSize = 12;
TB.HorizontalContentAlignment = HorizontalAlignment.Center;
TB.Content = n.Value.Stations[0].TrackId;
BitmapCacheBrush bcb = new BitmapCacheBrush(TB);
elip.Fill = bcb;
elip.Stroke = Brushes.Black;
elip.StrokeThickness = 0.5;
elip.MouseDown += ElipOnMouseDown;
Canvas.SetTop(elip, n.Value.Y - elip.Width / 2);
Canvas.SetLeft(elip, n.Value.X - elip.Height / 2);
cMain.Children.Add(elip);
You can stretch the text using a viewbox.
I recommend using a usercontrol to encapsulate all your markup for things like this. You would have to have many thousands of these things before the overhead of a usercontrol vs building rectangles and whatnot would be significant.
I built this usercontrol:
Height="18" Width="24">
<Border BorderBrush="Black"
BorderThickness="1"
Background="Magenta"
>
<Viewbox Stretch="Uniform">
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextAlignment="Center"
Margin="2"
/>
</Viewbox>
</Border>
</UserControl>
Might not be precisely what you want in terms of width, height or whatever.
You could instantiate one of these, set it's Tag, canvas top and left and add it to the canvas.
Here's the equivalent markup I used to prove it:
Title="MainWindow" >
<Grid>
<Canvas>
<local:StationView Canvas.Left="20"
Canvas.Top="100"
Tag="16B"/>
</Canvas>
</Grid>
</Window>

how to check if an element is going out of its parent in c# wpf

I am creating shapes at run time on canvas in my app and except all the shapes, ellipse is going out of canvas. How do I restrict it to canvas? All other shapes are contained in canvas because of the control points at their vertices. How do I keep a check as to not let ellipse go out of canvas without clipping. I have used ClipToBounds and it doesn't meet my needs.
Also, an alternate solution is if I can add a controlpoint at the left side of ellipse of radiusX property. I can't add a controlpoint to left side of radiusX on ellipse. If you could help me with either of that?
radiusXcp = new ControlPoint(this, EllipseGeom, EllipseGeometry.RadiusYProperty, 1, true, false);
radiusXcp.RelativeTo = EllipseGeometry.CenterProperty;
shape_ControlPoints.Add(radiusXcp);
radiusXcp = new ControlPoint(this, EllipseGeom, EllipseGeometry.RadiusXProperty, 0, true, false);
radiusXcp.RelativeTo = EllipseGeometry.CenterProperty;
shape_ControlPoints.Add(radiusXcp);
//EllipseGeom.RadiusX = -EllipseGeom.RadiusX;
//radiusXcp = new ControlPoint(this, EllipseGeom, EllipseGeometry.RadiusXProperty, 0, true, false);
//radiusXcp.RelativeTo = EllipseGeometry.CenterProperty;
//shape_ControlPoints.Add(radiusXcp);
//EllipseGeom.RadiusX = -EllipseGeom.RadiusX;
Here is a quick example of what i would do. It could be improved on and the code is mainly written to be easy to read and follow. It also does not handle the possibility to if the shape's size is bigger than the Canvas (not sure if that is a use case in your project).
For the example I used the "Loaded" event on the Canvas, to reset the position before drawing. You would want this check before you draw the Ellipse object.
private void TestCanvas_Loaded(object sender, RoutedEventArgs e)
{
//canvas = 450 x 800
Ellipse test_ellipse = new Ellipse();
test_ellipse.Width = 100;
test_ellipse.Height = 100;
test_ellipse.Fill = Brushes.Red;
Canvas.SetLeft(test_ellipse, 700);
Canvas.SetTop(test_ellipse, -500);
Reset_Ellipse_Bounds(TestCanvas, ref test_ellipse);
TestCanvas.Children.Add(test_ellipse);
}
private void Reset_Ellipse_Bounds(Canvas myCanvas, ref Ellipse myEllipse)
{
var left = Canvas.GetLeft(myEllipse);
var top = Canvas.GetTop(myEllipse);
//handle too far right
if (left + myEllipse.Width > myCanvas.ActualWidth)
Canvas.SetLeft(myEllipse, myCanvas.ActualWidth - myEllipse.Width);
//handle too far left
if(left < 0)
Canvas.SetLeft(myEllipse, 0);
//handle too far up
if (top < 0)
Canvas.SetTop(myEllipse, 0);
//handle too far down
if (top + myEllipse.Height > myCanvas.ActualHeight)
Canvas.SetTop(myEllipse, myCanvas.ActualHeight - myEllipse.Height);
}
For Completeness the XAML:
<Grid>
<Canvas x:Name="TestCanvas" Loaded="TestCanvas_Loaded" />
</Grid>
The idea is to check the bounding box against the Canvas edges. There are ways to improve this, but i figured the simplest solution is easier to follow.
Within each if statement you could add more logic or a method to do further processing, but this should answer the general question of knowing if it is outside the parent.
Just set ClipToBounds="true" to its father control, it avoids the canvas to be drawn outside of it.
In my case I set it to Grid as followed :
<Grid x:Name="MainGrid" Background="WhiteSmoke" ClipToBounds="true">
<Canvas Margin="10" Background="Transparent"
SizeChanged="ViewportSizeChanged"
MouseLeftButtonDown="ViewportMouseLeftButtonDown"
MouseLeftButtonUp="ViewportMouseLeftButtonUp"
MouseMove="ViewportMouseMove"
MouseWheel="ViewportMouseWheel">
<Canvas x:Name="canvas" Width="1000" Height="600"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform"/>
</Canvas.RenderTransform>
</Canvas>
<Canvas x:Name="canvas2" Width="1000" Height="600"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas.RenderTransform>
<MatrixTransform x:Name="transform2"/>
</Canvas.RenderTransform>
</Canvas>
</Canvas>
</Grid>

WPF fix Grid position inside a Button

A Button contains a Grid. The structure is like the follows:
<Button>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Viewbox Grid.Column="0">
<TextBlock></TextBlock>
</Viewbox>
<Viewbox Grid.Column="1">
<TextBlock></TextBlock>
</Viewbox>
</Grid>
</Button>
The problem is that the TextBlocks position aren't fixed, so you can see the following situation, where the gridline is moved to the right when the text on left becomes longer. How to make the TextBlocks position fixed inside the grid cell (so the middle grid line stays regardless of the text length)?
EDIT: The above XAML will fix the positons of the TextBlocks as it is, but will not work when the Grid is inside a button. Images below of a Grid with blue background on a silver button: we can see the grid line changes as the text changes (while the ratio stays the same?) primarily because the Grid occupies different region of the Button.
So guess my real question is: How to make the Grid occupy the entire Button? (if the grid stays fixed, so does its grid line.)
EDIT2:
Below is the code that generates the Button, Grid, Viewbox, TextBlock structure represented by the above XAML. This code generates the Buttons as shown below, where the green Grid does not stretch to the entire blue Button and its occupied area differs based on its content. Setting Grid's properties as g.HorizontalAlignment = HorizontalAlignment.Stretch;
g.VerticalAlignment = VerticalAlignment.Stretch; doesn't work.
Button b = new Button();
b.Background = Brushes.DeepSkyBlue;
Grid g = new Grid();
g.Name = "grid";
g.ShowGridLines = true;
g.Background = Brushes.LimeGreen;
g.HorizontalAlignment = HorizontalAlignment.Stretch;
g.VerticalAlignment = VerticalAlignment.Stretch;
//create columns
for (int i = 0; i < 2; i++)
{
int len = i == 0 ? 3 : 1;
ColumnDefinition cd = new ColumnDefinition();
cd.Width = new GridLength(len, GridUnitType.Star);
g.ColumnDefinitions.Add(cd);
}
//viewbox col 0
Viewbox vb = new Viewbox();
TextBlock tb1 = new TextBlock();
vb.Child = tb1;
Grid.SetRow(vb, 0);
Grid.SetColumn(vb, 0);
g.Children.Add(vb);
//viewbox col 1
Viewbox vb2 = new Viewbox();
TextBlock updown = new TextBlock();
vb2.Child = updown;
//specify the Marlett Font
updown.Style = (Style)Application.Current.FindResource("updownBlock");
Grid.SetRow(vb2, 0);
Grid.SetColumn(vb2, 1);
g.Children.Add(vb2);
//add grid to button
b.Content = g;
this should help
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Viewbox Grid.Column="0">
<TextBlock></TextBlock>
</Viewbox>
<Viewbox Grid.Column="1">
<TextBlock></TextBlock>
</Viewbox>
</Grid>
using this example, grid will allot the required space to right column first and then remaining space will be given to left column.
I also removed the row definition as 1 row with * height is the default for grid
EDIT
in order to distribute the space based weighted proportion of available space you may define the weight in star values to both of the columns
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
In above example grid will render left column 3 times bigger then the right column which will be rendered in 1/4 of the total space.
EDIT
set HorizontalContentAlignment="Stretch" & VerticalContentAlignment="Stretch" on the button so that the Grid can occupy whole space, by default it is Center.
Instead of setting the Grid's Alignment, setting the Button's ContentAlignment finally did the trick
b = new Button();
b.HorizontalContentAlignment = HorizontalAlignment.Stretch;
b.VerticalContentAlignment = VerticalAlignment.Stretch;
Now the gridline stays regardless of length of the content :)
Your ColumnDefinition width is 3 and *. * indicates all of the available space, also your textbox or Grid'd width does not seems to be fixed hence it is but obvious that textbloxk's position will change.
Possible steps you can take:
1. Set ColumnDefinition width="Auto"
2. Set the width of textblock and the parent Grid
Buttonobject.HorizontalContentAlignment = HorizontalAlignment.Stretch;
Buttonobject.VerticalContentAlignment = VerticalAlignment.Stretch;
Stretch property will make the content aligned to the button's grid

Rectangle visible only in certain grid area

How to make the green rectangle to be visible only inside the blue one? The blue is a border of a grid. I want to cut off everything that is not inside this grid. Notice that the green rectangle will be moving.
As there is no ClipToBounds property in Silverlight, you would have to set the Clip property to a RectangleGeometry.
When the Grid's size is fixed, you may simply set a fixed size rectangle:
<Grid>
<Grid.Clip>
<RectangleGeometry Rect="0,0,400,600"/>
</Grid.Clip>
...
</Grid>
When the Grid's size can change, you may set the Clip property in a SizeChanged handler:
<Grid SizeChanged="GridSizeChanged">
...
</Grid>
The handler code:
private void GridSizeChanged(object sender, SizeChangedEventArgs e)
{
((UIElement)sender).Clip =
new RectangleGeometry
{
Rect = new Rect(0, 0, e.NewSize.Width, e.NewSize.Height)
};
}
<Grid Width="200" Height="100">
<Grid.Clip>
<RectangleGeometry Rect="0, 0, 200, 100"/>
</Grid.Clip>
source: http://www.scottlogic.co.uk/blog/colin/2009/05/silverlight-cliptobounds-can-i-clip-it-yes-you-can/

Categories