Why is the Style defined inside of the control being ignored? - wpf

I have a ListBox inside a UserControl with its style defined in the ListBox.Resources, but the style is being ignored. We add a generic ListBox style in the Application.Resources.
Why does the Application.Resources style take presedence over the style defined in the control's resources?
This is how I define the ListBox and its style:
<ListBox x:Name="myListBox" SelectionMode="Extended" >
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Margin="0" Padding="2" Grid.Column="0" Grid.ColumnSpan="2" x:Name="Background" CornerRadius="0" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<CheckBox x:Name="myCheckBox" Grid.Column="0" IsChecked="{Binding MyBoolean}"
HorizontalAlignment="Center" IsThreeState="False" VerticalAlignment="Center" Background="Transparent" Click="myCheckBox_Click"/>
<TextBlock Grid.Column="1" Text="{Binding ItemName}" VerticalAlignment="Center" Margin="4,1,2,4">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Black" />
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
This is how we add the ListBox generic style in the App.xaml - Application.Resources:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MyAssembly;component/MyListBoxGenericStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Also, if I define the Style inside the ListBox.ItemContainerStyle it seems to work:
<ListBox x:Name="myListBox" SelectionMode="Extended" >
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Margin="0" Padding="2" Grid.Column="0" Grid.ColumnSpan="2" x:Name="Background" CornerRadius="0" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<CheckBox x:Name="myCheckBox" Grid.Column="0" IsChecked="{Binding MyBoolean}"
HorizontalAlignment="Center" IsThreeState="False" VerticalAlignment="Center" Background="Transparent" Click="myCheckBox_Click"/>
<TextBlock Grid.Column="1" Text="{Binding ItemName}" VerticalAlignment="Center" Margin="4,1,2,4">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Black" />
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>

You should define the style listed under the list-box resources under the dictionary instead. Putting styles or templates under the element to be manipulated is akin to ignoring CSS and doing styles directly in the element in HTML.
You can give the dictionary resource template a name and reference it in the tag as
<ListBox ItemTemplate="{StaticResource MyTemplate}">

Check if MyListBoxGenericStyle.xaml's style doesn't have ItemContainerStyle set explicitly to another value. This would render your default style useless.
Also, you can check by means of Snoop where does your ListBoxItem gets it style from (i.e. locally set, resources, theme, etc).

Related

How can I access child element of any object by using style?

I'm faced with a problem about to find child element. I want to access TextBlock element inside Label. But I can't find it.
Here is my MainWindow.xaml code:
<Label x:Name="text" Style="{DynamicResource labelstyle}">
<TextBlock>asdasdasd</TextBlock>
</Label>
Here is my style code:
<Style x:Key="labelstyle" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border BorderThickness="2" BorderBrush="Red">
<TextBox x:Name="textBox" Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TextBlock},
AncestorLevel=2},Path=Text}">
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to bind TextBox's Text property to Label inside TextBlock's Text property. What should I do ?
I hope I made my self clear.
Thank you.
Below is a sample image:
Instead of using a TextBlock in your label, just leave it in your template and have it reference the Label Content for the text to be displayed.
Below is an example:
<Label x:Name="text" Content="asdasdasd" Style="{StaticResource labelstyle}"/>
and for the styling/template
<Style x:Key="labelstyle" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Border BorderThickness="2" BorderBrush="Red">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style>
This should provide you with the centered text in the red border you seem to be trying to achieve.
Hopefully this helps you a bit.
This will let you display text through a binding and the user will be able to select it, but not type in the TextBox. If you want to be able to also type in the TextBox, remove the IsReadOnly="True"
<Label Height="30" Width="150">
<Label.Template>
<ControlTemplate TargetType="{x:Type Label}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
MinWidth="26"
Margin="2"
Source="{Binding myImageSource}"/>
<TextBox Grid.Column="1"
IsReadOnly="True"
Text="{Binding myTextValue}"
Margin="5,2"/>
</Grid>
</ControlTemplate>
</Label.Template>
</Label>

