Binding DataGridTemplateColumn in UserControl.Resources - c#
I am currently trying to write a small calendar program in C# WPF.
For the datagrid I have written a userControl.
This works like I want it to work. I will still need to update the optics but for now it is good.
The problem is the code of the DataGridTextColums.. It is all redundant.
So if I want to change something I need to change it everywhere.
<DataGrid x:Name="datagrid"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeColumns="False"
CanUserReorderColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
AutoGenerateColumns="False"
SelectionUnit="Cell"
SelectionMode="Extended"
HeadersVisibility="Column"
Background="Transparent"
BorderBrush="Transparent"
ItemsSource="{Binding DataGridSource.DataGridList, RelativeSource={RelativeSource AncestorType=UserControl}}">
<DataGrid.Columns>
<DataGridTextColumn Header="CW" Width="*" IsReadOnly="True" Binding="{Binding CwCell.Text}">
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{Binding CwCell.BackColor}"/>
<Setter Property="BorderBrush" Value="{Binding CwCell.BorderColor}"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Header="MO" Width="*" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border>
<StackPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Text="{Binding MondayCell.Text}" Margin="5,0,0,0" Width="18"/>
<ItemsControl ItemsSource="{Binding Path=MondayCell.Addons}" Margin="0,0,7,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" MaxHeight="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="4" Height="4" Margin="0,1,1,1" Fill="{Binding Color}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{Binding MondayCell.BackColor}"/>
<Setter Property="BorderBrush" Value="{Binding MondayCell.BorderColor}"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip BorderThickness="0" Background="Transparent">
<Grid>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<StackPanel Orientation="Vertical" DataContext="{Binding MondayCell}">
<TextBlock Text="{Binding AppointmentName}"
Background="White" Width="auto" Margin="5,5,5,1"
TextWrapping="Wrap"
FontWeight="Bold"/>
<Border BorderBrush="Silver" BorderThickness="0,1,0,0" Margin="0,1,0,0" />
<TextBlock Text="{Binding AppointmentDescr}"
Background="White" Margin="5,1,5,5"
TextWrapping="Wrap"/>
<Border BorderBrush="Silver" BorderThickness="0,1,0,0" Margin="0,1,0,0" />
<ItemsControl ItemsSource="{Binding Path=Addons}" Margin="0,0,7,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="4" Height="4" Fill="{Binding Color}" Margin="5,0,5,0"/>
<Border BorderBrush="Silver" BorderThickness="1,0,0,0" Margin="0,0,0,0"/>
<TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</Grid>
</ToolTip>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="DI" Width="*" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border>
<StackPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Text="{Binding TuesdayCell.Text}" Margin="5,0,0,0" Width="18"/>
<ItemsControl ItemsSource="{Binding Path=TuesdayCell.Addons}" Margin="0,0,7,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" MaxHeight="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="4" Height="4" Margin="0,1,1,1" Fill="{Binding Color}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{Binding TuesdayCell.BackColor}"/>
<Setter Property="BorderBrush" Value="{Binding TuesdayCell.BorderColor}"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip BorderThickness="0" Background="Transparent">
<Grid>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<StackPanel Orientation="Vertical" DataContext="{Binding TuesdayCell}">
<TextBlock Text="{Binding AppointmentName}"
Background="White" Width="auto" Margin="5,5,5,1"
TextWrapping="Wrap"
FontWeight="Bold"/>
<Border BorderBrush="Silver" BorderThickness="0,1,0,0" Margin="0,1,0,0" />
<TextBlock Text="{Binding AppointmentDescr}"
Background="White" Margin="5,1,5,5"
TextWrapping="Wrap"/>
<Border BorderBrush="Silver" BorderThickness="0,1,0,0" Margin="0,1,0,0" />
<ItemsControl ItemsSource="{Binding Path=Addons}" Margin="0,0,7,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="4" Height="4" Fill="{Binding Color}" Margin="5,0,5,0"/>
<Border BorderBrush="Silver" BorderThickness="1,0,0,0" Margin="0,0,0,0"/>
<TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</Grid>
</ToolTip>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<!--same for wednesdayCell to sundayCell-->
</DataGrid.Columns>
</DataGrid>
So as you can see each DataGridTemplateColumn binds to a different Cell (Monday to Sunday).
I tried to create a template out of this. If i keep "MondayCell" in the template (like in the Code below), it works. But obviously I cannot use any other source of data except "MondayCell" then.
See:
<UserControl.Resources>
<DataTemplate x:Key="MyCellTemplate">
<Border>
<StackPanel Orientation="Horizontal" Background="Transparent">
<TextBlock Text="{Binding MondayCell.Text}" Margin="5,0,0,0" Width="18"/>
<ItemsControl ItemsSource="{Binding Path=MondayCell.Addons}" Margin="0,0,7,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" MaxHeight="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse Width="4" Height="4" Margin="0,1,1,1" Fill="{Binding Color}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</DataTemplate>
<Style TargetType="{x:Type DataGridCell}" x:Key="MyCellStyle">
<Setter Property="Background" Value="{Binding MondayCell.BackColor}"/>
<Setter Property="BorderBrush" Value="{Binding MondayCell.BorderColor}"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip BorderThickness="0" Background="Transparent">
<Grid>
<Border Background="White" BorderBrush="Black" BorderThickness="1">
<StackPanel Orientation="Vertical" DataContext="{Binding MondayCell}">
<TextBlock Text="{Binding AppointmentName}"
Background="White" Width="auto" Margin="5,5,5,1"
TextWrapping="Wrap"
FontWeight="Bold"/>
<Border BorderBrush="Silver" BorderThickness="0,1,0,0" Margin="0,1,0,0" />
<TextBlock Text="{Binding AppointmentDescr}"
Background="White" Margin="5,1,5,5"
TextWrapping="Wrap"/>
<Border BorderBrush="Silver" BorderThickness="0,1,0,0" Margin="0,1,0,0" />
<ItemsControl ItemsSource="{Binding Path=Addons}" Margin="0,0,7,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical">
</StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Width="4" Height="4" Fill="{Binding Color}" Margin="5,0,5,0"/>
<Border BorderBrush="Silver" BorderThickness="1,0,0,0" Margin="0,0,0,0"/>
<TextBlock Text="{Binding Name}" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</Grid>
</ToolTip>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="OrangeRed"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
Usage in the Datagrid: This works, but only shows the data from MondayCell.
<DataGridTemplateColumn Header="MO" Width="*" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<StaticResource ResourceKey="MyCellTemplate"/>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<StaticResource ResourceKey="MyCellStyle"/>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
How I want to use it, but doesn't work like this:
Any help is appreciated.
For now I keep it redundant, but I would rather use a template so I only need to change 1 piece of code instead of 7.
Thank you for your help!
I would like to present a solution that is based on using a UserControl:
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:StandardColumn Column="{Binding MondayCell}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:StandardColumn Column="{Binding TuesdayCell}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The StandardColumn implementation , for simplicity , can be as following:
<UserControl x:Class="Problem8.StandardColumn"
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"
xmlns:local="clr-namespace:Problem8"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Name="Parent">
<StackPanel>
<TextBlock Text="{Binding ElementName=Parent,Path=Column}"/>
</StackPanel>
</UserControl>
In this userControl we have a dependency property called Column :
public partial class StandardColumn : UserControl
{
public StandardColumn()
{
InitializeComponent();
}
public static readonly DependencyProperty ColumnProperty =
DependencyProperty.Register("Column", typeof(object), typeof(StandardColumn));
public object Column
{
get { return (object)GetValue(ColumnProperty); }
set { SetValue(ColumnProperty, value); }
}
}
We can also make it even more simple by using the DataContext instead of the Column dependency property.
You could build your columns dynamically using a base string out a txt file and use xamlreader.Parse or persist to disk and then use xamlreader.load.
I answered a similar question a while back but I cannot find the answer.
Create a template as a txt file with placeholders for the parts you want to substitute. Here is the example I use in a sample I wrote:
<?xml version="1.0" encoding="utf-8" ?>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="xxMMMxx" TextAlignment="Center" Grid.ColumnSpan="2"/>
<TextBlock Text="Units" Margin="2,0,2,0" Grid.Row="1"/>
<TextBlock Text="Value" Margin="2,0,2,0" Grid.Row="1" Grid.Column="2" />
</Grid>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding MonthTotals[xxNumxx].Products}" Margin="2,0,2,0" TextAlignment="Right"/>
<TextBlock Text="{Binding MonthTotals[xxNumxx].Total}" Margin="2,0,2,0" TextAlignment="Right" Grid.Column="1"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Make that content, copy if newer and you will get a txt file next to your exe you can read in.
In this I have a dynamic datagrid as another txt file as well, but that is not totally necessary.
Here's my code builds and adds columns.
You could just work with the txt file as a string and do string substitution. This shows manipulating it as xml which has the potential for you to insert xelements etc. It is setting attributes whilst you can maybe forget that part and just do the string substitution, xamlreader.parse into a datagrid column and add to the columns collection.
It is substituting month numbers in the binding for a range of months.
If you consider a rolling last n month financial report, this is the general idea. Last month would be 3 now in april. 4 when you run in may.
private void Button_Click(object sender, RoutedEventArgs e)
{
// Get the datagrid shell
XElement xdg = GetXElement(#"pack://application:,,,/dg.txt");
XElement cols = xdg.Descendants().First(); // Column list
// Get the column template
XElement col = GetXElement(#"pack://application:,,,/col.txt");
DateTime mnth = DateTime.Now.AddMonths(-6);
for (int i = 0; i < 6; i++)
{
DateTime dat = mnth.AddMonths(i);
XElement el = new XElement(col);
// Month in mmm format in header
var mnthEl = el.Descendants("TextBlock")
.Single(x => x.Attribute("Text").Value.ToString() == "xxMMMxx");
mnthEl.SetAttributeValue("Text", dat.ToString("MMM"));
string monthNo = dat.AddMonths(-1).Month.ToString();
// Month as index for the product
var prodEl = el.Descendants("TextBlock")
.Single(x => x.Attribute("Text").Value == "{Binding MonthTotals[xxNumxx].Products}");
prodEl.SetAttributeValue("Text",
"{Binding MonthTotals[" + monthNo + "].Products}");
// Month as index for the total
var prodTot = el.Descendants("TextBlock")
.Single(x => x.Attribute("Text").Value == "{Binding MonthTotals[xxNumxx].Total}");
prodTot.SetAttributeValue("Text",
"{Binding MonthTotals[" + monthNo + "].Total}");
cols.Add(el);
}
string dgString = xdg.ToString();
ParserContext context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
DataGrid dg = (DataGrid)XamlReader.Parse(dgString, context);
Root.Children.Add(dg);
}
private XElement GetXElement(string uri)
{
XDocument xmlDoc = new XDocument();
var xmltxt = Application.GetContentStream(new Uri(uri));
string elfull = new StreamReader(xmltxt.Stream).ReadToEnd();
xmlDoc = XDocument.Parse(elfull);
return xmlDoc.Root;
}
}
You could potentially build out your entire year view using this sort of approach. Build it as xml or a string. Save to disk. xamlreader.load() it back in as controls off disk.
A similar string manipulation based approach would be to build your control as text. Save as a datatemplate in a resource dictionary. You can load such an uncompiled resource dictionary off disk then and just build once a year. You could build centrally in a small utility app intended just for the purpose and distribute to clients.
This is my second solution. I believe that the selection of the DataGrid as a base have created this complexity. Alternatively we can consider more basic controls.
I am drafting the solution:
The MonthView user control may look as following:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:HeaderView Grid.Row="0"/>
<ItemsControl ItemsSource="{Binding Weeks}" Grid.Row="1" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:WeekView/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The WeekView user control may look as following:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<local:DayView DataContext="{Binding CwCell}" Grid.Column="0"/>
<local:DayView DataContext="{Binding MondayCell}" Grid.Column="1"/>
<local:DayView DataContext="{Binding TuesdayCell}" Grid.Column="2"/>
<local:DayView DataContext="{Binding WednesdayCell}" Grid.Column="3"/>
<local:DayView DataContext="{Binding ThursdayCell}" Grid.Column="4"/>
<local:DayView DataContext="{Binding FridayCell}" Grid.Column="5"/>
<local:DayView DataContext="{Binding FridayCell}" Grid.Column="6"/>
<local:DayView DataContext="{Binding SaturdayCell}" Grid.Column="7"/>
<local:DayView DataContext="{Binding SundayCell}" Grid.Column="8"/>
</Grid>
The DayView user control may look as following:
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding Text}" Width="20" Height="20" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
The HeaderView user control may look as following:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<local:ColumnHeaderView DataContext="CW" Grid.Column="0" />
<local:ColumnHeaderView DataContext="MO" Grid.Column="1" />
<local:ColumnHeaderView DataContext="TU" Grid.Column="2" />
<local:ColumnHeaderView DataContext="WE" Grid.Column="3" />
<local:ColumnHeaderView DataContext="TH" Grid.Column="4" />
<local:ColumnHeaderView DataContext="FR" Grid.Column="5" />
<local:ColumnHeaderView DataContext="SA" Grid.Column="6" />
<local:ColumnHeaderView DataContext="SU" Grid.Column="7" />
</Grid>
The ColumnHeaderView user control may look as following:
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding }" Width="20" Height="15" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
Related
WPF C# - Select Rows in DataGrid when they are grouped
I'm working on a plugin. I need the user to be able to make a selection in the main application, and those objects become highlighted in the datagrid. I currently do this by having the application's GUIDs stored in the C# mapping class. In this way, I can do a linq iteration through all the objets in the data grid, and select the ones with the same GUIDs as the user selection. foreach(ModelObjectsData selectedObject in selectedObjects) { //find the index of the selected objects in the overall ObservableCollection used as the source for RyansGrid int index = mySources.FindIndex(x=>x.GUID.Equals(selectedObject.GUID)); object item = dataGrid.Items[index]; //select the row that contains the index dataGrid.SelectedItems.Add(item); dataGrid.ScrollIntoView(item); } This approach worked very well. However, it doesn't work when I have the dataGrid grouped. What do I need to do to make this work with groups? For some more context, I am passing the List of objects to the datagrid as a ListCollectionView. I think this is necessary to be able to group: private void CreateListCollection(List<ModelObjectsData> source) { ListCollectionView collectable = new ListCollectionView(source); collectable.GroupDescriptions.Add(new PropertyGroupDescription("Type")); collectable.GroupDescriptions.Add(new PropertyGroupDescription("CCS")); RyansGrid.ItemsSource = collectable; } Here is the XAML for some context about the way the datagrid is constructed: <Window.Resources> <CollectionViewSource x:Key="cvsTasks"> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="Type"/> <PropertyGroupDescription PropertyName="CCS"/> </CollectionViewSource.GroupDescriptions> </CollectionViewSource> </Window.Resources> <DataGrid x:Name="RyansGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="2" Grid.Column="0" Margin="43,24,0,34" SelectedCellsChanged="RyansGrid_SelectedCellsChanged" AutoGenerateColumns="False" CellEditEnding="RyansGrid_CellEditEnding" CanUserAddRows="False"> <DataGrid.Columns> <DataGridTextColumn Header="CCS" Width="25*" MinWidth="90" Binding="{Binding CCS}"/> <DataGridTextColumn Header="Type" Width="25*" MinWidth="90" Binding="{Binding Type}"/> <DataGridTextColumn Header="Profile" Width="25*" MinWidth="90" Binding="{Binding ProfileName}" IsReadOnly="True"/> <DataGridTextColumn Header="Count" Width="25*" MinWidth="90" IsReadOnly="True"/> </DataGrid.Columns> <DataGrid.GroupStyle> <GroupStyle> <GroupStyle.ContainerStyle> <Style TargetType="{x:Type GroupItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type GroupItem}"> <Expander x:Name="exp" IsExpanded="False" Background="LightGray" Foreground="Black" BorderThickness="1,1,1,5"> <Expander.Header> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="25*"/> <ColumnDefinition Width="25*"/> <ColumnDefinition Width="25*"/> <ColumnDefinition Width="25*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Path=Name}" Foreground="Black" FontWeight="Bold"/> <TextBlock Grid.Column="3" Text="{Binding Path=ItemCount}" Foreground="Black" FontWeight="Bold" HorizontalAlignment="Left" Margin="5,0,0,0"/> </Grid> </Expander.Header> <Expander.Content> <ItemsPresenter/> </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 x:Name="exp" IsExpanded="False" Background="White" Foreground="Black" BorderThickness="1,1,1,5"> <Expander.Header> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="25*"/> <ColumnDefinition Width="25*"/> <ColumnDefinition Width="25*"/> <ColumnDefinition Width="25*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Path=Name}" Foreground="Gray" FontWeight="Bold"/> <TextBlock Grid.Column="3" Text="{Binding Path=ItemCount}" Foreground="Gray" FontWeight="Bold" HorizontalAlignment="Left" Margin="5,0,0,0"/> </Grid> </Expander.Header> <Expander.Content> <ItemsPresenter/> </Expander.Content> </Expander> </ControlTemplate> </Setter.Value> </Setter> </Style> </GroupStyle.ContainerStyle> </GroupStyle> </DataGrid.GroupStyle> </DataGrid>
XAML : expand expander when an item in the group is selected
Is it possible for a expander to check for the IsExpanded Property if an item inside the group of the expander is selected then this expander is expanded so IsExpanded will be true? Because if i set it to true by default all expanders will be expanded at that's not the goal. <controls:CommandListBox Grid.IsSharedSizeScope="True" Visibility="{Binding IsComponentOrSystemPropertySearch, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.Column="2" HorizontalAlignment="Stretch" SelectedItem="{Binding SelectedResult}" ToolbarItems="{Binding ToolBarItems}" ItemsSource="{Binding SearchResults, UpdateSourceTrigger=PropertyChanged}"> <ItemsControl.Resources> <ResourceDictionary> <Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type GroupItem}"> <Expander x:Name="exp" IsExpanded="False" Margin="0,0,0,30" > <Expander.Header > <TextBlock Text="{Binding Path=Name}" FontSize="18" Padding="0" Margin="0"/> </Expander.Header> <Grid Margin="20 0 0 0" > <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ContentPresenter Grid.Column="0" Grid.Row="0" Content="{Binding Path=Items[0].DisplayRepresentation.Header}"/> <ItemsPresenter Grid.Column="0" Grid.Row="1"/> </Grid> </Expander> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> </ItemsControl.Resources> <ItemsControl.GroupStyle> <GroupStyle ContainerStyle="{StaticResource GroupHeaderStyle}"> <GroupStyle.Panel> <ItemsPanelTemplate> <DataGridRowsPresenter /> </ItemsPanelTemplate> </GroupStyle.Panel> </GroupStyle> </ItemsControl.GroupStyle> <ItemsControl.ItemTemplate> <DataTemplate DataType="{x:Type local:DeluxePropertySearchResult}"> <ContentPresenter Content="{Binding DisplayRepresentation}" /> </DataTemplate> </ItemsControl.ItemTemplate> </controls:CommandListBox>
WPF XAML returning PositiveInfinity as DesiredSize with vitalized warp panel
I needed to implement a third party solution for a vitalizing warp panel. The first two I tried didn't work because of this error message: ' ...should not return PositiveInfinity as its DesiredSize, even if Infinity is passed in as available size.' So next I went to a commercial solution which gave me the exact same error. So clearly there is something wrong with my code. But no matter what I try, the problem persists. To note, I tried the last solution with a basic example and that did indeed work. I presume that one of my controls are the problem, perhaps needing to stipulate the dimensions, but, I have tried that on every relevant control. Below is the code. It is a ListBox inside a Grid using a data template. <Grid x:Name="MainGrid" Background="#222"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="40" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="300" MinWidth="200" MaxWidth="400" /> <ColumnDefinition Width="*" MinWidth="200" /> <ColumnDefinition Width="400" MinWidth="270" MaxWidth="600" /> </Grid.ColumnDefinitions> ... <!--#region Main Library Column--> <ListBox x:Name="LibraryBox" Grid.Column="1" Grid.Row="0" Grid.RowSpan="4" Margin="20,10,20,10" BorderBrush="Transparent" Background="#1e1e1e" ItemsSource="{Binding}" ItemContainerStyle="{StaticResource ListBoxItemStyle}" IsSynchronizedWithCurrentItem="True" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.IsVirtualizingWhenGrouping="True" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.CacheLengthUnit="Page" VirtualizingPanel.CacheLength="1,2" VirtualizingPanel.VirtualizationMode="Standard" VirtualizingPanel.ScrollUnit="Pixel" ScrollViewer.IsDeferredScrollingEnabled="False" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Extended" SelectionChanged="LibraryBox_SelectionChanged" MouseDoubleClick="LibraryBox_MouseDoubleClick"> <ListBox.Resources> <!--#region scrollbar style--> <Style TargetType="{x:Type ScrollBar}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="Foreground" Value="#990000"/> <Setter Property="Width" Value="25"/> </Style> <!--#region Not working...--> <Style TargetType="{x:Type RepeatButton}"> <Setter Property="Background" Value="AliceBlue"/> <Setter Property="Foreground" Value="Transparent"/> <Setter Property="Width" Value="40"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type RepeatButton}"> <Border Background="Red" /> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--#endregion--> <!--#endregion--> <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/> <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Red"/> </ListBox.Resources> <ListBox.ContextMenu> <ContextMenu> <MenuItem Header="Edit" Click="ContextMenuItemEdit_Click"/> <MenuItem Header="ComicVine Scraper" Click="MenuItemScraper_Click"/> <Separator/> <MenuItem Header="Delete" Click="ContextMenuItemDelete_Click"/> </ContextMenu> </ListBox.ContextMenu> <!--#region Group Style--> <ListBox.GroupStyle> <GroupStyle> <GroupStyle.ContainerStyle> <Style TargetType="{x:Type GroupItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Expander IsExpanded="True"> <Expander.Header> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#dbdbdb" FontSize="16" FontFamily="Cordia New" VerticalAlignment="Bottom" /> <TextBlock Text="{Binding ItemCount}" FontSize="16" Foreground="#dbdbdb" FontStyle="Italic" Margin="10,0,0,0" FontFamily="Cordia New" VerticalAlignment="Bottom" /> </StackPanel> </Expander.Header> <ItemsPresenter /> </Expander> </ControlTemplate> </Setter.Value> </Setter> </Style> </GroupStyle.ContainerStyle> </GroupStyle> </ListBox.GroupStyle> <!--#endregion--> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <Viewbox Height="100" Width="70" Margin="2"> <Viewbox.LayoutTransform> <ScaleTransform ScaleX="{Binding Value, ElementName=ZoomSlider}" ScaleY="{Binding Value, ElementName=ZoomSlider}"/> </Viewbox.LayoutTransform> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="300"/> <ColumnDefinition Width="32"/> </Grid.ColumnDefinitions> <!--This is for thumbnails--> <Image x:Name="coverImage" Grid.Column="0" Grid.Row="0" Source="{Binding CoverPath, Converter={StaticResource UriToBitmapConverter}, IsAsync=True}"/> <Rectangle x:Name="ReadMarkerBottom" Grid.Column="1" Grid.Row="1" Margin="-16,0,0,0" Fill="#fff" Width="32" Height="32" Loaded="CoverImage_Loaded"/> <Rectangle x:Name="ReadMarkerTop" Grid.Column="1" Grid.Row="1" Margin="-16,0,0,0" Fill="#000" Width="30" Height="30" Loaded="CoverImage_Loaded"/> </Grid> </Viewbox> <TextBlock TextTrimming="CharacterEllipsis" TextAlignment="Center" Width="120" Foreground="#dbdbdb" Background="Transparent" Margin="0,0,0,5" Loaded="Text_Loaded" FontFamily="Cordia New" FontWeight="Bold"> </TextBlock> <TextBlock TextTrimming="CharacterEllipsis" TextAlignment="Center" Width="120" Foreground="#dbdbdb" Background="Transparent" Margin="0,0,0,5" Loaded="IssueNumer_Loaded" FontFamily="Cordia New"/> <TextBlock TextTrimming="CharacterEllipsis" TextAlignment="Center" Width="120" Foreground="#dbdbdb" Background="Transparent" Margin="0,0,0,5" Loaded="CountStack_Loaded" FontFamily="Cordia New"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <extendedPanelControls:BinaryVirtualizingWrapPanel AutoUpdateViewportOnDetectingTransactionalUpdatesToDataSource="True" ItemPositioningMode="Greedy" IsItemsHost="True" Orientation="Horizontal"/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> <!--#endregion-->
The answer was to set the MaxHeight on the <ItemsPresenter /> inside the GroupStyle so it new it was not going to go on forever
Updating listview height to fill new form content (c#)
I have a form with a border which contains a listview, within my c# code the height of the border is changed depending on a value. Right now the border height changes with no problem but how can I update the listview to have the same height as the border? Here is my xml: <DataTemplate x:Key="PackageTemplate"> <Border x:Name="PackageBorder" BorderBrush="Black" BorderThickness="2" Margin="10" Padding="0" Width="100" > <Border.Style> <Style> <Style.Triggers> <DataTrigger Binding="{Binding Path=Status}" Value="1"> <Setter Property="Border.Background" Value="#FF999696"/> </DataTrigger> <DataTrigger Binding="{Binding Path=Status}" Value="0"> <Setter Property="Border.Background" Value="#FFE4E4E4"/> </DataTrigger> <DataTrigger Binding="{Binding Path=Layout}" Value="0"> <Setter Property="Border.Height" Value="100"/> </DataTrigger> <DataTrigger Binding="{Binding Path=Layout}" Value="1"> <Setter Property="Border.Height" Value="200"/> </DataTrigger> </Style.Triggers> </Style> </Border.Style> <Grid> <Grid.RowDefinitions> <RowDefinition Height="70"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <ListView Grid.Row="0" Grid.Column="0" Background="{x:Null}" x:Name="List" ItemsSource="{Binding Path=Collection}" ItemTemplate="{DynamicResource ChipTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" BorderThickness="0" BorderBrush="{x:Null}" Foreground="Black" VerticalAlignment="Top" Width="90" > <ListView.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Horizontal" VerticalAlignment="Center" /> </ItemsPanelTemplate> </ListView.ItemsPanel> </ListView> <Label Grid.Row="1" Grid.Column="0" Content="{Binding Path=Location}" FontSize="15" FontFamily="Arial" Foreground="Black" Background="{x:Null}" VerticalAlignment="Bottom" HorizontalAlignment="Left"></Label> </Grid> </Border> </DataTemplate>
In the Xaml you have set the ListView Height to 70 by setting <RowDefinition Height="70"/> and the Width to 90, the ListView will not get any bigger then that, you will need to set <RowDefinition Height="70*"/> allow it to grow in height and remove the Width="90", or perhaps use DockPanel. <DockPanel> <Label DockPanel.Dock="Bottom" Content="{Binding Path=Location}" FontSize="15" FontFamily="Arial" Foreground="Black" /> <ListView x:Name="List" ItemsSource="{Binding Path=Collection}" ItemTemplate="{DynamicResource ChipTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" BorderThickness="0" BorderBrush="{x:Null}" Foreground="Black" > <ListView.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Orientation="Horizontal" VerticalAlignment="Center" /> </ItemsPanelTemplate> </ListView.ItemsPanel> </ListView> </DockPanel>
How to apply different styles to ListBox header and item
I have a ListBox which is bound to my custom class ObservableCollection<FieldPropertyItem> _fieldOrderCollection`; internal struct FieldPropertyItem { public string Name { get; set; } public string AliasName { get; set; } } Code for ListBox: <ListBox x:Name="FieldOrderListBox" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" SelectedIndex="0" ScrollViewer.VerticalScrollBarVisibility="Auto" SelectionChanged="FieldOrderListBox_SelectionChanged"> <ListBox.Resources> <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="#FF479EF3"></SolidColorBrush> </ListBox.Resources> <ListBox.ItemTemplate> <DataTemplate x:Name="MyTemplate"> <Grid Margin="-5,-1,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" SharedSizeGroup="FieldName" MinWidth="5" MaxWidth="300"/> <ColumnDefinition Width="2" /> <ColumnDefinition Width="*" MaxWidth="350"/> </Grid.ColumnDefinitions> <Border BorderBrush="Black" Grid.Column="0" BorderThickness="0.5,0.0,0.5,0.5" IsHitTestVisible="False"> <TextBlock Text="{Binding Name}" Grid.Column="0" ToolTip="{Binding Name}" Margin="2,0,2,0" VerticalAlignment="Center"/> </Border> <GridSplitter Grid.Column="1" Width="2" HorizontalAlignment="Left" Background="Black" Margin="-2,0,-1,0"/> <Border BorderBrush="Black" BorderThickness="0,0.0,0.5,0.5" Margin="-2,0,0,0" Padding="0,5,0,0" Grid.Column="2"> <TextBlock Text="{Binding AliasName}" Grid.Column="1" ToolTip="{Binding AliasName}" Margin="2,0,2,0" VerticalAlignment="Center" HorizontalAlignment="Stretch"/> </Border> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> Now i want that my ListBox first item should look like header. The remaining ones should have a light dim background.
You can check if the previous item is null using a RelativeSource binding in a trigger, e.g. this DataTemplate makes the first element bold: <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition SharedSizeGroup="A" /> <ColumnDefinition SharedSizeGroup="B" /> </Grid.ColumnDefinitions> <Grid.Style> <Style TargetType="{x:Type Grid}"> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}"> <Setter Property="TextElement.FontWeight" Value="Bold" /> </DataTrigger> </Style.Triggers> </Style> </Grid.Style> <TextBlock Grid.Column="0" Text="{Binding Name}" /> <TextBlock Grid.Column="1" Text="{Binding AliasName}" /> </Grid> </DataTemplate> (Use shared size groups as shown above to align the grids with one-another, set Grid.IsSharedSizeGroup to true on the ListBox element) I would not recommend doing this by the way, if your item collection contains the header there is definitely something wrong with your data-design.
1.) You can have a new Property IsFirstItem on your FieldPropertyItem Whenever your _fieldOrderCollection changes determine the IsFirtItem Property of the elements in your ObservableCollection. 2.) Define the corresponding Templates in XAML (Normal and FirstItem Template) 3.) You can then bind in XAML like this: <ListBox ItemsSource="{Binding Items}" > <ListBox.ItemTemplate> <DataTemplate> <ContentControl x:Name="contentControl" Content="{Binding}" ContentTemplate="{StaticResource normalTemplate}"/> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=IsFirstItem}" Value="True" > <Setter TargetName="contentControl" Property="ContentTemplate" Value="{StaticResource firstItemTemplate}"></Setter> </DataTrigger> </DataTemplate.Triggers> </ListBox.ItemTemplate> </ListBox>