How to split a WPF TabControl's tabs on left and right? - wpf

I would like to have a TabControl with half of the tabs on the left, the other half on the right and the content presenter in the middle. See picture below.
I tried to edit the TabControl ControlTemplate and replace the TabPanel with a DockPanel with two StackPanels as follows:
<TabControl ItemsSource="{Binding Sequences}" SelectedIndex="0">
<TabControl.Style>
<Style TargetType="TabControl">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<DockPanel>
<StackPanel DockPanel.Dock="Left" IsItemsHost="True" Width="100"/>
<StackPanel DockPanel.Dock="Right" IsItemsHost="True" Width="100"/>
<ContentPresenter ContentSource="SelectedContent" Margin="100,5,5,5" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Style>
<!-- ... -->
</TabControl>
But I get 6 tabs on each side with non working tabs on the left (there is no header and nothing displayed on the content presenter when I click on them).
How can I split TabControl's ItemsSource in my two StackPanels ?

Not exactly sure why this is working but it is. I stumbled across it by trying to solve this for you.
<TabControl>
<TabControl.Style>
<Style TargetType="TabControl" x:Name="myName">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<StackPanel Name="stk" Grid.Column="0" IsItemsHost="True" Width="100"/>
<ItemsControl Grid.Column="2" ItemsSource="{Binding ElementName=stk, Path=Children}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ContentPresenter Grid.Column="1" ContentSource="SelectedContent"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Style>
<TabItem Header="One">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Black">1</TextBlock>
</TabItem>
<TabItem Header="two">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Black">2</TextBlock>
</TabItem>
<TabItem Header="three">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Black">3</TextBlock>
</TabItem>
<TabItem Header="four">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Black">4</TextBlock>
</TabItem>
</TabControl>
If somebody could tell me how the framework decides which item to place where i'd be very courios!

Did not want to override my previous answer as it still a valid answer for those who dont want to bind the itemssource. I was able to make it look correct but the buttons dont work. Maybe you have an idea how to go on from here yourself:
<TabControl ItemsSource="{Binding Controls}">
<TabControl.Style>
<Style TargetType="TabControl" x:Name="myName" >
<Style.Resources>
<conv:ObservableCollectionSplitter x:Key="ObsColSplt"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="2" ItemsSource="{TemplateBinding ItemsSource, Converter={StaticResource ObsColSplt}, ConverterParameter=even}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ItemsControl Grid.Column="0" ItemsSource="{TemplateBinding ItemsSource, Converter={StaticResource ObsColSplt}, ConverterParameter=uneven}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<ContentPresenter Grid.Column="1" ContentSource="SelectedContent"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Style>
</TabControl>
and the converter (not my proudest bit of code):
public class ObservableCollectionSplitter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ObservableCollection<TabItem> partialCollection = null;
if (value != null)
{
partialCollection = new ObservableCollection<TabItem>();
ObservableCollection<TabItem> inputCollection = (value as ObservableCollection<TabItem>);
int a = 0;
if (parameter.ToString() == "even")
{
a = 1;
}
for (int i = a; i < inputCollection.Count; i = i + 2)
{
partialCollection.Add(inputCollection[i]);
}
}
return partialCollection;
}//END Convert
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}//END ConvertBack
}//END class BooleanInverter : IValueConverter

Related

Apply style to child content in WPF XAML with a template

