Reputation: 571
I am currently in the process of catching up on the theoretical basics of WPF and have come across a problem customizing grids.
My data source fills a grid with a number of properties that require different controls for editing. (say: combobox, date picker, tetbox etc.)
So far I have found 2 ways of accomplishing this:
listen to some event in the code behind and programmatically creating these controls in each grid cell.
Adding all edit controls I could need in each cell and binding their visibility to some selector variable that disables all the controls that aren't needed.
Both of these ways seem very untidy and hard to maintain. Is there a way to accomplish this in XAML only without having 6 or 7 active bindings to invisible and useless controls? What would be the proper way of doing this?
Upvotes: 0
Views: 148
Reputation: 5487
If the controls you wish to display at any given time have some sort of obvious logical grouping you could try creating a custom control for each group. This would make your xaml neater as your main page would only need to contain your custom controls (you might also be able to re-use them elsewhere).
Unfortunately I believe that you would still need to either place all controls on and then collapse them as required or add/remove them dynamically but you would only need to bind the visibility of your custom controls once rather than for each child element, you would also not have to see all the xaml they contain cluttering up your page.
Upvotes: 1
Reputation: 2120
You can use an ItemsControl
and use Templates to achieve this.
This is one of my recent controls. Basically I had several Options that were determined at runtime and each needed a visual representation on the UI. So I added an ItemsControl that - based on the bound property - selected the right ItemTemplate to display.
<ItemsControl ItemsSource="{Binding Path=DeliveryOrderOptions}" HorizontalContentAlignment="Stretch">
<ItemsControl.Resources>
<DataTemplate x:Key="DateTimeDataTemplate" DataType="{x:Type services:DeliveryOrderOption}">
<Grid Visibility="{Binding ConditionValid, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=Name}" />
<DatePicker Grid.Column="1"
SelectedDate="{Binding Path=Value, ConverterCulture=de-DE, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding Path=Error}" />
</Grid>
</DataTemplate>
<!-- more templates -->
</ItemsControl.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type services:DeliveryOrderOption}">
<GroupBox Header="{Binding Path=Title}" HorizontalContentAlignment="Center" Padding="6">
<ItemsControl ItemsSource="{Binding}" ItemTemplateSelector="{Binding Source={StaticResource OptionDataTemplateSelector}}" />
</GroupBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It utilizes an DataTemplateSelector like this to find the right DataTemplate to apply to the item based on its property Type and if it has a Dictionary of acceptable values.
public class OptionDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
DeliveryOrderOption option = item as DeliveryOrderOption;
DataTemplate template = null;
if (element != null && option != null)
{
switch (Nullable.GetUnderlyingType(option.Type)?.Name ?? option.Type.Name)
{
case "String":
if (option.HasDictionary)
{
template = element.FindResource("StringComboBoxDataTemplate") as DataTemplate;
}
else
{
template = element.FindResource("DefaultDataTemplate") as DataTemplate;
}
break;
case "Int32":
if (option.HasDictionary)
{
template = element.FindResource("IntComboBoxDataTemplate") as DataTemplate;
}
else
{
template = element.FindResource("IntDataTemplate") as DataTemplate;
}
break;
case "DateTime":
template = element.FindResource("DateTimeDataTemplate") as DataTemplate;
break;
//... more options
default:
template = null;
break;
}
}
return template;
}
}
Upvotes: 1