Stretch header width in grouped listbox view - c#

How can i stretch the header width in a grouped listbox?
The listbox ItemSource is bound to hierarchical data structure. A group item contains a name and a list of subitems.
public class MyGroup
{
public string GroupName { get; set; }
public ObservableCollection<MyItem> SubItems { get; }
}
public class MyItem
{
public string ItemName { get; set; }
public DateTime Creation { get: set: }
}
public class MyViewModel:
{
public ObservableCollection<MyGroup> Data { get; set; }
}
The page is defined as follows
<Page>
<Page.Resources>
<CollectionViewSource x:Name="cvsListBoxGroup" IsSourceGrouped="True" Source="{Binding Data}" ItemsPath="SubItems"/>
</Page.Resources>
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListBox Width="500" SelectionMode="Single" ItemsSource="{Binding Source={StaticResource cvsListBoxGroup}}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Background="Beige">
<TextBlock Text="{Binding GroupName}"></TextBlock>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Bisque">
<TextBlock Text="{Binding ItemName}" Foreground="Black" ></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
Groups and items are displayed. The items don't fill the listbox full width so we need to set the HorizontalContentAlignment for ListBox.ItemContainerStyle. This trick is common - no problem
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
I tried to figure out some similiar trick for the groupheader items - but nothing helps.
<GroupStyle.HeaderContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</GroupStyle.HeaderContainerStyle>
The problem seems the TargetType. For a listview there is a ListViewHeaderItem. But nothing similar exists for the listbox.

See the answer here, it's for ListView but that's basically the same:
https://social.msdn.microsoft.com/Forums/windowsapps/en-US/e8d4f7b3-93a8-4f17-9679-8b700b8d02e6/how-to-adjust-the-width-of-group-header-in-listview-to-stretch-full-width?forum=winappswithcsharp

Related

Bound TabControl Repeats Same Content Or Controls Across Tabs

In the example below when data is bound to the tab control, it correctly has the tab headers per the data, but the tab content is always the same; and the last item bound to.
"Frank Wright"'s "Wright" is on every page.
What is wrong? One expects that there are different controls on every page, not one control shared or data repeated.
<Page.Resources>
<model:People x:Key="People">
<model:Person First="Joe"
Last="Smith"
Phone="303-555-5555" />
<model:Person First="Jenny"
Last="Johnson"
Phone="720-867-5309" />
<model:Person First="Frank"
Last="Wright"
Phone="202-555-5555" />
</model:People>
</Page.Resources>
<TabControl ItemsSource="{StaticResource People}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header"
Value="{Binding First}" />
<Setter Property="Content">
<Setter.Value>
<TextBox Text="{Binding Last}" />
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
public class People : List<Person> { }
public class Person
{
public string First { get; set; }
public string Last { get; set; }
public string Phone { get; set; }
}
You want to set the ContentTemplate, not the Content:
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding Last}" />
<Label Content="{Binding Phone}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>

MenuItem template change based on bound data

Basically I have a View which is bound to a ViewModel which has MenuItems.
What I want to do is that whenever the menu title is "-" I want to place a separator.
Theoretically it seems that I can avoid TemplateSelectors but if you think that it's inevitable please share even those solutions.
Here is the XAML:
<Window x:Class="WpfApp1.MenuItemStyle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MenuItemStyle" Height="300" Width="300">
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MenuItems}">
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Title}" Value="-">
<Setter Property="ContentTemplate">
<Setter.Value>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Separator />
</HierarchicalDataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
<TextBlock Text="{Binding Title}" Background="Red" />
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<Grid>
</Grid>
</DockPanel>
</Window>
And here is the code behind:
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MenuItemStyle.xaml
/// </summary>
public partial class MenuItemStyle : Window
{
public MenuItemStyle()
{
InitializeComponent();
this.DataContext = this;
}
public ObservableCollection<MenuItem> MenuItems { get; set; } = new ObservableCollection<MenuItem> {
new MenuItem{ Title = "M1"
,Children= new ObservableCollection<MenuItem>{
new MenuItem{ Title = "M2"},
new MenuItem{ Title = "-"},
new MenuItem{ Title = "M3"},
}
}
};
}
public class MenuItem
{
public string Title { get; set; }
public ObservableCollection<MenuItem> Children { get; set; }
}
}
I have searched everywhere for a solution but couldn't find a pragmatic one.
You can create a Style for the MenuItems. Make it either local for a concrete menu instance (by placing in the menu's Resources) or place it in your resource dictionary:
<Menu ItemsSource="{Binding MenuItems}">
<Menu.Resources>
<Style TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Title}" Value="-">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Separator/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.Resources>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Title}" Background="Red" />
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>

