首页 文章

WPF TabControl模板

提问于
浏览
1

我是一个新的WPF用户,我试图围绕模板 . 特别是我试图模板TabControl .

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

Project Description:

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

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

Problem

这一切似乎渲染/绑定都很好,但是如果你改变ListBox的状态(例如滚动或选择一个项目)然后更改选项卡,看起来状态会转移到新选项卡上的ListBox,你只是选择 . 这使我假设只有一个ListBox实例(而不是5个独立的实例),当您更改选项卡时,数据是唯一发生变化的事情 .

在另一种情况下,我没有将集合绑定到模板,而是使用xaml中自己的ListBox显式定义每个TabItem对象 . 这次,当我从tab切换到tab时,每个ListBox都维护它自己的状态 .

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

在此先感谢您的帮助!

我的示例的源代码如下所示 .

View Classes

1. TabBinding

<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 Classes

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
    }
}

1 回答

  • 1

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

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

相关问题