WinUI TabView 根据选项卡类型更改选项卡视图模型(和布局)。

huangapple go评论72阅读模式
英文:

Winui TabView change tab view model (and layout) depending on tab type

问题

我有一个选项卡视图和多个视图模型,选项卡视图可以显示每个选项卡的不同视图模型,例如 ViewModel1 和 ViewModel2。每个选项卡还会根据其模型类型拥有自己的布局。我面临的问题是无法让视图更改并加载正确的模型。在WPF中,这相对简单,我可以编写如下的数据模板,它将呈现正确的视图:

<TabControl.Resources>
    <DataTemplate DataType="{x:Type tab:ViewModel1}">
        <!-- ViewModel1 的视图 -->
    </DataTemplate>
    
    <DataTemplate DataType="{x:Type tab:ViewModel2}">
        <!-- ViewModel2 的视图 -->
    </DataTemplate>
</TabControl.Resources>

然而,在 WinUI 中,不再支持 x:Type,我很难找到一种动态更改视图和模型的方法。

目前,我有一个名为 ITab 的接口,该接口存储 TabName 和 TabType(Model1 或 Model2):

string Name { get; set; }
TabType TabType { get; }

任何想要成为选项卡集合一部分的视图模型都必须实现此接口。然后,在父视图模型中,我有一个 ITab 的可观察集合,它绑定到选项卡的 ItemsSource。

在 XAML 中,我有以下代码(其中一些是从旧的 WPF 项目复制过来的):

<TabView.Resources>
    <DataTemplate x:DataType="model:Model1" x:Key="model1">
        <!-- Model1 的视图 -->
    </DataTemplate>
    
    <DataTemplate x:DataType="model:Model2" x:Key="model2">
        <!-- Model2 的视图 -->
    </DataTemplate>
</TabView.Resources>

<TabView.TabItemTemplate>
    <DataTemplate x:DataType="itab:ITab">
        <TabViewItem Header="{Binding Name}">
            <TextBlock>文本</TextBlock>
        </TabViewItem>
    </DataTemplate>
</TabView.TabItemTemplate>

这会渲染选项卡并显示选项卡名称,但是这仅适用于一个视图模型,如何指定如果 TabType 是 Model2,则使用 Model2,否则使用 Model1 呢?

我尝试创建一个自定义的 TabItemTemplateSelector:

<TabView.TabItems>
    <DataTemplate x:DataType="itab:ITab">
        <TabViewItem Header="{Binding Name}">
            <templates:TabItemTemplateSelector Model1="{StaticResource model1}" Model2="{StaticResource model2}" />
        </TabViewItem>
    </DataTemplate>
</TabView.TabItems>
public class TabItemTemplateSelector : DataTemplateSelector
{
    private DataTemplate _model1;
    private DataTemplate _model2;

    public DataTemplate Model1
    {
        get { return _model1; }
        set { _model1 = value; }
    }

    public DataTemplate Model2
    {
        get { return _model2; }
        set { _model2 = value; }
    }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item != null)
        {
            var tab = (ITab)item;

            switch (tab.TabType)
            {
                case TabType.Model1:
                    return _model1;
                case TabType.Model2:
                    return _model2;
                default:
                    throw new InvalidOperationException("未知的选项卡类型");
            }
        }

        return base.SelectTemplateCore(item, container);
    }
}

然而,当渲染时,它显示类名,例如 MyApp.Models.TabItemSelector。

英文:

I have a tab view and multiple viewmodels which the tab view can display, each tab can be a type of say ViewModel1 and ViewModel2, they will also have their own layout depending on the type of model it is. The issue I am facing is that I cannot get the view to change and load the correct model, it WPF this was fairly straight forward I could write something like the below for data template and it would render the correct view:

&lt;TabControl.Resources&gt;&lt;DataTemplate DataType=&quot;{x:Type tab:ViewModel}&quot;&gt;

    &lt;/DataTemplate&gt;
    
        &lt;DataTemplate DataType=&quot;{x:Type tab:ViewModel2}&quot;&gt;
    
    &lt;/DataTemplate&gt;

&lt;/TabControl.Resources&gt;

However, with winui I x:Type is no longer available, and I am struggling to figure out a way of dynamically changing the view and model.

Current I have an interface called ITab, this interface stores the TabName and the TabType (Model1 or 2)

string Name { get; set; } 
TabType TabType { get; }

Any viewmodel which wants to be part of the tab collection, must implement this interface. In the parent viewmodel I then have an observable collection of ITab, this is bind to the Tabs ItemsSource.

In the XAML I have this code here, (some of this will copied from an old WPF project)

&lt;TabView.Resources&gt;
    &lt;DataTemplate x:DataType=&quot;model:Model1&quot; x:Key=&quot;model1&quot;&gt;
        &lt;Grid&gt; &lt;!--VIEW FOR MODEL 1--&gt; &lt;/Grid&gt;
    &lt;/DataTemplate&gt;
    &lt;DataTemplate x:DataType=&quot;model:Model1&quot; x:Key=&quot;model2&quot;&gt; &lt;!--VIEW FOR MODEL 2--&gt; &lt;/DataTemplate&gt;
