Note: this answer is based on WPF, for UWP some changes may be required.
There are basically two cases:
- You have a data-driven attribute that needs to be bound to an element container.
- You have a view-only property.
Let's look at an individual list 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 ItemContainerStyle
to bind the viewmodel property to the binding, and then bind the same property inside the datatemplate
class MyData { public bool IsEditing { get; set; }
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 looks like you really do not have a data-driven property, and therefore the effects of your property should be reflected inside the ControlTemplate
.
In the following example, the toolbar becomes visible based on the IsEditing
property. To control this property, you can use the toggle switch, ItemTemplate
used as an internal element next to the toolbar and button, it does not know anything about the status of IsEditing
:
<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 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 can use Blend and create a control template, starting with the full ListViewItem
template and simply edit its changes.
If your DesignerItem usually has a special enhanced look, consider designing it in Themes/Generic.xaml
with the appropriate default style.
As noted, you can provide a separate data template for editing mode. To do this, add the MyListView
and DesignerItem
properties and use MyListView.PrepareContainerForItemOverride(...)
to transfer the template.
To apply a template without the Setter.Value
binding, you can use coercion of the value on the 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); } }
Note that for an easier example, I will activate edit mode using ListViewItem.IsSelected
using 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 enabled for editing, getting local:MyListView.ItemEditTemplate
(green) instead of the default template (red)