Dynamically creating buttons in Windows Phone 8.1 - c#

I created app in Windows Phone 8.1(universal). I need to create a dynamic field of the buttons. This is my example xaml:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="_root"
Grid.Row="1"
Margin="10" />
This is my code:
_root.Children.Clear();
_root.ColumnDefinitions.Clear();
Size = 5;
for (int i = 0; i < Size; i++)
{
_root.ColumnDefinitions.Add(new ColumnDefinition
{
Width = new GridLength(1, GridUnitType.Star)
});
}
for (int column = 0; column < Size; column++)
{
Button btnTemp = new Button();
btnTemp.Visibility = Visibility.Visible;
btnTemp.VerticalAlignment = VerticalAlignment.Stretch;
btnTemp.HorizontalAlignment = HorizontalAlignment.Stretch;
btnTemp.HorizontalContentAlignment = HorizontalAlignment.Center;
btnTemp.VerticalContentAlignment = VerticalAlignment.Center;
btnTemp.BorderBrush = new SolidColorBrush(Colors.Blue);
btnTemp.Content = column;
Grid.SetColumn(btnTemp, column);
Grid.SetRow(btnTemp, 0);
_root.Children.Add(btnTemp);
}
It is my result:
If I used :
Width = new GridLength(1, GridUnitType.Auto)
I get result:
I need to fix these bugs:
1.Align text to the center of the button.
Do not see the right border of the button "4".Button "4" other widely.

Fix in your case is quite easy - just define MinWidth of created button as 0 (default has some value).
btnTemp.MinWidth = 0;
I think it should help.
Nevertheless, in your case I wouldn't add buttons to grid, I would probably define ItemsPanel and suitable ItemsTemplate with Button, then bind to a collection.

Related

How to dynamically create and modify a new Grid row elements?

I'm just starting a new WPF app.
I have a grid and want to create the rows dynamically (pressing a button for example) and then create TextView/ProgressBar inside this row.
I already searched how to create the gridrows programatically. But in every solution, i can't access what's inside and it becomes useless.
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button x:Name="AddLineButton" Content="Click to add a new line" Click="AddLineButton_Click"/>
<Grid x:Name="beGrid" Grid.Row="1">
<!-- I need my new rows here -->
</Grid>
</Grid>
int i = 0; //nb of rows
private void AddLineButton_Click(object sender, RoutedEventArgs e)
{
Create_line();
i++;
}
private void Create_line()
{
RowDefinition gridRow = new RowDefinition();
gridRow.Height = new GridLength(1, GridUnitType.Star);
beGrid.RowDefinitions.Add(gridRow);
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.Text = "Question";
textBlock.Name = "Test" + i.ToString();
stack.Children.Add(textBlock);
beGrid.Children.Add(stack);
Grid.SetRow(stack, i);
}
I can't access a previously created element.
AFTER ANSWER :
private void Create_line()
{
RowDefinition gridRow = new RowDefinition();
gridRow.Height = new GridLength(1, GridUnitType.Star);
beGrid.RowDefinitions.Add(gridRow);
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.Text = "Question";
textBlock.Name = "Test" + i.ToString();
RegisterName(textBlock.Name, textBlock);
stack.Children.Add(textBlock);
beGrid.Children.Add(stack);
Grid.SetRow(stack, i);
}
To get the created TextBlock : var text = (TextBlock)FindName("Test"+i.ToString());
you can store all created StackPanel in a List.
private void AddLineButton_Click(object sender, RoutedEventArgs e)
{
Create_line();
}
List<StackPanel> items;
private void Create_line()
{
RowDefinition gridRow = new RowDefinition();
gridRow.Height = new GridLength(1, GridUnitType.Star);
beGrid.RowDefinitions.Add(gridRow);
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
int i = items.Count + 1;
TextBlock textBlock = new TextBlock();
textBlock.Text = "Question";
textBlock.Name = "Test" + i.ToString();
stack.Children.Add(textBlock);
beGrid.Children.Add(stack);
Grid.SetRow(stack, items.Count);
items.Add(stack);
}
you can access any previos panel by index, e.g. items[0], and get elements from Children property: items[0].Children[0] as TextBlock
Creating controls manually like this is really not the WPF way ...
The best methodology is to define an item class that holds properties for each value that you want to display / edit.
Then create an ObservableCollection (since you will be manually adding items on a button click) of these items within your Window, and set this as the ItemsSource property of an ItemsControl control. A DataTemplate is used to define the exact controls to display each item within the control, which will bind to the properties of the item.

