如何从DataTemplate创建实例

时间:2017-01-06 15:07:43

标签: c# wpf listview datatemplate

我实现了一个自定义控件,在控件模板中内部使用ListView。 FindParts可以访问此ListView。我需要将ListViews View属性设置为我必须在自定义控件中以编程方式实例化的实例。我希望能够从DataTemplate创建此实例,该DataTemplate可以绑定到我的自定义控件的属性。

问题是我不知道如何从DataTemplate创建实例。

请注意,我不想将GridView直接绑定到View属性(例如,从DataTemplates x:Shared属性设置为false的ResourceDictionary),因为这会导致XAML设计器出现问题(View can'由多个ListView共享。

编辑: Durig我与grek40的讨论很明显,无法在DataTemplate中提供GridView。因此,标记为解决方案的答案不会处理如何从我的问题标题暗示的DataTemplate创建实例的问题。

2 个答案:

答案 0 :(得分:2)

由于你没有分享很多代码,我凭空捏造了一些东西。想法:MyCustomControl正在托管项目,为此,ListView中使用了ControlTemplate。默认GridView直接包含在ControlTemplate中,但可以通过MyCustomControl.MyView

进行更改

自定义控件:

public class MyCustomControl : ItemsControl
{
    static MyCustomControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
    }


    public ViewBase MyView
    {
        get { return (ViewBase)GetValue(MyViewProperty); }
        set { SetValue(MyViewProperty, value); }
    }

    public static readonly DependencyProperty MyViewProperty =
        DependencyProperty.Register("MyView", typeof(ViewBase), typeof(MyCustomControl), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnMyViewChanged)));

    private static void OnMyViewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((MyCustomControl)d).HasCustomView = e.NewValue != null;
    }



    // readonly property to support the Trigger in the ControlTemplate in order to exchange the ListView.View
    public bool HasCustomView
    {
        get { return (bool)GetValue(HasCustomViewProperty); }
        private set { SetValue(HasCustomViewPropertyKey, value); }
    }

    private static readonly DependencyPropertyKey HasCustomViewPropertyKey =
        DependencyProperty.RegisterReadOnly("HasCustomView", typeof(bool), typeof(MyCustomControl), new PropertyMetadata(false));
    public static readonly DependencyProperty HasCustomViewProperty = HasCustomViewPropertyKey.DependencyProperty;
}

控制模板(注意默认的GridView和替换触发器):

<Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ListView x:Name="PART_ListView"
                              ItemsSource="{Binding Items,RelativeSource={RelativeSource TemplatedParent}}">
                        <ListView.View>
                            <GridView>
                                <GridView.Columns>
                                    <GridViewColumn Header="DEF"/>
                                </GridView.Columns>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasCustomView" Value="True">
                        <Setter TargetName="PART_ListView" Property="View" Value="{Binding MyView,RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

用法:

<local:MyCustomControl>
    <!--uses default view-->
    <TextBlock Text="Item 1"/>
    <TextBlock Text="Item 2"/>
</local:MyCustomControl>

<local:MyCustomControl>
    <!--uses custom view-->
    <local:MyCustomControl.MyView>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="ABC"/>
            </GridView.Columns>
        </GridView>
    </local:MyCustomControl.MyView>
    <TextBlock Text="Item 1"/>
    <TextBlock Text="Item 2"/>
</local:MyCustomControl>

随意询问是否仍有不清楚之处。如评论所述,我不知道如何将DataTemplate想法应用于所有这些内容。

修改

以下是资源部分的示例,用于定义一些数据和DataTemplate以及它们的可能用途。

