Dock elements on form - wpf

I am trying to order a WPF form to dock in this order
Menu Strip
Tool Bar
Tab Control
StatusBar
So as you can see, StatusBar is at the bottom, Menu strip is at the top following with Tool Bar and Tab Control. Usually you could just dock to Fill with WinForms but its very complicated and I don't quite understand it when it comes to doing it in WPF, I was wondering if anyone could help me?
I've got them all to dock correctly, and strech horizontally but the Vertical is a bit messed up as the StatusBar is very high in height and the tabControl is a bit low in height, how cam I do it how WinForms does it?
<Window x:Class="Proxy_Scraper.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:Proxy_Scraper"
mc:Ignorable="d"
Title="MainWindow" Height="377.5" Width="811.578">
<Grid>
<DockPanel>
<Menu DockPanel.Dock="Top" HorizontalAlignment="Stretch">
<MenuItem Header="MenuItem" Height="100" Width="100"/>
</Menu>
<ToolBar DockPanel.Dock="Top" HorizontalAlignment="Stretch"/>
<TabControl DockPanel.Dock="Top" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
<StatusBar DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">
<StatusBarItem>
<TextBlock>fff</TextBlock>
</StatusBarItem>
</StatusBar>
</DockPanel>
</Grid>
</Window>

If you set the LastChildFill attribute for the DockPanel to True, the child element you declare last will get all the remaining space.
So the right strategy would be to declare all the elements for which you want a fixed size first (the Menu, ToolBar & StatusBar) and the the last one (I presume the TabControl) will fill the remaining space:
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top" HorizontalAlignment="Stretch">
<MenuItem Header="MenuItem" Height="100" Width="100"/>
</Menu>
<ToolBar DockPanel.Dock="Top" HorizontalAlignment="Stretch"/>
<StatusBar DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">
<StatusBarItem>
<TextBlock>fff</TextBlock>
</StatusBarItem>
</StatusBar>
<TabControl DockPanel.Dock="Top" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</DockPanel>
Note: for complex layout you can easily nest several DockPanels.

Related

How to have a Tab control fill the center of a DockPanel in WPF

I have a WPF Window with a DockPanel that contains a Menu, a TabControl, and a Status Bar. I can get the Menu to dock to the top and the Status Bar to dock to the bottom, but I can't get the Tab control to fill the area between those two controls.
<Window x:Class="MediaCatalog.Window1"
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:MediaCatalog"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<DockPanel LastChildFill="False">
<Menu Width="Auto" VerticalAlignment="Top" HorizontalAlignment="Left"
DockPanel.Dock="Top">
</Menu>
<TabControl DockPanel.Dock="Top" Margin="0,0,0,0"
BorderThickness="1,1,1,1" Height="291">
</TabControl>
<StatusBar DockPanel.Dock="Bottom" Height="22">
<StatusBar/>
</StatusBar>
</DockPanel>
The last element will fill the DockPanel unless you set LastChildFill to false:
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="A" />
<MenuItem Header="B" />
</Menu>
<StatusBar DockPanel.Dock="Bottom" Height="22" />
<TabControl Margin="0,0,0,0" BorderThickness="1,1,1,1">
<TabItem Header="A" />
<TabItem Header="B" />
</TabControl>
</DockPanel>
Note that you shouldn't set the DockPanel.Dock attached property of the last element.
Also, you don't want to specify a default Height for the TabControl if you want it to fill the remaining space.
The above sample markup will make the TabControl fill the remaining space between the top Menu and the bottom StatusBar:

Displaying a large image in ScrollViewer in WPF