Grid is getting smaller after pressing "AddLexemesFromFolder" button

I develop one-page app. This is XAML of MainPage
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Row="0">
<AppBar x:Name="MenuAppBar" IsOpen="True">
<StackPanel Orientation="Horizontal">
<AppBarButton Icon="Add" Label="Добавить лексемы" Name="AddLexemesFromFolder" Click="OpenFolderAndGetLexemes_Click" HorizontalAlignment="Left"/>
<AppBarButton Icon="Save" Label="Сохранить лексемы" Name="SaveLexemes" Click="SaveLexemes_Click" HorizontalAlignment="Left"/>
</StackPanel>
</AppBar>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" VerticalScrollMode="Enabled">
<Grid x:Name="GridLexemesViewer" HorizontalAlignment="Stretch"/>
</ScrollViewer>
</Grid>
When I pressed "AddLexemesFromFolder" button more than two times, GridLexemesViewer is getting smaller over and over.
This is OpenFolderAndGetLexemes code
private async void OpenFolderAndGetLexemes_Click(object sender, RoutedEventArgs routedEventArgs)
{
await StartSaveLexemes();
var folderPicker = new Windows.Storage.Pickers.FolderPicker();
folderPicker.FileTypeFilter.Add("*");
Windows.Storage.StorageFolder folder = await folderPicker.PickSingleFolderAsync();
if (folder != null)
{
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);
await Task.Run(() => StartNewSessionForGetLexemes(folder.Path));
InitializeGrid();
}
}
I use "InitializeGrid" method for clear Children in GridLexemesViewer, use CreateRowsAndColumns and put TextBox with content to GridLexemesViewer.
This is code of InitializeGrid and CreateRowsAndColumns()
private void InitializeGrid()
{
GridLexemesViewer.Children.Clear();
CreateRowsAndColumns();
int index = 1;
foreach (var lexem in CurrentSession.Lexemes)
{
foreach (var item in lexem.Value)
{
Binding binding = new Binding
{
Source = item,
Path = new PropertyPath("Value"),
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
TextBox textBox = new TextBox { TextWrapping = TextWrapping.Wrap };
BindingOperations.SetBinding(textBox, TextBox.TextProperty, binding);
GridLexemesViewer.Children.Add(textBox);
Grid.SetColumn(textBox, CurrentSession.Languages.IndexOf(item.Language) + 1);
Grid.SetRow(textBox, index);
}
index++;
}
}
private void CreateRowsAndColumns()
{
int indexRow = 1;
int indexColumn = 1;
RowDefinition firstRowDefinition = new RowDefinition();
ColumnDefinition firstColumnDefinition = new ColumnDefinition { Width = GridLength.Auto };
GridLexemesViewer.ColumnDefinitions.Add(firstColumnDefinition);
GridLexemesViewer.RowDefinitions.Add(firstRowDefinition);
foreach (var key in CurrentSession.Lexemes.Keys)
{
RowDefinition rowDefinition = new RowDefinition();
GridLexemesViewer.RowDefinitions.Add(rowDefinition);
TextBlock textBlock = new TextBlock{Text = key};
GridLexemesViewer.Children.Add(textBlock);
Grid.SetRow(textBlock, indexRow);
indexRow++;
}
foreach (var language in CurrentSession.Languages)
{
ColumnDefinition columnDefinition = new ColumnDefinition { Width = new GridLength(1.0, GridUnitType.Star)};
GridLexemesViewer.ColumnDefinitions.Add(columnDefinition);
TextBlock textBlock = new TextBlock {Text = language};
GridLexemesViewer.Children.Add(textBlock);
Grid.SetRow(textBlock, 0);
Grid.SetColumn(textBlock, indexColumn);
indexColumn++;
}
}
This GIF shows how to reproduce bug
The problem is that you are calling CreateRowsAndColumns() each time but not removing the Rows and Columns from previous run. Using Grid.Clear() only deletes the children controls in the Grid, but the Grid.RowDefinitions and Grid.ColumnDefinitions stay intact.
To fix this, clear both definitions at the start of CreateRowsAndColumns():
GridLexemesViewer.RowDefinitions.Clear();
GridLexemesViewer.ColumnDefinitions.Clear();
However, definitely consider using the DataGrid control from the Windows Community Toolkit as it should have all the features you need and has better maintainability and performance then a custom Grid, especially for bigger data.

Create StackPanels dynamically and refer to any of them in code behind

In its simplest form...
I would like to create as many StackPanels as I want and then add Rectangles in them. Then to be able to change the Fill color of any one of the Rectangles when I click the Start Button for instance. All in Code Behind.
Any help would be appreciated.
For example, if our favorite beer wrote the framework I could do it like this:
XAML:
<Page
x:Class="Test2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Test2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Name="StartButton" Content="Start" Click="StartButton_Click" Height="30" Width="200" Margin="5"/>
</StackPanel>
<StackPanel Grid.Row="1" Name="myStackPanel" VerticalAlignment="Top"/>
</Grid>
</Page>
Code Behind:
namespace Test2
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
for (var i = 0; i < 5; i++) // The 5 here could be any number
{
myStackPanel.Children.Add(new StackPanel
{
Name = "myPanel" + i,
Orientation = Orientation.Horizontal
});
for (var j = 0; j < 10; j++) // The 10 here could be any number
{
("myPanel" + i).Children.Add(new Rectangle
{
Name = "myRectangle" + i + "-" + j,
Fill = new SolidColorBrush(Colors.Black),
Width = 20,
Height = 20,
Margin = new Thickness(1)
});
}
}
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
// E.G. To change the Fill color of Rectangle4 in StackPanel2
("myRectangle" + 2 + "-" + 4).Fill = new SolidColorBrush(Colors.Red);
}
}
}
Firstly, to add Rectangle shapes, we can create an instance of StackPanel and manipulate its Children elements:
for (var i = 0; i < 5; i++) // The 5 here could be any number
{
StackPanel sp = new StackPanel
{
Name = "myPanel" + i,
Orientation = Orientation.Horizontal
};
myStackPanel.Children.Add(sp);
for (var j = 0; j < 10; j++) // The 10 here could be any number
{
sp.Children.Add(new Rectangle
{
Name = "myRectangle" + i + "-" + j,
Fill = new SolidColorBrush(Colors.Black),
Width = 20,
Height = 20,
Margin = new Thickness(1)
});
}
}
Then to be able to change the Fill color of any one of the Rectangles when I click the Start Button for instance. All in Code Behind.
As tgpdyk mentioned, we need to use VisualTreeHelper to find the specified rectangle shape.
Helper class:
public static class FrameworkElementExtensions
{
public static T TraverseCTFindShape<T>(DependencyObject root, String name) where T : Windows.UI.Xaml.Shapes.Shape
{
T control = null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
{
var child = VisualTreeHelper.GetChild(root, i);
string childName = child.GetValue(FrameworkElement.NameProperty) as string;
control = child as T;
if (childName == name)
{
return control;
}
else
{
control = TraverseCTFindShape<T>(child, name);
if (control != null)
{
return control;
}
}
}
return control;
}
}
How to use it:
private void StartButton_Click(object sender, RoutedEventArgs e)
{
// E.G. To change the Fill color of Rectangle4 in StackPanel2
var rec = FrameworkElementExtensions.TraverseCTFindShape<Shape>(myStackPanel, "myRectangle" + 2 + "-" + 4);
rec.Fill = new SolidColorBrush(Colors.Red);
}
I've uploaded my sample to Github repository
That is not how you approach this in WPF, at all.
You usually do not concern yourself with any UI components but only the data. In this case you data bind an ItemsControl to a list of rows, each row containing a list of cells. In the ItemsControl definition you then set an ItemTemplate that contains another ItemsControl binding to the cells. In the nested ItemsControl you then can set an ItemTemplate where you bind the Background to a (notifying) property of your cells which you then just need to change in code.
Check out these overviews:
Data Binding Overview
Data Templating Overview
You may also want to look into the Model-View-ViewModel pattern to ensure a suitable application architecture.

