Howto add textboxes in runtime to a WPF Window in C#? [duplicate] - c#

This question already has answers here:
Append a child to a grid, set it's row and column
(4 answers)
Closed 1 year ago.
I am learning algorithmics and C#, and I wanted to write a sudoku solver with some WPF interface.
I need 81 textboxes (one for each sudoku box) and I wanted to generate them with code and put them in array, instead of having 81 different objects.
I used the Visual Studio toolbox to drag and drop buttons in my main window. I also drag and dropped TextBoxes to get an idea how they would look like.
I don't find how to add the TextBoxes to the main Window, using a loop.
Here is the MainWindow.x.aml code :
<Window x:Class="SudokuPOO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SudokuPOO"
mc:Ignorable="d"
Title="Solveur de Sudoku" Height="260" Width="210">
<Grid>
<!--
<TextBox x:Name="T0" HorizontalAlignment="Left" Margin=" 10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="20"/>
<TextBox x:Name="T1" HorizontalAlignment="Left" Margin=" 30,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="20"/>
[ I DELETED STUFF HERE ]
-->
<Button Content="Résoudre" HorizontalAlignment="Left" Margin="10,210,0,0" VerticalAlignment="Top" Click="Resoudre"/>
<Button Content="Effacer tout" HorizontalAlignment="Left" Margin="75,210,0,0" VerticalAlignment="Top" Click="Effacer"/>
</Grid>
Here is the MainWindow.x.aml.cs I am trying to edit :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); // Was already here.
GenerateTextBoxes(); // I added that manually.
}
public void GenerateTextBoxes()
{
TextBox[] TextBoxes = new TextBox[5];
for (int i = 0; i<=4; i++)
{
TextBox TBox = new TextBox();
TBox.Margin = new Thickness(10+20*i, 10, 0, 0);
TBox.Text = i.ToString();
TBox.Width = 20;
TBox.Height = 20;
// this.Controls.Add(TBox); // Doesn't work.
this.Content = TBox; // Trouble here.
TextBoxes[i] = TBox;
}
}
}
I tried with only 5 textboxes to understand what is happening.
This code runs, it displays the MainWindow, but with only one TextBox with "4" in it.
My guess is that
this.Content = Tbox;
replaces all content of the window with only one TBox.
How do I a just add the Tbox ?
The XAML files suggests that a "TextBox" is inside a "Grid" that is inside the "MainWindow", but "this.Grid" doesn't exist, neither "MainWindow.Grid".
I tried stuff like "LayoutRoot.Children.Add", "this.Controls.Add", "this.Grid.Add" and so on, nothing works. Almost all the code I can find online related to TextBox in C# uses "Windows Forms" and not "WPF" and doesn't work in my case (like how to define an array of textboxes in c#? ).

Give the root panel in the XAML markup a name:
<Grid x:Name="grid">
...and then add the textboxes to the panel's Children collection:
grid.Children.Add(TBox);
Also note that that there might be better panels to use for layout instead of mocking around with margins: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/panels-overview?view=netframeworkdesktop-4.8

Related

Programmatically created controls not rendering

