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.
Related
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.
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.
where's the problem?
I would like to add a new textbox to grid dynamically. (Silverlight)
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBox o = new TextBox();
o.SetValue(Canvas.TopProperty, 160); // margin top, I hope.
o.SetValue(Canvas.LeftProperty, 40); // margin left, I hope.
o.Height = 31;
o.Width = 140;
o.HorizontalAlignment= HorizontalAlignment.Left;
o.TextWrapping = TextWrapping.Wrap;
o.VerticalAlignment = VerticalAlignment.Top;
LayoutRoot.Children.Add(o);
}
Thank you so much.
Regards, Daniele.
I think LayoutRoot is a Grid in XAML:
<Grid Name="LayoutRoot">
In this case, you should remove the assignment of Canvas.Top and Canvas.Left and set the Margin of the TextBox:
TextBox o = new TextBox();
o.Margin = new Thickness(40, 160, 0, 0);
//...
Canvas.-properties has only an effect in the Canvas container. And you should use the corresponding methods to set the attached properties in codebehind:
Canvas.SetTop(o, 160);
Ihave some Problems to attach Textblocks in my Grid.
I cant use SetRow(Frameworkelement,index);
The ErrorMessage is something like that I cant access the MemberFunction with an instance reference.
Instead i should use a TypeName, but how?
private FrameworkElement CreateGrid(int i)
{
double w = 775;
double l = 1105;
TextBlock header = CreateHeader("someRndStuffHeader");
RowDefinition headerRowDefinition = new RowDefinition
{
MinHeight = header.ActualHeight,
MaxHeight = header.ActualHeight,
};
TextBlock footer = CreateFooter("someRndStuffFooter");
RowDefinition footerRowDefinition = new RowDefinition
{
MinHeight = footer.ActualHeight,
MaxHeight = footer.ActualHeight
};
double contentHeight = l- header.ActualHeight - footer.ActualHeight;
RowDefinition contentRowDefinition = new RowDefinition
{
MinHeight = contentHeight,
MaxHeight = contentHeight,
};
ColumnDefinition gridColumnDefinition = new ColumnDefinition()
{
MaxWidth = w,
MinWidth = w,
};
Grid page = new Grid();
string name = "printPage" + i.ToString();
page.Name = name;
page.RowDefinitions.Add(headerRowDefinition);
page.RowDefinitions.Add(contentRowDefinition);
page.RowDefinitions.Add(footerRowDefinition);
page.ColumnDefinitions.Add(gridColumnDefinition);
// I CANT USE THIS
page.SetRow(header, 1);
return page;
}
SetRow(FrameworkElement framework,int value) is a static method. Instance members cannot use it.
Use like this :-
Grid.SetRow(header,1);
However, before you can achieve that, you have to make header and footer TextBlocks children of the newly formed grid because SetRow method sets the row of framework element only when it is a child of any grid.
So you will have to add these two statements :-
page.Children.Add(header);
page.Children.Add(footer);
Further, here the new grid i.e page, it has to be also assigned a parent grid. When you create a new AppPage (BlankPage.xaml) then by default a parent grid is already rendered by the system. Name this parent grid as say x:Name="Layout" and then add grid 'page' to this 'Layout' grid. I am giving here full code, both xaml and .cs. I have created a button and then when i press the button, new grid is created
In XAMl :-
<Grid x:Name="layout" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Button" HorizontalAlignment="Left" Margin="587,475,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
In .cs :-
private FrameworkElement CreateGrid(int i)
{
double w = 775;
double l = 1105;
TextBlock header = CreateHeader("someRndStuffHeader");
RowDefinition headerRowDefinition = new RowDefinition
{
MinHeight = header.ActualHeight,
MaxHeight = header.ActualHeight,
};
TextBlock footer = CreateFooter("someRndStuffFooter");
RowDefinition footerRowDefinition = new RowDefinition
{
MinHeight = footer.ActualHeight,
MaxHeight = footer.ActualHeight
};
double contentHeight = l - header.ActualHeight - footer.ActualHeight;
RowDefinition contentRowDefinition = new RowDefinition
{
MinHeight = contentHeight,
MaxHeight = contentHeight,
};
ColumnDefinition gridColumnDefinition = new ColumnDefinition()
{
MaxWidth = w,
MinWidth = w,
};
Grid page = new Grid();
string name = "printPage" + i.ToString();
page.Name = name;
page.RowDefinitions.Add(headerRowDefinition);
page.RowDefinitions.Add(contentRowDefinition);
page.RowDefinitions.Add(footerRowDefinition);
page.ColumnDefinitions.Add(gridColumnDefinition);
**Grid.SetRow(header, 1);
page.Children.Add(header);
page.Children.Add(footer);**
return page;
}
private TextBlock CreateFooter(string p)
{
return new TextBlock() { Width=300,Height=300,Text=p};
}
private TextBlock CreateHeader(string p)
{
return new TextBlock() { Width = 300, Height = 300, Text = p };
}
private void Button_Click(object sender, RoutedEventArgs e)
{
layout.Children.Add(CreateGrid(1));
}
I made a ListBox through the xaml file. When the user enters something in a textbox, it'll add a textbox and a button to the Row of the ListBox.
I want that button to be a "delete-it's-own-row button". But when I try to do it, it won't let me access the ListBox because "Cannot access non-static field in a static context".
public class GenericRowItem
{
private StackPanel _genericStackPanel = new StackPanel();
private TextBox _genericTextBoxImage = new TextBox();
private TextBox _genericTextBoxText = new TextBox();
private CheckBox _genericCheckBox = new CheckBox();
private Button _genericButton = new Button();
public GenericRowItem(String text)
{
_genericButton.Content = "X";
_genericButton.VerticalAlignment = VerticalAlignment.Top;
_genericButton.HorizontalAlignment = HorizontalAlignment.Right;
_genericButton.Width = 25;
_genericButton.Height = 25;
_genericButton.VerticalContentAlignment = VerticalAlignment.Center;
_genericButton.HorizontalContentAlignment = HorizontalAlignment.Center;
_genericButton.Click += GenericListBox.Items.... <--- THIS DOESN'T WORK, IT WONT ACCESS GENERICLISTBOX
}
public StackPanel GetStackPanel()
{
return _genericStackPanel;
}
}
my xaml code:
<ListBox Name ="GenericListBox" HorizontalAlignment="Left" Height="195" Margin="25,345,0,0" VerticalAlignment="Top" Width="650" FontFamily="Courier New" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
</ListBox>