I want to display a large image in a Window where the Image will have scroll bars if it too big for the area of the screen it is in.
Underneath the image I want a button panel. For this I have put the Image inside a ScrollViewer in a DockPanel that contains a StackPanel to contain the Buttons in the Bottom part. The idea is to click the Browse button to set the image (from code behind handling Button Click)
The following example I put together will just keep the image size (2144 x 1424) and I cannot see the lower button panel.
<Window x:Class="WpfIssues.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:WpfIssues"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel>
<TextBlock Text="Test Image" FontSize="30" DockPanel.Dock="Top"></TextBlock>
<StackPanel Orientation="Vertical">
<DockPanel x:Name="PhotoPanel">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Click="Button_Click">Browse...</Button>
</StackPanel>
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<StackPanel>
<Image x:Name="PhotoImage"
Stretch="None"
Source="Resources/bear grills.png"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</StackPanel>
</DockPanel>
</Grid>
</Window>
I cant figure out why.
Try putting the button before the scrollviewer, like this:
<DockPanel>
<TextBlock Text="Test Image" FontSize="30" DockPanel.Dock="Top" />
<Button Content="Browse..." DockPanel.Dock="Bottom" />
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Image x:Name="PhotoImage"
Stretch="None"
Source="http://images6.fanpop.com/image/photos/33600000/Bear-Grylls-bear-grylls-33656894-3504-2336.jpg"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</ScrollViewer>
</DockPanel>

why can't I move a control within a tab in WPF designer?

I have a ComboBox within a tab and I can change its size, skew and rotation with the mouse. However, when I want to move it around, I'm not allowed to. To change the combobox's position, I have to manually enter the coordinates in the margin fields, which is really annoying. Why can't I simply move it by dragging it with the mouse?
UPDATE
This actually happens only in a second tab. In the first tab I can move around controls like expected.
So I cut&pasted the tab part in my xaml file in order to change the tab order. Now, I can move around controls in the first tab (former 2nd tab) whereas I can't move controls in the 2nd tab.
Sounds like a WPF designer bug to me...
UPDATE 2
This is a simple test case. The TestComboBox in the 2nd tab can't be moved.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="718" Width="728" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}">
<TabControl HorizontalAlignment="Left" VerticalAlignment="Top">
<TabItem Header="TabItem">
<Grid Margin="0,10,0,4" Height="639" Width="708">
</Grid>
</TabItem>
<TabItem Header="TabItem" Height="23">
<Grid Margin="0,10,0,4" Height="639" Width="708">
<ComboBox x:Name="TestComboBox" HorizontalAlignment="Left" Margin="84,10,0,0" Width="217" VerticalAlignment="Top" Height="22"/>
</Grid>
</TabItem>
</TabControl>
</Window>
After changing the tab order, TestComboBox can be moved:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="718" Width="728" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}">
<TabControl HorizontalAlignment="Left" VerticalAlignment="Top">
<TabItem Header="TabItem" Height="23">
<Grid Margin="0,10,0,4" Height="639" Width="708">
<ComboBox x:Name="TestComboBox" HorizontalAlignment="Left" Margin="84,10,0,0" Width="217" VerticalAlignment="Top" Height="22"/>
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Margin="0,10,0,4" Height="639" Width="708">
</Grid>
</TabItem>
</TabControl>
</Window>
I had the same problem. Solved it by placing the the TabControl inside a grid - see code below.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="718" Width="728" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}">
<Grid>
<TabControl HorizontalAlignment="Left" VerticalAlignment="Top">
<TabItem Header="TabItem" Height="23">
<Grid Margin="0,10,0,4" Height="639" Width="708">
<ComboBox x:Name="TestComboBox" HorizontalAlignment="Left" Margin="84,10,0,0" Width="217" VerticalAlignment="Top" Height="22"/>
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Margin="0,10,0,4" Height="639" Width="708">
<ComboBox x:Name="TestComboBox2" HorizontalAlignment="Left" Margin="84,10,0,0" Width="217" VerticalAlignment="Top" Height="22"/>
</Grid>
</TabItem>
</TabControl>
</Window>
I just tried adding a TabControl into a new WPF Application. I added two TabItem controls with a ComboBox in each. At first, Visual Studio allowed me to move the ComboBox in the first tab, but not the second.
After I moved the ComboBox in the second tab, it would jump back to its original position when I let go of the mouse button. On closer inspection, this was because there was a Grid in the first TabItem, but not the second... perhaps you had a similar problem?
However, after testing the code that you just added, I'm afraid to say that I don't have the same problem that you do. Perhaps you should restart Visual Studio and maybe even your computer?
I have the same problem while working with WPF, but i do this to "bypass" it.
Just comment the grids before the one you'll be working on.
I know that that is painfull to do when working with large projects, but it's the only way i have found.

