I want to add an array of grids to my WPF window:
Grid[] Tiles = new Grid[20];
public void LoadTile()
{
for (int X = 0; X < Tiles.Length; X++)
{
Tiles[X] = new Grid();
Tiles[X].Height = (TileData[X].SizeY * 90) - 10;
Tiles[X].Width = (TileData[X].SizeY * 90) - 10;
Tiles[X].Margin = new Thickness(0 + (TileData[X].PositionX * 90), 216 + (TileData[X].PositionY * 90), 0, 0);
Tiles[X].HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
Tiles[X].VerticalAlignment = System.Windows.VerticalAlignment.Center;
Tiles[X].Visibility = System.Windows.Visibility.Visible;
SolidColorBrush Brush1 = new SolidColorBrush(Colors.Black);
Brush1.Opacity = 0.2;
Tiles[X].Background = Brush1;
}
}
That's what I have.
(BTW: I do have a method calling that one I just didn't include it here)
I added:
Nine_Window.Content = Tiles[X];
But it made it so all I could display was one of them, because each time the loop did that piece of code again it overwrote the last one
Your usual use for a grid (let's assume 3x3) will look something along the following in the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
Regarding your problem with setting the Content, You are setting it to a specific tile instead of to the array. But Again, it'll be easy to do from the XAML I believe, and simply initialize it from code if you need to.
I think what you are actually looking for is row and column definitions of a grid. Add as many as you need by executing:
Grid Tile = new Grid()
// create new columns
ColumDefintion columnDefinition = new ColumnDefinition()
columnDefinition.Height = ... // set height here
Tile.ColumnDefinitions.Add(columnDefinition);
// create a row
Tile.RowDefinitions.Add(new RowDefinition());
Otherwise your changes will affect the whole grid object.
Well I donot second your approach but if you want to continue with it, do not add your Grids like this
Nine_Window.Content = Tiles[X];
instead add a stackPanel to NineWindow.Content
<Window ....>
<Grid>
<StackPanel x:Name="myStackPanel"></StackPanel>
</Grid>
and then in code behind
myStackPanel.Children.Add(Tile[X]);
Okay, Muds nearly got my answer but I'm gonna use a Canvas instead of a Stack Panel.
If you didn't get what I meant, it's simple, I wanted to create a multiple grid controls in an array and add them to my window.
Related
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);
I try to get two borders in one grid with c# code.
I have one Grid in xaml that looks like this:
<Grid Name="GridKalkAuswahl" ShowGridLines="False" HorizontalAlignment="Left"
Height="Auto" VerticalAlignment="Top" Width="463">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="38px"/>
<ColumnDefinition Width="16px"/>
<ColumnDefinition Width="40px"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
Now I want to insert two Borders into the Grid.
Border myborder = new Border();
myborder.BorderBrush = new SolidColorBrush(Colors.DarkGray);
myborder.BorderThickness = new Thickness(1);
GridKalkAuswahl.Children.Add(myborder);
Grid.SetRowSpan(myborder, noStaffel.Count);
Grid.SetColumnSpan(myborder, 4);
But this ist just one Border. How could i get a second one into the Grid? If I do that the same way the 2 Border are on the same place.
Thank you.
set Margin to inner border. make that Margin equal to outer border Thickness
Border myborder = new Border
{
BorderBrush = Brushes.DarkGray,
BorderThickness = new Thickness(1);
};
GridKalkAuswahl.Children.Add(myborder);
Grid.SetRowSpan(myborder, noStaffel.Count);
Grid.SetColumnSpan(myborder, 4);
Border myborder2 = new Border
{
BorderBrush = Brushes.Orange,
BorderThickness = new Thickness(1),
Margin = myborder.BorderThickness
};
GridKalkAuswahl.Children.Add(myborder2);
Grid.SetRowSpan(myborder2, noStaffel.Count);
Grid.SetColumnSpan(myborder2, 4);
"But this ist just one Border. How could i get a second one into the Grid? If I do that the same way the 2 Border are on the same place." Ofcourse they're on the same place. Everything works as expected with your code. If you will create two borders, add them to the grid as it's children it's fine but if you will set the same columnspan and rowspan they will simply be on the same place. Please take a look at the example below:
Border myborder = new Border();
myborder.BorderBrush = new SolidColorBrush(Colors.DarkGray);
myborder.BorderThickness = new Thickness(1);
GridKalkAuswahl.Children.Add(myborder);
Grid.SetRowSpan(myborder, 1);
Grid.SetColumnSpan(myborder, 4);
Border myborder2 = new Border();
myborder2.BorderBrush = new SolidColorBrush(Colors.Crimson);
myborder2.BorderThickness = new Thickness(1);
GridKalkAuswahl.Children.Add(myborder2);
Grid.SetRowSpan(myborder2, 1);
Grid.SetColumnSpan(myborder2, 3);
Grid.SetColumnSpan and Grid.SetRowSpan is the key to your problem.
I want to get only one number and one circle when I click the button so if I click the button 3 times I will get the 3 numbers with the circles obviously. It's suppose to be lottery numbers appearing after every click but I have no idea how can I do that.
private void btnGo_Click(object sender, RoutedEventArgs e)
{
Ellipse first = new Ellipse();
first.Fill = new SolidColorBrush(Colors.Red);
first.Height = 70;
first.Width = 70;
first.Margin = new Thickness(50, 100, 0, 0);
caPaper.Children.Add(first);
Ellipse second = new Ellipse();
second.Fill = new SolidColorBrush(Colors.Red);
second.Height = 70;
second.Width = 70;
second.Margin = new Thickness(150, 100, 0, 0);
caPaper.Children.Add(second);
Ellipse third = new Ellipse();
third.Fill = new SolidColorBrush(Colors.Red);
third.Height = 70;
third.Width = 70;
third.Margin = new Thickness(250, 100, 0, 0);
caPaper.Children.Add(third);
Random rd = new Random();
TextBlock txt1 = new TextBlock();
txt1.FontSize = 20;
txt1.Foreground = Brushes.White;
txt1.Text = " " + rd.Next(1, 45);
Canvas.SetTop(txt1, 120);
Canvas.SetLeft(txt1, 70);
caPaper.Children.Add(txt1);
TextBlock txt2 = new TextBlock();
txt2.FontSize = 20;
txt2.Foreground = Brushes.White;
txt2.Text = " " + rd.Next(1, 45);
Canvas.SetTop(txt2, 120);
Canvas.SetLeft(txt2, 170);
caPaper.Children.Add(txt2);
TextBlock txt3 = new TextBlock();
txt3.FontSize = 20;
txt3.Foreground = Brushes.White;
txt3.Text = " " + rd.Next(1, 45);
Canvas.SetTop(txt3, 120);
Canvas.SetLeft(txt3, 270);
caPaper.Children.Add(txt3);
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
caPaper.Children.Clear();
This is 100% the wrong approach to this.
You should never be dynamically creating UI controls like this. It's so wrong its not worth figuring out what is wrong in your code.
Use MVVM, it will make your life much, much, much easier. Your lottery numbers are data, the balls, a visual representation of that data. So in your VM:
public ObservableCollection<int> Draws {get;} = new ObservableCollection<int>();
// Invoked from command, or could be a click handler until you learn commands
public AddDraw()
{
int draw = GetNextDraw(); //Left as an exercise
Draws.Add(draw)
}
Now that the data is handled, we need to display it. An ItemsControl is how you display collections, use the ItemTemplate to control how the number is displayed (the ball) and ItemsPanel to control the layout (a horizontal WrapPanel is probably what you want):
<ItemsControl ItemsSource="{Binding Draws}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Ellipse Fill="Red/>
<TextBlock Text="{Binding Path=.}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
That's off the top of my head so I may have a property or class wrong. Intellisense should correct it pretty quickly though.
Once its set up you can add margin, change the colors, whatever. This design is what WPF was built for, take advantage of it.
Disclaimer: I have had to deal with creating UI controls at all ONCE. In 7 years of professional WPF work, and only because the data structure being represented was incredibly complex (and not similar to how it was being represented).
As mentioned below, your approach could use a bit of tweaking. WPF has some really powerful data binding features to build UI MUCH MORE pain free. However to answer your question...
It looks as though the only thing that is 'unique' per ball is the margin of the ball and the left value in Canvas.SetLeft
You should be able to derive those values based on the number of children in your canvas. For example something like this:
int marginLeft = 50 + (caPaper.Children.Length * 100);
Ellipse newBall = new Ellipse();
newBall.Fill = new SolidColorBrush(Colors.Red);
newBall.Height = 70;
newBall.Width = 70;
newBall.Margin = new Thickness(marginLeft, 100, 0, 0);
caPaper.Children.Add(newBall);
You'll want to adjust that of course but I wanted to give you an idea to work with. Also, the same will need to be done for the text. Then, each time you click it, it will create them in relation to the number of children it already has.
I have a canvas (not InkCanvas!) and I am able to draw Polylines on it. This is working just fine but there is a huge problem with drawing out of bounds like shown in the GIF below.
My canvas is inside a ScrollViewer and the ScrollViewer is inside a GridView.
I tried to catch the pointer leaving the canvas with the following event handlers:
canvas.PointerExited += Canvas_PointerExited;
canvas.PointerCaptureLost += Canvas_PointerCaptureLost;
But it seems those events are fired way too slow.
I tried to use the Clip property of my canvas but there is no change in behaviour. And there is no "ClipToBound" property for the UWP canvas.
My whole view is generated in Code-Behind because I have to generate multiple canvases on one view.
Is there a way to stop this behaviour?
EDIT1:
As requested: more insight of my code.
The XAML Page looks like this:
<Grid x:Name="BoundingGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="15*"/>
</Grid.RowDefinitions>
<Grid x:Name="InkGrid" VerticalAlignment="Top" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
<Grid x:Name="CanvasGrid" Grid.Row="1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" VerticalAlignment="Top"/>
</Grid>
It's all inside a Page.
My code behind looks like this:
My constructors:
public ImprovedCanvasManager(Grid boundingGrid, Grid overviewGrid, string filepath, double height)
{
drawCanvas = new Canvas();
overviewGrid.Loaded += OverviewGrid_Loaded;
overviewGrid.SizeChanged += OverviewGrid_SizeChanged;
RowDefinition rd = new RowDefinition();
rd.Height = new GridLength(height);
overviewGrid.RowDefinitions.Add(rd);
InitializeScrollViewer();
Grid.SetRow(scroll, overviewGrid.RowDefinitions.Count);
Grid.SetColumn(scroll, 0);
scroll.Content = drawCanvas;
overviewGrid.Children.Add(scroll);
LoadImage(filepath);
}
public ImprovedCanvasManager(Grid boundingGrid, Grid overviewGrid, Grid inkToolGrid, string filepath, double height = 1000) : this(boundingGrid, overviewGrid, filepath, height)
{
AddDrawingToolsToCanvas(inkToolGrid, overviewGrid);
EnableDrawingOnCanvas(drawCanvas);
}
I only got two contructors to make it simple for me to instantiate canvases with the ability to draw and without the ability to draw.
This is how i initialise my ScrollViewer:
private void InitializeScrollViewer()
{
scroll = new ScrollViewer();
scroll.VerticalAlignment = VerticalAlignment.Top;
scroll.VerticalScrollMode = ScrollMode.Auto;
scroll.HorizontalScrollMode = ScrollMode.Auto;
scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
scroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
scroll.ZoomMode = ZoomMode.Enabled;
scroll.ManipulationMode = ManipulationModes.All;
scroll.MinZoomFactor = 1;
scroll.MaxZoomFactor = 3;
}
Those are the only lines of code that affect any viewbuilding.
Edit 2:
My canvas doesn't fill the surrounding Grid on the left, but on the bottom.
The code in your PointerMoved handler should be relative to the canvas.
private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
var point = e.GetCurrentPoint(canvas); // <-- relative to canvas.
var x = point.Position.X;
var y = point.Position.Y;
x = Math.Max(x, 0);
y = Math.Max(y, 0);
x = Math.Min(canvas.ActualWidth, x);
y = Math.Min(canvas.ActualHeight, y);
// add point to polyline...
}
If x/y are negative or greater than the size of the canvas, then they are out of bounds. You can either limit the point to the border of the canvas as the code above does, or you can discard the point completely.
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