I want to create an app that has a listbox on the left side (I'll style it to make it look good later).
On the right side, I want to have an area where I can add controls, etc
So the question is what do I need to do to split the Window into two unequal parts
(left side approx 350 pixels wide and height should be the entire window) and the remainder is for my "canvas."
You can use a Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" /> <!-- Or Auto -->
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" />
<Canvas Grid.Column="1" />
</Grid>
Or you could use a DockPanel:
<DockPanel>
<ListBox DockPanel.Dock="Left" Width="350" />
<Canvas />
</DockPanel>
The benefit of the Grid is you have finer control over the layout and could allow the end user to dynamically resize the columns using a GridSplitter.
An alternate approach to CodeNaked's solution can be to use DockPanel where Canvas takes all space that is left automatically and you don't have to work out splits.
Of course this has the limitation of docking only to the four edges (with possibility of stacking up at edges) but I tend to prefer DockPanel when I'm making initial UI as they're rather quick and easy to setup compared to Grid setup which can get complex fairly quickly.
<DockPanel LastChildFill="True">
<ListBox DockPanel.Dock="Left" Width="350"/>
<Canvas />
</DockPanel>
Related
For reference, this is a chat application. This should give you some idea of a final goal.
Additionally, I am very new to WPF. This is one of my first applications and I am making this as a proof of concept. I've been using Windows Forms up until this point, so any comparison or reference to it would help me understand a bit better.
So, the issue at hand:
The chat box for my chat application is a StackPanel (should it be?) which is programmatically populated with TextBlock elements. I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise).
The question: How can I make a ScrollViewer properly size dynamically with a StackPanel?
Additionally, I also need this StackPanel to size dynamically as the window is sized. This, in turn, would affect the scroll bar.
My current "solution" is to use a ScrollViewer with the StackPanel nested. However, the ScrollViewer and StackPanel do not size properly with a change in window size, as shown in screenshot #2. The XAML code and a screenshot of the designer is shown below.
<Window x:Name="Main" x:Class="dprCxUiDemoWpf.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:dprCxUiDemoWpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="#FF171717">
<TextBox x:Name="ChatBox" TextWrapping="Wrap" Background="#FF4F4F4F" Foreground="White" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" RenderTransformOrigin="-0.118,12.093" Margin="146,0,0,1" VerticalAlignment="Bottom" Height="46" BorderBrush="#FFFF00F3" KeyDown="ChatBox_KeyDown"/>
<Image x:Name="DprLogo" Source="/dprCxUiDemoWpf;component/Images/logo1.png" HorizontalAlignment="Left" Height="60" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="123"/>
<ScrollViewer Background="Red" Height="Auto" Width="Auto" ScrollViewer.CanContentScroll="True" Margin="146,0,0,0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" VerticalAlignment="Top" MinHeight="372">
<StackPanel x:Name="ChatPanel" Height="Auto" Width="Auto" Background="DimGray" ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</Grid>
</Window>
(source: gcurtiss.dev)
Please note the following regarding the first screenshot:
A. The black column (containing the logo) is simply the background color of the window; there is nothing there.
B. The gray portion is ChatBox (the StackPanel)
C. The pink highlighted box below is the text box where messages are entered.
I appreciate and accept any and all help!
You have to use the Grid panel properly. You layout its children by defining rows and columns. Grid is a column/row based layout container. Then configure row and column definitions to control the resize behavior of the cells and their content.
Using absolute positioning and sizes will of course prevent controls from responding to their parent's size changes. Most control stretch to fit the available space. But this requires dimension properties being set to Auto.
You said you are "more of a hands-on learner", but you should still read some documentations. Otherwise you will significantly slow down your progress until stagnation.
There are tons of blogs waiting for you to read them. To poke around in the dark will get you nowhere. You need at least to know the basics. Instead of waiting 13+ hours for a copy & paste ready answer, you could have finished multiple tutorials already and solve this on your own. Success is a good feeling. This is a very trivial problem.
Find yourself a good tutorial that you find easy to understand and start to experiment with the Grid after reading it.
According to your posted code, you obviously have zero idea how this panel works and how you can use it to align your controls.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<StackPanel />
</ScrollViewer>
<Image Grid.Row="1" Grid.Column="0"
Height="60"
Width="123 />
<TextBox Grid.Row="1" Grid.Column="1" />
</Grid>
I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise)
You should read about data-binding and MVVM first. Usually you hold an ObservableCollection of items on your VM and bind them to eg a ListBox on your View.
Then you can scroll-down the ListBox, each time a new item got added to your collection:
private void ScrollRecordsListToBottom()
{
if (RecordsList.Items.Count == 0)
return;
var lastItem = RecordsList.Items[RecordsList.Items.Count - 1];
RecordsList.ScrollIntoView(lastItem);
}
I'm trying to use HorizontalAlignment="Left" in the following situation:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Background="Gray" Text="Small Text" TextWrapping="Wrap"/>
<TextBlock Grid.Column="1" Background="White" Text="This is a very large amount of text" TextWrapping="Wrap"/>
<TextBlock Grid.Column="2" Background="Gray" Text="Medium amount of text" TextWrapping="Wrap"/>
</Grid>
</Window>
My goal is to be able to resize the window, and have the three TextBlocks resize themselves proportionally. This works, but the grid is putting some blank space to the right of the final column, and as I try to resize towards the final column, the columns start to shrink. I want this shrinking behavior, but I don't want it to start until there is no more white space to the right of the rightmost column.
I can't use a UniformGrid as the text lengths can vary, and no other built-in WPF control that I've seen has the ability to resize all children when the parent size changes. I've looked into creating a custom panel, but that seems to be more trouble than it's worth. I feel like something much more simple can be done here.
Any suggestions or ideas are appreciated.
You'll have to build your own custom panel, and handle the case where the AvailableWidth is less then the panel's children DesiredWidth
Panel layout in WPF (Grid is a Panel) is a 2 step process, in the first pass the Panel iterates over its children and provides them with the panel's AvailableWidth. The children respond to this by computing their DesiredWidth.
In the second pass the Panel arranges the children according to their DesiredWidth. In this second pass you have access to the width that all the children (in your case, TextBlocks) require. If it is less than the panel's available width you can compute a percentage to give each one so that they appear to shrink uniformly.
Here's a resource that shows how you can create your own custom panel
What about this?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
For my WPF project in C#, I have to create a menu state with a layout. Here is an image sample of how menu state should look like this.
So, my question is, which layouts should I use to make my components scalable when I resize the window and with components arranged like in prototype? I have used so far gridlayout, but I'm not sure if it is the way to go with components arranged like in prototype image.
Ideally for components that are stacked identical you use a stack panel resizing automatically depends on what the default behaviours for the panels are.
With that said horizontal stack panels auto expand only vertically and vertical stack panels expand horizontally.
Some sample code of it expanding horizontally.
<Window x:Class="WpfApplication2.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">
<StackPanel>
<TextBlock Margin="5,0,5,60" HorizontalAlignment="Center">Title</TextBlock>
<Button>State</Button>
<Button>State</Button>
<Button>State</Button>
<Button>State</Button>
</StackPanel>
</Window>
If you want for them to grow both ways the easiest way is to use a grid and set them to proportional sizes.
Note the text inside the component won't auto grow. If you need that you have to use a viewbox.
How to grow/shrink a TextBlock (Font Size) to the available space in WPF?
I figured out myself how to do this. A trick is to use the properties for Horizontal and Vertical Alignment with value of Stretch and to not use width and height.
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
Example:
<Grid Background="White" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0">
<Viewbox>
<TextBlock>1</TextBlock>
</Viewbox>
</Button>
<Label Grid.Row="0" Grid.Column="1">
<Viewbox>
<TextBlock>2</TextBlock>
</Viewbox>
</Label>
<Button Grid.Row="0" Grid.Column="2">
<Viewbox>
<TextBlock>3</TextBlock>
</Viewbox>
</Button>
</Grid>
I have a page like this:
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
here are contents
they are forever absolutely in the center of the screen
no matter of the resolution/size
</StackPanel>
</Grid>
Everything is working fine. But now I want to add a back button in the top-left corner.
And I don't want to split the grid into 2 columns and 2 rows like this:
the contents are no longer absolutely centered, even we can split the grid by percent, because the contents are sometimes very wide and sometimes very narrow.
I want to know how can I keep the contents horizontal/vertical aligned "Center", and then set the back button's position relatively to the content.
I would suggest using a grid layout with 3 columns to ensure the content is centered with the columns widths set to *,Auto, *. This will ensure the main content is always centered and not care about the size of the button. You can then use margins to set the seperation as required. This is a techinique I have used in SL, WPF & Metro.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button HorizontalAlignment="Right" VerticalAlignment="Top" Content="Do Something"/>
<ContentControl VerticalAlignment="Top" Content="My custom content"/>
</Grid>
slightly hacky ansewer
You might be able to achieve by positioning your stackpanel in the center, and then set a negative left margin the width of the button to shift everything left by the required amount...
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="-45,0,0,0">
<button with width and padding totalling 45px />
here are contents
they are forever absolutely in the center of the screen
no matter of the resolution/size
</StackPanel>
</Grid>
I want to divide my window (wpf) in three columns: left column must be DockPanel ( I think StackPanel will not work on Canvas), the right column should be another DockPanel holding a listbox and in the middle I need to have a Canvas.
This is what I have done and I am having problem with left column since it is not expandable. I need the left column as holder of custom object so that user could drag/drop them on canvas. Please advise.
<DockPanel LastChildFill="True" Background="LightGray" Margin="5">
<Expander Header="Controls" Background="Gray" Margin="2"
Content="{StaticResource FC}" DockPanel.Dock="Top"
IsExpanded="True" Width="200" />
</DockPanel>
<GridSplitter Focusable="False" Width="2" Background="LightGray"
VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
<lib:MyCanvas x:Name="myCanvas" FlowDirection="LeftToRight"
Background="White" AllowDrop="True"
Mouse.MouseMove="MyCanvas _MouseMove">
</lib:MyCanvas >
Also, what control should be used on the right side so that can hold a listbox?
Looking at various question you have asked, it looks like you are looking at building an application similar to Visual studio. I would suggest you to look at following great series of articles on CodeProject similar to your requirement i.e. having ToolBox, various ToolBox items , a designer, drag & drop items on designer etc. -
WPF Diagram Designer - Part 4, Part 3, Part 2, Part 1
You will just have to add a PropertyGrid on the right side and bind your selected ToolBox item in designer with it.
You can embed another grid, or canvas, or other kind of container inside of a grid, you don't have to. But you have a GridSplitter so by definition it needs to be placed inside of a Grid in order to provide its functionality, otherwise it doesn't do anything.
It sounds like you want to do something like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDeifnition Width="*" />
<ColumnDefinition Width="100" /> <!-- whatever size you need here --->
</Grid.ColumnDefinitions>
<DockPanel LastChildFill="True" Background="LightGray" Margin="5" Grid.Column="0">
<Expander Header="Controls" Background="Gray" Margin="2"
Content="{StaticResource FC}" DockPanel.Dock="Top"
IsExpanded="True" Width="200" />
</DockPanel>
<GridSplitter Focusable="False" Width="2" Background="LightGray" Grid.Column="1"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ResizeDirection="Columns"/>
<lib:MyCanvas x:Name="myCanvas" FlowDirection="LeftToRight" Grid.Column="2"
Background="White" AllowDrop="True"
Mouse.MouseMove="MyCanvas _MouseMove">
</lib:MyCanvas >
<ListBox Grid.Column="3" ... />
</Grid>
This layout gives you your 3 columns, the 2nd column is your splitter that lets you resize the first 2 columns (sliding back and forth between them), and your 3rd column is a fixed size.
Create a Grid with 3 columns. For the 1st and 3rd put a DockPanel as child.
I think the general rule is don't put others under canvas, not the other way round.