.net 延迟加载WPF选项卡内容

.net 延迟加载WPF选项卡内容,.net,wpf,.net,Wpf,我的WPF应用程序被组织为一个TabControl,每个选项卡包含一个不同的屏幕 一个TabItem绑定到需要一段时间才能加载的数据。因为这个TabItem代表一个用户可能很少使用的屏幕,所以我希望在用户选择该选项卡之前不加载数据 如何执行此操作?您可以查看SelectionChanged事件: 更改所选选项卡时将调用的;根据您的选项卡是否通过绑定到集合创建(如果“不是”,效果最好),可以简单地创建包含页面所需所有控件的用户控件的实例,然后将其添加到一些面板(例如网格)作为该选项卡上的占位符存

我的WPF应用程序被组织为一个TabControl,每个选项卡包含一个不同的屏幕

一个TabItem绑定到需要一段时间才能加载的数据。因为这个TabItem代表一个用户可能很少使用的屏幕,所以我希望在用户选择该选项卡之前不加载数据


如何执行此操作?

您可以查看
SelectionChanged
事件:

更改所选选项卡时将调用的;根据您的选项卡是否通过绑定到集合创建(如果“不是”,效果最好),可以简单地创建包含页面所需所有控件的
用户控件的实例,然后将其添加到一些
面板(例如
网格
)作为该选项卡上的占位符存在


希望有帮助

