ListBoxItem style in <ListBox.Resources> or in <ListBox.ItemContainerStyle>? - wpf

I can put a xaml Style for ListBoxItem in <ListBox.Resources> or in <ListBox.ItemContainerStyle>. See code.
Question is: what is the difference, what should I prefer?
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.Resources>
OR:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
There is an answer that I accept, but behold and think about this strange symptom:
Either way gives me this strange databinding warning: Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null .... etc.
This is a binding that is hidden somewhere in the system Aero styles, it is not mine.
Only when I use both styles this warning disappears!

ItemContainerStyle is the right way to do this because its set explicitly and so wpf will not need to look up always where the style might be. Its faster and better. That is why that property is there.
When ItemContainerStyle is not set WPF will seek for the style in ListBox.Resources or Window.Resources or Application.Resources. That is bad for performance.

First is default Style and second is Explicit style.
First one will be applied to all ListBoxItems found under VisualTree of parent ListBox.
<ListBox>
<ListBox.Resources>
<!-- default Style -->
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<ListBox>
<ListBoxItem/> <-- Will be applied to this as well.
</ListBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
whereas second one will be applied to only ListBoxItems corresponding to outer ListBox.
Second is equivalent to:
<Grid>
<Grid.Resources>
<Style x:Key="MyStyle" TargetType="ListBoxItem"/>
</Grid.Resources>
<ListBox ItemContainerStyle="{StaticResource MyStyle}"/>
</Grid>
If you look at the default style declared under PresentationFramework.Aero.dll via ILSpy, you can see these two setters over there:
<Setter Property="HorizontalContentAlignment"
Value="{Binding Path=HorizontalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment"
Value="{Binding Path=VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
So, when you set that in your default style declared under resource, it overrides those setters and hence error goes away.
But, in case you set it on style declared inline of ItemContainerStyle that error won't go away since it still refer to the default style. In case you don't want to basedOn default style set BasedOn attribute to x:Null.
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{x:Null}"/>
</ListBox.ItemContainerStyle>
</ListBox>

Related

WPF Binding children property of a custom control to parent

I'm new to WPF. I'm trying to create a custom control with a HorizontalContentAlignment property that will change according to the setting of the container.
<Style x:Key="SimpleRadioButton" TargetType="{x:Type RadioButton}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Margin" Value="20 10 20 0"/>
<Setter Property="Height" Value="24"/>
<Setter Property="Padding" Value="6"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
</Style>
And then set them in the parent container.
<ctrl:ToggleExpander
Header="Worklist"
IsChecked="{Binding IsVisible, Mode=TwoWay}"
HorizontalContentAlignment="Right"
IsToggleEnabled="True">
<StackPanel>
<ctrl:SideBarPanel
HorizontalContentAlignment="Right"
Header="map provider">
<RadioButton
Content="Finished"
IsChecked="True"
Style="{DynamicResource SimpleRadioButton}"/>
</StackPanel>
</ctrl:ToggleExpander>
But it doesn't seem to work. Is there any way to go about it? Thanks! And sorry for my English.
you could use the following, and your radiobutton will take the value of the first parent framework element (for example in this case your ctrl:SideBarPanel)
<Setter Property="HorizontalContentAlignment" Value="{Binding RelativeSource={RelativeSource AncestorLevel=1, AncestorType=FrameworkElement}, Path=HorizontalContentAlignment}"/>
you could use this symply way in xaml:
<Setter Property="HorizontalAlignment">
<Setter.Value>
<Binding Path="HorizontalAlignment" RelativeSource="{RelativeSource AncestorLevel=1,AncestorType=FrameworkElement}" />
</Setter.Value>
</Setter>
I hope help you

First item custom style border in ListView

How can I set different style for first item in ListView? In my case, I want to change first item border, to get GUI like this:
My current code (no top border):
<ListView
ItemsSource="{Binding MyData}">
<ListView.ItemContainerStyle>
<Setter Property="BorderThickness" Value="0,0,0,1" />
</ListView.ItemContainerStyle>
</ListView>
There is a very simple solution. You don't have to write custom converters etc. Use PreviousData in RelativeSource
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource PreviousData}}" Value="{x:Null}">
<Setter Property="BorderThickness" Value="0,1,0,1"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>

