ItemsControl Validation style not updating when adding/removing items - c#

I have an ItemsControl that is bound to an ObservableCollection. This collection has a validation rule (using IDataErrorInfo) that basically says 'If there is a 'Quote' in this list, then there has to be a 'Customer' as well.
I can see that the validation rule is working as expected as I have a button bound to a command that can't execute if there are errors. However, the ItemsControl is not visually updating with the error style.
This error style does work. I'm using it on a number of other controls (mainly TextBox and ComboBox) and it does show on the ItemsControl if the bound collection is set to a new collection. It is purely on adding and removing that the error style does not update.
<ItemsControl Margin="5" ItemsSource="{e:Binding Path=CurrentBooking.Regardings}" Background="Transparent">
<!-- Items Panel and ItemTemplate Stuff -->
</ItemsControl>
e:Binding is a custom Binding that, amongst other things, sets the 3 ValidatesOn... properties to true. CurrentBooking.Regardings is the ObservableCollection
This is the empty user control. The plus icon is used to add items to the collection.
This should have the error style applied as there is a quote and no customer. The button mentioned above is disabled, showing that there is an error.
If I instead create a new collection with the added item, it does apply the style. However, I don't particularly want to create a new collection every time an item is added or removed.
Code for error style:
<Style x:Key="ErrorStyle" TargetType="FrameworkElement">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
<!--Clear Template if false-->
<Trigger Property="Validation.HasError" Value="false">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="ItemsControl" BasedOn="{StaticResource ErrorStyle}">
<Setter Property="Background" Value="Transparent"/>
</Style>

Related

WPF control not returning in previous state after triggering

XAML
<utility:InvalidNotification x:Name="InvalidNotificationControl"/>
<Button Content="Clean AppV Cache" Click="Button_Click">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=InvalidNotificationControl, Path=Visibility}" Value="Visible">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
NOTE
InvalidNotification is a custom UserControl
Now, the DataTrigger works fine initially and disable the button since the Usercontrol is visible.
The problem is when I collapse the Usercontrol based on another condition the button stays disable. I found this related answer which states that The properties changed by triggers are automatically reset to their previous value when the triggered condition is no longer satisfied. which is not my case. Why is that ?
EDIT
Thanks to #mm8 which led me to the solution. So if ever you're trying to bind a control on a UserControl's content (inner TextBlock in my case), just add a second trigger at the bottom of your Usercontrol like so,
<UserControl.Style>
<Style TargetType="UserControl">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Notification, Path=Visibility}" Value="Visible">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
where notification would be the name of my TextBlock
Your example works provided that you toggle/set the Visibility property of the InvalidNotification control itself, since it is this property that you bind to.
If you set the Visibility property of some element within the InvalidNotification control, you need to bind to this specific element.
You can't do this using an ElementName binding though because the Button and any element defined in the InvalidNotification control don't belong to the same namescope.

How does this ItemsContainerStyle know about ItemsControl.AlternationIndex

I've been reading through WPF Unleashed and something has confused me about styles.
<Style TargetType="{x:Type Control}" x:Key="altStyle">
<Setter Property="Background" Value="Green"></Setter>
<Setter Property="Foreground" Value="White" ></Setter>
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Foreground" Value="Black" ></Setter>
</Trigger>
</Style.Triggers>
</Style>
So I have a style here which can be applied to anything that derives from control. Assuming that's how the TargetType works. The confusing part for me is how it understands what ItemsControl.AlternationIndex is. I'm defining a style for a control, which is higher up the inheritance chain than ItemsControl. It's seems to break the rules of inheritance.
I then have a simple datagrid which uses this style as its ItemsContainerStyle:
<local:MyGrid AlternationCount="2" ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.Source}" Grid.Row="3" ItemContainerStyle="{StaticResource altStyle}"></local:MyGrid>
(MyGrid derives from DataGrid)
I'm assuming here that the style is applied to each DataRow in the grid, but how on earth is able to resolve this ItemsControl.AlternationIndex as this certainly does not apply to a DataRow does it? and just for clarity on how it looks:
So the question is. How does a style that applies to a datagrid row manage to resolve a property called ItemsControl.AlternationIndex
As an attached property the AlternationIndex can be set on any dependency object and can potentially be inherited. When the style queries the rows it will find that the attached property has been set on them by the creating ItemsControl (the grid control).

