WPF: custom TabControl - TabItem Header gets lost - c#

I'm using this sample to customize a TabControl:
TabControlStyle - Part Three.zip:
https://web.archive.org/web/20160319001935/http://www.blogs.intuidev.com/file.axd?file=2010%2f2%2fTabControlStyle+-+Part+Three.zip
from url:
https://web.archive.org/web/20160319001935/http://www.blogs.intuidev.com/post/2010/02/10/TabControlStyling_PartThree.aspx
now if you run and select sample 5 (TabControl_5_ScrollableTabPanel) you will see that everything works fine. Opening the popup works also.
However when adding a new tabitem with a custom header and opening the popup on the right makes that the tabitem's header gets reset to a minimum size and the content is lost.
In the sample in TabControl_5_ScrollableTabPanel.xaml I've just added an extra tabitem between the existing Tab 2 and Tab 3:
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Rectangle Width="16" Height="16" Fill="Red" />
<TextBlock Text="Test" />
</StackPanel>
</TabItem.Header>
</TabItem>
I've seen that after UIElement.Measure() the DesiredSize is incorrect for this tab with custom header.
In the ScrollableTabPanel I've tried to change the MeasureOverride method so that it does an extra check for the header:
//Loop through all child controls ...
foreach (UIElement uieChild in this.InternalChildren)
{
// test:
TabItem uieChildTabItem = uieChild as TabItem;
if (uieChildTabItem != null && uieChildTabItem.HasHeader && uieChildTabItem.Header != null)
{
UIElement uieChildHeader = uieChildTabItem.Header as UIElement;
if (uieChildHeader != null)
{
uieChildHeader.Measure(availableSize);
resultSize.Width += uieChildHeader.DesiredSize.Width;
}
}
// ...
...but it still doesn't bring the header back to its original size, showing its content.
Does anyone see where it goes wrong and why my solution doesn't seem to work?

Can you try to declare the TabItem as below :
<TabItem Header="TabTest">
<TabItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Width="16" Height="16" Fill="Red" />
<TextBlock Text="Test" />
</StackPanel>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
As per this Tab Panel implementation, it directly binds the TabItem's header to the context menu item header in the popup. This creates problem if you trying to give some UI element to tab item header instead of string, and this Tab Panel trying to host same UI element twice in a visual tree. Since it is not allowed , it removed the UI element from TabItem's header and shows only in the menu item's header.

Related

Need to modfy the default tab order of child elements

I have custom control like below. When press tab key focus will move in the order elements arrangement.
Query:
When stackpanel receive tab focus I need to change default tab order toggle button present in stackpanel
Default Tab Order:
DockPanel--Border---StackPanel-->Button1-->button2-->button3
Expected Order
DockPanel--Border---StackPanel-->Button3-->button2-->button1
I need update TabOrder based on its parent. Please suggestion solution modify the tab order based on parent
Note: I need UI as like below arrangements, only i need to modify the tab order for buttons
<DockPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Border x:Name="MainBorder">
<StackPanel>
<ToggleButton>Button 1</ToggleButton>
<ToggleButton>Button 3</ToggleButton>
<ToggleButton>Button 3</ToggleButton>
</StackPanel>
</Border>
</DockPanel>
As mentioned in comments do set the TabIndex property. To step within control do use KeyboardNavigation.TabNavigation attached property.
<DockPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Border x:Name="MainBorder">
<StackPanel KeyboardNavigation.TabNavigation="Local">
<ToggleButton KeyboardNavigation.TabIndex="3">Button 1</ToggleButton>
<ToggleButton KeyboardNavigation.TabIndex="2">Button 2</ToggleButton>
<ToggleButton KeyboardNavigation.TabIndex="1">Button 3</ToggleButton>
</StackPanel>
</Border>
</DockPanel>
If you want to modify the tab order at run time I would advice you to create a behavior for it. See Use of Behavior in WPF MVVM? To access attached property from code see Get and set WPF custom attached property from code behind

WPF - Setting tab order on large number of controls

So I have a large amount of controls (textboxes) as you can see below, but there are around 30 rows of this. These are loaded using arrays, and each column represents an array. So when I hit tab in a textbox, instead of tabbing horizontally, it tabs vertically instead.
Is there a way to set the tab order so it will tab horizontally, aside from changing the way the controls are loaded?
Another quirk is that when leaving one textbox, instead of focusing the next, it just kind of highlights the textbox, and I have to tab a second time to get inside the next textbox.
EDIT:
Main view (lots of code has been omitted, I'm pretty sure nothing has been left out that needs to be here)
<ListBox ItemsSource="{Binding Items}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
ItemsView
<UserControl>
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:Item}">
<views:ItemView/>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<ContentControl Content="{Binding item <!-- about 30 different items here, omitted for readability -->}" />
</StackPanel>
</UserControl>
ItemView
<UserControl ... IsTabStop="False">
<TextBox Text="{Binding Value}" />
</UserControl>
The ItemView is nested in the ItemsView, which is nested in the MainView. Since the textboxes are generated based on the array values, I can't easily set the TabIndex property unless there is a way I don't know about (I am pretty new at WPF).
The TabIndex property provides a way to control the tab order independently of the order controls are loaded.
Usage example:
<Grid>
<TextBox TabIndex="2" /><!-- will receive focus second -->
<TextBox TabIndex="1" /><!-- will receive focus first-->
</Grid>
I would guess the unwanted focusing you are seeing is due to a parent UserControl that your TextBoxes are placed in.
If this is the case, you could prevent that by setting IsTabStop="false" on that parent control.
For example:
<UserControl .... IsTabStop="False">
<Grid>
<!-- other graphics -->
<TextBox TabIndex="1" />
</Grid>
</UserControl>
Using a view model to populate the data
public class CellViewModel
{
public double Value { get; set; }
public int TabIndex { get; set; }
}
public IEnumerable<IEnumerable<CellViewModel>> GetMatrix(
List<List<double>> matrixValues)
{
var columnCount = matrixValues.Count;
return matrixValues
.Select((x, i) => GetColumn(x, columnCount, i));
}
public IEnumerable<CellViewModel> GetColumn(
List<double> columnValues,
int columnCount,
int columnIndex)
{
return columnValues
.Select((x, i) =>
new CellViewModel { Value = x, TabIndex = columnIndex + columnCount * i });
}
Your ItemsSource for your ListBox (which you've now changed to ItemsControl) should be a new Matrix property, which you populate using GetMatrix().
In your ItemView, you would want something like this:
<UserControl ... IsTabStop="False">
<TextBox Text="{Binding Value}" TabIndex="{Binding TabIndex}" />
</UserControl>

ToolBar OverflowPanel remains open

I have a ToolBar with a ItemsTemplate which works fine until the OverflowPanel is Available.
The OverflowPanel does not close if i select one of the context actions.
The Problem only occurs if the Items are added via the ItemsSource binding:
<ToolBar ItemsSource="{Binding ContextActionViewModels}"
Background="Transparent"
ToolBarTray.IsLocked="True"
FocusManager.IsFocusScope="False">
<ToolBar.ItemTemplateSelector>
<views:ContextActionTemplateSelector>
<views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<DataTemplate DataType="{x:Type viewModels:SimpleContextActionViewModel}">
<Button Name="Button"
Command="{Binding ActionCommand}"
Style="{StaticResource ToolBarButtonStyle}"
ToolTip="{userInterface:Translation Binding={Binding ToolTip}}">
<ContentControl Template="{Binding Icon,Converter={StaticResource NameToResourceConverter}}"
Margin="5"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</DataTemplate>
</views:ContextActionTemplateSelector.SimpleContextActionDataTemplate>
<!-- Multiple DataTemplates follow!-->
Why is the DataTemplate / ItemTemplteSelector not working properly. While hardcoded Buttons in XAML work properly?
I uploaded a full sample that illustrates what is not working here:
Just Resize the window and try invoking one off the buttons in the OverflowPanel. While the 'ICommand' is executed properly the Popup stays open.
In the .NET framework source you can find the method that handles the closing behavior of OverflowPanel for ToolBar class:
private static void _OnClick(object e, RoutedEventArgs args)
{
ToolBar toolBar = (ToolBar)e;
ButtonBase bb = args.OriginalSource as ButtonBase;
if (toolBar.IsOverflowOpen && bb != null && bb.Parent == toolBar)
toolBar.Close();
}
When you define a DataTemplate and use ItemsSource property, the Parent property of the created button becomes null and the if check fails. This is the expected behavior of DataTemplate as described here:
For templates, the Parent of the template eventually will be null. To get past this point and extend into the logical tree where the template is actually applied, use TemplatedParent.
As a solution you can set the IsOverflowOpen property to false when you click any of the buttons:
<ToolBar Name="SomeToolBar" ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate DataType="local:ItemViewModel">
<Button Command="{Binding Command}" Content="{Binding Name}" Click="ButtonBase_OnClick"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SomeToolBar.IsOverflowOpen = false;
}

How to set button in side stackpanel to right edge in windows 10 universal app development

I an new to windows app development,i searched for this but not found any where.I need the button at right edge and Stretch the textbox till buttons start.But I am unable to set the button to Right edge.
How to acheve this.
A stackpanel works like a container. If you define layout properties on your stackpanel, then the objects inside your stackpanel cannot be displayed outside of the stackpanel's limits.
For example :
If I set my row and column number in my stackpanel,
<StackPanel Grid.Row="0" Grid.Column="2" Name="Version" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
<Label Grid.Column="4" Grid.Row="6" Name="Ver" Content="V." HorizontalAlignment="Right" />
<TextBlock Name="Vers" Text="1.0" TextAlignment="Right" />
</StackPanel>
Then the row/column properties set on my label are ignored and the 'HorizontalAlignment="Right"' will place my label on the right side of the stackpanel, not the grid.
A solution may be to remove your button from your stackpanel, you are then free to place your button anywhere on the grid.
Another solution can be to expand your stackpanel's limits.
To do so, you can use the Grid.ColumnSpan property or simply set your stackpanel on the right of the grid.
Hope that helped.

WPF TabItem not highlighted it should

I have three tabs. By simply being clicked individually, they will be highlighted individually as they should.
There are RelyCommand behind these tabs. Whenever the mune is clicked, the program should bring back the first TabItem and it should be highlighted. However, when the second tab is clicked, the first tab would not be highlighted as it should, although it behaves like it does get clicked. It is just not highlighted.
Here is the code behind
xaml code for the two tabs at View level:
<StackPanel Orientation="Horizontal"
Background="{x:Null}">
<TabControl Height="50" Margin="12,0,0,0">
<TabItem Name="tiCaptureSetup" IsSelected="{Binding Path=IsCaptureSetupTabSelected, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<TabItem.Header>
<Button Name="btnCaptureSetup"
Grid.Column="0"
Width="90"
Height="40"
Margin="5"
ToolTip="Capture Setup"
Content="Capture Setup"
Click="btnCaptureSetup_Click"
IsEnabled="{Binding Path=CaptureSetupButtonStatus, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsDefault="True"
></Button>
</TabItem.Header>
</TabItem>
<TabItem Name="tiCapture" IsSelected="{Binding Path=IsCaptureTabSelected, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<TabItem.Header>
<Button Name="btnCapture"
Grid.Column="0"
Margin="5"
Width="90"
Height="40"
ToolTip="Capture"
Content="Capture"
Click="btnCapture_Click"
IsEnabled="{Binding Path=CaptureButtonStatus, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></Button>
</TabItem.Header>
</TabItem>
The C# code at ViewModel level (CaptureSetup() is the RelyCommand for clicking the first tab, and HardwareSetupLS() is the RelyCommand for the pop-up window on the menu, and RefereshCaptureSetup() is basically trying to retrieve the first tab when the menu window pops up)
public void CaptureSetup()
{
Command command = new Command();
command.Message = "Capture Setup";
command.CommandGUID = new Guid("6ecb028e-754e-4b50-b0ef-df8f344b668e");
_eventAggregator.GetEvent<CommandShowDialogEvent>().Publish(command);
}
public void HardwareSetupLS()
{
//RefereshCaptureSetup(); // refresh panel when hardware setting window is loaded.
Command command = new Command();
command.Message = "HardwareSetupLS";
command.CommandGUID = new Guid("64c695e6-8959-496c-91f7-5a9a95d91e0d");
_eventAggregator.GetEvent<CommandShowDialogEvent>().Publish(command);
RefereshCaptureSetup();
}
public void RefereshCaptureSetup() // refresh CaptureSetup UI
{
_isCaptureSetupTabSelected = true;
_isCaptureTabSelected = false;
_isReviewTabSelected = false;
Command command = new Command();
command.Message = "Capture Setup";
command.CommandGUID = new Guid("{6ecb028e-754e-4b50-b0ef-df8f344b668e}");
_eventAggregator.GetEvent<CommandShowDialogEvent>().Publish(command);
}
I am very confused at this point what else I can do to make the first TabItem highlighted as it should.
I feel like there is some important logic missing in your question (e.g. how the IsCaptureSetupTabSelected and IsCaptureTabSelected are updated) but anyway here are three pointers from looking at your code:
UpdateSourceTrigger=PropertyChanged is useless since your bindings are OneWay (from the source in your ViewModel towards your UI, the source is never updated). If you have written some logic expected to receive IsSelected change notification upon mouse clicks, this won't happen.
You seem to be updating the inner properties wrapped by your bound properties (e.g. _isCaptureSetupTabSelected = true instead of IsCaptureSetupTabSelected = true ) and thus, could be missing the proper INotifyPropertyChanged event that the UI is expecting.
Make sure that the proper TabItem is on focus.

Categories

Resources