How to change Foreground of TextBlock in a style of a control through Datatemplate?

I have a style for control InputField, code xaml:
<Style TargetType="{x:Type c:InputField}" BasedOn="{StaticResource TextBoxStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:InputField}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Label, RelativeSource={RelativeSource TemplatedParent}}" Grid.Row="0"
Foreground="{DynamicResource InputFieldLabelForegroundBrush}" Name="Label"
/>
<Border Grid.Row="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" >
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Foreground="{DynamicResource InputFieldLabelForegroundBrush}" specification foreground of TextBlock.
I use this control InputField in a DataTemplate:
<DataTemplate DataType="{x:Type g:ItemTextBox}">
<c:InputField Label="{Binding Caption}" Text="{Binding Text}"/>
</DataTemplate>
It work fine with TextBlock has Foreground is InputFieldLabelForegroundBrush. I use ItemTextBox a lot of places. One of them is:
<DataTemplate DataType="{x:Type v:DetailSettings}">
<Border Width="290" Background="{DynamicResource DetailSettingsBackgroundBrush}">
<Grid>
<ItemsControl ItemsSource="{Binding Properties}"/>
</Grid>
</Border>
</DataTemplate>
Properties is ItemTextBox. My problem now is: "How to change Foreground of TextBlock in InputField only for this DataTemplate, that mean I must not change the Foreground color of TextBlock for all windows, but only for this DataTemplate?"
Thanks for help me !!!
In your Style, use the Setter element to declare the foreground which by default, is your InputFieldLabelForegroundBrush.
<Style TargetType="{x:Type c:InputField}" BasedOn="{StaticResource TextBoxStyle}">
<Setter Property="Foreground" Value="{DynamicResource InputFieldLabelForegroundBrush}"/>
...
</Style>
In the Template for your Style, you can then reference this by using a TemplateBinding.
<TextBlock Foreground="{TemplateBinding Foreground}" ... />
Now all you need to do in your DataTemplate, is explicitly set the Foreground on the TextBlock, and the TemplateBinding will do the rest.
<c:InputField Label="{Binding Caption}" Text="{Binding Text}" Foreground="Red"/>

WPF ListBoxItem Style Not Working

I have a Border inside a DataTemplate for a ListBox. When the mouse is over the item, I want to show the Border. Here's my XAML:
<ListBox Grid.Row="3"
Grid.Column="0"
ItemsSource="{Binding Devices}"
SelectedItem="{Binding SelectedDevice}"
MaxWidth="350">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/>
</Style.Resources>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="SteelBlue"
BorderThickness="0"
HorizontalAlignment="Stretch"
CornerRadius="3"
MinHeight="65"
Margin="3">
<Border.Style>
<Style>
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="True">
<Setter Property="Border.BorderThickness" Value="1" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Source="/DFT.Falcon6.UI.Desktop;component/Media/Images/fc6logo.png"
Height="50"
Width="50"
Margin="5"/>
<TextBlock Grid.Row="0"
Grid.Column="1"
Text="{Binding UnitIdentifier}"
Style="{StaticResource devicetextStyle}"
Margin="2"/>
<TextBlock Grid.Row="1"
Grid.Column="1"
Text="{Binding IPAddress}"
Style="{StaticResource devicetextStyle}"
Margin="2"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can see that I have a trigger defined in the Border control to set the BorderThickness to 1. However when I run it and mouse over the item, nothing happens.
What am I doing wrong here?
Thanks
Try this:
<Style>
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderThickness" Value="1" />
</Trigger>
</Style.Triggers>
</Style>
The style is applied to the Border control, so there is no need to specify "Border" again on the bindings. I've found that it sometimes (always?) seems that an attribute defined directly on the control will override the change from a trigger. You will also have to remove the borderTickness attribute from the Border control as well.
Also, keep an eye on the Output Window while running. Binding errors will happen silently in the application, but generally show up as red text in the output window that can sometimes help track down errors.

WPF ListBox items with template become invisible after grouping

