如何设置从ItemTemplate绑定到ItemsControl中的托管容器?(UWP)

Sup*_*JMN 6 .net c# wpf xaml uwp

给定一个任意的ItemsControl,比如a ListView,我想从内部设置一个绑定ItemsTemplate到托管容器.我怎么能这么容易呢?例如,在WPF中我们可以在里面使用它ItemTemplate

<ListView.ItemTemplate>
    <DataTemplate>
        <SomeControl Property="{Binding Path=TargetProperty, RelativeSouce={RelativeSource FindAncestor, AncestorType={x:Type MyContainer}}}" />
    </DataTemplate>
<ListView.ItemTemplate>
Run Code Online (Sandbox Code Playgroud)

在此示例中(为WPF)的结合会之间设定PropertySomeControlTargetPropertyListViewItem(隐含的,因为它会被动态生成ListView来承载每个其项).

我们怎样才能在UWP中实现同样的目标?

我想要一些MVVM友好的东西.可能附加属性或交互行为.

gre*_*k40 1

注意:此答案基于 WPF,UWP 可能需要进行一些更改。

基本上有两种情况需要考虑:

  1. 您有一个数据驱动的方面需要绑定到项目容器
  2. 您有一个仅供查看的房产

让我们假设这两种情况都有一个自定义的列表视图:

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));
}
Run Code Online (Sandbox Code Playgroud)

在情况 1 中,您可以使用将ItemContainerStyleviewmodel 属性与绑定链接起来,然后在数据模板内绑定相同的属性

class MyData
{
    public bool IsEditing { get; set; } // also need to implement INotifyPropertyChanged here!
}
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

在情况 2 中,您似乎并没有真正拥有数据驱动的属性,因此,您的属性的效果应反映在控件 ( ControlTemplate) 内。

在以下示例中,工具栏根据属性变得可见IsEditing。切换按钮可用于控制属性,ItemTemplate用作工具栏和按钮旁边的内部元素,它对状态一无所知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 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>
Run Code Online (Sandbox Code Playgroud)

为了获得更好的控件模板,您可以选择使用 Blend 并从完整ListViewItem模板开始创建控件模板,然后将更改编辑到其中。

如果您的 DesignerItem 通常具有特定的增强外观,请考虑使用Themes/Generic.xaml适当的默认样式对其进行设计。


正如所评论的,您可以为编辑模式提供单独的数据模板。为此,请向MyListView和 中添加一个属性DesignerItem,并使用它MyListView.PrepareContainerForItemOverride(...)来传输模板。

为了应用模板而不需要绑定,您可以使用基于的Setter.Value值强制。DesignerItem.ContentTemplateIsEditing

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));
}
Run Code Online (Sandbox Code Playgroud)

ListViewItem.IsSelected请注意,为了更简单的示例,我将通过一些触发器激活“编辑”模式:

<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>
Run Code Online (Sandbox Code Playgroud)

预期行为:所选项目变为可编辑状态,获得local:MyListView.ItemEditTemplate(绿色)而不是默认模板(红色)