How to right-align last TabControl tab? - c#

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
}

Related

WPF changing several properties one after another

I have a TabControl that contains a TextBox per tab. I need to select a tab programmatically and then set the selection of the TextBox and set the focus to the TextBox.
My problem is, when I first select a new tab in code and then set the focus to the textbox it doesn't work.
When the desired tab is already the selected tab and I set the focus to the textbox inside the tab, it's working.
It seems, that performing several actions on WPF elements does not work.
What is the right way to first switch the tab of a tabcontrol and then set the focus to a child in the newly selected TabItem?
Edit: I've found something on the Internet: Waiting for the render thread to complete:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/693fbedb-efa6-413e-ab66-530c6961d3fb/how-to-wait-for-the-wpf-render-thread-to-catch-up?forum=wpf
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => { })).Wait();
But is this the right way?
BTW: What is the difference between:
Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() => { })).Wait();
Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => { }));
Use the FocusManager.SetFocusedElement() method.
Focus has some nuances, you can learn more here.
XAML
<TabControl>
<TabItem Header="One"
x:Name="tabOne">
<Button HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="Set Focus"
Click="Button_Click" />
</TabItem>
<TabItem Header="Two"
Name="tabTwo">
<StackPanel>
<TextBox Name="txtOne" />
<TextBox Name="txtTwo" />
</StackPanel>
</TabItem>
<TabItem Header="Three"
x:Name="tabThree">
<StackPanel>
<TextBox Name="txtThree" />
<TextBox Name="txtFour" />
</StackPanel>
</TabItem>
</TabControl>
CODE
private void Button_Click(object sender, RoutedEventArgs e)
{
tabTwo.IsSelected = true;
FocusManager.SetFocusedElement(tabTwo, txtTwo);
}

How to change TabControl selected tab

I have a TabControl on a window which has three tabs in total. I would like to select the first tab programmatically. I've tried TabControlMain.SelectedItem = 0, but this doesn't work. How can I do this?
<TabControl x:Name="TabControlMain">
<TabItem x:Name="TabItemA" Header="A">
...
</TabItem>
<TabItem x:Name="TabItemB" Header="B">
...
</TabItem>
<TabItem x:Name="TabItemC" Header="C">
...
</TabItem>
</TabControl>
You can set TabControl.SelectedIndex = 0 (docs)
Or set YourDesiredTabItem.IsSelected = true (docs)
TabControl.SelectedItem can only be a TabItem object or null (docs).

Auto alignment of buttons in toolbar

Is there a way to have a toolbar (stock/custom) which aligns all buttons together to one side, say, left?
I have 3 buttons, a,b,c on toolbar. If i dynamically make b hidden, then there is a visible gap between a and c. How can I have a toolbar which automatically closes gap between a and c together, but restores the order when b becomes visible again?
Thanks!
Info
I have tried <Toolbar />, <Stackpanel />, <DockPanel /> so far, but they did not align the buttons.
You need to make b Collapsed instead of Hidden
Refer to MSDN UIElement.Visibility Property:
In the WPF model, Hidden denotes a visibility state where the object
should not render, but should still occupy space in a WPF layout.
And more specific, as #Nayan pointed out, can refer to Visibility Enumeration:
Collapsed Do not display the element, and do not reserve space for it in layout.
Hidden Do not display the element, but reserve space for the element in layout.
Visible Display the element.
PS: That is why for BooleanToVisibilityConverter:
The Convert method returns Visibility.Visible when true is passed in or Visibility.Collapsed when false is passed in.
Use a StackPanel:
<StackPanel Orientation="Horizontal">
<Button Width="100" Click="Button_Click" />
<Button Width="100" Click="Button_Click" />
<Button Width="100" Click="Button_Click" />
<Button Width="100" Click="Button_Click" />
</StackPanel>
private void Button_Click(object sender, RoutedEventArgs e)
{
((Button)sender).Visibility = Visibility.Collapsed;
}

Remember tab size where the tabs have been resized

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;
}

How to align RibbonGroup to the right?

I use RibbonControlsLibrary. How to align one RibbonGroup to the right? It should be only one group in tab. All other groups should be aligned to the left.
You cannot align the RibbonGroup to the right. The Ribbon doesn't provide the ability to do this.
What you can do is to align page header items... but I don't know if it's enough for you:
DevExpress
I had the same issue, and I finally found something to do this :
I have 3 RibbonGroupBox. Groupe1 may be aligned on left, Groupe3 may be aligned on right. Groupe2 is just an empty RibbonGroupBox I inserted between Groupe1 and Groupe3.
Code XAML :
<Fluent:Ribbon DockPanel.Dock="Top" Title="{x:Static p:Resources.MiseEnBarre}" x:Name="mainRibbon">
<Fluent:RibbonTabItem x:Name="MainMenu" Header="{x:Static p:Resources.MainMenu}" SizeChanged="MainMenu_SizeChanged">
<Fluent:RibbonGroupBox x:Name="Groupe1">
<Fluent:Button x:Name="autoNest" SizeDefinition="Large" LargeIcon="img\image_bar_Nesting.png" Header="{x:Static p:Resources.MenuAutoNest}" Click="AutoNest_Click" />
<Fluent:Button x:Name="saveFile" SizeDefinition="Large" LargeIcon="img\image_save.png" Header="{x:Static p:Resources.MenuSauvegarder}" Click="Sauvegarder_Click" />
</Fluent:RibbonGroupBox>
<Fluent:RibbonGroupBox x:Name="Groupe2">
</Fluent:RibbonGroupBox>
<Fluent:RibbonGroupBox x:Name="Groupe3">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=AvailableCNClist}" HorizontalAlignment="Left"/>
<TextBlock Grid.Column="1" Text="{Binding Path=AvailableCNClist2}" HorizontalAlignment="Right"/>
</Grid>
</Fluent:RibbonGroupBox>
</Fluent:RibbonTabItem>
</Fluent:Ribbon>
Then to manage the Windows redimensioning, I add on my main window the event SizeChanged="MainWindow_SizeChanged" (In the case your RibbonGroupBox dimensions could also change, just add the same event on them).
private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateAlignRibbon();
}
private void UpdateAlignRibbon()
{
Groupe2.Width = MyWindow.ActualWidth - Groupe1.ActualWidth - Groupe3.ActualWidth;
}
In my case the Groupe3 RibbonGroupBox may change of dimension, so I call UpdateAlignRibbon() from 3 points :
After Initialization of my window(including defining the GroupBoxes content)
When the MainWindow has its dimensions changed
When Groupe1 or Groupe3 have its dimensions changed
Rover, You can try add RibbonGroup between last left RibbonGroup and Right align RibbonGroup and assign size to newly added ribbon related to window size.
example <RibbonGroup Width="400"></RibbonGroup>
it's looks following image
You can sort of hack in alignment but I'd recommend against it.
<r:RibbonGroup Header="This is a Filler Header With No Functionality but to Take Up Space" Visibility="Hidden">
<s:RibbonButton2/>
<s:RibbonButton2/>
<s:RibbonButton2/>
<s:RibbonButton2/>
</r:RibbonGroup>
The customer wanted their logo on the ribbon across the top of the page, but the more you add "false" elements to the bar, the quicker "true" elements will collapse when shrinking the window size.
Try this:
<RibbonTab Header="Home" x:Name="rtabHome" FlowDirection="RightToLeft" >
<RibbonGroup Header="Group">
<TextBlock Text="Example"/>
</RibbonGroup>
</RibbonTab>
Works with FlowDirection="RightToLeft".

Categories