I have ObservableCollection with items which I want to display in a ListBox.
Also I write a template for ListboxItem for correct display of my collection.
On this stage everything works fine.
in .cs
Sensors = new ObservableCollection<Sensor>();
...
lstBox.ItemsSource = Sensors;
in .xaml
...
<DataTemplate x:Key="SensorTileTemplate">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" Grid.Row="0" Grid.ColumnSpan="3"></TextBlock>
<Image Source="{Binding ImageModel.ImgSource}" Style="{StaticResource ImageGlowStyle}" Height="72" Grid.Row="1" Grid.Column="0"></Image>
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5">
<TextBlock Text="IP:"></TextBlock>
<TextBlock Text="Port:"></TextBlock>
<TextBlock Text="Command port:"></TextBlock>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="2" Margin="5">
<TextBlock Text="{Binding DeviceAddress}"></TextBlock>
<TextBlock Text="{Binding DeviceDataPort}"></TextBlock>
<TextBlock Text="{Binding DeviceControlPort}"></TextBlock>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
<Style x:Key="ContainerStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="ListBoxItem.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
...
<ListBox Name="lstBox" Focusable="False"
SelectionChanged="lstBox_SelectionChanged"
HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource SensorTileTemplate}"
ItemContainerStyle="{StaticResource ContainerStyle}">
</ListBox>
The problem appears when I need to group certain items using expander as a group container.
in .cs
...
ICollectionView view = CollectionViewSource.GetDefaultView(Sensors);
view.GroupDescriptions.Add(new PropertyGroupDescription("GroupNumber"));
lstBox.ItemsSource = view;
...
in .xaml
<!--Same Template and Style-->
...
...
<Style x:Key="GroupContainerStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Group #" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<ListBox Name="lstBox" Focusable="False"
SelectionChanged="lstBox_SelectionChanged"
HorizontalContentAlignment="Stretch"
ItemTemplate="{StaticResource SensorTileTemplate}"
ItemContainerStyle="{StaticResource ContainerStyle}">
<ListBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource GroupContainerStyle}" />
</ListBox.GroupStyle>
</ListBox>
This code works and groups items but items become invisible.
So without grouping items display correctly but with grouping expanders show nothing in it.
I think there is something about ItemsPresenter in Expander but can't figure out what.
The problem was in one third party theme I use in my app. That theme has a ListBox template like:
<Style TargetType="{x:Type ListBox}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid>
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Background="{DynamicResource ControlBackgroundBrush}" />
<ScrollViewer Margin="1" Style="{DynamicResource NuclearScrollViewer}" Focusable="false" Background="{x:Null}">
<StackPanel Margin="1,1,1,1" IsItemsHost="true" />
</ScrollViewer>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="{DynamicResource DisabledBackgroundBrush}" TargetName="Border" />
<Setter Property="BorderBrush" Value="{DynamicResource DisabledBorderBrush}" TargetName="Border" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So I use an ItemsPresenter instead of the StackPanel in that template and everything works now.

WPF HeaderContent Control Vertical Alignment

How can I get HCC using a tab control to "Stretch" all the way down vertically? How can I achieve this?
Here is the header content control XAML:
<HeaderedContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}" />
Here is the corresponding style info:
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
</DataTemplate>
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel>
<Button
Command="{Binding Path=CloseCommand}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Arial"
FontSize="9"
FontWeight="Bold"
Margin="0,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16"
/>
<ContentPresenter
Content="{Binding Path=DisplayName}"
/>
</DockPanel>
If you examine the control template for the HeaderedContentControl you'll find it puts the content in a StackPanel which is why content isn't stretching vertically. This is the default template:
<Style x:Key="HeaderedContentControlStyle"
TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<StackPanel>
<ContentPresenter ContentSource="Header"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So if we replace the StackPanel with a Grid like this:
<Style x:Key="HeaderedContentControlStyle"
TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentPresenter ContentSource="Header"/>
<ContentPresenter Grid.Row="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and use that style
<HeaderedContentControl
Style="{StaticResource HeaderedContentControlStyle}"
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}" />
then the content should stretch vertically.

Resources