Change ListBoxItem Style - wpf

I want do something like this.
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Style">
<Setter.Value>
<Border Style="{StaticResource BorderStyle}" Width="200" >
</Border>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
<Setter Property="Background" Value="{StaticResource BackBrush}" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="0.5" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="4" />
</Style>
But it gives next error
Cannot add content of type 'System.Windows.Controls.Border' to an object of type 'System.Object'.
and the code which use it
for (int i = 0; i < 10; i++)
{
ListBoxItem lbItem = new ListBoxItem();
lbItem.Content = "Item" + i;
lb1.Add(lbItem);
}
where "lb1" is my ListBox in xaml form
How can i give ListBoxItemStyle properly?

It looks like you're confused about the semantics of XAML. Until you get more used to XAML it might help to think about it as the C# equivalent. This is essentially what you're doing right now:
Style BorderStyle = new Style();
Border inlineStyle = new Border { Style = BorderStyle };
Style listBoxItemDefaultStyle = new Style();
listBoxItemDefaultStyle.Setters.Add(new Setter(StyleProperty, inlineStyle));
ListBoxItem item = new ListBoxItem { Style = listBoxItemDefaultStyle };
One issue is that you're setting a Style for the ListBoxItem from a Setter inside the Style for your ListBoxItem, which is of course going to cause some sort of problem with recursion. So removing that extra Style from the code we get:
Style BorderStyle = new Style();
Border inlineStyle = new Border { Style = BorderStyle };
ListBoxItem item = new ListBoxItem { Style = inlineStyle };
This is invalid because it's trying to set a Style property (of Type Style) to a Border object. This is essentially what's at the core of the error you're seeing.
What you really want in this case is to change the ListBoxItem ControlTemplate to incorporate your Border Style. This is the default Style modified to use your Border instead of the standard one using TemplateBindings to set its properties:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" Style="{StaticResource BorderStyle}" Width="200">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

In the AlternationIndex trigger, why doesn't my background property work?

code show as below:
This is part of my custom datagrid appearance.
<Style x:Key="DatagridStyle_1" TargetType="DataGrid">
<!--<Setter Property="ColumnHeaderStyle" Value="{DynamicResource Datagrid_HearderStyle_1}"/>
<Setter Property="CellStyle" Value="{DynamicResource Datagrid_CellStyle_1}"/>-->
<Setter Property="RowStyle" Value="{DynamicResource ConTemplate_DgRow_1}"/>
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="AlternationCount" Value="2"/>
<!--<Setter Property="Template" Value="{DynamicResource DataGridTemplate1211}"/>-->
<Style x:Key="ConTemplate_DgRow_1" TargetType="{x:Type DataGridRow}">
<Setter Property="Background" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridRow">
<Grid SnapsToDevicePixels="True" Background="Transparent">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1"
>
</Border>
<DataGridCellsPresenter Grid.Column="1"
ItemsPanel="{TemplateBinding ItemsPanel}"
/>
</Grid>
<!--<Border BorderThickness="1" Background="LightGreen" SnapsToDevicePixels="True">
<DataGridCellsPresenter Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}"/>
</Border>-->
<ControlTemplate.Triggers>
<!--<Trigger Property="IsSelected" Value="true">
<Setter Property="BorderBrush" Value="#00BCD4"/>
</Trigger>-->
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#00BCD4"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="0">
<Setter Property="Background" Value="DarkBlue"/>
<Setter Property="Foreground" Value="LightGreen"/>
</Trigger>
</Style.Triggers>
</Style>
this part doesn't work,but the other part
<Setter Property="Foreground" Value="LightGreen"/> works fine
In the above code, I set the AlternationIndex trigger in the penultimate part of the code. In the same trigger, the Foreground property has already taken effect, but the Background property has never taken effect. I have modified the cell template and datagrid template before, and set their background color to transparent, but it has no effect.
I'd like to understand why this is the case and how to make setting the background color work.

Change ItemContainerStyle of ListBox MVVM in WPF MVVM Pattern

