Why doesn't my DataTrigger update the SelectedIndex property of a TabControl? - c#

I have a TabControl which has its SelectedIndex property bound to a boolean value like this:
<TabControl>
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsRunning, UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="SelectedIndex" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabItem Header="Foo" />
<TabItem Header="Bar" />
</TabControl>
The TabControl should only switch to the second tab, if the IsRunningproperty changes to True, but the problem now is, that as soon as the IsRunning property changes, the TabControl does not update itself to display the second TabItem.
Is there a way to do this through XAML, or do I have to implement a SelectedIndex property in my viewmodel, that binds directly to the SelectedIndexof the TabControl?

This works for me just as expected, if the property changes to true the tab switches. Maybe there's a problem with the binding? (Or did i misuderstand the question?)

This is an old thread but who knows someone else may stuble upon this just like me looking for an answer.
Solution: Just add a setter in TabControl style to set SelectedIndex to initial value. e.g. Setter Property="SelectedIndex" Value="0"
<TabControl>
<TabControl.Style>
<Style TargetType="TabControl">
<Setter Property="SelectedIndex" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsRunning, UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="SelectedIndex" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabItem Header="Foo" />
<TabItem Header="Bar" />
</TabControl>

Related

WPF Set GroupBox opacity to 60 percent if a checkbox is ticked

Is there any way in XAML to bind the opacity of a GroupBox depending on if a checkbox is ticked or not?
For example, I want a GroupBox to be 100% opacity if the checkbox is ticked, otherwise it will be 60% opacity if the checkbox is un-ticked.
Can I use element binding to achieve this?
Thanks all.
You could use a Style with a DataTrigger that binds to the IsChecked property of the CheckBox:
<CheckBox x:Name="chk" />
<GroupBox>
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="Opacity" Value="0.6" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=chk}" Value="True">
<Setter Property="Opacity" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
</GroupBox>

HasItems Property in TreeViewItem true, even if all children are Visibility = Collapsed

Can you please give me a hint, to make HasItems Property better.
I have a TreeView like this:
<TreeView ItemsSource="{Binding Customers}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding HasItems, RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
</Style.Triggers>
<Setter Property="AutomationProperties.AutomationId" Value="{Binding AutomationId}" />
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Customers}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding FamilyName}" Margin="5,0,0,0" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code behind:
public ObservableCollection<Customer> Customers { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
Customers = new ObservableCollection<Customer>();
var homer = new Customer("Homer", "Simpson");
homer.Customers.Add(new Customer("Bart", "Simpson"));
homer.Customers.Add(new Customer("Lisa", "Simpson"));
homer.Customers.Add(new Customer("Maggie", "Simpson"));
var chief = new Customer("Chief", "Wiggum");
chief.Customers.Add(new Customer("Ralf", "Wiggum"));
Customers.Add(homer);
Customers.Add(chief);
}
The Class Customer implements INotifyPropertyChanged and everything is fine.
As you see here, I have a DataTrigger to change color depending on "HasItems" Property of the TreeViewItem.
The problem is: HasItems is true, even if all children are Hidden or Collapsed.
See here: I made the VISIBILITY of son of "Chief Wiggum" Collapsed. And "Chief Wiggum"-TreeViewItem is still red.
Well, as you could figure out, the fact the items are hidden doesn't mean the tree view has no items.
One possible approach is changing your DataTrigger in the following way:
<DataTrigger Binding="{Binding Items, Converter={StaticResource HasVisibleItemsConverter}, RelativeSource={RelativeSource Self}}" Value="True">
Create a HasVisibleItemsConverter converter class that implements IValueConverter, there you should check if there are any items that are visible - I'll leave that for your own exercise.
Then you create an instance of HasVisibleItemsConverter in the Resources area (either Window.Resources or UserControl.Resources):
<Window.Resources>
<conv:HasVisibleItemsConverter x:Key="HasVisibleItemsConverter" />
</Window.Resources>
And don't forget to add conv="..." in the namespace definition for your Window/UserControl pointing to the assembly and namespace where your converter is.

ContextMenu bound to ObservableCollection<string> - show item if count == 0

