WPF TabControl 模板 [英] WPF TabControl Templating

查看:45
本文介绍了WPF TabControl 模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一个新的 WPF 用户,我正在尝试围绕模板进行处理.特别是我正在尝试模板化 TabControl.

I am a new WPF user and I am trying to wrap my head around templating. In particular I am trying to template a TabControl.

我对 TabControl 绑定到数据时实际生成的控件感到困惑.我需要的是每个选项卡在其内容部分都有一个单独的控件副本.我发现似乎创建了一组控件,当用户从一个选项卡单击另一个选项卡时,只是绑定的数据发生了变化.

I am confused by the controls that actually get generated when the TabControl is bound to data. What I need is for each tab to have a separate copy of the controls in it's content section. What I have found is that it appears that a single set of controls are created, and when the user clicks from tab to tab, just the bound data changes.

项目描述:

示例项目由一个带有 TabControl 的视图组成.在 TabControl 的内容模板中是一个 ListBox.该视图绑定到 TabBindingViewModel 类的对象.这个类有一个名为 Tabs 的属性,它是 TabViewModel 对象的 ObservableCollection.TabViewModel 类有两个属性:TabHeader 和 Guids.TabViewModel.TabHeader 是包含将出现在选项卡上的文本的字符串,而 TabViewModel.Guids 是字符串的 ObservableCollection.

The example project consists of a single view with a TabControl. In the content template of the TabControl is a ListBox. The view gets bound to an object of the TabBindingViewModel class. This class has one property called Tabs which is an ObservableCollection of TabViewModel objects. The TabViewModel class has two properties: TabHeader and Guids. TabViewModel.TabHeader is a string that contains the text that will appear on the tab, and TabViewModel.Guids is an ObservableCollection of strings.

绑定后,TabBindingViewModel.Tabs 集合中的每个 TabViewModel 都应在 TabControl 对象中生成一个选项卡,该选项卡的标题是 TabViewModel.TabHeader 属性.每个选项卡的内容应该是用 TabViewModel.Guids 集合中的字符串集合填充的 ListBox.

When bound, each TabViewModel in the TabBindingViewModel.Tabs collection should generate a tab in the TabControl object with the header of the tab being the TabViewModel.TabHeader property. The content of each tab should be the ListBox populated with the collection of strings in the TabViewModel.Guids collection.

问题

这一切似乎都可以很好地呈现/绑定,但是如果您更改 ListBox 的状态(例如滚动或选择一个项目)然后更改选项卡,则状态似乎会转移到新选项卡上的 ListBox你刚刚选择的.这让我假设只有一个 ListBox 实例(而不是 5 个单独的实例),并且当您更改选项卡时,数据是唯一更改的内容.

This all appears to render/bind just fine, however if you change the state of a ListBox (for example scrolling or selecting an item) and then change tabs, it appears that the state carries over to the ListBox on the new tab that you just selected. This makes me assume that there is only one instance of the ListBox (instead of 5 separate instances) and when you change tabs, the data is the only thing that changes.

在另一种情况下,我没有将集合绑定到模板,而是在 xaml 中使用它自己的 ListBox 显式定义每个 TabItem 对象.这一次,当我从一个选项卡单击到另一个选项卡时,每个 ListBox 都保持着自己的状态.

In another scenario, instead of binding a collection to a template, I explicitly define each TabItem object with it's own ListBox in the xaml. This time, when I click from tab to tab, each ListBox maintains it's own state.

我的假设是否正确?如果是这样,我如何在仍然利用模板的同时实现第二个场景中描述的结果?(这是一个简化的例子.我现实世界的内容要复杂得多.)

Am I correct in my assumptions? And if so, how do I achieve the result described in the second scenario while still taking advantage of templating? (This is a simplified example. My real world content is much more complex.)

在此先感谢您的帮助!

下面列出了我的示例的源代码.

The source code for my example is listed below.

