WinUI3中的TabVew控件的MVVM操作

本文介绍使用mvvm模式操作TabView的标签添加和关闭,视图有两种样式,如下:

选项卡1的视图

选项卡2的视图

以下1.和2.为标准代码

 1.定义对应于TabViewItem的视图模式

    public class TabItemViewModel : INotifyPropertyChanged
    {
        #region 构造函数
        public TabItemViewModel()
        {

        }
        public TabItemViewModel(string headerName)
        {
            _Header = headerName;
        }
        public TabItemViewModel(string headerName,object data)
        {
            _Header = headerName;
            _Data = data;
        }
        public TabItemViewModel(string headerName, object data, string tooltip)
        {
            _Header = headerName;
            _Data = data;
            _ToolTip = tooltip;
        }
        
        #endregion
        #region 公共属性
        [XmlIgnore]
        public TabMainViewModel Parent { get; set; }
        bool _DataChanged;
        /// <summary>
        /// 数据已改变
        /// </summary>
        [XmlAttribute]
        public bool DataChanged
        {
            get
            {
                return _DataChanged;
            }
            set
            {
                if (_DataChanged != value)
                {
                    _DataChanged = value;
                    this.OnPropertyChanged();
                }
            }
        }
        string _Header;
        /// <summary>
        /// Tab标头内容
        /// </summary>
        [XmlAttribute]
        public string Header
        {
            get
            {
                return _Header;
            }
            set
            {
                if (_Header != value)
                {
                    _Header = value;
                    this.OnPropertyChanged();
                }
            }
        }
        string _ToolTip;
        /// <summary>
        /// Tab标头提示文本
        /// </summary>
        [XmlAttribute]
        public string ToolTip
        {
            get
            {
                return _ToolTip;
            }
            set
            {
                if (_ToolTip != value)
                {
                    if (value != null && value.Trim() == "")
                        _ToolTip = null;
                    else
                        _ToolTip = value;
                    this.OnPropertyChanged();
                }
            }
        }
        bool _IsSelected = false;
        /// <summary>
        /// 设置Tab项是否被选择
        /// </summary>
        [XmlAttribute]
        public bool IsSelected
        {
            get
            {
                return _IsSelected;
            }
            set
            {
                if (_IsSelected != value)
                {
                    _IsSelected = value;
                    this.OnPropertyChanged();                 
                }
            }
        }
        object _Icon;
        /// <summary>
        /// Tab项图标
        /// </summary>
        [XmlAttribute]
        public object Icon
        {
            get
            {
                return _Icon;
            }
            set
            {
                if (_Icon != value)
                {
                    _Icon = value;
                    this.OnPropertyChanged();
                }
            }
        }
        object _Data;
        /// <summary>
        /// 用于绑定的数据内容
        /// </summary>
        [XmlAttribute]
        public object Data
        {
            get
            {
                return _Data;
            }
            set
            {
                if (_Data != value)
                {
                    _Data = value;
                    this.OnPropertyChanged();
                }
            }
        }

        #endregion
        
        #region 属性通知
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        #endregion
    }//

    2.定义对应于TabView的视图模式

    public class TabMainViewModel : INotifyPropertyChanged
    {
        #region 构造函数
        public TabMainViewModel()
        {

        }
        public TabMainViewModel(Func<TabItemViewModel, bool> preClose)
        {
            PreClose=preClose;
        }
        #endregion
        #region 属性定义
        ObservableCollection<TabItemViewModel> _Items;
        /// <summary>
        /// 项集合属性
        /// </summary>
        public ObservableCollection<TabItemViewModel> Items
        {
            get
            {
                if (_Items == null)
                {
                    _Items = new ObservableCollection<TabItemViewModel>();
                }
                return _Items;
            }
        }

        #endregion
        #region 关闭方法,仅用于winui3的tabview  
        public Func<TabItemViewModel, bool> PreClose { get; set; }
        public void TabCloseRequested(TabView sender, TabViewTabCloseRequestedEventArgs args)
        {
            var tabItem = args.Tab.DataContext as TabItemViewModel;
            if (PreClose!=null && PreClose(tabItem)) //确定关闭
            {
                Items.Remove(tabItem);
            }
            //sender.TabItems.Remove(args.Tab);
        }
        #endregion
        #region 属性通知
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        #endregion
    }//

3.定义内容视图的数据模型(用于绑定TabViewItem中的Content)

internal class TabItem1ViewModel
    {
        public TabItem1ViewModel()
        {
            Name = "Item1";
            Description = "Desc1";
            Items = new List<string>()
            {
                "str1","str2","str3","str4","str5","str6","str7","str8","str9"
            };
            
        }
        public string Name { get; set; }
        public string Description { get; set; }
        public List<string> Items { get; set; }
  }
    internal class TabItem2ViewModel
    {
        public TabItem2ViewModel()
        {
            Name = "Item2";
            Description = "Desc2";
            
        }
        public string Name { get; set; }
        public string Description { get; set; }
}