<StackPanel>
    <StackPanel.Resources>
        <x:Array x:Key="Items1" Type="{x:Type sys:Int32}">
            <sys:Int32>1</sys:Int32>
            <sys:Int32>2</sys:Int32>
            <sys:Int32>3</sys:Int32>
            <sys:Int32>4</sys:Int32>
        </x:Array>

        <x:Array x:Key="Items2" Type="{x:Type sys:Int32}">
            <sys:Int32>5</sys:Int32>
            <sys:Int32>4</sys:Int32>
            <sys:Int32>6</sys:Int32>
            <sys:Int32>7</sys:Int32>
        </x:Array>

        <CollectionViewSource x:Key="ItemsSource1" Source="{StaticResource Items1}"/>
        <CollectionViewSource x:Key="ItemsSource2" Source="{StaticResource Items2}"/>

        <DataTemplate x:Key="IntegerListTemplate1">
            <local:MyCustomControl ItemsSource="{Binding}">
                <!--uses default view-->
            </local:MyCustomControl>
        </DataTemplate>

        <DataTemplate x:Key="IntegerListTemplate2">
            <local:MyCustomControl ItemsSource="{Binding}">
                <!--uses custom view-->
                <local:MyCustomControl.MyView>
                    <GridView>
                        <GridView.Columns>
                            <GridViewColumn Header="ABC"/>
                        </GridView.Columns>
                    </GridView>
                </local:MyCustomControl.MyView>
            </local:MyCustomControl>
        </DataTemplate>
    </StackPanel.Resources>
    <!--the default view-->
    <ContentControl ContentTemplate="{StaticResource IntegerListTemplate1}" Content="{Binding Source={StaticResource ItemsSource1}}"/>
    <Separator Margin="3"/>
    <!--same items other view-->
    <ContentControl ContentTemplate="{StaticResource IntegerListTemplate2}" Content="{Binding Source={StaticResource ItemsSource1}}"/>
    <Separator Margin="3"/>
    <!--different items same view as second one, no complaints about reusing-->
    <ContentControl ContentTemplate="{StaticResource IntegerListTemplate2}" Content="{Binding Source={StaticResource ItemsSource2}}"/>
</StackPanel>

编辑2,定位评论的MCVE:

我会像我在第一次编辑中解释的那样解决这个问题。将您的控件转换为DataTemplate,而不是视图。

在MainWindow.xaml中替换:

<!-- old -->
<myctrls:MyControl Grid.Row="0" Grid.Column="0" Style="{StaticResource AddressStyle}" ItemsSource="{Binding Addresses}"/>
<!-- new -->
<ContentControl Grid.Row="0" Grid.Column="0" ContentTemplate="{StaticResource AddressTemplate}" Content="{Binding Addresses}"/>

与其他事件相同。

在MyControlStyles.xaml(或指示DataTemplate用法的重命名文件)中,将GridView资源和Stylemyctrls:MyControl合并为DataTemplate }。

旧:

<GridView x:Key="AddressGridView" x:Shared="False">
    <GridViewColumn Header="City" Width="Auto" >
        <GridViewColumn.CellTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding City}" HorizontalAlignment="Left"/>
            </DataTemplate>
        </GridViewColumn.CellTemplate>
    </GridViewColumn>
    <GridViewColumn Header="Country" Width="Auto" >
        <GridViewColumn.CellTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Country}" HorizontalAlignment="Left"/>
            </DataTemplate>
        </GridViewColumn.CellTemplate>
    </GridViewColumn>
</GridView>

<Style x:Key="AddressStyle" TargetType="{x:Type myctrls:MyControl}">
    <Setter Property="SuggestionsView" Value="{DynamicResource AddressGridView}"/>
</Style>

新:

<DataTemplate x:Key="AddressTemplate">
    <!-- the base control -->
    <myctrls:MyControl ItemsSource="{Binding}">
        <!-- this was previously assigned by AddressStyle -->
        <myctrls:MyControl.SuggestionsView>
            <!-- this was previously the AddressGridView -->
            <!-- Same as before, only removed the x:Key and x:Shared -->
            <GridView>
                <GridViewColumn Header="City" Width="Auto" >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding City}" HorizontalAlignment="Left"/>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Country" Width="Auto" >
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Country}" HorizontalAlignment="Left"/>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </myctrls:MyControl.SuggestionsView>
    </myctrls:MyControl>
</DataTemplate>

ProductsStyle的相同程序。

如果您不喜欢ContentControl连接方法,请考虑创建其他托管集合的视图模型,例如AddressListViewModel。这样,您就可以DataTemplate处理DataType,而不是ContentControl中的资源键和显式用法。

答案 1 :(得分:0)

您可以实现自己的DataTemplateSelector,然后动态创建一次性使用的DataTemplate,大致如下:

public DataTemplate GenerateTemplate() {
  var template = new DataTemplate();
  var p = new FrameworkElementFactory(typeof(Grid));
  p.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Left);
  p.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
  ...
  template.VisualTree = p;
  return template;
}