I've got an observable collection of strings to thats data bound to my XAML contextmenu:
The ViewModel-Property:
public ObservableCollection<string> Indexes
{
get { return _Indexes; }
private set
{
if (value != _Indexes)
{
_Indexes = value;
OnPropertyChanged("Indexes");
}
}
}
The XAML code:
<viewmodel:IndexViewModel x:Key="IndexViewModel" />
<ContextMenu x:Key="ContextMenu_Index" Placement="Mouse" IsOpen="False">
<ContextMenu.ItemsSource>
<CompositeCollection>
<MenuItem Header="No items!" IsEnabled="False" Visibility="Collapsed">
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource IndexViewModel}, Path=Indexes.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
<CollectionContainer Collection="{Binding Path=Indexes, Source={StaticResource IndexViewModel}}" />
</CompositeCollection>
</ContextMenu.ItemsSource>
<ContextMenu.Style>
<Style TargetType="ContextMenu"></Style>
</ContextMenu.Style>
<ContextMenu.ItemTemplate>
<DataTemplate DataType="string">
<TextBlock Text="{Binding}" MouseDown="TextBlock_Index_MouseDown"></TextBlock>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
Now I want to show the "No items" menu item if the count of Indexes is 0. But unfortunately it doesn't work this way, the "No items!" menu item is not shown. Do you have some hints?
There is a Dependency Property Setting Precedence List and because of that when you manually set Visibility it has priority over style trigger. Bring default value as setter into your Style instead of setting it against MenuItem and then Style.Trigger will be able to change that value:
<MenuItem Header="No items!" IsEnabled="False">
<MenuItem.Style>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding Source={StaticResource IndexViewModel}, Path=Indexes.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
In my opinion, displaying a MenuItem to say 'No items!' is unhelpful and incorrect... surely your users can tell when there are no MenuItems without being told that. Even if you feel that you absolutely do have to do that, then why don't you simply add an actual item into your data bound collection?:
Indexes.Add("No items!");
In your AddItem method, you'd just need to check for the existence of this item before adding a new item:
if (Indexes.Contains("No items!")) Indexes.Remove("No items!");
Indexes.Add(newItem);
In your comment, you said that you couldn't Style this item differently... I don't know why you'd want to do that anyway, but you could just use the DataTemplateSelector Class to do that for you. It would easier for you to implement your requirements this way.

Is there something equivalent to TemplateBinding outside of a ControlTemplate?

I'm new to the styling part of WPF. What I want to do is to get the value of an attached property in a setter, e.g.:
<Trigger Property="SomeProperty" Value="SomeValue">
<Setter Property="SomeProperty"
Value="(My attached property, let's say lcl:MyClass.MyString)"/>
</Trigger>
I know that you can get something to this effect using a {TemplateBinding lcl:MyClass.MyString} in a ControlTemplate. My question is: can you do this in a style, without using a ControlTemplate?
You can try to use:
<Setter Property="SomeProperty" Value="{Binding Path=(lcl:MyClass.MyString), RelativeSource={RelativeSource self}}"/>
if your attached property applies to the element as your style. If not, you can use RelativeSource or ElementName to find the appropriate element.
I am not sure how you have done that since your code lacks of details. Below code works:
<UserControl.Resources>
<Style x:Key="LabelStyle" TargetType="{x:Type Label}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Content"
Value="{Binding Path=(TestWebBrowser:AttachP.ValueEditorState), RelativeSource={RelativeSource self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<StackPanel>
<Label x:Name="label" TestWebBrowser:AttachP.ValueEditorState="HelloWorld" Style="{StaticResource LabelStyle}"/>
<Button Content="Disable Label" Click="Button_Click"/>
</StackPanel>
Button's click event handler will set Label's IsEnabled to false to trigger the trigger. And note that you have to use Path= with parenthesis in the binding.

Setting ItemTemplate based on CheckBox value

I have a DataTemplate which contains a CheckBox and ListBox. When the CheckBox is checked, I want to change the ItemTemplate property on the ListBox to change the appearance of each item.
Right now, it looks like this:
<DataTemplate DataType={x:Type MyViewModel}>
<DockPanel>
<CheckBox DockPanel.Dock="Bottom"
Content="Show Details"
HorizontalAlignment="Right"
IsChecked="{Binding ShowDetails}"
Margin="0 5 10 5" />
<ListBox ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource SimpleItemTemplate}"
Margin="10 0 10 5">
<ListBox.Triggers>
<DataTrigger Binding="{Binding ShowDetails}" Value="True">
<Setter Property="ItemTemplate"
Value="{StaticResource DetailedItemTemplate}" />
</DataTrigger>
</ListBox.Triggers>
</ListBox>
</DockPanel>
</DataTemplate>
However, when I try to compile, I get the following error messages:
Value 'ItemTemplate' cannot be assigned to property 'Property'. Invalid PropertyDescriptor value.
and
Cannot find the static member 'ItemTemplateProperty' on the type 'ContentPresenter'.
I'm still fairly new to WPF, so perhaps there is something I'm not quite understanding?
You need to do this through the ListBox Style rather than directly through its Triggers collection. A FrameworkElement's Triggers collection can only contain EventTriggers (so I'm surprised your sample got as far as complaining about the properties!). Here's what you need to do:
<ListBox ItemsSource="{Binding Items}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{StaticResource SimpleItemTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ShowDetails}" Value="True">
<Setter Property="ItemTemplate"
Value="{StaticResource DetailedItemTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

Categories

Resources