web-dev-qa-db-ja.com

WPFタブコントロールとMVVMの選択

[〜#〜] mvvm [〜#〜][〜#〜] wpf [〜#〜] アプリケーションにTabControlがあります。次のように定義されます。

<TabControl Style="{StaticResource PortfolioSelectionTabControl}" SelectedItem="{Binding SelectedParameterTab}" >
    <TabItem Header="Trades" Style="{StaticResource PortfolioSelectionTabItem}">
        <ContentControl Margin="0,10,0,5" Name="NSDetailTradeRegion" cal:RegionManager.RegionName="NSDetailTradeRegion" />
    </TabItem>
    <TabItem Header="Ccy Rates" Style="{StaticResource PortfolioSelectionTabItem}">
        <ContentControl Margin="0,10,0,5" Name="NSDetailCcyRegion" cal:RegionManager.RegionName="NSDetailCcyRegion" />
    </TabItem>
    <TabItem Header="Correlations / Shocks" Style="{StaticResource PortfolioSelectionTabItem}">
        <ContentControl Name="NSDetailCorrelationRegion" cal:RegionManager.RegionName="NSDetailCorrelationRegion" />
    </TabItem>
    <TabItem Header="Facility Overrides" Style="{StaticResource PortfolioSelectionTabItem}" IsEnabled="False">
        <ContentControl Name="NSDetailFacilityOverrides" cal:RegionManager.RegionName="NSDetailFacilityOverrides" />
    </TabItem>
</TabControl>

そのため、各タブ項目のコンテンツには、独自のビューが関連付けられています。これらの各ビューには [〜#〜] mef [〜#〜][Export]属性があり、ビューの検出を通じて関連する地域に関連付けられているため、上記のコードだけで十分です。タブでロードを制御し、それらを切り替えます。それらはすべて同じ背後の同じ共有ViewModelオブジェクトを参照するため、すべてシームレスに対話します。

私の問題は、ユーザーが親ウィンドウに移動するときに、タブコントロールを2番目のタブ項目にデフォルト設定することです。これは、TabItem番号2のXAML IsSelected="True"で指定することにより、ウィンドウが最初にロードされたときに簡単に実行できます。

タブコントロールにSelectedItem={Binding SelectedTabItem}プロパティを持つことを考えたので、ViewModelで選択したタブをプログラムで設定できましたが、問題は、上記で宣言されているViewModelのTabItemオブジェクトの知識がないことです。 XAMLのみ。したがって、setterプロパティに渡すTabItemオブジェクトはありません。

私が考えていた1つのアイデアは、子ビュー(上記の各タブ項目のコンテンツを形成する)に、XAMLのUserControlレベルのスタイルを持たせることでした。

<Style TargetType={x:Type UserControl}>
    <Style.Triggers>
        <DataTrigger Property="IsSelected" Value="True">
             <Setter Property="{ElementName={FindAncestor, Parent, typeof(TabItem)}, Path=IsSelected", Value="True" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Findancestorビットが正しくないことは知っています。意図を特定するためにそこに置いただけですが、正確な構文はわかりません。基本的に、各UserControlがViewModelのプロパティをリッスンするトリガーを持つようにします(明らかに、同じプロパティをすべてリッスンできないか、プロパティが設定されたときに同時に選択するため、異なるUserControlを区別する方法はわかりません) True、ただし、各ユーザーコントロールのプロパティはいように見えます)、その親TabItemコンテナを見つけて、IsSelected値をtrueに設定します。

私はここで解決策を講じていますか?私が考えていることをすることは可能ですか?よりきれいな解決策はありますか?

19
NZJames

MSDNの TabControl Class ページを見ると、SelectedIndexというintというプロパティがあります。したがって、ビューモデルにintプロパティを追加し、BindプロパティをTabControl.SelectedIndexプロパティを使用すると、ビューモデルからいつでも好きなタブを選択できます。

<TabControl SelectedIndex="{Binding SelectedIndex}">
    ...
</TabControl>

更新>>>

このメソッドを使用すると、「スタートアップ」タブの設定がさらに簡単になります。

ビューモデル:

private int selectedIndex = 2; // Set the field to whichever tab you want to start on

public int SelectedIndex { get; set; } // Implement INotifyPropertyChanged here
42
Sheridan

以下のコードサンプルは、MVVMを使用して動的タブを作成します。

[〜#〜] xaml [〜#〜]

<TabControl Margin="20" x:Name="tabCategory"
                ItemsSource="{Binding tabCategory}"
                SelectedItem="{Binding SelectedCategory}">

    <TabControl.ItemTemplate>
        <DataTemplate>
            <HeaderedContentControl Header="{Binding TabHeader}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>

    <TabControl.ContentTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding TabContent}" />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

モーダルクラス

TabCategoryItemは各タブ項目を表します。 2つのプロパティで、TabHeaderはタブキャプションを表示し、TabContentには各タブに入力するコンテンツ/コントロール。

Public Class TabCategoryItem

    Public Property TabHeader As String
    Public Property TabContent As UIElement
End Class

VMクラス

Public Class vmClass

    Public Property tabCategory As ObjectModel.ObservableCollection(Of TabCategoryItem)
    Public Property SelectedCategory As TabCategoryItem
End Class

以下のコードはコンテンツを埋めてバインドします。 2つのタブtab1とtab2を作成しています。両方のタブにはテキストボックスが含まれます。テキストボックスの代わりに任意のUIelementを使用できます。

Dim vm As New vmClass

vm.tabCategory = New ObjectModel.ObservableCollection(Of TabCategoryItem)

'VM.tabCategory colection will create all tabs

vm.tabCategory.Add(New TabCategoryItem() With {.TabHeader = "Tab1", .TabContent = new TextBlock().Text = "My first Tab control1"})
vm.tabCategory.Add(New TabCategoryItem() With {.TabHeader = "Tab2", .TabContent = new TextBlock().Text = "My first Tab control2"})

mywindow.DataContent = vm
10
Rosh

参考までに、ObservableCollectionソースを使用して動的にタブを追加しても、最後に追加したタブが選択されないという同じ問題を経験しました。 SelectedIndexに従ってTabを選択するとシェリダンが言ったことと同じ変更を行いました。最後に追加されたタブが選択されますが、フォーカスされていません。そのため、タブにフォーカスするには、Set Binding IsAsyncプロパティをTrueに追加する必要があります。

<TabControl ItemsSource="{Binding Workspaces}" Margin="5" SelectedIndex="{Binding TabIndex, Mode=OneWay,UpdateSourceTrigger=PropertyChanged, IsAsync=True}">
9
Sudhir