I have a TabControl with six tabs in my ResultView. The ViewModel that sits behind this View can be either a ResultTypeOneViewModel or ResultTypeTwoViewModel, each of which derives from ResultViewModel but you can interchangeably use the result viewer with either result type.
The difference is that in ResultTypeOneViewModel, tabs 1 & 3 need to be visible and the rest hidden. In ResultTypeTwoViewModel, tabs 2, 3, 4, 5, 6 need to be visible and tab 1 hidden.
I wanted to do this via something like
<TabItem Name="1" Visibility={Binding IsTabVisible(0)}>
<TabItem Name="2" Visibility={Binding IsTabVisible(1)}>
<TabItem Name="3" Visibility={Binding IsTabVisible(2)}>
etc...
And have an abstract method declaration in ResultsViewModel such as
public abstract Visibility IsTabVisible(int index);
And in ResultsOneViewModel have
public override Visibility IsTabVisible(int index)
{
if (index == 0 || index == 2) return Visibility.Visible;
return Visibility.Hidden;
}
And in ResultsTwoViewModel have
public override Visibility IsTabVisible(int index)
{
if (index == 0) return Visibility.Hidden;
return Visibility.Visible;
}
But I cannot figure out how to call a method like this with a parameter through bindings iN WPF XAML.
Can anyone suggest how I can do this or if it's not possible via this method, another way I could solve this problem?
You can use an indexer property to be able to pass a single parameter to a property. Although it's probably not very intuitive to return a boolean value from an indexer property it works fine for me. Also keep in mind the indexer property looses it's expected functionality.
class MyClass
{
public bool this[int tabNumber]
{
get
{
// Do logic to determine visibility here or in a separate method
return IsTabVisible(tabNumber);
}
}
bool IsTabVisible(int tabNumber)
{
bool result;
// Method logic...
return result;
}
}
So in XAML you can use the class name and provide a parameter between square brackets.
<TabItem Visibility="{Binding MyClass[0]}}"/>
If you need to raise a NotifyPropertyChanged for the indexer property use:
NotifyPropertyChanged("Item[]");
I don't know if this fits into the MVVM pattern but it could be useful for anyone who wants to bind to a method with a single parameter.
To answer your question directly, you can use the ObjectDataProvider
to call a method for you so you can work with the results:
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Windows="clr-namespace:System.Windows;assembly=PresentationCore"
...
<Window.Resources>
<ObjectDataProvider x:Key="IsTab1VisibleMethod"
ObjectType="{x:Type Windows:Visibility}" IsAsynchronous="True"
MethodName="IsTabVisible">
<ObjectDataProvider.MethodParameters>
<System:Int32>0</System:Int32>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
You should then be able to access the result like this (but you'd need one of these for each TabItem
):
<TabItem Visibility="{Binding Source={StaticResource IsTab1VisibleMethod}}" />
However, it's generally not good practice to manipulate UI properties such as Visibility
in code, so a better idea would be to use a BooleanToVisibilityConverter
to Bind
the Visibility
properties to bool
values as @Highcore suggested. You can see an example of this in the Binding a Button Visibility to bool value in ViewModel post here on StackOverflow.
In my opinion, an even better solution would be to simply provide one view for every view model.
A better suggestion to this question is to use style.DataTrigger in style of TabItem like this:
<TabItem>
<TabItem.Style>
<Style target="{x:Type TabItem}">
<Style.DataTriggers>
<DataTrigger Binding="{Binding IsTabVisible}" Value="False">
<Setter Property="Visibility" Value = "Collapsed"/>
</DataTrigger>
</Style.DataTrigers>
</Style>
<TabItem.Style>
</TabItem>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With