4.定义内容视图(对应TabViewItem中的ContentTemplate

<UserControl
    x:Class="AppTest.View.TabItem1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AppTest.View"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBox Text="{Binding Name}"/>
        <TextBox Text="{Binding Description}" Grid.Row="1"/>
        <ListBox ItemsSource="{Binding Items}" Grid.Row="2"/>
    </Grid>
</UserControl>

<UserControl
    x:Class="AppTest.View.TabItem2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:AppTest.View"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="40"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <TextBox Text="{Binding Name}"/>
        <TextBox Text="{Binding Description}" Grid.Row="1"/>
    </Grid>
</UserControl>

5.定义两个视图的数据模板 

    在数据模板的定义中,不同的是TabViewItem.ContentTemplate,内部分别嵌套上面定义的两个UserControl,通过Content="{Binding Data}",将上面定义的TabItem1ViewModel/ TabItem2ViewModel传递到UserControl

<DataTemplate x:Key="TabItem1ViewModel">
                <TabViewItem Header="{Binding Header}"
                             Content="{Binding Data}"
                             IsSelected="{Binding IsSelected,Mode=TwoWay}"
                             ToolTipService.ToolTip="{Binding ToolTip}">
                    <TabViewItem.IconSource>
                        <FontIconSource Glyph="{Binding Icon}" />
                    </TabViewItem.IconSource>
                    <TabViewItem.ContentTemplate>
                        <DataTemplate>
                            <view:TabItem1/>
                        </DataTemplate>
                    </TabViewItem.ContentTemplate>
                </TabViewItem>
            </DataTemplate>
            <DataTemplate x:Key="TabItem2ViewModel">
                <TabViewItem Header="{Binding Header}" 
                             Content="{Binding Data}"
                             IsSelected="{Binding IsSelected,Mode=TwoWay}"
                             ToolTipService.ToolTip="{Binding ToolTip}">
                    <TabViewItem.IconSource>
                        <FontIconSource Glyph="{Binding Icon}" />
                    </TabViewItem.IconSource>
                    <TabViewItem.ContentTemplate>
                        <DataTemplate>
                            <view:TabItem2/>
                        </DataTemplate>
                    </TabViewItem.ContentTemplate>
                </TabViewItem>
           </DataTemplate>

上面xaml代码有很多的重复,譬如Header/ Content/ IsSelected的绑定都是相同的,如果使用的是WPF,至少有两个方式解决问题,第一种根本就不需要使用上面的这种方式定义数据模板,只需要定义不带x:Key的数据模板,类似如下,即可实现视图模式与视图的关联:

 <DataTemplate DataType="{ x: Type WpfCode:XAMLCodeViewModel}">
        <WpfCode:XAMLCodeView />
</DataTemplate>

可惜WinUI不容许,第二种就是定义TabViewItem的样式,不用修改模板,仅是属性的绑定,然后在上面指定样式资源即可,但在这里WinUI不行,即使定义了不修改模板的样式也不行。

6.定义模板选择器

<view:TabTemplateSelector x:Key="TabItemTemplateSelector"
               TabItem1Template="{StaticResource TabItem1ViewModel}"
               TabItem2Template="{StaticResource TabItem2ViewModel}" />
public class TabTemplateSelector : DataTemplateSelector
    {
        public DataTemplate TabItem1Template { get; set; }
        public DataTemplate TabItem2Template { get; set; }
       
        protected override DataTemplate SelectTemplateCore(object item)
        {
            var tabitem = (TabItemViewModel)item;
            return tabitem.Data is TabItem1ViewModel ? TabItem1Template : TabItem2Template;
        }
   }

7.TabView的xaml代码

<TabView TabItemsSource="{x:Bind TabViewData.Items, Mode=OneWay}"
                 TabItemTemplateSelector="{StaticResource TabItemTemplateSelector}" 
                 TabCloseRequested="{x:Bind TabViewData.TabCloseRequested}"
                 >
        </TabView>

   

8.后台测试代码

       TabMainViewModel TabViewData = new TabMainViewModel();
        void InitTabData()
        {
            TabItemViewModel item1 = new TabItemViewModel("项1", new TabItem1ViewModel());
            TabItemViewModel item2 = new TabItemViewModel("项2", new TabItem2ViewModel());
            item1.Icon= ((char)0xE75A).ToString();
            item2.Icon = ((char)0xE102).ToString();
            TabViewData.Items.Add(item1);
            TabViewData.Items.Add(item2);
            TabViewData.PreClose = PreClose;
        }
        bool PreClose(TabItemViewModel tabitem)
        {
            //处理是否保存/是否关闭之类的功能
            return true;
        }

       第一次使用WinUI,个人感觉WinUI在xaml语法上是WPF的阉割版,除了添加了一个x:Bind类型的绑定,但这也是在标准Binding的一个扩展。

        WinUI3确实做得很漂亮,效率也很高。如果个人想自定义样式,可以下载winui3组件的源码,然后找到相应的文件夹里面的组件xaml打开,不用打开整个项目,因为其后台代码使用的是C++,这样基本上可以看到完整的xaml源码,对于自定义样式或者数据绑定有很大的帮助。

      WinUI3自定义了很多文字图标,上面的TabItem中就用到了,查阅图标对应的字符串可以在微软网站上找到:

Segoe MDL2 Assets icons - Windows apps | Microsoft Docs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值