I'm using a ListViewto display a menu. I want to apply a style to all ListViewItem. So far I have succeeded in one part of the concern but I would like to improve to avoid code replication. Here where I am :
<Window.Resources>
<Style x:Key="_listViewItemStyle" TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border x:Name="Bd"
...
</Border>
<ControlTemplate.Triggers>
...
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
And then use the style like that :
<ListView x:Name="_listViewMenu"
Background="Transparent"
BorderBrush="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListViewItem Style="{StaticResource _listViewItemStyle}">
<StackPanel Orientation="Horizontal"
Width="230"
Margin="10,0,0,0">
<Image Width="30"
Source="Images/Settings.png"
Stretch="Uniform"/>
<TextBlock Text="Paramètres"
Margin="25,0,0,0"
Style="{StaticResource _fontStyle}"/>
</StackPanel>
</ListViewItem>
</ListView>
As you can see there are some stuff that I imagine I can avoid to duplicate if I have 50 ListViewItems, let's say :
<StackPanel Orientation="Horizontal"
Width="230"
Margin="10,0,0,0">
or
<Image Width="30"
...
Stretch="Uniform"/>
etc...
How to include in the style _listViewItemStyle all required properties to format StackPanel, Image, textbox, etc... of the ListViewItem?
Thanks.
You can define a class which has necessary properties and use it as item type of collection for ItemsSource which may be in view (xaml or code behind). Such class would be as follows.
public class SourceItem
{
public string? ImagePath { get; init; }
public string? Text { get; init; }
}
Then, if you just want to show items, ItemsControl would be suffice. I don't know how you intend to use Triggers though.
<ItemsControl Background="Blue" HorizontalAlignment="Left">
<local:SourceItem ImagePath="Images/Settings.png" Text="Paramètres"/>
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="Bd">
<StackPanel Orientation="Horizontal"
Width="230"
Margin="10,0,0,0">
<Image Width="30"
Source="{Binding ImagePath, Mode=OneTime}"
Stretch="Uniform"/>
<TextBlock Text="{Binding Text, Mode=OneTime}"
Margin="25,0,0,0"
Style="{StaticResource _fontStyle}"/>
</StackPanel>
</Border>
<DataTemplate.Triggers>
...
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In addition to the answer from #emoacht, you will need the following InputBindings:
<Border.InputBindings>
<MouseBinding Command="{Binding ItemCommand}" MouseAction="LeftClick"/>
</Border.InputBindings>

Setting the background of each grouping in a Itemcontrol based on the items

I have an itemscontrol that I used grouping on for a bunch for large colored buttons. I would like the groups to have a slight background to them that match the button color, whatever it might be (it's randomly defined by the user, but only if all the buttons in the group are the same. If all the buttons in the group do not have the same color, the background of the group should be transparent.
<DataTemplate x:Key="ButtonTemplate">
<Button Margin="0,0,8,8" Padding="0" Style="{StaticResource TileButton}" Command="{Binding NavigateToContentsCommand}">
<Grid Height="120" Width="271" Background="{Binding BackgroundBrush}">
<Grid Margin="30">
<TextBlock Grid.Column="1" Style="{StaticResource MediumHeader}" Text="{Binding Name}"/>
</Grid>
</Grid>
</Button>
</DataTemplate>
<ItemsPanelTemplate x:Key="ButtonPanel">
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
<ItemsControl ItemsSource="{Binding Items.View}" ItemTemplate="{StaticResource ButtonTemplate}" ItemsPanel="{StaticResource ButtonPanel}">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource DetailsTextBlock}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" Columns="{Binding Items.View.Groups.Count}"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Grid VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.RowSpan="2" Background="{BINDING NOTSURE}" Opacity=".2"/>
<ContentPresenter Grid.Row="0"/>
<ItemsPresenter Grid.Row="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
</ItemsControl>
Where I put the Binding not sure is where I got lost on how to proceed. I'm not sure if I need to examine the children of the group or how to go about that. Anyone have any experience with this?
Thanks!
Modify the border to trigger a loaded event
<Border x:Name="ColorBorder" Loaded="ColorBorder_Loaded" Grid.RowSpan="2" Opacity=".2"/>
Then add this event to loop through the items, if they exist.
private void ColorBorder_Loaded(object sender, RoutedEventArgs e)
{
Brush newBackgroundBrush = Brushes.Transparent;
try
{
var grid = VisualTreeHelper.GetParent((DependencyObject)sender);
GroupItem groupItem = VisualTreeHelper.GetParent(grid) as GroupItem;
CollectionViewGroup collectionViewGroup = (CollectionViewGroup)groupItem.Content;
if (collectionViewGroup.ItemCount > 0)
{
WorkstationNavigationViewModel.NavigationLevelViewModel navigationLevelViewModel = (WorkstationNavigationViewModel.NavigationLevelViewModel)collectionViewGroup.Items[0];
newBackgroundBrush = navigationLevelViewModel.BackgroundBrush;
for (int index = 1; index < collectionViewGroup.ItemCount; index++)
{
navigationLevelViewModel = (WorkstationNavigationViewModel.NavigationLevelViewModel)collectionViewGroup.Items[index];
if (navigationLevelViewModel.BackgroundBrush != newBackgroundBrush)
{
newBackgroundBrush = Brushes.Transparent;
break;
}
}
}
}
catch
{
newBackgroundBrush = Brushes.Transparent;
}
finally
{
Border border = (Border)sender;
border.Background = newBackgroundBrush;
}
}

