How can I make a similar looking grid?
I'm going to be writing some desktop applications that simulate something, and I thought it would be a good opportunity to try some new technology. Since the app is for Windows and I saw a Visual Studio Community version somewhere, I decided to try WPF.
So here's the thing. The base view should look like a simple grid where each rectangle is an actual TextBox. I can click and write some text in each text.
That's not bad. At first, I was playing with Grid and ColumnDefinitions and RowDefinitions, which worked well for hardcoded code. After that, I tried using ItemTemplate's ItemsControl and it almost worked.
But now there is a conspiracy twist. I want to be able to edit each individual TextBox. By edit, I mean split it into several smaller TextBoxes. So if I split the second in two and the third in three, it should look like:
And I don't know how to fix this. Since it's not like the others, I don't think I can use the ItemsControl with templates anymore (or can I?). I'm pretty new to WPF, so maybe there's something obvious I haven't seen. So if someone out there knows WPF very well and can point me in the right direction, or at least tell me "what are you doing? WPF is not for this type of application, use XXX instead".
That's a good question, there are of course many ways to lay out controls depending on what the app does and how each control's data is wired to the app/model/whatever.
This answer focuses on layout while taking all available space and allowing duplication of content within each container. The content flows smoothly until the window becomes too small, too narrow, etc. This is where you need to decide what the application will allow the user to do. There's still a lot of work to do before this code can be converted to production quality, but this is a good example of getting familiar with some WPF basics of the WPF platform.
I added some borders, margins and background colors for testing so you can see which container takes up what space. Suitable for testing; may be removed or changed to "transparent" in final release.
Main window
XAML
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<TextBlock Text="WPF" FontSize="36" Margin="20" Foreground="Orange" HorizontalAlignment="Center"/>
</StackPanel>
<Grid Grid.Row="1" Margin="5">
<Border Background="LightGray" BorderBrush="Red" BorderThickness="1">
<UniformGrid Columns="4" Name="MainPanel"/>
</Border>
</Grid>
</Grid>
code
public partial class MainWindow : Window
{
private static int _nextId = 0;
public static int NextId
{
get { return _nextId++; }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// add non-multiple of 8 to see how layout works
for (var i=0; i<7; i++)
{
MainPanel.Children.Add(new EditPanelControl());
}
}
}
EditPanelControl (User Control)
XAML
<Grid Margin="5">
<Border Background="LightYellow" BorderBrush="Green" BorderThickness="1">
<!-- Make this a viewbox if you want to show all items but have them shrink -->
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel Name="MainPanel" VerticalAlignment="Center"/>
</ScrollViewer>
</Border>
</Grid>
code
public partial class EditPanelControl : UserControl
{
public EditPanelControl()
{
InitializeComponent();
DataContext = this;
Loaded += EditPanelControl_Loaded;
}
private void EditPanelControl_Loaded(object sender, RoutedEventArgs e)
{
AddSuperTextControl();
}
private void AddSuperTextControl()
{
var stc = new SuperTextControl();
stc.SplitEvent += Stc_SplitEvent;
stc.DeleteEvent += Stc_DeleteEvent;
stc.SuperTextBox.Text = MainWindow.NextId.ToString();
MainPanel.Children.Add(stc);
}
private void Stc_DeleteEvent(object sender, EventArgs e)
{
// todo: don't allow delete if only 1 child
var stc = (SuperTextControl)sender;
MainPanel.Children.Remove(stc);
}
private void Stc_SplitEvent(object sender, EventArgs e)
{
var stc = (SuperTextControl)sender; // fyi
AddSuperTextControl();
}
}
SuperTextControl (User Control)
XAML
<Grid Margin="5">
<Border Background="Wheat" BorderBrush="Blue" BorderThickness="1">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Stretch">
<TextBox Name="SuperTextBox" Margin="5"/>
<DockPanel LastChildFill="False" Margin="0,0,0,5">
<Button Content="Split" Click="SplitHandler" Margin="5,0" DockPanel.Dock="Left"/>
<Button Content="Delete" Click="DeleteHandler" Margin="5,0" DockPanel.Dock="Right"/>
</DockPanel>
</StackPanel>
</Border>
</Grid>
code
public partial class SuperTextControl : UserControl
{
public event EventHandler SplitEvent;
public event EventHandler DeleteEvent;
public SuperTextControl()
{
InitializeComponent();
}
private void SplitHandler(object sender, RoutedEventArgs e)
{
var button = (Button)sender; // fyi
if (SplitEvent != null)
{
SplitEvent(this, new EventArgs());
}
}
private void DeleteHandler(object sender, RoutedEventArgs e)
{
var button = (Button)sender; // fyi
if (DeleteEvent != null)
{
DeleteEvent(this, new EventArgs());
}
}
}