I understand there are a few post asking about adding buttons dynamically but I could not find out how to organize them on the stackpanel.
I have no issue adding new buttons but is there any way to organize them in column and row?
<Grid Margin="400,0,0,0">
<StackPanel x:Name="stackpanel">
<Button x:Name="Button" Height="30" Width="100" Content="Button" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="20,20,0,0" Click="Button_Click"/>
</StackPanel>
</Grid>
private void Button_Click(object sender, RoutedEventArgs e)
{
Button b = new Button(); ;
stackpanel.Children.Add(b);
b.Content = "Button";
}
Please help.
Thanks.
Update:
I'd like to add button(s) based on how many times the button is clicked. It adds until 4th rom then move to the next/new column.
From the comments i took, that it is possbile to use a Grid too. To achieve the desired layout you could use the following:
XAML:
<Grid Margin="400,0,0,0">
<Grid x:Name="myGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="addBtn" Height="30" Width="100" Content="Button" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="20,20,0,0" Click="Button_Click"></Button>
</Grid>
</Grid>
I replaced your StackPanel with a Grid and added definitions for the four rows and the first column.
CS:
First we need to add a counter:
public int buttonCounter = 1;
Then we need to change the Button_Click method:
private void Button_Click(object sender, RoutedEventArgs e)
{
//Create the button
Button b = new Button();
b.Height = 30;
b.Width = 100;
b.VerticalAlignment = VerticalAlignment.Top;
b.HorizontalAlignment = HorizontalAlignment.Left;
b.Margin = new Thickness(20, 20, 0, 0);
b.Content = "Button " + buttonCounter;
b.Click += Button_Click;
//Calculate the place of the button
int column = (int)(buttonCounter / 4);
int row = buttonCounter % 4;
//Check if you need to add a columns
if(row == 0)
{
ColumnDefinition col = new ColumnDefinition();
col.Width = new GridLength(column, GridUnitType.Auto);
myGrid.ColumnDefinitions.Add(col);
}
//Add the button
myGrid.Children.Add(b);
Grid.SetColumn(b, column);
Grid.SetRow(b, row);
buttonCounter++;
}
Inside this method, the position of the new button is automatically calculated and if needed, a new column is added to the grid.
Try this:
<Grid Margin="400,0,0,0">
<UniformGrid x:Name="ButtonsUniformGrid" Columns="2" Rows="4"/>
</Grid>
If you need to have a button at the beginning, when you initialize your view do the following:
Button b = new Button(); ;
b.Content = "Button";
b.Click += Button_Click;
ButtonsUniformGrid.Children.Add(b);
Then, everytime you click on it will put a new button in the grid. Let me know if you have any other questions about this :)
As commented Uwp doesn't include UniformGrid if you don't specify it in your xaml. If you want to use it, just include this:
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
and your UniformGrid will be like this:
<controls:UniformGrid...../>
Related
I have a main window written in WPF that contains three sub windows and a user control with buttons. It looks like this:
What I want to do is to have the sub windows' ratio and the buttons' position fixed proportionally with the main window resizing.
I've handled the sub windows' size ratio, but I can't keep the buttons on the left side when the main widow's width is expanded:
And here is my code:
private void MainWindowResize(object sender, SizeChangedEventArgs e)
{
// sub windows size ratio
formA.Height = (this.ActualHeight - 80) * 0.5;
formA.Width = this.ActualWidth;
formB.Height = (this.ActualHeight - 80) * 0.5;
formB.Width = this.ActualWidth * 0.5;
formC.Height = (this.ActualHeight - 80) * 0.5;
formC.Width = this.ActualWidth * 0.5;
// buttons will not move to the left with this code
btnFrame.Width = this.ActualWidth;
}
+) WPF code:
MainWindow
<Grid x:Name="maingrid">
<DockPanel x:Name="panel1" LastChildFill="false">
<Frame DockPanel.Dock="Bottom" Height="35" Width="800" Source="pack://application:,,,/FormBottom;component/form_bottom.xaml" />
<WindowsFormsHost x:Name="formA" DockPanel.Dock="Top" Height="207" Width="800" >
<wftop:form_top x:Name="formTop" Dock="Fill"/>
</WindowsFormsHost>
<WindowsFormsHost x:Name="formB" DockPanel.Dock="Left" Height="207" Width="400" >
<wflt:form_left x:Name="formLeft" Dock="Fill"/>
</WindowsFormsHost>
<WindowsFormsHost x:Name="formC" DockPanel.Dock="Left" Height="207" Width="400">
<wfrt:form_right x:Name="formRight" Dock="Fill"/>
</WindowsFormsHost>
</DockPanel>
<DockPanel x:Name="panel2" LastChildFill="false" >
<Frame DockPanel.Dock="Bottom" Height="35" Width="800" Source="pack://application:,,,/FormBottom;component/form_bottom.xaml" />
</DockPanel>
</Grid>
btnFrame
<UserControl x:Class="FormBottom.form_bottom"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FormBottom"
mc:Ignorable="d"
Height="35" Width="800">
<StackPanel x:Name="bottompanel" Orientation="Horizontal" Height="35" VerticalAlignment="Bottom">
<Button Content="Panel1" MinWidth="70" Click="Button_Click" />
<Button Content="Panel2" MinWidth="70" Click="Button_Click_1" Margin="10,0,0,0" />
</StackPanel>
</UserControl>
Is there a way to have the buttons fixed on the left?
You do not have size control on your own. WPF provides various Panels for layouting out-of-the-box and there are also panels for proportional layouts like Grid or DockPanel.
Your example could look like this in XAML. The Rectangles represent your views. Using Grid panel you can define rows and columns and via their RowDefinition and ColumnDefinition you can set Height and Width to either explicit sizes, e.g. 100, let the size be determined automatically to fit the content with Auto or set star-sizes like 2* which lets you define proportions. The default value is * so in the layout below, the last row sizes to its content and the other rows are sized in proportion 1:1.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Fill="Pink"/>
<Rectangle Grid.Row="1" Grid.Column="0" Fill="MediumSeaGreen"/>
<Rectangle Grid.Row="1" Grid.Column="1" Fill="LightBlue"/>
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<Button Content="A" Width="100" Height="50"/>
<Button Content="B" Width="100" Height="50" Margin="10, 0, 0, 0"/>
</StackPanel>
</Grid>
You can achieve the same layout with different panels, so this is just an example. What is the most suitable approach depends on your requirements and preferences. The same layout in code:
var grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions.Add(new RowDefinition());
grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
grid.ColumnDefinitions.Add(new ColumnDefinition());
grid.ColumnDefinitions.Add(new ColumnDefinition());
var pinkRectangle = new System.Windows.Shapes.Rectangle { Fill = Brushes.Pink };
grid.Children.Add(pinkRectangle);
Grid.SetRow(pinkRectangle, 0);
Grid.SetColumn(pinkRectangle, 0);
Grid.SetColumnSpan(pinkRectangle, 2);
var greenRectangle = new System.Windows.Shapes.Rectangle { Fill = Brushes.MediumSeaGreen };
grid.Children.Add(greenRectangle);
Grid.SetRow(greenRectangle, 1);
Grid.SetColumn(greenRectangle, 0);
var blueRectangle = new System.Windows.Shapes.Rectangle { Fill = Brushes.LightBlue };
grid.Children.Add(blueRectangle);
Grid.SetRow(blueRectangle, 1);
Grid.SetColumn(blueRectangle, 1);
var buttonA = new Button
{
Content = "A",
Width = 100,
Height = 50
};
var buttonB = new Button
{
Content = "B",
Width = 100,
Height = 50,
Margin = new Thickness(10, 0, 0, 0)
};
var stackPanel = new StackPanel { Orientation = Orientation.Horizontal };
grid.Children.Add(stackPanel);
Grid.SetRow(stackPanel, 2);
Grid.SetColumn(stackPanel, 0);
Grid.SetColumnSpan(stackPanel, 2);
stackPanel.Children.Add(buttonA);
stackPanel.Children.Add(buttonB);
I don't know what kind of control the btnFrame is but you can put the buttons inside of a e.g. StackPanel and set the HorizontalAlignment="Left" on it.
Your WPF code would be helpful to provide a better answer.
After you've posted your WPF I think you should just remove the Width="800" attribute from your Frame so that it always stretches to fit its containing DockPanel. Besides I can't see where the btnFrame name is set in the WPF.
I am new to WPF. I have a Button.I want to create Dynamic textBoxes.when ever I got the focus on dynamic textbox the button move to beside textbox. I dont know how to do this. please help me
<Grid Name="mymy" HorizontalAlignment="Left" Height="243" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" Width="263" Margin="462,105,0,0" Grid.RowSpan="2" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<WrapPanel Grid.Column="1" x:Name="abc" HorizontalAlignment="Left" Height="232" Margin="0,0,-250,-218" VerticalAlignment="Top" Width="262" Grid.Row="1"/>
</Grid>
<!--<DockPanel Name="mymy1" HorizontalAlignment="Left" Height="191" LastChildFill="True" Margin="424,94,0,0" VerticalAlignment="Top" Width="282" Grid.RowSpan="2"/>-->
.cs code
private void button_Click(object sender, RoutedEventArgs e)
{
txtSource = new TextBox();
txtSource.MinHeight = 15;
txtSource.Width = 100;
txtSource.Height = 25;
txtSource.Name = "txtSource";
//Binding txtBinding = new Binding("PurchaseOrder.PickupSrcCodeName"); /*txtBinding.Mode = BindingMode.OneWay;*/
//txtSource.SetBinding(TextBox.TextProperty, txtBinding);
ColumnDefinition colDef1;
colDef1 = new ColumnDefinition();
mymy.ColumnDefinitions.Add(colDef1);
RowDefinition rowDef1;
rowDef1 = new RowDefinition();
mymy.RowDefinitions.Add(rowDef1);
++count;
abc.Children.Add(txtSource);
Grid.SetColumn(txtSource, count);
Grid.SetRow(txtSource, 0);
txtSource.GotFocus += t_GotFocus;
txtSource.TextChanged += this.t_TextChanged;
}
private void t_TextChanged(object sender, RoutedEventArgs e)
{
button.Visibility = Visibility.Visible;
}
enter image description here
Welcome to SO.
Consider these two things:
It's extremely bad habit to change the layout of a grid at run time (and by layout I mean rows and columns)
Just the same way that you set your textboxes positions in your grid you can position your button within it as well. You just need to create the rows and columns required by it before head.
My opinion here is you should have a stack layout adding controls to it dynamically. That control then can be custom control consisting of a grid with a text box and a position for button (or maybe a button that can be hidden or visible with a property)
If you like my idea please see this to know how to create custom controls.
I have a ScrollViewer that contains a Grid of images. I am not sure if using a grid is the correct choice. Here is a mockup image of what I want it to look like:
The red box represents the ScrollViewer. Inside it, is some type of layout container (Grid at the moment) that has two rows of images (green squares) but a dynamic amount of columns that can change at runtime, that can be scrolled to. Another condition is that I want to resize them so that 6 images (and only 6!) are always visible.
So in XAML:
<ScrollViewer Name="scrollViewer1">
<Grid Name="grid1"></Grid>
</ScrollViewer>
Then using C# I think I need to dynamically add columns. Then listening to scrollViewer1's SizeChanged event I need to dynamically calculate the size of the rows and columns so that 3 images are always in view. For example:
ColumnDefinition gridColN = new ColumnDefinition();
grid1.ColumnDefinitions.Add(gridColN);
Problem #1: Dynamically adding more columns makes the grid cells keep getting smaller and smaller and never scroll within the ScrollViewer until there are 10+ columns.
Expected result: The end result should be a horizontal stream of images, 6 visible at a time, that will resize when the outter container or window is resized. I am trying to size their width dynamically, but setting them to 1/3 of the containers width does not work.
Questions: Is this the correct approach? Should I use Grid inside the ScrollViewer? Do I have to manually calculate the sizes or is there a way to let them fill the container?
Grid width should be calculated as
grid1.Width = (scrollViewer1.ViewportWidth / 3) * grid1.ColumnDefinitions.Count;
grid1.Height = (scrollViewer1.ViewportHeight / 2) * grid1.RowDefinitions.Count;
This seemed to work for me:
XAML:
<DockPanel>
<ListBox Width="150" DockPanel.Dock="Left" BorderBrush="AliceBlue" BorderThickness="2">
<Button Name="AddColumn_Button" Width="100" Height="25" Content="Add Column" Click="AddColumn_Button_Click" Margin="5"/>
<Button Name="AddRow_Button" Width="100" Height="25" Content="Add Row" Margin="5" Click="AddRow_Button_Click" />
</ListBox>
<ScrollViewer Name="scrollViewer1" BorderBrush="AliceBlue" BorderThickness="2" SizeChanged="scrollViewer1_SizeChanged" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="1">
<Grid Name="grid1" ShowGridLines="True" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
</DockPanel>
CODE BEHIND:
private void scrollViewer1_SizeChanged(object sender, SizeChangedEventArgs e)
{
SizeGrid();
}
private void AddColumn_Button_Click(object sender, RoutedEventArgs e)
{
ColumnDefinition gridColN = new ColumnDefinition();
grid1.ColumnDefinitions.Add(gridColN);
SizeGrid();
}
private void AddRow_Button_Click(object sender, RoutedEventArgs e)
{
RowDefinition row = new RowDefinition();
grid1.RowDefinitions.Add(row);
SizeGrid();
}
private void SizeGrid()
{
grid1.Width = (scrollViewer1.ViewportWidth / 3) * grid1.ColumnDefinitions.Count;
grid1.Height = (scrollViewer1.ViewportHeight / 2) * grid1.RowDefinitions.Count;
}
My grid has 2 rows, 2 columns, and I want to add a textblock dynamically to first row, second column.
This is my code, which doesnt throw an exception but it doesnt show anything
<Grid HorizontalAlignment="Left" Height="768" VerticalAlignment="Top" Width="1366">
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="250"/>
</Grid.ColumnDefinitions>
</Grid>
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
TextBlock txt = new TextBlock();
txt.Width = 200;
txt.Height = 100;
txt.Foreground = new SolidColorBrush(Colors.Yellow);
var location = await InitializeLocationServices();
txt.Text = location;
Grid.SetRow(txt, 0);
Grid.SetColumn(txt, 1);
}
You are never adding your TextBlock to the grid. You should name your Grid (eg. x:Name="myGrid") and call myGrid.Children.Add(txt) at some point.
I'm trying to create a grid programmatically and appending a custom control as a child to the grid as row 0 column 0 out of a 2x2 matrix. To make matters more tricky, I'm using the MVVM design pattern. Heres some code to help everyone get the idea:
App.xaml.cs
base.OnStartup(e);
var viewModel = new MainWindowViewModel();
var mainWindow = new MainWindow();
mainWindow.GridWindows = viewModel.Window.GridWindows;
MainWindowViewModel - method returns the GridWindows.
private Grid CreateGrid()
{
Grid grid = new Grid();
// Create column definitions.
ColumnDefinition columnDefinition1 = new ColumnDefinition();
ColumnDefinition columnDefinition2 = new ColumnDefinition();
columnDefinition1.Width = new GridLength(640);
columnDefinition2.Width = new GridLength(640);
// Create row definitions.
RowDefinition rowDefinition1 = new RowDefinition();
RowDefinition rowDefinition2 = new RowDefinition();
rowDefinition1.Height = new GridLength(340);
rowDefinition2.Height = new GridLength(340);
// Attached definitions to grid.
grid.ColumnDefinitions.Add(columnDefinition1);
grid.ColumnDefinitions.Add(columnDefinition2);
grid.RowDefinitions.Add(rowDefinition1);
grid.RowDefinitions.Add(rowDefinition2);
// Create preview window.
Border border = new Border();
border.BorderThickness = new Thickness(20);
border.Padding = new Thickness(8);
border.SetResourceReference(Control.BackgroundProperty, "PreviewWindow");
MediaRTSPElement previewElement = new MediaRTSPElement();
previewElement.Name = "RTSPStreamPlayer";
previewElement.Stretch = Stretch.UniformToFill;
previewElement.Source = "rtsp://192.100.100.22/media/video1";
previewElement.VideoRenderer = VideoRendererType.EnhancedVideoRenderer;
previewElement.LoadedBehavior = WPFEVR.DirectShow.Players.MediaState.Play;
previewElement.SpeedRatio = 0.5;
//border.Child = previewElement;
// Add preview window.
for (int i = 0; i < 4; i++)
{
grid.Children.Add(previewElement as UIElement);
Grid.SetColumn(previewElement, i);
Grid.SetRow(previewElement, i);
break;
}
return grid;
}
And the XAML Markup that the grid should assign to
<Grid x:Name="GridWindows"></Grid>
The problem is my custom control does not appear in the grid layout, heres the xaml code that does it without code-behind, and this does work:
<Grid x:Name="GridWindows">
<!--<Grid.ColumnDefinitions>
<ColumnDefinition Width="640" />
<ColumnDefinition Width="640" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="340" />
<RowDefinition Height="340" />
</Grid.RowDefinitions>
<Border BorderThickness="20" Padding="8" Background="{DynamicResource ResourceKey=PreviewWindow}" Grid.Row="0" Grid.Column="0">
<evr:MediaRTSPElement x:Name="RTSPStreamPlayer"
Stretch="UniformToFill"
VideoRenderer="EnhancedVideoRenderer"
LoadedBehavior="Play"
Source="rtsp://192.100.100.22/media/video1"
SpeedRatio="0.5" />
</Border>
<Border BorderThickness="20" Padding="8" Background="{DynamicResource ResourceKey=PreviewWindow}" Grid.Row="0" Grid.Column="1">
<evr:MediaRTSPElement x:Name="RTSPStreamPlayer2"
Stretch="UniformToFill"
VideoRenderer="EnhancedVideoRenderer"
LoadedBehavior="Play"
Source="rtsp://192.100.100.78/media/video1"
SpeedRatio="0.5" />
</Border>
<Border BorderThickness="20" Padding="8" Background="{DynamicResource ResourceKey=PreviewWindow}" Grid.Row="1" Grid.Column="0">
<evr:MediaRTSPElement x:Name="RTSPStreamPlayer3"
Stretch="UniformToFill"
VideoRenderer="EnhancedVideoRenderer"
LoadedBehavior="Play"
Source="rtsp://192.100.100.78/media/video1"
SpeedRatio="0.5" />
</Border>
<Border BorderThickness="20" Padding="8" Background="{DynamicResource ResourceKey=PreviewWindow}" Grid.Row="1" Grid.Column="1">
<evr:MediaRTSPElement x:Name="RTSPStreamPlayer4"
Stretch="UniformToFill"
VideoRenderer="EnhancedVideoRenderer"
LoadedBehavior="Play"
Source="rtsp://192.100.100.22/media/video1"
SpeedRatio="0.5" />
</Border>-->
</Grid>
Any ideas as to why programmatic code isn't working?
if you're creating Grid in the xaml you can't later set it in code. Grid (instance) is already in visualtree. Overwriting variable won't do any effect. You should set your Grid as content of xaml defined control. I'm guessing that your code looks like this:
Code:
this.GridWindows = createdGrid;
Xaml:
<Grid x:Name="GridWindows"></Grid>
In code you should have something like this:
this.GridWindows.Children.Add(createdGrid);