I have a ListBox which has an itemcontainerstyle, it has background color(lets say Green) too in this style, I want to change the background color from MVVM.
The color will change on some specific condition,otherwise the default color(ie Green) should apply.
<ListBox x:Name="lst1" ItemsSource="{Binding DataSource}" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Disabled" Style="{DynamicResource StepListBox}" ItemContainerStyle="{DynamicResource ListBoxItemStyle}" Margin="-10,0,0,0">
//Listbox Items
</ListBox>
and this is my ListboxItemStyle,its defined in a separate xaml file
<Style x:Key="ListboxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Margin" Value="20,0,0,10"/>
<Setter Property="Background" Value="{DynamicResource Green}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TertiaryMediumStroke}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource SecondaryDark}"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="10,10,0,10"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<Grid Margin="0">
<Rectangle x:Name="BgColor" Fill="{DynamicResource TertiaryMediumStroke}" Margin="-10,-10,0,-10" Opacity="0.1"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<!--#FFD1EFD6-->
<Setter Property="Background" TargetName="Bd" Value="#FFD7F0DB" />
<Setter Property="Opacity" TargetName="BgColor" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource ForeLight}"/>
</Trigger>
<!--
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
-->
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="BgColor" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource ForeDisable}"/>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TertiaryMedium}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I change this in my ViewModel?
When using MVVM, the view models should really know nothing about the views. However, if you really insist on changing UI element colours from the view model, then you can follow this approach. The idea is to have one or more properties with primitive types, such as bool, int, or even enum, that are data bound in the UI. Using DataTriggers, your UI can 'listen' for changes in these properties and update colours accordingly. Take this example:
If you have just one or two colours to update, you can use a bool property and a some DataTriggers:
<Style>
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsElementRed}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
If you have a range of colours to update, you could use a custom enum:
<Style>
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding EnumInstance}" Value="SomeValue">
<Setter Property="Background" Value="Red" />
</DataTrigger>
...
<DataTrigger Binding="{Binding EnumInstance}" Value="SomeOtherValue">
<Setter Property="Background" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
Alternatively, you could also use an IValueConverter to convert between the enum instance and the various required colours.
You'll only need a DataTrigger in your Style on a boolean in your ViewModel layer:
<Style.Triggers>
<DataTrigger Binding="{Binding MyBooleanProperty, Mode=OneWay}" Value="True">
<Setter Property="Background" Value="WhatEverColorYouWant"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Background" Value="Green"/>
And in your ViewModel:
public bool MyBool {
get { return _firstCondition && _secondCondition && _thirdCondition; }
}
And when one of the condition changes, you'll just want to raise a NotifyPropertyChanged on MyBool

Defining a control template for a rectangle in XAML

In my current project, I have this XAML file where I define the visual style that must be applied to different types of custom widgets.
For example, the style for a 'DirectLineButton' (a custom class that inherits from WPF's Button) is as follows:
<Style x:Key="DirectLineButtonTemplate" TargetType="{x:Type View:DirectLineButton}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="MinHeight" Value="23"/>
<Setter Property="MinWidth" Value="75"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="MyBorder"
CornerRadius="2"
BorderThickness="2"
Background="Gold"
BorderBrush="Gray">
<ContentPresenter Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineAvailable">
<Setter TargetName="MyBorder" Property="Background" Value="Gold"/>
</Trigger>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineIdle">
<Setter TargetName="MyBorder" Property="Background" Value="Silver"/>
</Trigger>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineBusy">
<Setter TargetName="MyBorder" Property="Background" Value="Green"/>
</Trigger>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineCalled">
<Setter TargetName="MyBorder" Property="Background" Value="LightBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now, I need to replicate this idea by defining the style for a Rectangle. Unfortunately, WPF flags an error message when I try to define a ControlTemplate for a rectangle, can you suggest a workaround for this? See the code below of what I'm trying to attempt:
<Style x:Key="MyRectangleTemplate" TargetType="{x:Type Rectangle}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="MinHeight" Value="23"/>
<Setter Property="MinWidth" Value="75"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Rectangle}">
<Border x:Name="MyBorder"
CornerRadius="2"
BorderThickness="2"
Background="Gold"
BorderBrush="Gray">
<ContentPresenter Margin="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineAvailable">
<Setter TargetName="MyBorder" Property="Background" Value="Gold"/>
</Trigger>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineIdle">
<Setter TargetName="MyBorder" Property="Background" Value="Silver"/>
</Trigger>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineBusy">
<Setter TargetName="MyBorder" Property="Background" Value="Green"/>
</Trigger>
<Trigger Property="View:DirectLineButton.State" Value="DirectLineCalled">
<Setter TargetName="MyBorder" Property="Background" Value="LightBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks in advance!
Hi you will have to do it with Border only.Controls that inherits FrameworkElement only can have Template . But Rectangle , Line etc are lighter versions they inherits only UIElement not FrameworkElement.I hope this will help. Conclusion: Rectange do not have Template property
You can't define a control template for a Rectangle, since it's not a control, but a Shape. You can only define a control template for classes derived from Control.
You should
1) use a Style (not a ControlTemplate) to have rounded borders.
2) Use a Trigger inside your style.
The following Xaml should get you closer from your goal :
<Style x:Key="MyRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="MinHeight" Value="23"/>
<Setter Property="MinWidth" Value="75"/>
<Setter Property="Fill" Value="Blue"/>
<Setter Property="RadiusX" Value="2" />
<Setter Property="RadiusY" Value="2" />
<Style.Triggers>
<DataTrigger Binding="{Binding DirectLineState}"
Value="{x:Static l:DLS.Available}">
<Setter Property="Fill" Value="Gold"/>
</DataTrigger>
<DataTrigger Binding="{Binding DirectLineState}"
Value="{x:Static l:DLS.Idle}" >
<Setter Property="Fill" Value="Silver"/>
</DataTrigger >
<DataTrigger Binding="{Binding DirectLineState}"
Value="{x:Static l:DLS.Available}">
<Setter Property="Fill" Value="Green"/>
</DataTrigger >
<DataTrigger Binding="{Binding DirectLineState}"
Value="{x:Static l:DLS.Called}">
<Setter Property="Fill" Value="LightBlue"/>
</DataTrigger >
</Style.Triggers>
</Style>
(notice that :
1) i changed the key for "MyRectangleStyle".
2) If you want this Style to be the default Style, do not give it a Key, but just a TargetType...
3) ...OR set the Key to "{x:Type Rectangle}". msdn seems to prefer that way.
btw : shouldn't you be using DataTriggers on public properties instead of Triggers ? but i don't know the whole architecture of your application. )
Rq : for the code above to work, you need :
1) a public proprety called DirectLineState raising NotifyPropertyChanged on change.
2) an enum called DLS defined in a separate file (like a class) in your project
3) you need "l" to be your project NameSpace.
<xmlns:l="clr-namespace:MyProjectNameSpace" >
4) Assign Style and set proper DataContext for the Rectangle

