following some examples and blogs, I've done a little project to test the grouping of some elements in my xaml. The code is:
<Window.Resources>
<XmlDataProvider x:Key="data">
<x:XData>
<Devices xmlns="">
<Terminal name="Gasoline" Code="00001001" />
<Terminal name="cherosene" Code="00001002" />
<Terminal name="Oil" Code="00001002" />
<Terminal name="Wather" Code="00001003" />
<Terminal name="cherosene" Code="00001003" />
<Terminal name="Wather" Code="00001004" />
<Terminal name="cherosene" Code="00001004" />
<Terminal name="Oil" Code="00001004" />
<Terminal name="cherosene" Code="00001004" />
<Terminal name="alcohol" Code="00001005" />
</Devices>
</x:XData>
</XmlDataProvider>
<CollectionViewSource x:Key="TerminalByCodes" Source="{Binding Source={StaticResource data}, XPath=Devices/Terminal}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="#Code" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<DockPanel>
<ScrollViewer DockPanel.Dock="Bottom" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Source={StaticResource TerminalByCodes}}" >
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Header="{Binding Name}">
<ItemsPresenter/>
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}" Background="#FFDBA8A8" Margin="0,0,10,0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DockPanel>
</Grid>
This code has this output:
As you can see, the data are written in the xaml. But, as you imagine, this isn't how I have to work. How can I update my code to make it work with "code-generated-data"? And how if is it made with a mvvm binding?
Salve Piero !
You can instanciate CollectionViewsSource in a view model class :
CollectionViewSource viewSource = new CollectionViewSource();
and provide some data to it :
private List<Terminal> terminals = new List<Terminal>
{
new Terminal{ Code="00001001", Name= "Gasoline" },
new Terminal{ Code="00001001", Name= "cherosene"},
new Terminal{ Code="00001001", Name= "Oil"},
new Terminal{ Code="00001003", Name= "Gasoline" },
new Terminal{ Code="00001003", Name= "cherosene"},
new Terminal{ Code="00001003", Name= "Oil"},
};
terminalsViewSource.Source = terminals;
Create a property in a view model class so that Databinding can work :
public Object TerminalsView
{
get { return terminalsViewSource.View; }
}
In code behind you can create the ViewModel :
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
Bind to it in .xaml :
<DataGrid x:Name="datagridTerminals" ItemsSource="{Binding TerminalsView}" AutoGenerateColumns="True" >
Additionaly in Viewmodel you can add, Filter, IsSorted, IsGrouped properties so that your data can be filtered (by name for instance), grouped (or not), and sorted.
For instance :
<TextBox x:Name="textboxFilter" Text="{Binding Filter}" />
Property in ViewModel :
public String Filter
{
get { return filter; }
set
{
filter = value;
terminalsViewSource.View.Refresh();
}
}
All that works in the code sample here :
http://1drv.ms/1P8cMVc
Forza !
Related
In a WPF Project I have a set of record objects with the properties School, Subject, FirstName and LastName. The records are grouped by School and Subject using CollectionViewSource in XAML and this is used by a TreeView to show the grouped items. The groupings work fine.
The problem is I would like to show the FirstName and LastName of the records under the Subject in a ListView using a GridView as its View with FirstName and LastName as columns, but I can't figure out how to do this.
Here is an image of the current display:
Here's some sample code to show what I mean.
public class Record
{
public string School { get; set; }
public string Subject { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var records = new Record[]
{
new Record() { School = "School A", Subject = "Maths" , FirstName = "Fred" , LastName = "Blogs"},
new Record() { School = "School A", Subject = "English" , FirstName = "Alice" , LastName = "Lane"},
new Record() { School = "School B", Subject = "Geography" , FirstName = "John" , LastName = "Smith"},
new Record() { School = "School B", Subject = "Geography" , FirstName = "Burt" , LastName = "Lancaster"},
new Record() { School = "School C", Subject = "Chemistry" , FirstName = "Dee" , LastName = "Kaye"}
};
this.DataContext = records;
}
}
XAML :
<Window x:Class="ListViewInTreeView.MainWindow"
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:ListViewInTreeView"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="listings"
Source="{Binding .}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="School" />
<PropertyGroupDescription PropertyName="Subject" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<TreeView Grid.Row="4" DataContext="{StaticResource listings}" ItemsSource="{Binding}" >
<TreeView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="0,0,0,0" IsExpanded="True" BorderBrush="#FFA4B97F"
BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter Margin="20,0,0,0" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</TreeView.GroupStyle>
<TreeView.Resources>
<!-- NEED HELP HERE I THINK ?-->
<HierarchicalDataTemplate DataType="{x:Type GroupItem}">
<ListView ItemsSource="{Binding .}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
</GridView>
</ListView.View>
</ListView>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
Use 2 GroupStyles, one for the School group and another one for the Subject group:
<TreeView Grid.Row="4" DataContext="{StaticResource listings}" ItemsSource="{Binding}" >
<TreeView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="0,0,0,0" IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter Margin="20,0,0,0" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Margin="0,0,0,0" IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
</GridView>
</ListView.View>
</ListView>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</TreeView.GroupStyle>
</TreeView>
You probably also want to move the common property settings for the Expander and GroupItem to two styles that are used by both GroupStyles:
<TreeView Grid.Row="4" DataContext="{StaticResource listings}" ItemsSource="{Binding}" >
<TreeView.Resources>
<Style TargetType="GroupItem">
<Setter Property="Margin" Value="0,0,0,5"/>
</Style>
<Style TargetType="Expander">
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="IsExpanded" Value="True" />
<Setter Property="BorderBrush" Value="#FFA4B97F" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Setter Property="Header" Value="{Binding}" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}"/>
<TextBlock Text=" : "/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
<TextBlock Text=" items(s)"/>
</DockPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
<TreeView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Content>
<ItemsPresenter Margin="20,0,0,0" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Content>
<ListView ItemsSource="{Binding Items}">
<ListView.View>
<GridView>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding LastName}"/>
</GridView>
</ListView.View>
</ListView>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</TreeView.GroupStyle>
</TreeView>
I want make a footer to my DataGrid using this method:
How do I add a footer row in a WPF datagrid?
But, i need to make this by code behind, and that's don't working.
I think it's because the binding of the grid can't find the column.
private DataGridColumn dgInsertCol(ref int idx, DataGridColumn dgc, tblEtatRecapColonne pCol, string pBinding = "") {
var dhHeadName = $"dgHead{pCol.Id}";
dgc.SetValue(NameProperty, dhHeadName);
dgc.HeaderTemplate = GetDtHeader(pCol, pBinding);
dgc.Width = new DataGridLength(1.0, (ckbSize.IsChecked.Value) ? DataGridLengthUnitType.Star : DataGridLengthUnitType.SizeToCells);
dgMain.Columns.Insert(idx++, dgc);
// Faire le footer associƩ
var t = new TextBlock() { Margin = new Thickness(5, 0, 0, 0), Text = pBinding, Background = new SolidColorBrush(Color.FromRgb(50, 100, 150)) };
var g = new Grid() { MinWidth = 10 };
g.SetBinding(Grid.WidthProperty, new Binding("ActualWidth") { ElementName = dhHeadName, Path=new PropertyPath("ActualWidth", null) }); // DataGridColumn
g.Children.Add(t);
pnlDgFooter.Children.Add(g);
return dgc;
}
<DataGrid Grid.Row="1" x:Name="dgMain" AutoGenerateColumns="False" SelectionUnit="FullRow" LoadingRow="dgMain_LoadingRow" MouseDown="dgMain_MouseDown" Sorting="dgMain_Sorting"
CanUserReorderColumns="False" CanUserResizeColumns="True" CanUserResizeRows="False" CanUserSortColumns="True" CanUserAddRows="False"
Style="{StaticResource dg}" RowStyle="{StaticResource dgRow}" CellStyle="{StaticResource dgCell}" ColumnHeaderStyle="{StaticResource dgColHeader}" RowHeaderStyle="{StaticResource dgRowHeader}"
ItemsSource="{Binding NotifyOnSourceUpdated=True, Source={StaticResource cvsElmts}}" HorizontalAlignment="Left">
<!--DataGrid.DataContext><Binding Source="{StaticResource tblUsers}"/></DataGrid.DataContext-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<mvvm:EventToCommand Command="{Binding SendCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=dgMain}" PassEventArgsToCommand="False"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderThickness="1,1,1,5">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
<!-- Style for groups under the top level. -->
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<DockPanel Background="LightBlue">
<TextBlock Text="{Binding Path=Name}" Foreground="Blue" Margin="30,0,0,0" Width="100"/>
<TextBlock Text="{Binding Path=ItemCount}" Foreground="Blue"/>
</DockPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
<DataGrid.Columns>
<StaticResource ResourceKey="rowBtnDetail"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2" Name="pnlDgFooter" HorizontalAlignment="Left" Orientation="Horizontal" >
</StackPanel>
Why use ElementName at all? Set the Source of the Binding directly. Also, don't set Path twice, so either write
g.SetBinding(Grid.WidthProperty,
new Binding
{
Source = dgc,
Path = new PropertyPath("ActualWidth")
});
or
g.SetBinding(Grid.WidthProperty, new Binding("ActualWidth") { Source = dgc });
I have ItemControl
<ItemsControl ItemsSource="{Binding CanvasCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="{Binding CanvasSize}" Height="{Binding CanvasSize}" Background="RoyalBlue" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:VertexViewModel}">
<Thumb Width="11" Height="11">
<Thumb.Template>
<ControlTemplate>
<Ellipse Width="11" Height="11" Fill="{Binding Fill}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And composite collection with currently one ObservableCollection:
private ObservableCollection<VertexViewModel> Points { get; set; }
public CompositeCollection CanvasCollection { get; set; }
While debugging, I see that collection contains two elements, as I accepted, but only first is displaying on Canvas. When I call Refresh() method in model, I see that binding working, but only for first element.
Adding Point to CompositeCollection:
Points = new ObservableCollection<VertexViewModel>();
CanvasCollection = new CompositeCollection()
{
Points
};
I had to use container for collectoin:
CanvasCollection = new CompositeCollection
{
new CollectionContainer() {Collection = Points},
Polygon,
};
I'm trying to make a PropertyGrid Custom Control. The PropertyGrid will be very similar to the PropertyGrid used in Visual Studio. I've tried using the PropertyGrid of the Extended WPF Toolkit but you have to specify the category of a property with an attribute and we need to change the categories runtime. Which as far as I know is impossible with attributes.
So I'm making the PropertyGrid myself. This is my code so far:
The Generic.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:HomeMadePropertyGrid"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"></BooleanToVisibilityConverter>
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<ControlTemplate x:Key="toggleButtonTemplate" TargetType="ToggleButton">
<Grid Width="15" Height="13" Background="Transparent">
<Path x:Name="ExpandPath" Fill="{StaticResource GlyphBrush}" Data="M 4 0 L 8 4 L 4 8 Z" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="1,1,1,1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="toggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Template" Value="{StaticResource toggleButtonTemplate}" />
</Style>
<Style TargetType="{x:Type local:PropertyGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PropertyGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ItemsControl ItemsSource="{TemplateBinding ItemsSource}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="toggleButton" Height="20" Width="20" Style="{StaticResource toggleButtonStyle}"/>
<TextBlock Text="{Binding Name}" FontWeight="Bold"></TextBlock>
</StackPanel>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}"
Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<TextBlock Background="White" Text="{Binding Path=Name}"/>
</Border>
<GridSplitter Width="1"
Grid.RowSpan="4" Grid.Column="1"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}"/>
<Border Grid.Column="2" BorderThickness="1" BorderBrush="{Binding GridColor, RelativeSource={RelativeSource AncestorType=local:PropertyGrid}}">
<ContentPresenter Grid.Column="2" Content="{Binding Value}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type System:String}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Int32}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
TextAlignment="Right"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Double}">
<TextBox Text="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
TextAlignment="Right"
BorderThickness="0"/>
</DataTemplate>
<DataTemplate DataType="{x:Type System:Boolean}">
<CheckBox IsChecked="{Binding Path=Content, RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
PropertyGrid.cs
public class PropertyGrid : ItemsControl
{
public Brush GridColor
{
get { return (Brush)GetValue(GridColorProperty); }
set { SetValue(GridColorProperty, value); }
}
public static readonly DependencyProperty GridColorProperty =
DependencyProperty.Register("GridColor", typeof(Brush), typeof(PropertyGrid), new UIPropertyMetadata(new SolidColorBrush(Colors.Transparent)));
static PropertyGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PropertyGrid), new FrameworkPropertyMetadata(typeof(PropertyGrid)));
}
}
PropertyGroup
public class PropertyGroup
{
public string Name { get; set; }
public List<PropertyGridItem> Items { get; set; }
public PropertyGroup()
{
Items = new List<PropertyGridItem>();
Name = "";
}
}
PropertyGridItem
public class PropertyGridItem
{
public string Name { get; set; }
public object Value { get; set; }
public PropertyGridItem(string propertyName, object propertyValue)
{
Name = propertyName;
Value = propertyValue;
}
}
This code in my MainWindow.xaml:
<local:PropertyGrid ItemsSource="{Binding Path=Groups}" GridColor="#f0f0f0"/>
Code behind of my ViewModel:
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
The ViewModel
public class ViewModel
{
public List<PropertyGroup> Groups { get; set; }
public ViewModel()
{
Groups = new List<PropertyGroup>();
PropertyGroup group1 = new PropertyGroup();
group1.Name = "Group1";
group1.Items.Add(new PropertyGridItem("Item1", "test"));
group1.Items.Add(new PropertyGridItem("Item2", 300));
group1.Items.Add(new PropertyGridItem("Item3", true));
group1.Items.Add(new PropertyGridItem("Item4", 5.2));
Groups.Add(group1);
PropertyGroup group2 = new PropertyGroup();
group2.Name = "Group2";
group2.Items.Add(new PropertyGridItem("Item1", "test"));
group2.Items.Add(new PropertyGridItem("Item2", 300));
group2.Items.Add(new PropertyGridItem("Item3", true));
group2.Items.Add(new PropertyGridItem("Item4", 5.2));
Groups.Add(group2);
}
}
The problem I'm having is that the GridSplitter is applied every row. I want the GridSplitter to be applied to all rows of a group. I understand that this is because I make a new Grid for every item. For the attached properties to work the items have to be a direct child of the Grid.
A DataGrid also isn't an option because the GridSplitter is only available between column headers.
So to make a long story short: how can I use a Grid in an ItemsControl with a GridSplitter that applies to all rows of ideally a group or the entire Grid if that isn't possible.
I finally found the solution to this problem.
To get this to work I had to:
Set the Grid.IsSharedSizeScope to true on the parent ItemsControl.
Set the SharedSizeGroup property on the name column with an arbitrary name.
Remove the star sizing on the name column. Having both the first and third column with star sizing resulted in a stuck GridSplitter for some reason.
Set a fixed width on the GridSplitter, I set the Width to 2.
Set the ResizeBehaviour of the GridSplitter to PreviousAndNext.
Here is a relevant piece of the resulting code:
<ItemsControl ItemsSource="{Binding Items}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter={StaticResource BoolToVisConverter}}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="nameColumn"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource BodyPropertyGrid_CellBorder}">
<TextBlock Text="{Binding Path=Name}"/>
</Border>
<GridSplitter Grid.Column="1" Width="2"
ResizeBehavior="PreviousAndNext"
Style="{StaticResource BodyPropertyGridSplitter}"/>
<Border Grid.Column="2" Style="{StaticResource BodyPropertyGrid_CellBorder}">
<ContentControl Content="{Binding}"
ContentTemplateSelector="{StaticResource propertyItemTemplateSelector}"/>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Unfortunately you didn't provide us with a nice simple piece of code that only demonstrates your problem and I don't have time to get it to compile and run in my test project, so I can only give you suggestions and not tested solutions. Next time, please take the time to show a code example that we can just copy and paste into a project.
You might be able to get your 'stretched' GridSplitter if you join all of the row Grids using the Grid.IsSharedSizeScope Attached Property:
<Grid Visibility="{Binding ElementName=toggleButton, Path=IsChecked, Converter=
{StaticResource BoolToVisConverter}}" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="FirstColumn" />
<ColumnDefinition Width="Auto" SharedSizeGroup="GridSplitterColumn" />
<ColumnDefinition Width="*" SharedSizeGroup="LastColumn" />
</Grid.ColumnDefinitions>
...
</Grid>
My View is clickable like a radiobutton with about 7 other of these radiobuttons, but my listbox is not updating it's selected property unless I click outside of my radiobutton.
Basically I have a AbstractTask as my base. I have 7 child classes. I want to be able to select one of these AbstractTasks. That's all i'm after. So in my main window i have this.
<Window x:Class="AdvancedTaskAssigner.View.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:AdvancedTaskAssigner.View"
Title="MainWindowView" Height="300" Width="300" SizeToContent="Height">
<DockPanel>
<TextBlock Text="TextBlock" DockPanel.Dock="Top" />
<TextBlock Text="{Binding ElementName=listTasks, Path=SelectedItem.Name}" DockPanel.Dock="Top" />
<ListBox x:Name="listTasks" ItemsSource="{Binding Tasks}" HorizontalContentAlignment="Stretch" SelectedItem="{Binding IsSelected}">
<ListBox.ItemTemplate>
<DataTemplate>
<v:AbstractTaskView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
since this is a library and not a application I had to put this in the Constructor of MainWindowView
MainWindowView.xaml.cs
public MainWindowView()
{
InitializeComponent();
var atvm = new ViewModel.MainWindowViewModel();
atvm.LoadTasks();
this.DataContext = atvm;
}
MainWindowViewModel.cs
class MainWindowViewModel
{
internal void LoadTasks()
{
var assembly = Assembly.GetAssembly(typeof(AbstractTask)).GetTypes().Where(t => t.IsSubclassOf(typeof(AbstractTask)));
Type[] typelist = GetTypesInNamespace(Assembly.GetAssembly(typeof(AbstractTask)), typeof(AbstractTask));
foreach (Type t in typelist)
{
if(!t.IsAbstract && t.BaseType.Equals(typeof(AbstractTask)))
{
tasks.Add(new AbstractTaskViewModel(t));
}
}
}
private Type[] GetTypesInNamespace(Assembly assembly, Type baseClass)
{
return assembly.GetTypes().Where(t => t.IsSubclassOf(baseClass)).ToArray();
}
private ObservableCollection<AbstractTaskViewModel> tasks = new ObservableCollection<AbstractTaskViewModel>();
public ObservableCollection<AbstractTaskViewModel> Tasks
{
get { return tasks; }
}
}
AbstractTaskViewModel.cs
public class AbstractTaskViewModel
{
public AbstractTaskViewModel(Type task)
{
if (!task.IsSubclassOf(typeof(AbstractTask)))
{
throw new NotSupportedException(string.Format("{0} is not a subclass of AbstractTask", task.Name));
}
Task = task;
}
public string Description
{
get
{
return GetCustomAttribute(0);
}
}
public string Name
{
get
{
return GetCustomAttribute(1);
}
}
public bool IsSelected{get;set;}
private string GetCustomAttribute(int index)
{
var descriptions = (DescriptionAttribute[])Task.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (descriptions.Length == 0)
{
return null;
}
return descriptions[index].Description;
}
protected readonly Type Task;
}
AbstractTaskView.xaml
<RadioButton
x:Class="AdvancedTaskAssigner.View.AbstractTaskView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" GroupName="AbstractTasks" Background="Transparent" IsChecked="{Binding IsSelected, Mode=TwoWay}">
<RadioButton.Template>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Grid>
<Border x:Name="MyHead" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Background="LightGray" Margin="20,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Panel.ZIndex="2">
<TextBlock Text="{Binding Name}" Margin="5,2" MinWidth="50" />
</Border>
<Border x:Name="Myoooo" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Background="Transparent" Margin="0,10,0,0" Panel.ZIndex="1">
<TextBlock Text="{Binding Description}" Margin="5,15,5,2" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="MyHead" Property="Background" Value="LightGreen" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
this is what i am getting..
I want the green border to be the selected item. not what the Listbox's Selected item is. The bottom text box is the name of the selected item.
There are some issues related to binding IsChecked property of RadioButton. You can read about it here or here. You can apply the solution presented in the first link using your AbstractTaskView instead of RadioButton. Replace your listbox in MainWindowView with the code:
<ListBox x:Name="listTasks" ItemsSource="{Binding Tasks}" HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<v:AbstractTaskView Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
You have to also remove the folowing part of code from AbstractTaskView:
IsChecked="{Binding IsSelected, Mode=TwoWay}"
I hope you don't need IsSelected property of AbstractTaskViewModel to be set. But if you do, you can set it for example in Checked event handler in AbstractTaskView code behind (I know it's not pretty solution).
I see you're binding the IsSelected (boolean) to the SelectedItem (object)