Dock a resizable wpf DataGrid at bottom of window

In a WPF project, I want to dock a DataGrid to the bottom of a window so that if the window resizes, I will be able to utilize more of the DataGrid. Like this:
How do I do that? All my DockPanel attempts have failed.
The current attempt is here:
<Window x:Class="Foo.SQLDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:Foo.Controls"
Title="test" ResizeMode="CanResize" Width="400" Height="400">
<StackPanel Orientation="Vertical" Height="Auto" Width="Auto">
<StackPanel Orientation="Vertical">
<Label Content="SQL" HorizontalAlignment="Left"/>
<TextBox Width="377" Height="100" Name="txtSQL"/>
<Button Content="Run SQL" Click="Button_Click_1" />
</StackPanel>
<Label Content="Result" HorizontalAlignment="Left"/>
<ScrollViewer Width="Auto" Height="180" DockPanel.Dock="Right,Bottom"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid x:Name="dataResult" />
</ScrollViewer>
</StackPanel>
</Window>
The height of the scrollviewer+datagrid will not adapt however.
First of all, using DockPanel.Dock without having a DockPanel as a parent doesn't do much...
In my example I changed your root StackPanel to a DockPanel so it will work as you want.
I also used DockPanel.LastChildFill property which makes sure the last child of the DockPanel will get all the remaining space:
<DockPanel LastChildFill="True">
<StackPanel Orientation="Vertical" DockPanel.Dock="Top">
<Label Content="SQL" HorizontalAlignment="Left"/>
<TextBox Width="377" Height="100" Name="txtSQL"/>
<Button Content="Run SQL" Click="Button_Click_1" />
</StackPanel>
<Label Content="Result" HorizontalAlignment="Left" DockPanel.Dock="Top"/>
<ScrollViewer DockPanel.Dock="Bottom,Right"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid x:Name="dataResult" />
</ScrollViewer>
</DockPanel>
Finally, to make it really stretch on all the remaining space, I removed the Height property you set, as this blocked it from stretching.
Not sure if useful or if i understand you question the right way but have you tried this:
<DataGrid DockPanel.Dock="Right, Bottom" VerticalAlignment="Bottom" HorizontalAlignment="Right" ></DataGrid>

How to reuse layouts in WPF