TreeView's contextmenu appears on the topnode instead of TreeViewItem's

I've got a problem I couldn't find solution for. Here is my TreeView's XAML:
<TreeView ItemsSource="{Binding Parents}" ContextMenu="{StaticResource TreeViewContextMenu}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And I've also got this style in my ContentControl.Resources:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="ContextMenu" Value="{StaticResource TreeViewItemContextMenu}" />
</Style>
When I rightclick any item apart from the very first one, the TreeViewItemContextMenu appears, just as intended.
But the issue is when I rightclick the topnode, the TreeViewContextMenu appears instead.
I tried to do without the TreeViewContextMenu at all, but then the topnode had no ContextMenu either.
I would be very glad to know, what and where I missed. Thanks in advance.
I'm trying using your xaml code. This is works.
<Window x:Class="WpfApplication1.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">
<Window.Resources>
<ContextMenu x:Key="TreeViewItemContextMenu">
<MenuItem Header="Item Menu"/>
</ContextMenu>
<ContextMenu x:Key="TreeViewContextMenu">
<MenuItem Header="Tree Menu"/>
</ContextMenu>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
<Setter Property="ContextMenu" Value="{StaticResource TreeViewItemContextMenu}" />
</Style>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding Parents}" ContextMenu="{StaticResource TreeViewContextMenu}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
In code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Parents = new ObservableCollection<MyTreeItem>();
var children=new ObservableCollection<MyTreeItem>();
children.Add(new MyTreeItem(){ Name="child1"});
children.Add(new MyTreeItem(){ Name="child2"});
Parents.Add(new MyTreeItem() { Name = "Parent Node", Children = children });
this.DataContext = this;
}
public ObservableCollection<MyTreeItem> Parents { get; set; }
}
public class MyTreeItem {
public string Name { get; set; }
public ObservableCollection<MyTreeItem> Children { get; set; }
}
When i click on the top node and child node, Context menu is same

WPF binding multiple UserControls to a listbox

I have multiple UserControls and i want to show them on a ListBox
Lets assume i have an Employee abstract UserControl and i have 2 or more UserControls than use that Employee like AdministrativeEmployee and FactoryEmployee for each of those Employees i have diffrent data but the UserControl is very similar (same size, almost same fields), on the ViewModel side i have an abstract EmployeeViewModel, the AdministrativeEmployeeViewModel and the FatoryEmployeeViewModel, on the EmployeesView i have the ListBox with the databinding and on the EmployeesViewModel i have an ICollection Employees
EmployeesView.xaml:
xmlns:local="clr-namespace:Solution.Controls"
-
<ListBox ItemsSource={Binding Employees}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" Name="Presenter" />
<DataTemplate.Triggers>
<DataTrigger Value="{x:Type local:AdministrativeEmployeeView}">
<Setter TargetName="Presenter" Property="Content">
<Setter.Value>
<local:AdministrativeEmployeeView />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Value="{x:Type local:FactoryEmployeeView}">
<Setter TargetName="Presenter" Property="Content">
<Setter.Value>
<local:FactoryEmployeeView/>
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EmployeesViewModel.cs:
public ICollection<EmployeeViewModel> Employees { get; set; }
But that show me System.ItemTemplate on the ListBox and not the UserControls for each type of Employee
In ListBox resources define two DataTemplates:
<ListBox ItemsSource="{Binding Employees}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:AdministrativeEmployee}">
<local:AdministrativeEmployeeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:FactoryEmployee}">
<local:FactoryEmployeeView />
</DataTemplate>
</ListBox.Resources>
</ListBox>
Model classes:
public class Employee
{
public string Name { get; set; }
}
public class AdministrativeEmployee : Employee
{
}
public class FactoryEmployee : Employee
{
}
Sample data:
List<Employee> _source = new List<Employee>();
_source.Add(new AdministrativeEmployee { Name = "A test1" });
_source.Add(new FactoryEmployee { Name = "F test1" });
_source.Add(new AdministrativeEmployee { Name = "A test2" });
_source.Add(new FactoryEmployee { Name = "F test2" });
Employees = _source;
AdministrativeEmployeeView:
<UserControl ...>
<Grid>
<TextBlock Text="{Binding Name}" Background="Red" />
</Grid>
</UserControl>
FactoryEmployeeView:
<UserControl ...>
<Grid>
<TextBlock Text="{Binding Name}" Background="Green" />
</Grid>
</UserControl>
Result:

