Reputation: 2183
I've got an application consisting of a TabControl
with TabItem
s that are hosting custom UserControl
s like this:
<TabControl>
<TabItem Header="UC_1 and UC_2">
<StackPanel>
<local:UC_1/>
<Separator/>
<local:UC_2/>
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3/>
</TabItem>
<TabItem Header="UC_4">
<local:UC_3/>
</TabItem>
</TabControl>
The UserControl
s have a bunch of different controls on them, like this:
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="Surname"/>
<Border>
<TextBox Text="{Binding Model.Surname}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Firstname" />
<Border>
<TextBox Text="{Binding Model.Firstname}" />
</Border>
</DockPanel>
</StackPanel>
</ScrollViewer>
Now I want to set the whole TabControl
to ReadOnly
, depending on a property in the Model, so the user can still read but not edit the content of the controls.
Unfortunately, IsReadOnly
is not a property of a TabControl
, nor a TabItem
nor a UserControl
. So I decided to go with the IsEnabled
-property instead.
But if I disable the whole TabControl
, the user would be unable to switch between the TabItems
anymore to read the data, as those cannot be clicked on if they are disabled (which seems legit, given the meaning of the IsEnabled
-property).
So, to achieve my "diabled but still clickable"-TabItem
s, I've tried to setup a Style
with a DataTrigger
inside the Resources of the TabControl
, so disable the UserControl
s inside the TabItem
s instead of the whole TabControl
or the TabItem
s:
<TabControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Model.TCEnabledProperty}" Value="9">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
But this is not working.
So I changed
<Style TargetType="{x:Type UserControl}">
to
<Style TargetType="{x:Type StackPanel}">
and suddenly I got the expected behavior but (of course) only if the given UserControl
has a StackPanel
hosting all the other controls, what is not the case for all of those UserControl
s.
Why is the Style
working for the TargetType = StackPanel
but not for the TargetType = UserControl
? I don't get the point that I can access the Controls inside the UserControl
but not the UserControl
itself.
If there is no other option, I'll just add a wrapping StackPanel
to the UserControl
s not having one already, but for the sake of curiosity I would like to understand the whole behavior I'm experiencing here...
Upvotes: 0
Views: 1718
Reputation: 27338
Why dont just bind each TabItem's content IsEnabled property separatelly?
<TabItem Header="UC_1 and UC_2">
<StackPanel IsEnabled="{Binding ...}"/> <!-- use converter if you can't create property called 'IsTCEnabled' in viewmodel -->
<local:UC_1 />
<Separator />
<local:UC_2 />
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3 IsEnabled="{Binding ...}"/>
</TabItem>
<TabItem Header="UC_4">
<local:UC_3 IsEnabled="{Binding ...}"/>
</TabItem>
you may also create style to avoid repeating the binding:
<Style x:Key="TabContent" TargetType="FrameworkElement">
<Setter Property="IsEnabled" Value="{Binding }" />
</Style>
<TabItem Header="UC_1 and UC_2">
<StackPanel Style="{StaticResource TabContent}" />
<local:UC_1 />
<Separator />
<local:UC_2 />
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3 Style="{StaticResource TabContent}" />
</TabItem>
<TabItem Header="UC_4">
<local:UC_3 Style="{StaticResource TabContent}" />
</TabItem>
EDIT: Answer to your question
Why is the Style working for the TargetType = StackPanel but not for the TargetType = UserControl?
it's simple. StackPanel style works, because you have added StackPanel to the first tab. UserControl does not work, because you have added UC_1 and UC_2 to the second tab. Implicit styles does not work for inherited controls. TargetType must exactly match the type of the element.
Upvotes: 2
Reputation: 2183
Edit:
Liero provided an even better solution to the problem. I'm still leaving my answer up, as I was confusing base classes with concrete objects by the time I was wondering why I was unable to apply aStyle
toTargetType=UserControl
in my View and the text below might help others to understand the difference as well.
The discussion in the comments to Sinatrs answer lead me to another post here on stackoverflow, where I found the solution to the question.
Why isn't the setter working for TargetType=UserControl
?
The short answer is:
Style
s can only be applied to concrete objects.
UserControl
is just a class, not a concrete object. The concrete object would be <local:UC_1/>
.
The StackPanels
on the other hand, surely are concrete objects, even though they are hosted further down the visual tree, inside the UserControl
, and therefore a Style
can be applied.
Also, it seems like Style
s can only be set from top down if there is not already a Style
set for the object in the UserControl
.
Any Style
I'm trying to set from top down would just be overridden again by the Style
nested deeper inside the visual tree (last Style
wins).
This applies to any Style
s set by a ResourceDirectory
for objects in a UserControl
as well.
Upvotes: 0
Reputation: 21979
Add IsReadOnly
dependency property to UserControl
:
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="Surname"/>
<Border>
<TextBox Text="{Binding Model.Surname}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Firstname" />
<Border>
<TextBlock x:Name="textBlock" Text="{Binding Model.Firstname}" />
<TextBox x:Name="textBox" Text="{Binding Model.Firstname}" />
</Border>
</DockPanel>
</StackPanel>
</ScrollViewer>
and code (could be done using bindings and converters, x:Name
-way is just quicker way to demonstrate):
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(MyUserControl), new PropertyMetadata(false, (d, e) =>
{
var userControl = (MyUserControl)s;
var value = (bool)e.NewValue;
userControl.textBlock.Visible = value ? Visibility.Visible : Visibility.Hidden;
userControl.textBox.Visible = !value ? Visibility.Visible : Visibility.Hidden;
}));
Then you should be able to set it in tab page:
<TabItem Header="MyUserControl">
<local:MyUserControl IsReadOnly="{Binding SomeBinding}"/>
</TabItem>
Upvotes: 0