I'm trying to create a application that will be tabbed where each tab will have a button area and a view area.
Now each tab will essentially have the same layout just different things in the layout and I wanted to be able to reuse the same layout so that I won't have to change at many places ( it's just not good programming ). Can I accomplish this using resources or perhaps Styles.
Please supply a light code example if possible.
EDIT: I've decided to add an example of what I'm trying to do because I'm still not getting it.
Under each TabItem I'm trying to recreate this grid (It's a bit more complicated but you get the idea ):
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Margin="10"
BorderBrush="{StaticResource MediumColorBrush}"
CornerRadius="10"
BorderThickness="2"
Grid.Row="0">
<!-- First content goes here -->
</Border>
<Border Margin="10"
BorderBrush="{StaticResource MediumColorBrush}"
CornerRadius="10"
BorderThickness="2"
Grid.Row="1">
<!-- Second content goes here -->
</Border>
</Grid>
as you can see also the 2 borders are the same. Now I need to put some content placeholder where my comments are. I wan't to declare this Grid layout in a resource dictionary and then where I use it put seperate content into each border.
I may have alot of TabItems so repeating this code isn't a good idea and each Tab page will have different content in the 2 placeholders.
I'm able to use the
<ContentPresenter Content="{Binding}" />
thing but only for 1 content, what happens when there will be more.
Ingo,
Code is always available on MSDN. Check this: UserControl, Custom controls, DataTemplates.
Here are some examples of each approach. For sake of simplicity, let's assume the layout you want to replicate is one line of text with green foreground (in reality it may be really different, but you get the idea).
1. User Control
Xaml:
<UserControl x:Class="WpfApplication1.GreenTextUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock x:Name="txt" Foreground="Green"/>
</UserControl>
C#:
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class GreenTextUserControl : UserControl
{
public string Text
{
get { return txt.Text;}
set { txt.Text = value; }
}
public GreenTextUserControl()
{
InitializeComponent();
}
}
}
Tab control:
<TabControl>
<TabItem Header="Tab 1">
<loc:GreenTextUserControl Text="This is Tab 1"/>
</TabItem>
<TabItem Header="Tab 2">
<loc:GreenTextUserControl Text="This is Tab 2"/>
</TabItem>
<TabItem Header="Tab 3">
<loc:GreenTextUserControl Text="This is Tab 3"/>
</TabItem>
</TabControl>
2. Custom control
C#:
public class GreenTextBlock : TextBlock
{
public GreenTextBlock()
{
Foreground = Brushes.Green;
}
}
TabControl:
<TabControl>
<TabItem Header="Tab 1">
<loc:GreenTextBlock Text="This is Tab 1"/>
</TabItem>
<TabItem Header="Tab 2">
<loc:GreenTextBlock Text="This is Tab 2"/>
</TabItem>
<TabItem Header="Tab 3">
<loc:GreenTextBlock Text="This is Tab 3"/>
</TabItem>
</TabControl>
If your layout is more complex than textblock, custom controls also allows you to define it in XAML but it differs from UserControls.
3. DataTemplate
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="GreenText" Type="{x:Type System:String}">
<System:String>This is Tab 1</System:String>
<System:String>This is Tab 2</System:String>
<System:String>This is Tab 3</System:String>
</x:Array>
<!--Tab item content data template-->
<DataTemplate x:Key="GreenTextTemplate">
<TextBlock Text="{Binding}" Foreground="Green"/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{StaticResource GreenText}">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="ContentTemplate" Value="{StaticResource GreenTextTemplate}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
</Grid>
</Window>
That's it :). Hope it helps.
TabItem is a ContentControl which allows any child content, but also allows templating of the content, which is exactly what you're trying to do. You can use a DataTemplate like this to do your shared layout. ContentPresenter is the placeholder for the different content of each TabItem.
<DataTemplate x:Key="ButtonViewerTemplate">
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="OK"/>
<Button DockPanel.Dock="Bottom" Content="Cancel"/>
<Border Background="Aqua" BorderBrush="Red" BorderThickness="2" Padding="5">
<ContentPresenter Content="{Binding}" />
</Border>
</DockPanel>
</DataTemplate>
To use the template just set it to each TabItem's ContentTemplate. This works with anything derived from ContentControl.
<TabControl>
<TabItem ContentTemplate="{StaticResource ButtonViewerTemplate}" Header="Some Buttons">
<UniformGrid>
<Button Content="XXXXX"/>
<Button Content="XXXXX"/>
<Button Content="XXXXX"/>
<Button Content="XXXXX"/>
</UniformGrid>
</TabItem>
<TabItem ContentTemplate="{StaticResource ButtonViewerTemplate}" Header="All Blue">
<Border Background="Blue" MinHeight="50"/>
</TabItem>
<TabItem ContentTemplate="{StaticResource ButtonViewerTemplate}" Header="Image">
<Image Source="http://i.msdn.microsoft.com/Platform/Controls/StoMastheadMSDN/resources/logo_msdn.png"/>
</TabItem>
</TabControl>

Resources