&lt;/TabView.Resources&gt;

&lt;TabView.TabItemTemplate&gt;
    &lt;DataTemplate x:DataType=&quot;itab:ITab&quot;&gt;
        &lt;TabViewItem Header=&quot;{Binding Name}&quot;&gt;
            &lt;TextBlock&gt;Text&lt;/TextBlock&gt;
        &lt;/TabViewItem&gt;
    &lt;/DataTemplate&gt;
&lt;/TabView.TabItemTemplate&gt;

This renders the tab and display the tab name, however, this only happens for one viewmodel, how do I specify to use Model2 if the tabtype is Model2 else Model1?

I did try making a custom TabItemTemplateSelector

&lt;TabView.TabItems&gt;
    &lt;DataTemplate x:DataType=&quot;itab:ITab&quot;&gt;
        &lt;TabViewItem Header=&quot;{Binding Name}&quot;&gt;
            &lt;templates:TabItemTemplateSelector Model1=&quot;{StaticResource model1}&quot; Model2=&quot;{StaticResource model2}&quot; /&gt;
        &lt;/TabViewItem&gt;
    &lt;/DataTemplate&gt;
&lt;/TabView.TabItems&gt;
public class TabItemTemplateSelector : DataTemplateSelector { private DataTemplate _model1; private DataTemplate _model2;

    public DataTemplate Model1
    {
        get { return _model1; }
        set { _model1 = value; }
    }
    
    public DataTemplate Model2
    {
        get { return _model2; }
        set { _model2 = value; }
    }
    
    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        if (item != null)
        {
            var tab = (ITab)item;
    
            switch (tab.TabType)
            {
                case TabType.Model1:
                    return _model1;
                case TabType.Model2:
                    return _model2;
                default:
                    throw new InvalidOperationException(&quot;Uknown tab type&quot;);
            }
        }
    
        return base.SelectTemplateCore(item, container);
    }
    
    } 

However, when this is rendered it displays the class name e.g. MyApp.Models.TabItemSelector

答案1

得分: 1

你可以参考这个帖子&lt;templates:TabItemTemplateSelector Model1=&quot;{S...应该用作TabView的属性TabView.TabItemTemplateSelector,而不是在DataTemplate中使用。

所以你需要在TabView的上级容器中定义DataTemplateSelector资源,使用TabItemTemplateSelector来使用DataTemplateSelector。

&lt;Grid&gt;
    &lt;Grid.Resources&gt;
        &lt;DataTemplate x:DataType=&quot;model:Model1&quot; x:Key=&quot;model1&quot;&gt;
            &lt;Grid&gt;
                &lt;!--MODEL 1的视图--&gt;
            &lt;/Grid&gt;
        &lt;/DataTemplate&gt;
        &lt;DataTemplate x:DataType=&quot;model:Model1&quot; x:Key=&quot;model2&quot;&gt;
            &lt;!--MODEL 2的视图--&gt;
        &lt;/DataTemplate&gt;

        &lt;local:TabItemTemplateSelector x:Key=&quot;MyDataTemplateSelector&quot; Model1=&quot;{StaticResource model1}&quot; Model2=&quot;{StaticResource model2}&quot; /&gt;
    &lt;/Grid.Resources&gt;

    &lt;TabView TabItemsSource=&quot;{x:Bind TabList}&quot; TabItemTemplateSelector=&quot;{StaticResource MyDataTemplateSelector}&quot;&gt;
        
    &lt;/TabView&gt;
    
&lt;/Grid&gt;
英文:

You can refer to this thread. &lt;templates:TabItemTemplateSelector Model1=&quot;{S... should be used as TabView Property TabView.TabItemTemplateSelector, instead of in DataTemplate.

So you need to define DataTemplateSelector resources in the upper level container of TabView, use TabItemTemplateSelector to use the DataTemplateSelector.

&lt;Grid&gt;
    &lt;Grid.Resources&gt;
        &lt;DataTemplate x:DataType=&quot;model:Model1&quot; x:Key=&quot;model1&quot;&gt;
            &lt;Grid&gt;
                &lt;!--VIEW FOR MODEL 1--&gt;
            &lt;/Grid&gt;
        &lt;/DataTemplate&gt;
        &lt;DataTemplate x:DataType=&quot;model:Model1&quot; x:Key=&quot;model2&quot;&gt;
            &lt;!--VIEW FOR MODEL 2--&gt;
        &lt;/DataTemplate&gt;

        &lt;local:TabItemTemplateSelector x:Key=&quot;MyDataTemplateSelector&quot; Model1=&quot;{StaticResource model1}&quot; Model2=&quot;{StaticResource model2}&quot; /&gt;
    &lt;/Grid.Resources&gt;

    &lt;TabView TabItemsSource=&quot;{x:Bind TabList}&quot; TabItemTemplateSelector=&quot;{StaticResource MyDataTemplateSelector}&quot;&gt;
        
    &lt;/TabView&gt;
    
&lt;/Grid&gt;

huangapple
  • 本文由 发表于 2023年6月1日 00:06:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76375423.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定