Datagrid, Merge/Combine rows, cells and columns

I'm trying to acomplish exactly what this question is requesting, but unfortunately the code sample that was provided as the answer is gone, and I'm also not using WPF Toolkit, here the question that he did:
I am trying to Merge cells in WPF toolkit datagrid .I am trying to do something as shown in the image below.We can do this in Winforms datagrid.But how to do this using WPF toolkit datagrid ?.Or is there any alternative controls..?
Can we do this using listview or listbox..? Or is there any free
controls available which have this functionality ?
I found several answers that manage to do this with the DataGridView control, but I do not want to use Form objects in a WPF project, is there a way to acomplish this?
Recource
<Window.Resources>
<Color x:Key="customBlue" A="255" R="54" G="95" B="177" />
<SolidColorBrush x:Key="customBlueBrush" Color="{StaticResource customBlue}"></SolidColorBrush>
<SolidColorBrush x:Key="customBlueBrushOpacity" Color="LightGray" Opacity="0.11"></SolidColorBrush>
<Style x:Key="calcyListbox" TargetType="ListBox">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="35"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Height="30" VerticalAlignment="Top" Background="{StaticResource customBlueBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Manufacturer" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock>
<TextBlock Text="Name" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"></TextBlock>
<TextBlock Text="CPU" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"></TextBlock>
<TextBlock Text="RAM" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"></TextBlock>
<TextBlock Text="Price" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="4"></TextBlock>
</Grid>
<Border Grid.Row="1" SnapsToDevicePixels="True" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0">
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0">
<ItemsPresenter />
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="noStyleToListboxItem" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border>
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
xaml
<ListBox MaxHeight="300" ItemsSource="{Binding ManufacturerList}" Background="{StaticResource customBlueBrushOpacity}" x:Name="ManufacturerListBox" ScrollViewer.VerticalScrollBarVisibility="Auto" Style="{StaticResource calcyListbox}" ItemContainerStyle="{StaticResource noStyleToListboxItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Company}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Border BorderThickness="0,0,0,1" BorderBrush="Black" ></Border>
<ListBox Grid.Column="1" BorderThickness="1,0,1,1" Background="{StaticResource customBlueBrushOpacity}" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="0"></Border>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="1"></Border>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="2"></Border>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0"/>
<TextBlock Text="{Binding CPU}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"/>
<TextBlock Text="{Binding Ram}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"/>
<TextBlock Text="{Binding price}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
c#
InitializeComponent();
List<Manufacturer> ManufacturerList = new List<Manufacturer>();
ManufacturerList.Add(new Manufacturer()
{
Company = "DEll",
Models = new List<Model>(){new Model(){CPU = "T7250", Name = "Inspiron1525", price =234434 , Ram= "2048 MB" },
new Model(){CPU = "T5750", Name = "Studio 1535", price =234443 , Ram= "2048 MB" },
new Model(){CPU = "T5780", Name = "Vastro 1510", price =234434 , Ram= "2048 MB" },}
});
ManufacturerList.Add(new Manufacturer()
{
Company = "Lenovo",
Models = new List<Model>(){new Model(){CPU = "T1230", Name = "l123", price =23546454 , Ram= "1024 MB" },
new Model(){CPU = "T1230", Name = "l1423", price =2346456 , Ram= "1024 MB" },
new Model(){CPU = "T1230", Name = "ldf123", price =2344646 , Ram= "1024 MB" },}
});
ManufacturerListBox.ItemsSource = ManufacturerList;
public class Manufacturer
{
public string Company { get; set; }
public List<Model> Models { get; set; }
}
public class Model
{
public string Name { get; set; }
public string Ram { get; set; }
public double price { get; set; }
public string CPU { get; set; }
}
For a merged cell, bind its Margin and set it to a negative value equal to the sum width/height of the associated merged cells - then the cell will spill across and on top the neighboring cells.
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="{{Binding BorderThickness[5], Mode=OneWay}}"/>
<Setter Property="Margin" Value="{{Binding CellMargins[5], Mode=OneWay}}"/>
<Setter Property="Block.TextAlignment" Value="Center"/>
</Style>
The bindings are indexed because CellMargins is an ObservableCollection of Thickness. The viewmodel is row-level, so each column binds to a different index.
Before this gets downvoted, let me say this can lead to some undesirable visual quirks. Getting this to work perfectly requires a bit of code-behind, such as:
The cell margins' negative values need to be updated whenever a column width changes, so I handled the datagrid's LayoutUpdated event, where I iterated through the columns, measured their current ActualWidths and updated the margins accordingly. I likewise update the BorderThickness to show/hide cell borders as needed. Disabling the selection highlight for cells that are "hidden" is another trick.
Note this means keeping track of what cells (i.e., which row viewmodels and column indices) you want merged separately in code-behind.
This method is complicated and probably not the easiest for every situation, but I found it useful for my purposes. I needed a Datagrid where the user could merge/unmerge cells and create/delete columns, similar to Excel. Furthermore, I wanted to stick with DataGridTextColumns (instead of custom TemplateColums) because of their built-in functionality (recognized keyboard edit commands, copy/paste, etc.)

