I am trying to fix this display issue I am having with my WPF c# application where when I click on a tab item, it correctly displays that tab, but there are now lines are the border of the tab after I click a second time:
On Initialization or one-click:
Initialization or first click screenshot of desktop application
After second-click:
second click screenshot of desktop application
Is there a way to get rid of these lines or never display them at all?
xaml code:
<TabControl Grid.Row="2" Grid.Column="0" SelectedIndex="{Binding SelectedTabIndex}">
<TabItem Header="Test1">
<local:UiTestOneTabView DataContext="{Binding TestOneTabViewModel}"/>
</TabItem>
<TabItem Header="Test2">
<local:UiTestTwoTabView DataContext="{Binding TestTwoTabViewModel}"/>
</TabItem>
<TabItem Header="Test3">
<local:UiTestThreeTabView DataContext="{Binding TestThreeTabViewModel}"/>
</TabItem>
<TabItem Header="Test4">
<local:UiTestFourTabView DataContext="{Binding TestFourTabViewModel}" />
</TabItem>
</TabControl>
I have a tab control in my WPF application and I'm trying to position the TabItem headers to the left as shown in this MSDN post using the below code (copied from the post):
<TabControl TabStripPlacement="Left" Margin="0, 0, 0, 10">
<TabItem Name="TabOne" Header="Tab One">
<TabItem.Content>
<TextBlock TextWrapping="WrapWithOverflow">
Tab One Here.
</TextBlock>
</TabItem.Content>
</TabItem>
<TabItem Name="TabTwo" Header="Tab Two">
<TabItem.Content>
<TextBlock TextWrapping="WrapWithOverflow">
Tab Two here.
</TextBlock>
</TabItem.Content>
</TabItem>
</TabControl>
According to the post this is what Im supposed to get:
But this is what I'm currently getting:
I've copied the exact same code from the post to my application and can't understand what has gone wrong.
Here is my full xaml:
<Window x:Class="OptionsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TabStripPlacementSample" Height="200" Width="250" UseLayoutRounding="True">
<Grid>
<TabControl TabStripPlacement="Left" Margin="0,0,33,10">
<TabItem Name="TabOne" Header="Tab One">
<TabItem.Content>
<TextBlock TextWrapping="WrapWithOverflow">
Tab One Here.
</TextBlock>
</TabItem.Content>
</TabItem>
<TabItem Name="TabTwo" Header="Tab Two">
<TabItem.Content>
<TextBlock TextWrapping="WrapWithOverflow">
Tab Two here.
</TextBlock>
</TabItem.Content>
</TabItem>
</TabControl>
</Grid>
I have created a couple of UserControl views and now I want to show the corresponding view when a tab item is clicked. So one tab item gets one view. I would like to do this in MVVM but don't know how.
Please take a look at the following code and give me some advice on how to achieve that:
The MainView (with the TabControl only):
...
<TabControl Name="pnlFormButtons"
Margin="25"
Background="Black"
SelectedItem="{Binding SelTab}"
>
<TabItem Name="tabInventurartikel" Header="Inventurartikel hinzufügen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
>
</TabItem>
<TabItem Name="tabSonderartikel" Header="Sonderartikel hinzufügen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2">
</TabItem>
<TabItem Name="tabAnlegen" Header="Lieferschein anlegen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
<TabItem Name="tabDrucken" Header="Lieferschein drucken"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
<TabItem Name="tabHilfeseite" Header="Hilfeseite aufrufen"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
<TabItem Name="tabFehlerMelden" Header="Fehler bzw. Bug melden"
Background="BlanchedAlmond" Foreground="Black"
FontFamily="Verdana"
BorderBrush="Black"
BorderThickness="2"
IsEnabled="False">
</TabItem>
</TabControl>
...
The MainViewModel (only relevant code):
...
//Binding Property SelTab - It binds to the selected tab item
private string _selTab;
public string SelTab
{
get { return _selTab; }
set
{
_selTab = value;
OnPropertyChanged("SelTab"); //INotifyPropertyChanged
GetSelTab(); //check which tab item is selected and display the corresponding view
}
}
public void GetSelTab()
{
UserControl usc = null; //initialize user control object
switch(SelTab) //which tab item is selected?
{
case "tabInventurartikel": // = TabControl.SelectedItem
usc = new Inventurartikel(); //Initialize (Show) Inventurartikel.xaml
SelTab.Content = usc; //Here I don't know how to actually show the view in the tab item because SelectedItem.Content does not exist...
break;
case "tabSonderartikel":
usc = new neuerArtikel(); //same problem here...
break;
default:
break;
}
}
...
NOTE:
The views for the tab items are basically just user control forms that I want to show inside the tab item when the corresponding tab item is selected.
I shouldn't post them here because I want to keep the focus on the actual problem as simple and as clear as possible. Any help is highly appreciated!
The easiest solution would be to bind the tab control's item source to a list of view models. Then, if you add/remove view models, tabs are added/removed accordingly.
Main window xaml:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="CustomHeaderTemplate">
<Label Content="{Binding TabName}" />
</DataTemplate>
</Grid.Resources>
<TabControl x:Name="tbCtrl" ItemsSource="{Binding Items}" Loaded="tbCtrl_Loaded" SelectionChanged="tbCtrl_SelectionChanged" ItemTemplate="{StaticResource CustomHeaderTemplate}">
<TabControl.ContentTemplate>
<DataTemplate>
<uc:DeviceTab/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
The important thing is the binding of ItemSource.
Tab control view model:
class TabControlViewModel
{
public ObservableCollection<ItemViewModel> Items { get; } = new ObservableCollection<ItemViewModel>();
}
Tab control code behind Loaded event. Here you can add view models and the tab control sets up the tabs accordingly:
private void tbCtrl_Loaded(object sender, RoutedEventArgs e)
{
var tabControlViewModel = new TabControlViewModel();
tabControlViewModel.Items.Add(new ItemViewModel());
DataContext = tabControlViewModel;
tbCtrl.SelectedIndex = 0;
}
This only works if all tabs are the same. There's also a solution if you need different user controls for each tab. In that case, you need to specify a data template for the tab item's content. Basically you can tell it to load user controls based on the type of the view model. Unfortunately I don't know how to do that, but I've seen examples for it. I know it's not the exact answer you need, but I hope it helps!
Assume there are tab1 and tab2 as two tab items and currently active tab is tab1. SelectedIndex Property set is only called when there is a change in SelectedIndex. I want an which executes even on currently active tab. MouseDown event is not working on tab item.
Any other solutions?
I suggest you to make a Tab HeaderTemplate to handle the click. Look at the sample code below:
<Window.Resources>
<DataTemplate x:Key="myTabHeaderTemplate">
<!-- Handle click here -->
<Grid MouseDown="TabControl_MouseDown">
<TextBlock Text="{Binding}" Margin="5" />
</Grid>
</DataTemplate>
</Window.Resources>
<TabControl
Background="WhiteSmoke"
SelectedItem="{Binding CurrentTab}">
<TabItem Header="tab 1" Background="LightPink" HeaderTemplate="{StaticResource myTabHeaderTemplate}" />
<TabItem Header="tab 2" Background="LightGreen" HeaderTemplate="{StaticResource myTabHeaderTemplate}" />
</TabControl>
TabControl_MouseDown() will be invoked even if your tab is already selected
You have to implement this method in code behind:
private void TabControl_MouseDown(object sender, MouseButtonEventArgs e){ ... }
Or you can bind it to a command in your ViewModel...
In my application I have used WPF TabControl I want to handle click event of the TabItem.
How do i achieve it?
You can do this by adding labels to the header property for each tabitem in the tabcontrol. Then you can set an event for the label.
xaml
<TabControl Height="100" HorizontalAlignment="Left" Name="tabControl1">
<TabItem Name="tabItem1">
<TabItem.Header>
<Label Content="tabItem1"
MouseLeftButtonDown="tabItem1_Clicked"
HorizontalAlignment="Stretch"/>
</TabItem.Header>
<Grid />
</TabItem>
<TabItem Name="tabItem2">
<TabItem.Header>
<Label Content="tabItem2"
MouseLeftButtonDown="tabItem2_Clicked"
HorizontalAlignment="Stretch"/>
</TabItem.Header>
<Grid />
</TabItem>
</TabControl>
C# / Code Behind
private void tabItem1_Clicked(object sender, MouseButtonEventArgs e)
{
//DO SOMETHING
}
private void tabItem2_Clicked(object sender, MouseButtonEventArgs e)
{
//DO SOMETHING
}
Hope this helps.
This is an old question but I find an answer after being in the same situation.
I use SelectionChanged event on the TabControl (XAML)
<TabControl SelectionChanged="TabControl_SelectionChanged">
Code behind (C#):
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Stuff
}
This is not the click HimSelf but its working for refreshing stuff..
You can use SelectionChanged event in TabControl and use switch case to do anything you like.
// XAML Code
<TabControl SelectionChanged="TabControl_SelectionChanged">
<TabItem Header="Item1"></TabItem>
<TabItem Header="Item2"></TabItem>
<TabItem Header="Item3"></TabItem>
</TabControl>
// Behind Code
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string tabItem = ((sender as TabControl).SelectedItem as TabItem).Header as string;
switch(tabItem)
{
case "Item1":
break;
case "Item2":
break;
case "Item3":
break;
default:
return;
}
}
Note that, although the answer provided by d.moncada and others addresses the question, the question itself might not be what is intended. Knowing when the user clicks on a tab is different from knowing when the user makes a tab frontmost - it can be done by clicking on a tab, but it can also be achieved by other means. For example, if you click in the tab that is already frontmost, then use the left/right arrows, you can bring another tab to the front without mouse clicking - the mouse-left-button-down event does not get called in this case (as you would expect).
Wrap the header in a no-template button.
If you use the ItemsSource:
<TabControl ItemsSource="{Binding Data}">
<TabControl.ItemTemplate>
<DataTemplate>
<Button Click="Tab_Click">
<Button.Template>
<ControlTemplate>
<ContentPresenter />
</ControlTemplate>
</Button.Template>
<Button.Content>
<!-- Actual header goes here -->
</Button.Content>
</Button>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
If you have static content you can insert it into the header right away:
<TabControl>
<TabItem>
<TabItem.Header>
<Button Click="Tab_Click">
<Button.Template>
<ControlTemplate>
<ContentPresenter />
</ControlTemplate>
</Button.Template>
<Button.Content>
<!-- Actual header goes here -->
</Button.Content>
</Button>
</TabItem.Header>
</TabItem>
</TabControl>
You can do this by adding labels to the header property for each
tabitem in the tabcontrol. Then you can set an event for the label.
The solution works pretty well; however, the "label" has margin properties which can prevent the "MouseLeftButtonDown" handler from being fired unless the user clicks the label dead on. Additionally, it looks like the styling for the rest of the tabs are affected due to label padding.
You could alleviate this by overriding the default margin/padding properties of the label...or even more simply, using TextBlock.
<TabItem x:Name="tabItem1" >
<TabItem.Header>
<TextBlock MouseLeftButtonDown="tabItem1_Click">
Click Me
</TextBlock>
</TabItem.Header>
...
</TabItem>
try to find solution with GotFocus event.
private void addTabPage_GotFocus(object sender, RoutedEventArgs e)
{
addTabPage(); //method for adding page
TabControlPages.SelectedIndex = TabControlPages.Items.Count - 1; //select added page
TabControlPages.Focus(); //change forcus to selected page
}
also method for adding page (just example)
private void addTabPage()
{
TabItem tc = new TabItem();
tc.Header = "New page";
TabControlPages.Items.Insert(TabControlPages.Items.Count - 1, tc); //insert new page
}
hope this will be helpfull