查看课程

1.标签绑定

<Window x:Class="TabBindingQuestion.View.TabBinding"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:TabBindingQuestion.ViewModel"
        Title="TabTemplateView" 
        Height="344" Width="618">
    <Window.Resources>
        <vm:TabBindingViewModel x:Key="Data" />
    </Window.Resources>
    <Grid DataContext="{StaticResource Data}">
        <TabControl Width="Auto"
                    Height="Auto"
                    ItemsSource="{Binding Tabs}"
                    IsSynchronizedWithCurrentItem="True">
            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="Header" Value="{Binding TabHeader}" />
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <ListBox Width="Auto"
                                         Height="Auto"
                                         ItemsSource="{Binding Guids}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>
    </Grid>
</Window>

ViewModel 类

2.ViewModelBase

using System.ComponentModel;

namespace TabBindingQuestion.ViewModel
{
    class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(object sender, string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(sender, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

3.TabBindingViewModel

using System.Collections.ObjectModel;

namespace TabBindingQuestion.ViewModel
{
    class TabBindingViewModel : ViewModelBase
    {
        #region Members

        private ObservableCollection<TabViewModel> _Tabs;
        public ObservableCollection<TabViewModel> Tabs
        {
            get { return _Tabs; }
            set
            {
                _Tabs = value;
                OnPropertyChanged(this, "Tabs");
            }
        }

        #endregion

        #region Constructor

        public TabBindingViewModel()
        {
            var tabs = new ObservableCollection<TabViewModel>();
            for (int i = 1; i <= 5; i++)
            {
                tabs.Add(new TabViewModel() { TabHeader = "Tab " + i.ToString() });
            }
            Tabs = tabs;
        }

        #endregion
    }
}

4.TabViewModel

using System;
using System.Collections.ObjectModel;

namespace TabBindingQuestion.ViewModel
{
    class TabViewModel : ViewModelBase
    {
        #region Members

        private string _TabHeader;
        public string TabHeader
        {
            get { return _TabHeader; }
            set
            {
                _TabHeader = value;
                OnPropertyChanged(this, "TabHeader");
            }
        }

        private ObservableCollection<string> _Guids;
        public ObservableCollection<string> Guids
        {
            get { return _Guids; }
            set
            {
                _Guids = value;
                OnPropertyChanged(this, "Guids");
            }
        }

        #endregion

        #region Constructors

        public TabViewModel()
        {
            var guids = new ObservableCollection<string>();
            for (int i = 1; i < 100; i++)
            {
                guids.Add(Guid.NewGuid().ToString());
            }
            Guids = guids;
        }

        #endregion
    }
}

推荐答案

是的,如果切换选项卡时控件相同,则 TabControl 会重新使用控件.它还根据需要卸载/重新加载控件,这会导致重置未绑定的控件.例如,如果 Tab1 有一个 ListBox,其中 ItemA 被选中,而您选择了 ItemB 并将选项卡切换到一个没有列表框的选项卡,当您返回 Tab1 时,ItemA 再次被选中.

Yes the TabControl re-uses controls if they are the same when switching tabs. It also Unloads/Reloads controls as needed which causes unbound controls to be reset. For example, if Tab1 has a ListBox with the ItemA selected and you select ItemB and switch tabs to one without the listbox, when you go back to Tab1 ItemA is selected again.

最好的办法是将您的 UI 属性绑定到后面代码中的某些内容.例如,您的 TabControl 可能包含一个 TabItemViewModel 列表,每个 ViewModel 应包含表示您的 UI 状态的属性,例如 ListBox.SelectedItems 或 CheckBox.IsChecked.

Best thing to do is bind your UI properties to something in the code behind. For example, your TabControl might contain a list of TabItemViewModel and each ViewModel should contain properties representing the state of your UI, such as ListBox.SelectedItems or CheckBox.IsChecked.

这篇关于WPF TabControl 模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