Sharing control instance within a view in WPF

I'm having some issues with the wpf tab control. Not sure the title of the question is right I will refine it accoring to answers.
I want to create a simple panel system. I want to inject ot my "panel viewModel" 2 view model
MainViewModel will be display as the main area
PanelViewModel will be display as a panel on the right hand side of the view
the panelViewModel will be hidden by default and a button will display it on top of the main view model when needed
The view look like this:
<UserControl.Resources>
<DataTemplate x:Key="MainWindowTemplate" DataType="{x:Type UserControl}">
<ContentPresenter Content="{Binding DataContext.MainViewModel, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid Visibility="{Binding IsPanelHidden, Converter={StaticResource bool2VisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" ContentTemplate="{StaticResource MainWindowTemplate}" />
<Button Grid.Column="1" Content="{Binding PanelTitle}" Command="{Binding Path=ShowPanelCommand}">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
</Button>
</Grid>
<Grid Visibility="{Binding IsPanelHidden, Converter={StaticResource revertBool2VisibilityConverter}}">
<ContentControl ContentTemplate="{StaticResource MainWindowTemplate}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="2" VerticalAlignment="Stretch" Background="Red" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border HorizontalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding PanelTitle}" Margin="5,5,0,2" HorizontalAlignment="Left"></TextBlock>
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="0,0,5,0" HorizontalAlignment="Right" >
<Button Content="Minimze" Command="{Binding HidePanelCommand}"/>
</StackPanel>
</Grid>
</Border>
<ContentPresenter Grid.Row="1" Margin="2" Content="{Binding PanelViewModel}" VerticalAlignment="Top"/>
</Grid>
</Border>
</Grid>
</Grid>
The view model look like that:
public class TestTabViewModel : ObservableObject
{
#region private attributes
#endregion
public TestTabViewModel(string panelName, object panelViewModel, object mainViewModel)
{
IsPanelHidden = true;
PanelTitle = panelName;
PanelViewModel = panelViewModel;
MainViewModel = mainViewModel;
ShowPanelCommand = new DelegateCommand(() =>ManagePanelVisibility(true));
HidePanelCommand = new DelegateCommand(() => ManagePanelVisibility(false));
}
#region properties
public string PanelTitle { get; private set; }
public bool IsPanelHidden { get; private set; }
public object PanelViewModel { get; private set; }
public object MainViewModel { get; private set; }
public DelegateCommand ShowPanelCommand { get; private set; }
public DelegateCommand HidePanelCommand { get; private set; }
#endregion
#region private methods
private void ManagePanelVisibility(bool visible)
{
IsPanelHidden = !visible;
RaisePropertyChanged(() => IsPanelHidden);
}
#endregion
}
So for so good, this system work fine I aslo added some pin command but I remove them from here to make it "simple".
My problem come when the main view model hold a tab control. In this, case if I select a tab and "open" the panel, the tab selected is "changed". In fact it's not changed it's just that I display another contentControl which is not synchronize with the previouse one. I guess that the view instance is not the same even if the viewmodel behind is.
So how do I share a view instance within a view (or have the selection process synchornized)? My first guest was to use the datatemplate (as show in the example) but it did not solve my problem.
By the way, I know some third-party handling panel docking pin ... (eg avalon) but all the one I found are really too much for my simple need.
Thanks for the help
Your best bet would probably be to replace your two Grids with a single ContentControl, and switch the Template on button click or in a Trigger. This way your actual Content (the TabControl) will be the same, but the template used to display the Content will change
Here's a quick example:
<Window.Resources>
<ControlTemplate x:Key="Grid1Template" TargetType="{x:Type ContentControl}">
<DockPanel>
<Grid Background="CornflowerBlue" Width="100" DockPanel.Dock="Left" />
<ContentPresenter Content="{TemplateBinding Content}" />
</DockPanel>
</ControlTemplate>
<ControlTemplate x:Key="Grid2Template" TargetType="{x:Type ContentControl}">
<DockPanel>
<Grid Background="CornflowerBlue" Width="100" DockPanel.Dock="Right" />
<ContentPresenter Content="{TemplateBinding Content}" />
</DockPanel>
</ControlTemplate>
</Window.Resources>
<DockPanel>
<ToggleButton x:Name="btnToggle" Content="Toggle View" DockPanel.Dock="Top" />
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Template" Value="{StaticResource Grid1Template}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=btnToggle, Path=IsChecked}" Value="True">
<Setter Property="Template" Value="{StaticResource Grid2Template}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
<TabControl>
<TabItem Header="Tab1" />
<TabItem Header="Tab2" />
<TabItem Header="Tab3" />
</TabControl>
</ContentControl>
</DockPanel>
i dont know if i get what you want but i think that you could do the following.
i assume that you want some "MainViewmodeldata" be presented as your tabcontrol.
so i woulf first create a datatemplate for this.
<UserControl.Resources>
<DataTemplate DataType="{x:Type MainViewmodeldata}">
<TabControl>
<TabItem Header="Tab1">
<TextBlock Grid.Column="1" Text="Tab1Content"/>
</TabItem>
<TabItem Header="Tab2">
<TextBlock Grid.Column="1" Text="Tab2Content"/>
</TabItem>
</TabControl>
</DataTemplate>
</UserControl.Resources>
now i would just bind my this mainviewmodeldata to the contentcontrol and let wpf render it for you. i really dont know if you still need these two grids, cause i dont know what you wanna achieve.
<Grid x:Name="Grid1" Visibility="{Binding IsPanelHidden, Converter={StaticResource bool2VisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding MainViewmodelData}" />
<StackPanel Grid.Column="1">
<Button x:Name="Button1" Content="Switch look" Command="{Binding ShowPanelCommand}"/>
<TextBlock Text="Look1"/>
</StackPanel>
</Grid>
<Grid x:Name="Grid2" Visibility="{Binding IsPanelHidden, Converter={StaticResource revertBool2VisibilityConverter}}">
<ContentControl Content="{Binding MainViewmodelData}" />
<StackPanel HorizontalAlignment="Right">
<Button x:Name="Button2" Content="Switch look" Command="{Binding HidePanelCommand}"/>
<TextBlock Text="Look2"/>
</StackPanel>
</Grid>
</Grid>

WPF items not visible when grouping is applied

I'm having this strange issue with my ItemsControl grouping. I have the following setup:
<ItemsControl Margin="3" ItemsSource="{Binding Communications.View}" >
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemCount, StringFormat='{}[{0}] '}" FontWeight="Bold" />
<TextBlock Grid.Column="1" Text="{Binding Name, Converter={StaticResource GroupingFormatter}, StringFormat='{}Subject: {0}'}" FontWeight="Bold" />
</Grid>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" Text="{Binding Inspector, Converter={StaticResource NameFormatter}, StringFormat='{}From {0}:'}" Margin="3" />
<TextBlock Text="{Binding SentDate, StringFormat='{}{0:dd/MM/yy}'}" Grid.Row="1" Margin="3"/>
<TextBlock Text="{Binding Message }" Grid.Column="1" Grid.RowSpan="2" Margin="3"/>
<Button Command="vm:CommunicationViewModel.DeleteMessageCommand" CommandParameter="{Binding}" HorizontalAlignment="Right" Grid.Column="2">Delete</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In my ViewModel, I expose a CollectionViewSource named 'Communications'. I proceed to adding a grouping patter like so:
Communications.GroupDescriptions.Add(new PropertyGroupDescription("Subject"));
Now, the problem i'm experience is the grouping work fine, but I can't see any items inside the groups. What am I doing wrong? Any pointers would be much appreciated.
I can't seem to reproduce the problem - I assume you are using a CollectionViewSource? It might be because you bound to the View property directly.
Here's the C# code I used:
public class Communication
{
public string Subject { get; set; }
public string Body { get; set; }
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var source = (CollectionViewSource)Resources["Communications"];
source.Source = new List<Communication>()
{
new Communication { Subject = "WPF 4.0", Body = "I love what's happening with 4.0"},
new Communication { Subject = "WPF 4.0", Body = "I hear the text rendering is the best feature"},
new Communication { Subject = "Blend 3.0", Body = "Behaviors in Blend 3 change everything"}
};
source.GroupDescriptions.Add(new PropertyGroupDescription("Subject"));
}
}
Here is the XAML - it's the same as yours but with a couple of things removed since I don't have your converters or commands:
<Window
x:Class="GroupStyleDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
>
<Window.Resources>
<CollectionViewSource x:Key="Communications" />
</Window.Resources>
<Grid>
<ItemsControl Margin="3" ItemsSource="{Binding Source={StaticResource Communications}}" >
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemCount, StringFormat='{}[{0}] '}" FontWeight="Bold" />
<TextBlock Grid.Column="1" Text="{Binding Path=Name}" FontWeight="Bold" />
</Grid>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Body }" Grid.Column="1" Grid.RowSpan="2" Margin="3"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
After hitting this problem myself I discovered the cause: the template for ItemsControl directly including a panel with IsItemsHost="true".
You must insert an ItemPresenter into your template and set the ItemsControl.ItemsPanel property instead.

Resources