Updating a ListBox with different Content On Button Clicks in WPF

So I have a listbox and a tool bar in my WPF app. The tool bar just has regular controls, and the listbox has vertical expanders.
I need the listbox to have a different set of expanders depending on what button is clicked. Right now it looks like such:
<ListBox>
<local:Select_Analysis_Panel/>
</ListBox>
Where local:Select_Analysis_Panel is seperate user control file containing the expanders. What is the best way to go about dynamically updating the ListBox control's content upon a button click?
For the last couple hours I've been trying to use set DataTemplates for each expander set and bind the to the items control property with little avail with the code below. I'm just trying to get basic framework laid out before setting up a MVVM interface. Later on I was going to replace the ItemsSource="Network_anal" with you know ItemsSource="{Binding WhatExpanderViewModelProperty}" or something like that.
<ListBox Width="250" Margin="5,0,0,0">
<ListBox.Resources>
<DataTemplate DataType="Select_Analysis_Panel">
<local:Select_Analysis_Panel/>
</DataTemplate>
<DataTemplate x:Key="Network_anal" DataType="NetworkAnalysis">
<local:NetworkAnalysis/>
</DataTemplate>.Resources>
<ListBox.Template>
<ControlTemplate>
<Border Background="Red"/>
</ControlTemplate>
</ListBox.Template>
<ItemsControl ItemsSource="Network_anal"/>
</ListBox>
Am I taking the right approach to this at all?
Here's what I'm trying to do. Below when the "File" button is clicked the side bar displays these 2 expanders:
And when "Network Design" button these expanders are dipslayed:
Option 1:
Subclassing the sections:
each of these sections could be subclassed from a base section class and a specific DataTemplate could be used for each:
<Window x:Class="MiscSamples.MultiToolbar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples"
Title="MultiToolbar" Height="300" Width="300">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>
<DockPanel>
<ListBox ItemsSource="{Binding Sections}"
SelectedItem="{Binding SelectedSection}"
DisplayMemberPath="Name"
DockPanel.Dock="Top">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}"/>
<Setter Property="MinWidth" Value="80"/>
<Setter Property="MinHeight" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border BorderBrush="Black" BorderThickness="1">
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}">
<ContentPresenter ContentSource="Content"/>
</ToggleButton>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
<ScrollViewer Width="300" DockPanel.Dock="Left">
<ContentPresenter Content="{Binding SelectedSection}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type local:FileSection}">
<TextBlock Text="User Control For File Section"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:NetworkDesignSection}">
<TextBlock Text="User Control For Network Design"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SelectAnalysisSection}">
<TextBlock Text="User Control For Select Analysis"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</ScrollViewer>
<Grid Background="Gray">
<TextBlock Text="Design Surface" TextAlignment="Center" VerticalAlignment="Center" FontWeight="Bold"/>
</Grid>
</DockPanel>
</Window>
Code Behind:
public partial class MultiToolbar : Window
{
public MultiToolbar()
{
InitializeComponent();
var vm = new MainViewModel();
vm.Sections.Add(new FileSection() {Name = "File"});
vm.Sections.Add(new NetworkDesignSection() { Name = "Network Design" });
vm.Sections.Add(new SelectAnalysisSection() { Name = "Select Analysis" });
DataContext = vm;
}
}
Main ViewModel:
public class MainViewModel: PropertyChangedBase
{
private ObservableCollection<Section> _sections;
public ObservableCollection<Section> Sections
{
get { return _sections ?? (_sections = new ObservableCollection<Section>()); }
}
private Section _selectedSection;
public Section SelectedSection
{
get { return _selectedSection; }
set
{
_selectedSection = value;
OnPropertyChanged("SelectedSection");
}
}
}
Sections:
public abstract class Section:PropertyChangedBase
{
public string Name { get; set; }
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
OnPropertyChanged("IsEnabled");
}
}
private bool _isVisible = true;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
OnPropertyChanged("IsVisible");
}
}
//Optionally
//public string ImageSource {get;set;}
//ImageSource = "/Resources/MySection.png";
}
public class FileSection: Section
{
///... Custom logic specific to this Section
}
public class NetworkDesignSection:Section
{
///... Custom logic specific to this Section
}
public class SelectAnalysisSection: Section
{
///... Custom logic specific to File Section
}
//...etc etc etc
Result:
Notice that I'm using ToggleButtons bound to the ListBoxItem.IsSelected property to simulate a TabControl-like behavior.
You can set the DataContext of the whole form and bind the ItemsSource of the listbox, or set ItemsSource of the listbox to some collection directly.

Categories

Resources