As I was required to sort of mask input in a textbox, I decided to construct my own control to handle this.
One of many templates could be "Size {enter size} Colour {enter colour}" which I've broken down to create a series of controls. The custom control that extends StackPanel which I've named CustomTextBox generates the following from the constructor.
// Pseudo
Children = {
Label = { Content = "Size" },
TextBox = { Text = "enter size" },
Label = { Content = "Colour" },
TextBox = { Text = "enter colour" }
// .. and an arbitrary amount of more Labels and TextBoxes in no particular order
}
So far so good. But when I want it to render.. That's where my headache starts.
I've tried to add the controls to the Children property and Measure/Arrange on the parent, itself and all the Children. ActualHeight and ActualWidth do change to something other than 0, but they won't render/display/become visible whatsoever.
I've also tried to use an ItemsControl and add the controls to the ItemsSource property to no avail.
I've tried to predefine sizes on everything, colour the background red and all, but the elusive controls remain to be caught and tied to my screen.
There's got to be a huge "Oooh..." here that I just can't find. I refuse to believe that this can't be done. I mean, it's WPF. WPF is awesome.
Edit Updated to what I currently have that seems most likely to work - still doesn't though.
Whatever I do in the designer shows up, but nothing I do in the CustomTextBox makes any visible difference.
Edit
New headline that fits the problem better.
Also, I've found several examples of programmatically adding controls. Take this article for example. I fail to see the difference between my scenario and theirs, except that theirs work and the buttons are visible.
Update3
The mistake was to assume, that one can simply replace control in visual tree by assigning in codebehind a new control to it's name (specified in xaml)
Updated2
Your mistake was following. If you write
<TextBlock Name="tb" Text="tb"/>
and then in code you will do
tb = new TextBlock() { Text = "Test" };
then you will have a new textblock as a variable, and nothing in xaml will change. You either have to change existing control, or remove old control and add new.
I'm talking about your Headline, Subtext & Description. You don't change them
Updated:
Here is an example of dynamically creating controls by specifying input mask:
MainWindow.xaml
<Window x:Class="WpfApplication35.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:local="clr-namespace:WpfApplication35">
<Grid>
<local:UserControl1 x:Name="myUserControl"/>
</Grid>
</Window>
MainWindow.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
myUserControl.BuildControls("a {enter a} b {enter b1}{enter c2}");
}
}
UserControl1.xaml
<UserControl x:Class="WpfApplication35.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="300">
<WrapPanel Name="root" Orientation="Horizontal"/>
</UserControl>
UserControl1.cs
public partial class UserControl1 : UserControl
{
public List<CustomField> Fields = new List<CustomField>();
public UserControl1()
{
InitializeComponent();
}
public UserControl1(string mask)
{
InitializeComponent();
BuildControls(mask);
}
public void BuildControls(string mask)
{
//Parsing Input
var fields = Regex.Split(mask, #"(.*?\}\s)");
foreach (var item in fields)
{
if (item != "")
{
int index = item.IndexOf('{');
string namestring = item.Substring(0, index).Trim();
var field = new CustomField() { Name = namestring };
string valuesstring = item.Substring(index, item.Length - index).Trim();
var values = valuesstring.Split(new char[] { '{', '}' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var val in values)
{
var valuewrapper = new FieldValue() { Value = val };
field.Values.Add(valuewrapper);
}
Fields.Add(field);
}
}
foreach (var field in Fields)
{
var stackPanel = new StackPanel() { Orientation = Orientation.Horizontal };
var label = new Label() { Content = field.Name, Margin = new Thickness(4) };
stackPanel.Children.Add(label);
foreach (var item in field.Values)
{
var tb = new TextBox() { Margin = new Thickness(4), Width = 200 };
tb.SetBinding(TextBox.TextProperty, new Binding() { Path = new PropertyPath("Value"), Source = item, Mode = BindingMode.TwoWay });
stackPanel.Children.Add(tb);
}
root.Children.Add(stackPanel);
}
}
}
public class CustomField
{
public string Name { get; set; }
public List<FieldValue> Values = new List<FieldValue>();
}
public class FieldValue
{
public string Value { get; set; }
}
This way fields and values are gonna be represented by Fields collection in UserControl1. Values of fields are updated as user types something. But only one-way, i.e. user input updates corresponding Value property, but changing Value property at runtime will not affect corresponding textbox. To implement updating from Value to textbox you have to implement INotifyProperty interface
Obsolete
Since you've asked.
There are hundreds of possible implementations, depending on what are you trying to archieve, how do you want validation to be, do you want to use MVVM, do you want to use bindings etc. There are generally 2 approaches : creating usercontrol and creating custom control. First one suits you better I believe.
Create a usercontrol with following xaml:
<Grid Height="24">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Size: " Grid.Column="0"/>
<TextBox Name="tbSize" Grid.Column="1"/>
<Label Content="Colour:" Grid.Column="2"/>
<TextBox Name="tbColour" Grid.Column="3"/>
</Grid>
In code-behind you can access TextBoxes by their name and do whatever you want to do.
You can use usercontrol in both xaml and codebehind.
In xaml:
Specify alias for namespace of your usercontrol (look at xmlns:local)
<Window x:Class="WpfApplication35.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfApplication35">
<Grid>
<local:UserControl1/>
</Grid>
</Window>
In codebehind you can use it like this:
public MainWindow()
{
InitializeComponent();
var myUserControl = new UserControl1();
}
There is a lot to say and these are basic things, so check tutorials and ask questions.
P.S. If you are learning WPF it's mandatory to learn bindings.

Printing a WPF UserControl created in Code behind

I have a UserControl that I instantiate in code behind and would like to print. When I print this UserControl the code prints a blank piece of paper. Why is this? My code is as follows
private void PrintCurrentTab(object sender, RoutedEventArgs e)
{
PrintDialog printDlg = new PrintDialog();
var child = MyMainWindowViewModel.SelectedTab.Content;
if (child is ScrollViewer)
{
child = (((ScrollViewer)child).Content);
}
if (printDlg.ShowDialog() == true)
{
var printControl = new PrintingTemplate();
printDlg.PrintVisual(printControl, "User Control Printing.");
}
}
My UserControl is as follows
<UserControl x:Class="MyApp.Views.PrintingTemplate"
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"
mc:Ignorable="d"
MinHeight="500"
MaxHeight="1000"
MinWidth="200"
MaxWidth="1000"
Height="1056"
Width="816">
<StackPanel>
<Grid>
<Image Source="..\Resources\Images\PrintLogo.jpg" Width="150" HorizontalAlignment="Right" Margin="20"/>
<Rectangle Fill="Black" Margin="10,40,150,0" Height="2"/>
</Grid>
<Grid Name="PrintingGrid"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Label Content="Printed By:"/>
<Label Name="PrintedBy"/>
<Label Content="Printed On:"/>
<Label Name="PrintedDate"/>
</StackPanel>
</StackPanel>
</UserControl>
I got a similar problem where I was able to print from some computers but not from one (only blank page), on a physical printer (working OK with XPS). I finally got a working solution here:
» https://social.msdn.microsoft.com/Forums/vstudio/en-US/9eb79e11-ee5a-4687-ad4c-a6d96276a8f4/printing-a-wpf-usercontrol?forum=wpf
UserControlToPrint.Measure(New Size(816, 1056))
UserControlToPrint.Arrange(New Rect(New Size(816, 1056)))
UserControlToPrint.UpdateLayout()
Regards,
François from Québec
perhaps the new in the following code is the culprit
if (printDlg.ShowDialog() == true)
{
var printControl = new PrintingTemplate();
printDlg.PrintVisual(printControl, "User Control Printing.");
}
try to get the handle to the current control rather than create a new
Have you debugged this at all? Your question seems incomplete, and I don't really know what you're trying to accomplish. I DO notice that an important step is missing. Here are some questions I have though:
Is MyMainWindowViewModel.SelectedTab.Content == null?
What is child's type?
What is the purpose of child = (((ScrollViewer)child).Content);--that's ugly
Actually why is any of the child code in there, its not being used!
Anyways, you can't print a control that hasn't gone through a layout pass. So now you might ask, "how do I force a control to be rendered?" Simple, like this:
if (printDlg.ShowDialog() == true)
{
var printableArea = new Size(printDlg.PrintableAreaWidth, printDlg.PrintableAreaHeight)
var printControl = new PrintingTemplate();
//Set the drawing dimensions/boundaries - notice (Acutal)Width/Height = 0
printControl.Measure(printableArea);
printControl.Arrange(new Rect(new Point(), printableArea);
//"Render"!
printcontrol.UpdateLayout();
//At this point you should see the (Acutal)Width/Height be > 0!
printDlg.PrintVisual(printControl, "User Control Printing.");
}

How to change the position of dynamic buttons in wp8 c#

In my windows phone application I am creating a dynamic button like below:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="24,0,0,0">
<TextBox x:Name="tb_groupname"
Height="90"
Background="White"
Margin="0,0,125,517"
Foreground="Blue" TextChanged="tb_groupname_TextChanged" GotFocus="tb_groupname_GotFocus" LostFocus="tb_groupname_LostFocus"/>
<Button x:Name="btn_groupname"
Content="Add"
Background="AliceBlue"
Foreground="Blue"
FontSize="25"
Width="120"
Height="90"
HorizontalAlignment="Right"
VerticalAlignment="Top" Click="btn_groupname_Click"></Button>
<ListBox x:Name="lb_groupofcontacts" ItemsSource="{Binding}" Margin="0,118,0,0">
</ListBox>
And below is the xaml.cs page code
private void btn_groupname_Click(object sender, RoutedEventArgs e)
{
if (tb_groupname.Text != string.Empty)
{
List<Button> buttons = new List<Button>();
Button btn = new Button();// Cast the control to Button.
btn.Content = tb_groupname.Text;
btn.Width = 200;
btn.Height = 200;
btn.Click += new RoutedEventHandler(btn_Click); // Add event to button.
buttons.Add(btn);
buttonName = tb_groupname.Text;
tb_groupname.Text = string.Empty;
lb_groupofcontacts.DataContext = buttons;
}
}
And add button into listbox lb_groupofcontacts but when I create another dynamic button it map onto the previous button means it not change the position of the button
And I want that
first button created on top left
second button created on top right
third button created on center left
forth button created on center right
fifth button created on bottom left
sixth button created on botton right
Kindly suggest me how do I change the position of the like above or if you know any other way to change the position of dynamic both then tell me, waiting for your reply.
Thanks.
First of all You are creating object of list into Button_click so every time it is creating new list and adds only new one button and taking that list as listbox's Content so you are not able to add Second Button.
You can set Position of Button Dynamically by:
btn.Margin = new Thickness(LEFT,TOP,RIGHT,BOTTOM);
OR
btn.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
btn.VerticalAlignment = System.Windows.VerticalAlignment.Center;
You can manipulate Button's position by setting its Margin property to different values ("0, 0" for the first button, "0, 200" for the second one and so on).
But that's not very good approach, the right way depends on what you need to do. I'd suggest to replace ListBox with Grid having 6 cells as the most simple decision.

Adding dynamic control with animation

I've got this function:
void Title(string t)
{
if (called == true)
{
GrdLogo.Children.Remove((TextBlock)GrdLogo.FindName("Tb"));
}
TextBlock Tb = new TextBlock();
Tb.Text = t;
Tb.Name = "Tb";
Tb.Height = 160;
Tb.FontSize = 70;
Tb.Margin = new Thickness(Img.Width * 2 + 30, 30, 0, 0);
GrdLogo.Children.Add(Tb);
}
And after calling this function I get error:
WinRT information: The name already exists in the tree: Tb.
Xaml code of thisGrid (GrdLogo):
<Grid x:Name="GrdLogo" HorizontalAlignment="Left" Height="160" VerticalAlignment="Top" Width="1366" Background="#FF1D1D1D">
<Grid.ChildrenTransitions>
<TransitionCollection>
<AddDeleteThemeTransition />
</TransitionCollection>
</Grid.ChildrenTransitions>
</Grid>
Additionally, without transistion it works but I don't know how it can make any difference so I'm asking for help. I'm still pretty fresh in creating dynamic controls. Anyway for me code looks good but maybe I should use some kind of async method to delete this TextBlock?
The error appears because GrdLogo instance doesn't know you removed element named Bd from it's visual tree. You need to notify it by calling FrameworkElement.UnregisterName:
if (called == true)
{
GrdLogo.Children.Remove((TextBlock)GrdLogo.FindName("Tb"));
GrdLogo.UnregisterName("Tb");
}
EDIT
Since you cannot use that method in your W8 code (there is no namescope access in WinRT), you should avoid using named elements altogether. Remove your textblock name in xaml and use other means of removing control like:
GrdLogo.Children.Remove(GrdLogo.Children.OfType<TextBlock>().Single());

Dynamically Generate Set of RadioButtons With Different Content in WPF

I am a C++ developer and recently shifted to C#. I am working on a WPF app where I need to dynamically generate 4 radio buttons. I tried to do lot of RnD but looks like this scenario is rare.
XAML:
<RadioButton Content="Base 0x" Height="16" Name="radioButton1" Width="80" />
Now here is the scenario: I should generate this radio button 4 times with different Content as follows:
<RadioButton Content = Base 0x0 />
<RadioButton Content = Base 0x40 />
<RadioButton Content = Base 0x80 />
<RadioButton Content = Base 0xc0 />
I had done this in my C++ application as follows:
#define MAX_FPGA_REGISTERS 0x40;
for(i = 0; i < 4; i++)
{
m_registerBase[i] = new ToggleButton(String(T("Base 0x")) + String::toHexString(i * MAX_FPGA_REGISTERS));
addAndMakeVisible(m_registerBase[i]);
m_registerBase[i]->addButtonListener(this);
}
m_registerBase[0]->setToggleState(true);
If you notice above, Every-time for loop runs Content name becomes Base 0x0, Base 0x40, base 0x80 and base 0xc0 and sets the toggle state of first radiobutton as true. Thus if you notice there will be single button click method for all these 4 buttons and based on index each will perform operation.
How can i achieve this in my WPF app? :)
I was going to write a set of code for you, but realized your question is probably already answered here:
WPF/C# - example for programmatically create & use Radio Buttons
It's probably the cleanest way of doing it, depending on your requirements of course. If you want the simplest case, here it is:
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid >
<StackPanel x:Name="MyStackPanel" />
</Grid>
</Window>
C#:
public MainWindow()
{
InitializeComponent();
for (int i = 0; i < 4; i++)
{
RadioButton rb = new RadioButton() { Content = "Radio button " + i, IsChecked = i == 0 };
rb.Checked += (sender, args) =>
{
Console.WriteLine("Pressed " + ( sender as RadioButton ).Tag );
};
rb.Unchecked += (sender, args) => { /* Do stuff */ };
rb.Tag = i;
MyStackPanel.Children.Add( rb );
}
}
Just add in whatever logic you need for the content, tags and so on.

Categories