Dynamic Viewbox not Stretching

In my earlier example of Input Box, I have a window variable. I created some controls(Textbox, Label, Buttons). Parent of these controls is canvas. Parent of canvas is a ViewBox (because ViewBox can only contain one child) and parent of ViewBox is the window.
So hierarchy is like Window->Viewbox->Canvas-> Controls. All these control creation and parenting is done dynamically.
winInputDialog = new Window();
lblPrompt = new Label();
btnOK = new Button();
btnCancel = new Button();
txtInput = new TextBox();
cvContainer = new Canvas();
VB = new Viewbox();
//
// lblPrompt
//
lblPrompt.Background = new SolidColorBrush(SystemColors.ControlColor);
lblPrompt.FontFamily = new FontFamily("Microsoft Sans Serif");
lblPrompt.FontSize = 12;
lblPrompt.Background = new SolidColorBrush(Colors.Transparent);
lblPrompt.FontStyle = FontStyles.Normal;
lblPrompt.Margin = new Thickness(8, 9, 0, 0);
lblPrompt.Name = "lblPrompt";
lblPrompt.Width = 302;
lblPrompt.Height = 82;
lblPrompt.TabIndex = 3;
//
// btnOK
//
btnOK.Margin = new Thickness(322, 8, 0, 0);
btnOK.Name = "btnOK";
btnOK.Width = 64;
btnOK.Height = 24;
btnOK.TabIndex = 1;
btnOK.Content = "OK";
btnOK.Click += new RoutedEventHandler(btnOK_Click);
//
// btnCancel
//
btnCancel.Margin = new Thickness(322, 40, 0, 0);
btnCancel.Name = "btnCancel";
btnCancel.Width = 64;
btnCancel.Height = 24;
btnCancel.TabIndex = 2;
btnCancel.Content = "Cancel";
btnCancel.Click += new RoutedEventHandler(btnCancel_Click);
//
// txtInput
//
txtInput.Margin = new Thickness(8, 70, 0, 0);
txtInput.Name = "txtInput";
txtInput.Width = 379;
txtInput.Height = 25;
txtInput.TabIndex = 0;
//
//Canvas
//
double width = System.Windows.SystemParameters.PrimaryScreenWidth / 3, height = System.Windows.SystemParameters.PrimaryScreenHeight / 4;
cvContainer.Height = height;
cvContainer.Width = width;
cvContainer.Children.Add(txtInput);
cvContainer.Children.Add(btnCancel);
cvContainer.Children.Add(btnOK);
cvContainer.Children.Add(lblPrompt);
cvContainer.ClipToBounds = true;
//
//ViewBox
//
VB.Stretch = Stretch.Fill;
VB.Child = cvContainer;
//
// InputBoxDialog
//
winInputDialog.Width = width;
winInputDialog.Height = height;
winInputDialog.Content = VB;
winInputDialog.Icon = new System.Windows.Media.Imaging.BitmapImage(new System.Uri(System.IO.Directory.GetCurrentDirectory() + #"\drop-box-icon.png"));
winInputDialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
//winInputDialog.WindowStyle = WindowStyle.SingleBorderWindow;
winInputDialog.ResizeMode = ResizeMode.CanResizeWithGrip;
winInputDialog.Name = "InputBoxDialog";
I have set the width and height property of canvas equal to window. But why my screen look like this:
Why is there space between controls and window borders even though they are in viewbox. I even tried Cliptobounds but still the same.
If i set Viewbox height and width it does not stretch and behave unlike a Viewbox.
i want to set this screen dynamically. How?
Sample Project is at http://122.160.24.172/download/customer_data/InputBox_New.rar.
If you want your window to have a dynamic layout, why won't you use a dynamic container unlike Canvas which is static?
You could use a Grid like this -
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.8*"/>
<ColumnDefinition Width ="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="Hello"/>
<Button Grid.Column="1" Content="Ok"/>
<Button Grid.Row="1" Grid.Column="1" Content="Cancel"/>
<TextBox Grid.Row="2" Grid.ColumnSpan="2" Text="Hello"/>
</Grid>
This way your window will layout itself when its size is changed.
You can still adjust the button size and margin if you'd like.
Don't use a Canvas unless you really need support for exact pixel coordination positioning layout.
Also: Why are you layouting your window programatically and not in XAML?

convert this UniformGrid xaml language to C#

Hi everyone i want to convert this xaml code to c# code
please help me using a looping to save memory or line
please help me
<UniformGrid Width="500" Height="500" x:Name="ChessBoard" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid x:Name="Grid1" Background="Yellow" />
<Grid x:Name="Grid2" Background="Black" />
<Grid x:Name="Grid3" Background="Yellow" />
<Grid x:Name="Grid4" Background="Black" />
<Grid x:Name="Grid5" Background="Yellow" />
<Grid x:Name="Grid6" Background="Black" />
<Grid x:Name="Grid7" Background="Yellow" />
<Grid x:Name="Grid8" Background="Black" />
<Grid x:Name="Grid9" Background="Yellow" />
</UniformGrid>
please
UniformGrid ChessBoard = new UniformGrid();
ChessBoard.Width = 500;
ChessBoard.Height = 500;
ChessBoard.HorizontalAlignment = HorizontalAlignment.Center;
ChessBoard.VerticalAlignment = VerticalAlignment.Center;
Grid chressBox = new Grid();
SolidColorBrush yell = new SolidColorBrush(Colors.Yellow);
SolidColorBrush blk = new SolidColorBrush(Colors.Black);
for (int ii = 1; ii <= 9; ii++)
{
if (ii % 2 == 0)
{
chressBox.Background = blk;
chressBox.Name = "Grid" + ii.ToString();
ChessBoard.Children.Add(chressBox);
}
else
{
chressBox.Background = yell;
chressBox.Name = "Grid" + ii.ToString();
ChessBoard.Children.Add(chressBox);
}
}
LayoutRoot.Children.Add(ChessBoard);
itry to create that, but still wrong
For a full chessboard you could do something like this
Brush defaultBrush = new SolidColorBrush(Colors.Yellow);
Brush alternateBrush = new SolidColorBrush(Colors.Black);
for (int x = 0; x < 8; ++x)
{
for (int y = 0; y < 8; ++y)
{
Grid cell = new Grid();
cell.Background = (y+x) % 2 == 0 ? defaultBrush : alternateBrush;
ChessBoard.Children.Add(cell);
}
}
Here is a version with only a single for loop
Brush defaultBrush = new SolidColorBrush(Colors.Yellow);
Brush alternateBrush = new SolidColorBrush(Colors.Black);
for (int i = 0; i < 64; ++i)
{
Grid cell = new Grid();
cell.Background = (i + i / 8) % 2 == 0 ? defaultBrush : alternateBrush;
ChessBoard.Children.Add(cell);
}
This code assumes you have a ChessBoard defined as a UniformGrid as in your example.

Categories