erriding the selected background color for a themed tabitem

I'm trying to do something which I'd think would be pretty simple, but is turning out to be rather complicated.
I'm working with a TabControl that has already been styled by a theme in an included assembly, but I'd like the background color of a TabItem when the TabItem is selected to be different. While I could change this on the theme in the other assembly, I'd rather just override it for my application.
The problem seems to be that setting the background color for selected TabItems involves setting a control template, and in particular, a control template trigger on the IsSelected property. I can't find an easy way to only override that one trigger without fully defining the control template like so (resulting in a bunch of unnecessary copy/pasted code). Here's what I have right now- is there a way to cut this down to target only the part I care about? It doesn't seem like ControlTemplates are inheritable. Thanks for any help you can offer.
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border"
Margin="0,0,-4,0"
Background="{DynamicResource {x:Static themes:HarmonyBrushes.BrushDKey}}"
BorderBrush="{DynamicResource {x:Static themes:HarmonyBrushes.BrushDKey}}"
BorderThickness="1,1,1,1"
CornerRadius="3,3,0,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100" />
<Setter TargetName="Border"
Property="Background"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.BrushLightKey}}" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.BrushLightKey}}" />
<Setter Property="Foreground"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.TextBrushLightKey}}" />
<Setter TargetName="Border"
Property="BorderThickness"
Value="1,1,1,0" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border"
Property="Background"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.BrushDarkKey}}" />
<Setter TargetName="Border"
Property="BorderBrush"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.BrushDarkKey}}" />
<Setter Property="Foreground"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.BrushKey}}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static themes:HarmonyBrushes.TextBrushLightKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I think the simplest way to change the colour is to override the resource:
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="{x:Static themes:HarmonyBrushes.BrushLightKey}" Color="Green" />
</Grid.Resources>
<TabControl>
<TabItem>Tab One</TabItem>
<TabItem>Tab Two</TabItem>
</TabControl>
</Grid>

Validation Error Templates For UserControl

I have built an UserControl. I don't like the red border showing around it when validation errors occur. I have a textbox inside my control.
How can I override the validation error style to get rid of the red border in the whole control and just show a red background in the textbox inside my usercontrol?
Thanks!
I am using this template that will color the background of the textbox instead of showing just the border.
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true" >
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="MistyRose"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="1.0"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource
Self},Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
And all I have to do to your DocPannel Where the controls are located for example for me inside a DockPanel then i have to set its Validation.Error template to nothing this will remove the border.
For Ex:
<TextBox >
<Validation.ErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
On the style for your user control:
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
On the style for your textbox:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border
Name="Border"
CornerRadius="5"
Padding="2"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="LightGray"/>
<Setter TargetName="Border" Property="BorderBrush" Value="Black"/>
<Setter Property="Foreground" Value="Gray"/>
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="BorderBrush" TargetName="Border" Value="{DynamicResource ErrorBorderColor}"/>
<Setter Property="Background" TargetName="Border" Value="{DynamicResource ErrorBackgroundColor}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

Resources