DataGridCell Validation.ErrorTemplate ignored

I'm trying to set the Validation.ErrorTemplate of the DataGridCells, here's the xaml code:
<Style x:Key="{x:Type DataGridCell}" x:Uid="dataGridCellErrorTemplate" TargetType="{x:Type DataGridCell}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate >
<Border BorderBrush="Green" BorderThickness="2" ToolTip="Heidenei"></Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- following line only for demonstration that the setter is working ... -->
<Setter Property="Background" Value="Aquamarine"></Setter>
</Style>
while the background of the datagridcells is successfully colored green (independant from any validation result) the used Validation.ErrorTemplate is still the default one, i.e. the red border.
I know there have been similar issues here in stackoverflow, e.g.
Styling DataGridCell Error Template
but they do not really solve my problem.
Any help is appreciated
Frank
I believe that I'm experiencing the same issue.
When using a DataGridTemplateColumn the content is presented with a ContentPresenter. This content presenter uses the default error template.
I can't find a direct way to remove this template for an individual DataGridTemplateColumn but you can remove it for all content presenters in the DataGrid by adding a style to the DataGrid's resources.
<DataGrid.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Style>
</DataGrid.Resources>
I had luck removing the irritating red border by using the following TextBlock style.
<Style TargetType="TextBlock">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Style>
From Microsoft, attempting to change the ErrorTemplate on a DataGrid just will not work. Their example is a trigger in a style where you set the background as red (or whatever) when the Validation.HasError property is true.
To customize cell validation feedback: Set the column's EditingElementStyle property to a style appropriate for the column's editing control. Because the editing controls are created at run time, you cannot use the Validation.ErrorTemplate attached property like you would with simple controls.
This approach worked for me, and it also removed the default error template (the red border around the cell). The red border is replaced with the BorderBrush in the style, and the little exclamation point can be removed by setting the RowValidationErrorTemplate property of DataGrid to {x:Null}. I have looked at the other related questions on SO and have not found this solution anywhere. Here is an example of my solution, with the style definition and a DataGridTextColumn which uses the style:
<Style
x:Key="DataGridTextBoxValidationStyle"
TargetType="{x:Type TextBox}">
<Setter Property="Padding" Value="-2" />
<Setter Property="MaxWidth" Value="250"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, Path=ActualWidth}"/>
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, Path=ActualHeight}"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Firebrick" />
<Setter Property="Foreground" Value="White" />
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
<Setter Property="BorderBrush" Value="Firebrick"/>
</Trigger>
</Style.Triggers>
</Style>
<DataGrid RowValidationErrorTemplate="{x:Null}">
<DataGrid.Columns>
<DataGridTextColumn
EditingElementStyle="{StaticResource DataGridTextBoxValidationStyle}">
<!-- other stuff here -->
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>

For WPF TreeView, how can I use a theme while also setting the TreeView.ItemContainerStyle

I use this in XAML to load the treeview children from a view model based on Josh Smith's sample code here:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
This causes the theme I'm using for TreeViewItem to be ignored. It makes the selected item text black and the background darkblue so it's hard to read. Is there a way to use both the theme and the code above at the same time?
Try setting BasedOn to {StaticResource {x:Type TreeViewItem}}.
This will take the default style for TreeViewItems (which is provided by the theme) as the base for your style.
Just the code formated:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:TypeTreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>

Databinding in style to property on parent window

Is it possible to declare a style that sets the fontsize of tooltips to the same size as the parent form? I have tried this...
<Style TargetType="{x:Type ToolTip}">
<Setter Property="FontSize" Value="{Binding ElementName=MainWindow, Path=FontSize}"/>
</Style>
...but that doesnt work. Any suggestions?
I found a solution to my problem.
<Style TargetType="{x:Type ToolTip}">
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=FontSize}"/>
</Style>

Resources