C#WPF-更改模板后ListView内未找到ScrollContentPresenter

时间:2018-09-05 19:43:31

标签: c# wpf listview templates

我有一个程序,其中listview模板必须根据用户的喜好进行更改。

很抱歉,如果有更多的代码段超出了必要范围,但我希望代码尽可能完整。

我试图将代码减少到最少,以便代码尽可能清晰易读。

XAML:

<Style TargetType="{x:Type ListView}">
        <Style.Triggers>

            <!-- Details View -->
            <DataTrigger Binding="{Binding View}" Value="0">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListView}" >
                            <Border Margin="5" Name="Border" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true" >
                                <ScrollViewer x:Name="scroll" Padding="{TemplateBinding Padding}" Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
                                    <VirtualizingStackPanel IsItemsHost="True" x:Name="ItemsHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                </ScrollViewer>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="View">
                    <Setter.Value>
                        <GridView>
                            <GridViewColumn Header="Name">
                                <GridViewColumn.CellTemplate>
                                    <DataTemplate>
                                        <StackPanel Orientation="Horizontal">
                                            <Image Height="20" Width="20" Stretch="Uniform" Source="{Binding Image}" />
                                            <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                                                <TextBlock Margin="2,0,6,0" Text="{Binding First}" />
                                                <TextBlock Text="{Binding Last}" />
                                            </StackPanel>
                                        </StackPanel>
                                    </DataTemplate>
                                </GridViewColumn.CellTemplate>
                            </GridViewColumn>
                        </GridView>
                    </Setter.Value>
                </Setter>
            </DataTrigger>

            <!-- Tiles View -->
            <DataTrigger Binding="{Binding View}" Value="1">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListView}">
                            <Border Name="Border" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                                <ScrollViewer x:Name="scroll" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Padding="{TemplateBinding Padding}">
                                    <WrapPanel IsItemsHost="True" x:Name="ItemsHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </ScrollViewer>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="ItemTemplate" >
                    <Setter.Value>
                        <DataTemplate DataType="{x:Type src:ItemModel}">
                            <Grid Width="100">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="80" />
                                    <RowDefinition Height="Auto"/>
                                </Grid.RowDefinitions>
                                <Image Grid.Row="0" Stretch="Uniform" Source="{Binding Image}" />
                                <TextBox HorizontalAlignment="Center" VerticalAlignment="Top" Grid.Row="1" Text="{Binding First}" TextWrapping="Wrap" Height="Auto" MaxLines="10"/>
                            </Grid>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>

ListViewModel:

public class ListViewModel : INotifyPropertyChanged
{
    private int _view = 0;
    public int View
    {
        get { return _view; }
        set
        {
            _view = value;
            RaisePropertyChanged("View");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

MainWindow:

private ListView listView;
private ListViewModel listViewModel;

public MainWindow()
{
    InitializeComponent();

    listView = mylistview; //defined in xaml
    listViewModel = new ListViewModel();
    this.DataContext = listViewModel;
}

public void ChangeView(int view)
{
    listViewModel.View = view;
    ScrollContentPresenter scrollContent = FindChild<ScrollContentPresenter>(this.listView);
    //Error : ScrollContentPresenter not found.
    //It needs a further search a few seconds later, to be found.
}

private static T FindChild<T>(DependencyObject reference) where T : class
{
     var queue = new Queue<DependencyObject>();
     queue.Enqueue(reference);
     while (queue.Count > 0)
     {
         DependencyObject child = queue.Dequeue();
         T obj = child as T;
         if (obj != null)
         {
             return obj;
         }

         for (int i = 0; i < VisualTreeHelper.GetChildrenCount(child); i++)
         {
             queue.Enqueue(VisualTreeHelper.GetChild(child, i));
         }
     }

     return null;
}

一切正常,当我需要更改列表视图模板时出现问题[例如:ChangeView(0); ]。

更改模板后,不再找到ScrollContentPresenter 。 找到它的唯一方法是等待几秒钟后重试。

有人可以向我解释原因,以及是否有解决此问题的方法吗?


我尝试使用自定义ListView控件在OnApplyTemplate()事件中调用ScrollContentPresenter搜索,但是问题仍然存在。

示例:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    ScrollContentPresenter scrollContent = FindChild<ScrollContentPresenter>(this.listView);
}

1 个答案:

答案 0 :(得分:1)

在调用FindChild方法之前,您需要等待直到新视图已实际加载或强制布局更新:

public void ChangeView(int view)
{
    listViewModel.View = view;
    listView.Measure(new Size(listView.ActualWidth, listView.ActualHeight));
    listView.Arrange(new Rect(0, 0, listView.ActualWidth, listView.ActualHeight));
    ScrollContentPresenter scrollContent = FindChild<ScrollContentPresenter>(this.listView);
}