I have a tab control in a window with several tabs on it.
By default the size of the window resizes around the TabControl which is sized to the contents of the TabItems. This is desired behaviour. When one tab is resized all the tabs become that size. Presumably this is because it is the window that is being resized. However the desired behaviour is for the window to size to the tab unless that tab has been resized. If that tab has been resized its size needs to be remembered. I have been unable to implement this correctly, usually it will resize the first time but when the tab is set to again and resized it appears that only the window resizes and not the tabs.
Additionally I am unable to hard code the sizes as the controls on the TabItems are dynamically created and will not always be the same size. Some of these are WPF and some are WinForms controls in a WindowsFormHost. There are about a dozen of these but just using 3 in my example code. One is on a scroll viewer.
<Window....
SizeToContent="WidthAndHeight" ResizeMode="CanResize">
<Grid>
<TabControl x:Name="tabControl" SizeChanged="tabControl_SizeChanged">
<TabItem x:Name="tabItem1" Selector.Selected="tabParams_Selected">
<Grid>
<ScrollViewer MaxHeight="1000">
<DynamicWpfcontrol/>
</ScrollViewer>
</Grid>
</TabItem>
<TabItem x:Name="tabItem2" Selector.Selected="tabRepresentations_Selected">
<Grid>
<WindowsFormsHost Margin="3">
<my:DynamicWinformControl AutoScroll="True" AutoSize="True" AutoSizeMode="GrowAndShrink"/>
</WindowsFormsHost>
</Grid>
</TabItem>
<TabItem x:Name="tabItem3" Selector.Selected="tabAttributes_Selected">
<Grid>
<WindowsFormsHost Margin="3">
<DynamicWinformControl AutoScroll="True" AutoSize="True" AutoSizeMode="GrowAndShrink"/>
</WindowsFormsHost>
</Grid>
</TabItem>
</TabControl>
</Grid>
private Size tab1Size;
private Size tab2Size;
private Size tab3Size;
private void tabControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (tabItem1.IsSelected)
tab1Size = e.NewSize;
else if (tabItem2.IsSelected)
tab2Size = e.NewSize;
else if (tabItem1.IsSelected)
tab3Size = e.NewSize;
}
EDIT - I have updated the tabItemSelected events to use Measure and UpdateLayout instead of setting the width and height. I understand that measure sets the desired size. This is now working for remembering the width but the height is still being set back to the height of the contents.
private void tabItem1_Selected(object sender, RoutedEventArgs e)
{
if (tab1Size != Size.Empty && !(tab1Size.Height == 0 && tab1Size.Width == 0))
{
tabControl.Measure(tab1Size);
tabControl.Arrange(new Rect(tab1Size));
}
this.SizeToContent = System.Windows.SizeToContent.WidthAndHeight;
}
Related
I have a Window defined as follows:
<Window x:Class="AutomatedSQLMigration.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="Height"
DataContext="{Binding MainPageViewModel, Source={StaticResource Locator}}">
<DockPanel Name="MasterDockPanel">
...
</DockPanel>
</Window>
I have a number of tabs in a tab control and the window correctly resizes the height to fit the content of the selected tab as long as the user does not change the window size. Once the user changes the window size manually, the window will not resize itself to fit the content of the selected tab.
What can I do to get the window to resize automatically even after the user has manually changed the size?
As far as I know, you required behavior outside the box, so I can see these solutions:
Set the minimum Height for the Window (like this <Window MinHeight="150" ...), in order the user can not change the Height is less than the specified minimum content of TabControl. I think this is the most simple and reliable solution.
Use a hack like this:
XAML
<Window x:Class="AutoResizeProblem.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"
Loaded="Window_Loaded"
SizeToContent="Height">
<DockPanel Name="MasterDockPanel" Background="Aquamarine">
<TabControl SelectionChanged="TabControl_SelectionChanged">
<TabItem Header="Test1">
<TextBlock Name="Test1Content" Background="Red" Height="100">TEST1</TextBlock>
</TabItem>
<TabItem Header="Test2">
<TextBlock>TEST2</TextBlock>
</TabItem>
<TabItem Header="Test3">
<TextBlock>TEST3</TextBlock>
</TabItem>
</TabControl>
</DockPanel>
</Window>
Code-behind
public partial class MainWindow : Window
{
double _initWindowHeight = 0;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_initWindowHeight = this.Height;
}
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_initWindowHeight > 0)
this.Height = _initWindowHeight;
}
}
In this case, you save the initial Height of the Window in Loaded event. Then, when you change tabs (SelectionChanged event) is assigned to the saved value for the Window.
I cannot understand why in the hell this simple silverlight application freezes up. Here is the code:
namespace test
{
public partial class MainPage : UserControl
{
TextBlock txtword;
public MainPage()
{
InitializeComponent();
txtword = new TextBlock();
txtword.Text = "TEST";
LayoutRoot.Children.Add(txtword);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
txtword.Text = "SuperDuper";
}
}
}
After the textblock is added to the layoutroot if you try to hover or click on the button you can tell that the app has frozen for some reason. Any idea what is going on here??
If i add the text block in the XAML i am able to change its text property in the button click. LayoutRoot.Children.Add() is causing the app to freeze..
From reading your comments it seems the XAML in MainPage.xaml is something like the following:
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Do stuff" Click="Button_Click" />
</Grid>
After adding the TextBlock, either in code or in XAML, you effectively end up with the following:
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Do stuff" Click="Button_Click" />
<TextBlock Text="TEST" />
</Grid>
Your Grid doesn't specify any ColumnDefinitions or RowDefinitions, so you have a 1 × 1 grid with all child controls of the Grid given the entire width and height of the grid.
As neither your Button nor your TextBlock specify a z-index value (using Canvas.ZIndex), their z-order is defined by their position within the grid's Children. The TextBlock comes after the Button, so it is the one that is 'on top'.
The TextBlock may contain only a tiny amount of text, but the TextBlock itself will still fill the Grid. TextBlocks do not automatically resize to fit the text they contain and nothing else. Your Button appears not to work because the TextBlock is on top of it and receives all of the mouse events. TextBlocks are static controls that do nothing in response to any mouse event, and this should explain why your app is appearing to freeze.
Setting the HorizontalAlignment and/or VerticalAlignment of the TextBlock to a value other than Stretch stops the TextBlock being given the entire width and height of the Grid and allows the Button to receive mouse events.
Consider the following XAML:
<Window x:Class="WpfApplication4.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>
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="100" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox>
<ListBox.Items>
<ListBoxItem>a</ListBoxItem>
<!-- Another 11 items -->
</ListBox.Items>
</ListBox>
<ListBox Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.Items>
<ListBoxItem>1</ListBoxItem>
<!-- Another 23 items -->
</ListBox.Items>
</ListBox>
</Grid>
</Window>
The ListBox in the second row shows the vertical scrollbar as disabled and simply cuts off the content.
I want it to be constrained to the visible area of the window. How to achieve this?
Rational behind setting the height of the second grid row to Auto:
I want the second ListBox to display all its content without a scrollbar if there is enough space for it and the first ListBox should take the remaining space.
I don't think there's any way to do what you want in pure XAML - you have to set a specific height for one or other of the two listboxes, or set fixed proportions for them.
I think you could do what you want with a bit of code in the code behind. Give your RowDefinitions and Listboxes names, as follows, and subscribe to the GridSizedChanged event:
<Grid SizeChanged="GridSizeChanged">
<Grid.RowDefinitions>
<RowDefinition x:Name="row1"/>
<RowDefinition x:Name="row2"/>
</Grid.RowDefinitions>
<ListBox x:Name="lb1">
<ListBox.Items>
<ListBoxItem>a</ListBoxItem>
</ListBox.Items>
</ListBox>
<ListBox x:Name="lb2" Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.Items>
<ListBoxItem>1</ListBoxItem>
<!-- Another 23 items -->
</ListBox.Items>
</ListBox>
</Grid>
Then handle the event in the following way:
private void GridSizeChanged(object sender, SizeChangedEventArgs e)
{
double newHeight = e.NewSize.Height;
int lb1ItemCount = lb1.Items.Count;
int lb2ItemCount = lb2.Items.Count;
row1.Height = new GridLength(newHeight * lb1ItemCount / (lb1ItemCount + lb2ItemCount));
row2.Height = new GridLength(newHeight * lb2ItemCount / (lb1ItemCount + lb2ItemCount));
}
This sets the size of the two listboxes to be proportional to the number of items they have inside them. If you want to set a minimum size of 100 for the first listbox, you'll have to do a bit more work to set that size first, then base the second size off the calculated value for the first size.
Edit:
I think I've written a version of GridSizeChanged that does exactly what you require. This version will set the height of lb2 to either the whole grid except the top 100px (if the desired listbox size is bigger than this), or just to its own desired size if that is smaller. The first listbox will then fill all remaining space, and will have a minimum height of 100px as you require, because we did not allow lb2 to fill the top 100px.
private void GridSizeChanged(object sender, SizeChangedEventArgs e)
{
lb2.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double lb2DesiredHeight = lb2.DesiredSize.Height;
double newHeight = e.NewSize.Height;
double lb2AvailableHeight = newHeight - 100;
double lb2ActualHeight = Math.Min(lb2DesiredHeight, lb2AvailableHeight);
row1.Height = new GridLength(newHeight - lb2ActualHeight);
row2.Height = new GridLength(lb2ActualHeight);
}
I am creating an application in WPF and the window has one main grid with 3 rows. There are 3 buttons in the 3rd row and on the click of each button, a panel is displayed in the 2nd grid row. I achieved this by setting the visibility option of the panels. However, now I would like to add an effect/animation as the panels become visible. I don't know where to start, so kindly help.
My xaml code is similar to this
<Window>
<Grid>
<!-- 3row definitions -->
<Grid Grid.Row="0"> </Grid>
<Grid Name="panel1" Grid.row="1" Visibility="Hidden"></Grid>
<Grid Name="panel2" Grid.row="1" Visibility="Hidden"></Grid>
<Grid Name="panel3" Grid.row="1" Visibility="Hidden"></Grid>
<Grid Grid.Row="2"></Grid>
</Grid>
</Windows>
Xaml.cs code to change the visibility is similar to this
private void Image_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
panel1.Visibility = System.Windows.Visibility.Visible;
panel2.Visibility = System.Windows.Visibility.Hidden;
panel3.Visibility = System.Windows.Visibility.Hidden;
}
this can be done using expression studio, in expression blend open your wpf projects, there you can add animations to your wpf controls, you also need to start and stop animation when your application launches,
http://www.youtube.com/watch?v=JpGvl1TayAQ
here is a video tutorial, you can get more tutorials by googling it,
Is there a way to make last tab on TabControl right-aligned ?
Want to make the last one separate from the first few.
Thanks !
This post may be old, but i stumbled across it while searching for an answer to the same question, so i thought I'd share the quick and dirty solution I ended up with.
I just put two TabControls on top of each other in a Grid, and right-aligned the TabPanel on one of them (thanks go out to Meleak):
<Grid>
<TabControl x:Name="_tabsRight" GotFocus="OnTabFocused" >
<TabControl.Resources>
<Style TargetType="TabPanel">
<Setter Property="HorizontalAlignment" Value="Right"/>
</Style>
</TabControl.Resources>
<TabItem x:Name="JustAHiddenTabItemToDeselectTheRealOne" Visibility="Hidden" />
<!-- Last tab -->
<TabItem Header="Last one" >
<!-- Last content... -->
</TabItem>
</TabControl>
<TabControl x:Name="_tabsLeft" GotFocus="OnTabFocused" >
<!-- First tab -->
<TabItem Header="1st" >
<!-- First content... -->
</TabItem>
<!-- Second tab -->
<TabItem Header="2nd" >
<!-- Second content... -->
</TabItem>
</TabControl>
</Grid>
Then, in the OnTabFocused event handler, we need to bring the bottom-most TabControl to the front when the user clicks a TabItem:
private int _zIncrementor = 0;
/// <summary>
/// Hack to make two TabControls act as one.
/// </summary>
private void OnTabFocused(object sender, RoutedEventArgs e)
{
var tab = (TabControl)sender;
var otherTab = (tab == _tabsLeft) ? _tabsRight : _tabsLeft;
Grid.SetZIndex(tab, ++_zIncrementor);
otherTab.SelectedItem = null;
}
Here is an example project on templating the TabControl tabs. I would probably use a Grid with three columns of width "Auto", * and "Auto" and put a StackPanel in the first column to hold the first set of tabs and then just the last tab by itself in the last column with the middle column being empty and just taking up the remaining space.
If you want to have two tabs at left and one at right, You can have the third invisible tab inbetween and width of the invisible tab can be calculated by subtracting the Width of all three visible tabs from the Actualwidth of the Window which gives us the remaining space.
Here is the sample code
<TabControl x:Name="_tabsLeft" GotFocus="OnTabFocused" >
<!-- First tab -->
<TabItem Header="1st" >
<!-- First content... -->
</TabItem>
<!-- Second tab -->
<TabItem Header="2nd" >
<!-- Second content... -->
</TabItem>
<!-- Third invisible tab -->
<TabItem Header="Im not visible in UI" Visibility="Hidden" x:Name="invisibletab" >
<!-- I'm not visible in UI... -->
</TabItem>
<!-- Last tab -->
<TabItem Header="Last one" >
<!-- Last content... -->
</TabItem>
</TabControl>
Backend Code:
public MainWindow()
{
InitializeComponent();
this.SizeChanged += window_SizeChanged;
}
private void window_SizeChanged(object sender, SizeChangedEventArgs e)
{
invisibletab.Width = this.ActualWidth - 550; // where the 550 is the sum of the actual width of visible tabs
}