C# WPF Xaml: Globally set all text in a view to one color, and all backgrounds to another

Is there a way (using data binding or simply xaml) to set the background of all elements in a view to one color, and all text to another?
I know I can edit each element in the view one by one, but I I'd like to see if this is possible with settings at a global level. Kind of like how everything by default is set to black on white.
I guess what I'm asking is if there is a feature/setting of a WPF application that offers what I'm looking for, and/or what I should search to find an answer online.
My project isn't using anything but what visual studio offers when you create a WPF project, so I can't use a prism or mvvm light approach.
Thanks in advance for your answer!
Globally...or simply XAML...
if there is a feature/setting of a WPF application that offers what I'm looking for
In Application Resource add style like this:
<Style TargetType="Control">
<Setter Property="Background" Value ="Blue"/>
<Setter Property="Foreground" Value ="Red"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Background" Value ="Blue"/>
<Setter Property="Foreground" Value ="Red"/>
</Style>
Based on your target element you want to set background.
When you say "the background of all elements in a view" you should be more specific, If by 'element' you mean UIElement then there is no Background property in UIElement. If it means Control then not all UIElementsderive from Control (e.g. TextBlock) and finally if it means every UIElement derived type defined in your view that have a Background property, then you should add different styles for each type without setting the x:key to the YourView.Resources like this:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Blue" />
</Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Blue" />
</Style>
...
</Window.Resources>
uses controls collection through which you can control all controls in WPF

Bind Path in ResourceDictionary's dependant on VM property

I've created a ResourceDictionary that defines a bunch of System.Windows.Shapes.Path that are used in the ContentPresenter of a Button ControlTemplate.
I'd like to change one of the Paths out based on a ViewModel property. If true the button uses one path from the ResourceDictionary, if false a different one.
Currently I just reference a StaticResource in the xaml to point directly to the path I want displayed.
What's the best way to do this?
You would have to modify the content template of the button in style by referring to the elements from you resource dictionary.
Something like this:
<Button.Style>
<Style TargetType="Button">
<Setter Property="ContentTemplate" Value="{StaticResource cp2}"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource cp1}"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
I have used mouse over property as my trigger to change the content template.
You can use DataTrigger instead of Trigger

Changing listview (MVVM)

I wonder what the best design is to show different content dynamically in a list view.
What I want is (e.g.) depending on a radio button, a list view be filled with items (setting ItemsSource, changing column width, names and the row information itself).
What I'm currently doing is dynamically changing all the list view properties.
I wonder if it would be better to use multiple list views (maybe even as user controls), all on the same window position and hide all non-used.
Also, since I'm using MVVM Light/WPF I want to easily connect it to the XAML code if possible.
If changes in ItemsSource occur in time (or you want change it depending on some conditions like RadioButton's IsChecked property) you need to reflect on change your DataTemplate. The way out is to create DataTrigger which will swap your DataTemplates based on some criteria.
<ListView>
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemsSource" Value="{Binding InitalSource}"/>
<Setter Property="ItemTemplate" Value="{StaticResource InitialDataTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=RadioButton, Path=IsChecked}" Value="True">
<Setter Property="ItemsSource" Value="{Binding AnotherSource}"/>
<Setter Property="ItemTemplate" Value="{StaticResource AnotherDataTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
Whenever condition of DataTrigger stops being true all changes made by Setters vanish, by default, therefore you do not need to add another DataTrigger in case of False value for RadioButton's IsChecked.

Categories

Resources