选项卡控件有两种工作方式

  • 当我们显式添加选项卡项时,每个选项卡项都会立即加载并初始化,其中包含所有内容
  • 当我们将ItemsSource绑定到项目列表,并为每个数据项设置不同的数据模板时,tab控件将仅创建所选数据项的一个“内容”视图,并且仅当选择tab项时,才会触发内容视图的“加载”事件并加载内容。当选择不同的选项卡项时,将为以前选择的内容视图触发“卸载”事件,并为新选择的数据项触发“加载”事件
  • 使用第二种方法有点复杂,但在运行时它肯定会减少所使用的资源,但在切换选项卡时,它可能会慢一点

    您必须创建自定义数据类,如下所示

    class TabItemData{
       public string Header {get;set;}
       public string ResourceKey {get;set;}
       public object MyBusinessObject {get;set;}
    }
    
    您必须创建TabItemData的列表或数组,并且必须将TabControl的items源设置为TabItemData的列表/数组

    然后创建TabControl的ItemTemplate作为数据模板绑定“Header”属性

    然后创建TabControl的ContentTemplate作为包含ContentControl的数据模板,并在ResourceKey属性中找到资源密钥的ContentTemplate。

    可能太晚了:),但是那些寻找答案的人可以尝试以下方法:

    <TabItem>
        <TabItem.Style>
            <Style TargetType="TabItem">
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Content">
                            <Setter.Value>
                                <!-- Your tab item content -->
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="IsSelected" Value="False">
                        <Setter Property="Content" Value="{Binding Content, RelativeSource={RelativeSource Self}}"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </TabItem.Style>  
    </TabItem>
    
    选项卡项样式:

    <Style TargetType="TabItem">
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="Content" Value="{Binding Path=(namespace:Deferred.Content), RelativeSource={RelativeSource Self}}"/>
            </Trigger>
            <Trigger Property="IsSelected" Value="False">
                <Setter Property="Content" Value="{Binding Content, RelativeSource={RelativeSource Self}}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    
    
    
    例如:

    <TabControl>
        <TabItem Header="TabItem1">
            <namespace:Deferred.Content>
                <TextBlock>
                    DeferredContent1
                </TextBlock>
            </namespace:Deferred.Content>
        </TabItem>
        <TabItem Header="TabItem2">
            <namespace:Deferred.Content>
                <TextBlock>
                    DeferredContent2
                </TextBlock>
            </namespace:Deferred.Content>
        </TabItem>
    </TabControl>
    
    
    推迟的内容1
    推迟的内容2
    
    几天前我也遇到过同样的问题,这是迄今为止我发现的最好的方法:

    在多任务界面中,内容用户控件绑定到其加载事件中的数据。这将为整个应用程序加载时间增加更多时间。然后,我通过Dispatcher将用户控件的绑定从加载的事件区分为较低优先级的操作:

    Dispatcher.BeginInvoke(new Action(() => { Bind(); }), DispatcherPriority.Background, null);
    
    正如前面提到的,最简单的方法是通过
    ContentTemplate
    DataTemplate
    添加一个检测级别,从而推迟值的绑定:-

    <TabControl>
        <TabItem Header="A" Content="{Binding A}">
            <TabItem.ContentTemplate>
                <DataTemplate>
                    <local:AView DataContext="{Binding Value}" />
                </DataTemplate>
            </TabItem.ContentTemplate>
        </TabItem>
        <TabItem Header="B" Content="{Binding B}">
            <TabItem.ContentTemplate>
                <DataTemplate>
                    <local:BView DataContext="{Binding Value}" />
                </DataTemplate>
            </TabItem.ContentTemplate>
        </TabItem>
    </TabControl>
    
    使用这样的辅助对象:

    public class LazyModelA : Lazy<ModelA>
    {
        public LazyModelA(Func<ModelA> factory) : base(factory)
        {
        }
    }
    
    public class LazyModelB : Lazy<ModelB>
    {
        public LazyModelB(Func<ModelB> factory) : base(factory)
        {
        }
    }
    
    通过引入松散类型的ViewModel,可以使该方法更加通用:

    public class LazyModel
    {
        public static LazyModel Create<T>(Lazy<T> inner)
        {
            return new LazyModel { _get = () => inner.Value };
        }
    
        Func<object> _get;
    
        LazyModel(Func<object> get)
        {
            _get = get;
        }
    
        public object Value { get { return _get(); } }
    }
    
    并允许Xaml:

    <UserControl.Resources>
        <DataTemplate DataType="{x:Type local:ModelA}">
            <local:ViewA />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:ModelB}">
            <local:ViewB />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:LazyModel}">
            <ContentPresenter Content="{Binding Value}" />
        </DataTemplate>
    </UserControl.Resources>
    <TabControl>
        <TabItem Header="A" Content="{Binding AXaml}" />
        <TabItem Header="B" Content="{Binding BXaml}" />
    </TabControl>
    

    我找到了一种更简单的方法。只需等待初始化ViewModel,直到激活该选项卡

    public int ActiveTab
    {
        get
        {
            return _ActiveTab;
        }
        set
        {
            _ActiveTab = value;
            if (_ActiveTab == 3 && InventoryVM == null) InventoryVM = new InventoryVM();
        }
    }
    

    一个快速而简单的以
    数据为中心的解决方案是在选择选项卡
    时按样式设置
    DataContext

    <Style TargetType="{x:Type TabItem}">
        <Setter Property="DataContext" Value="{x:Null}"/> <!--unset previous dc-->
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="DataContext" Value="{Binding LazyProperty}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    

    +1如果您正在执行MVVM,您自然会使用第二个选项。使用第二个选项会导致选项卡在卸载时失去状态吗?是的,但您可以使用MyBusinessObject中的属性来定义可以与可视状态和任何其他逻辑控制状态同步的状态。听起来不错,但是如何将ContentControl绑定到viewmodel中的资源键?我所有的尝试都失败了。如果有概念证明,这会更有用。它太难阅读了。这是一个很好的解决方案,因为在不使用延迟内容时,它还保持了正常的tabitem行为。这和预期的完全一样。+1但由于数据绑定可能会中断,我对它束手无策。
    public class LazyModel
    {
        public static LazyModel Create<T>(Lazy<T> inner)
        {
            return new LazyModel { _get = () => inner.Value };
        }
    
        Func<object> _get;
    
        LazyModel(Func<object> get)
        {
            _get = get;
        }
    
        public object Value { get { return _get(); } }
    }
    
    public class PageModel
    {
        public PageModel()
        {
            A = new Lazy<ModelA>(() => new ModelA());
            B = new Lazy<ModelB>(() => new ModelB());
        }
    
        public Lazy<ModelA> A { get; private set; }
        public Lazy<ModelB> B { get; private set; }
    
        // Ideal for sticking in a #region :)
        public LazyModel AXaml { get { return LazyModel.Create(A); } }
        public LazyModel BXaml { get { return LazyModel.Create(B); } }
    
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type local:ModelA}">
            <local:ViewA />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:ModelB}">
            <local:ViewB />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:LazyModel}">
            <ContentPresenter Content="{Binding Value}" />
        </DataTemplate>
    </UserControl.Resources>
    <TabControl>
        <TabItem Header="A" Content="{Binding AXaml}" />
        <TabItem Header="B" Content="{Binding BXaml}" />
    </TabControl>
    
    public int ActiveTab
    {
        get
        {
            return _ActiveTab;
        }
        set
        {
            _ActiveTab = value;
            if (_ActiveTab == 3 && InventoryVM == null) InventoryVM = new InventoryVM();
        }
    }
    
    <Style TargetType="{x:Type TabItem}">
        <Setter Property="DataContext" Value="{x:Null}"/> <!--unset previous dc-->
        <Style.Triggers>
            <Trigger Property="IsSelected" Value="True">
                <Setter Property="DataContext" Value="{Binding LazyProperty}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    
    private MyVM _lazyProperty;
    public MyVM LazyProperty => _lazyProperty ?? (_lazyProperty = new MyVM());