Note: this answer is based on WPF, there might be some changes necessary for UWP.
There are basically two cases to consider:
- You have a data driven aspect that needs to be bound to the item container
- You have a view-only property
Lets assume a customized listview for both cases:
public class MyListView : ListView
{
protected override DependencyObject GetContainerForItemOverride()
{
return new DesignerItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is DesignerItem;
}
}
public class DesignerItem : ListViewItem
{
public bool IsEditing
{
get { return (bool)GetValue(IsEditingProperty); }
set { SetValue(IsEditingProperty, value); }
}
public static readonly DependencyProperty IsEditingProperty =
DependencyProperty.Register("IsEditing", typeof(bool), typeof(DesignerItem));
}
In case 1, you can use the ItemContainerStyle
to link your viewmodel property with a binding and then bind the same property inside the datatemplate
class MyData
{
public bool IsEditing { get; set; } // also need to implement INotifyPropertyChanged here!
}
XAML:
<local:MyListView ItemsSource="{Binding Source={StaticResource items}}">
<local:MyListView.ItemContainerStyle>
<Style TargetType="{x:Type local:DesignerItem}">
<Setter Property="IsEditing" Value="{Binding IsEditing,Mode=TwoWay}"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</local:MyListView.ItemContainerStyle>
<local:MyListView.ItemTemplate>
<DataTemplate>
<Border Background="Red" Margin="5" Padding="5">
<CheckBox IsChecked="{Binding IsEditing}"/>
</Border>
</DataTemplate>
</local:MyListView.ItemTemplate>
</local:MyListView>
In case 2, it appears that you don't really have a data driven property and consequently, the effects of your property should be reflected within the control (ControlTemplate
).
In the following example a toolbar is made visible based on the IsEditing
property. A togglebutton can be used to control the property, the ItemTemplate
is used as an inner element next to the toolbar and button, it knows nothing of the IsEditing
state:
<local:MyListView ItemsSource="{Binding Source={StaticResource items}}">
<local:MyListView.ItemContainerStyle>
<Style TargetType="{x:Type local:DesignerItem}">
<Setter Property="IsEditing" Value="{Binding IsEditing,Mode=TwoWay}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DesignerItem}">
<DockPanel>
<ToggleButton DockPanel.Dock="Right" Margin="5" VerticalAlignment="Top" IsChecked="{Binding IsEditing,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" Content="Edit"/>
<!--Toolbar is something control related, rather than data related-->
<ToolBar x:Name="MyToolBar" DockPanel.Dock="Top" Visibility="Collapsed">
<Button Content="Tool"/>
</ToolBar>
<ContentPresenter ContentSource="Content"/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsEditing" Value="True">
<Setter TargetName="MyToolBar" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:MyListView.ItemContainerStyle>
<local:MyListView.ItemTemplate>
<DataTemplate>
<Border Background="Red" Margin="5" Padding="5">
<TextBlock Text="Hello World"/>
</Border>
</DataTemplate>
</local:MyListView.ItemTemplate>
</local:MyListView>
For a better control template, you may chose to use Blend and create the control template starting at the full ListViewItem
template and just editing your changes into it.
If your DesignerItem generally has a specific enhanced appearance, consider designing it in the Themes/Generic.xaml
with the appropriate default style.
As commented, you could provide a separate data template for the editing mode. To do this, add a property to MyListView
and to DesignerItem
and use MyListView.PrepareContainerForItemOverride(...)
to transfer the template.
In order to apply the template without the need for Setter.Value
bindings, you can use value coercion on DesignerItem.ContentTemplate
based on IsEditing
.
public class MyListView : ListView
{
protected override DependencyObject GetContainerForItemOverride()
{
return new DesignerItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is DesignerItem;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var elem = element as DesignerItem;
elem.ContentEditTemplate = ItemEditTemplate;
}
public DataTemplate ItemEditTemplate
{
get { return (DataTemplate)GetValue(ItemEditTemplateProperty); }
set { SetValue(ItemEditTemplateProperty, value); }
}
public static readonly DependencyProperty ItemEditTemplateProperty =
DependencyProperty.Register("ItemEditTemplate", typeof(DataTemplate), typeof(MyListView));
}
public class DesignerItem : ListViewItem
{
static DesignerItem()
{
ContentTemplateProperty.OverrideMetadata(typeof(DesignerItem), new FrameworkPropertyMetadata(
null, new CoerceValueCallback(CoerceContentTemplate)));
}
private static object CoerceContentTemplate(DependencyObject d, object baseValue)
{
var self = d as DesignerItem;
if (self != null && self.IsEditing)
{
return self.ContentEditTemplate;
}
return baseValue;
}
private static void OnIsEditingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.CoerceValue(ContentTemplateProperty);
}
public bool IsEditing
{
get { return (bool)GetValue(IsEditingProperty); }
set { SetValue(IsEditingProperty, value); }
}
public static readonly DependencyProperty IsEditingProperty =
DependencyProperty.Register("IsEditing", typeof(bool), typeof(DesignerItem), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEditingChanged)));
public DataTemplate ContentEditTemplate
{
get { return (DataTemplate)GetValue(ContentEditTemplateProperty); }
set { SetValue(ContentEditTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for ContentEditTemplate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContentEditTemplateProperty =
DependencyProperty.Register("ContentEditTemplate", typeof(DataTemplate), typeof(DesignerItem));
}
Note, for an easier example I will activate the "edit" mode by ListViewItem.IsSelected
with some trigger:
<local:MyListView ItemsSource="{Binding Source={StaticResource items}}">
<local:MyListView.ItemContainerStyle>
<Style TargetType="{x:Type local:DesignerItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="IsEditing" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</local:MyListView.ItemContainerStyle>
<local:MyListView.ItemTemplate>
<DataTemplate>
<Border Background="Red" Margin="5" Padding="5">
<TextBlock Text="Hello World"/>
</Border>
</DataTemplate>
</local:MyListView.ItemTemplate>
<local:MyListView.ItemEditTemplate>
<DataTemplate>
<Border Background="Green" Margin="5" Padding="5">
<TextBlock Text="Hello World"/>
</Border>
</DataTemplate>
</local:MyListView.ItemEditTemplate>
</local:MyListView>
Intended behavior: the selected item becomes edit-enabled, getting the local:MyListView.ItemEditTemplate
(green) instead of the default template (red)