Setting focus rectangle to red / transparent and ensuring visible - c#

I have a related question with a proposed answer here but my issue is more complex and I think it is best to start a new question. I hope that is OK.
I think it is important to visually show you what I am trying to do first. Here is an overview of the editor:
Down the left are several controls that are used to display the chosen students (and assistants where applicable). The editor supports 3 classes for the students. Thus, I have the students for each class on a TabItem control.
Example:
As you can see, it has a TabControl with 3 items (Main Hall, Class 1 and Class 2). On each of those tab items they then have their own TabControl with the 3 student items.
You see those small icons next to the text boxes? If you click those then they affect a combo that is over on the right side of the editor:
So when you click a small icon, it automatically selects the right entry in the combo on the right. Then, as you select a student from the grid it gets populated into the right part of the editor:
The Issue
When the user manually changes the combo over on the right, it then needs to set a focus rectangle over on th left hand side correctly. So when a item is selected it needs to:
Set the tab item as selected (if needed).
Set the border brush of the rectangle to red instead of transparent.
I have got it to set the rectangle colours as required but I have not yet managed to do that using behaviours (as per the related question). In addition, when I try fiddling with the IsSelected property of the 'TabItem' objects I find that they sometimes kind of collapse on screen. I have commented out my code.
I am sorry for the wordier question but I will get asked for clarification so I am trying to cover it up front.
So:
There are two main TabControl objects:
The first is the bible reading items:
<TabControl Grid.Row="1" Grid.ColumnSpan="2">
<TabItem x:Name="tabTFGWReadingMainHall" Header="Main Hall" DataContext="{Binding Meeting}">
<TabItem.Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsEnabled" Value="False"/>
<!--<Setter Property="IsSelected" Value="False"/>-->
<Style.Triggers>
<DataTrigger Binding="{Binding NumberClasses, ConverterParameter=1, Converter={StaticResource IsEqualOrGreaterThanConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
<!--<DataTrigger Binding="{Binding SelectedItem, ElementName=comboActiveStudentAssignmentType}" Value="{x:Static StudentInfoEnums:StudentAssignmentType.BibleReadingMain}">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>-->
</Style.Triggers>
</Style>
</TabItem.Style>
<Border x:Name="borderBibleReadingMain" BorderThickness="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=comboActiveStudentAssignmentType}" Value="{x:Static StudentInfoEnums:StudentAssignmentType.BibleReadingMain}">
<Setter Property="BorderBrush" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding NumberClasses, ConverterParameter=1, Converter={StaticResource IsEqualOrGreaterThanConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label Content="Student:"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="textBibleReadingMain" Grid.Column="0" Margin="2" IsEnabled="False"
Text="{Binding BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="buttonBibleReadingMain" Grid.Column="1" Background="Transparent"
DataContext="{Binding DataContext, ElementName=oclmEditor}"
Command="{Binding ApplicationCommand}" CommandParameter="BibleReadingMain">
<Image Source="Images/AssignmentTypeBibleReading16.png" Margin="2"/>
</Button>
</Grid>
<Label Content="Study:"/>
<ComboBox DataContext="{Binding DataContext, ElementName=oclmEditor}"
ItemsSource="{Binding ReadingStudyPointsList}"
ItemContainerStyle="{StaticResource StudyPointComboBoxStyle}"
ItemTemplate="{StaticResource StudyPointComboItem}"
Validation.ErrorTemplate="{StaticResource StudyPointValidationTemplate}"
Tag="{Binding Meeting.BibleReadingMainName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding Meeting.BibleReadingMainStudyPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Number">
<!--<ComboBox.Style>
<Style
TargetType="ComboBox"
BasedOn="{StaticResource {x:Type ComboBox}}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter
Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
<ComboBox.SelectedValue>
<Binding Path="Meeting.BibleReadingMainStudyPoint"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<ValidationRules:StudyPointValidationRule BibleReading="True"/>
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedValue>-->
</ComboBox>
</StackPanel>
</Border>
</TabItem>
<TabItem x:Name="tabTFGWReadingClass1" Header="Class 1" DataContext="{Binding Meeting}">
<TabItem.Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="IsSelected" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding NumberClasses, ConverterParameter=2, Converter={StaticResource IsEqualOrGreaterThanConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding SelectedItem, ElementName=comboActiveStudentAssignmentType}" Value="{x:Static StudentInfoEnums:StudentAssignmentType.BibleReadingClass1}">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
<Border x:Name="borderBibleReadingClass1" BorderThickness="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=comboActiveStudentAssignmentType}" Value="{x:Static StudentInfoEnums:StudentAssignmentType.BibleReadingClass1}">
<Setter Property="BorderBrush" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding NumberClasses, ConverterParameter=2, Converter={StaticResource IsEqualOrGreaterThanConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label Content="Student:"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="textBibleReadingClass1" Grid.Column="0" Margin="2" IsEnabled="False"
Text="{Binding BibleReadingClass1Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="buttonBibleReadingClass1" Grid.Column="1" Background="Transparent"
DataContext="{Binding DataContext, ElementName=oclmEditor}"
Command="{Binding ApplicationCommand}" CommandParameter="BibleReadingClass1">
<Image Source="Images/AssignmentTypeBibleReading16.png" Margin="2"/>
</Button>
</Grid>
<Label Content="Study:"/>
<ComboBox DataContext="{Binding DataContext, ElementName=oclmEditor}"
ItemsSource="{Binding ReadingStudyPointsList}"
ItemContainerStyle="{StaticResource StudyPointComboBoxStyle}"
ItemTemplate="{StaticResource StudyPointComboItem}"
Tag="{Binding Meeting.BibleReadingClass1Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding Meeting.BibleReadingClass1StudyPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Number"/>
</StackPanel>
</Border>
</TabItem>
<TabItem x:Name="tabTFGWReadingClass2" Header="Class 2" DataContext="{Binding Meeting}">
<TabItem.Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="IsSelected" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding NumberClasses, ConverterParameter=3, Converter={StaticResource IsEqualOrGreaterThanConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding SelectedItem, ElementName=comboActiveStudentAssignmentType}" Value="{x:Static StudentInfoEnums:StudentAssignmentType.BibleReadingClass2}">
<Setter Property="IsSelected" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
<Border x:Name="borderBibleReadingClass2" BorderThickness="5">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem, ElementName=comboActiveStudentAssignmentType}" Value="{x:Static StudentInfoEnums:StudentAssignmentType.BibleReadingClass2}">
<Setter Property="BorderBrush" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<StackPanel.Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding NumberClasses, ConverterParameter=3, Converter={StaticResource IsEqualOrGreaterThanConverter}}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label Content="Student:"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="textBibleReadingClass2" Grid.Column="0" Margin="2" IsEnabled="False"
Text="{Binding BibleReadingClass2Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="buttonBibleReadingClass2" Grid.Column="1" Background="Transparent"
DataContext="{Binding DataContext, ElementName=oclmEditor}"
Command="{Binding ApplicationCommand}" CommandParameter="BibleReadingClass2">
<Image Source="Images/AssignmentTypeBibleReading16.png" Margin="2"/>
</Button>
</Grid>
<Label Content="Study:"/>
<ComboBox DataContext="{Binding DataContext, ElementName=oclmEditor}"
ItemsSource="{Binding ReadingStudyPointsList}"
ItemContainerStyle="{StaticResource StudyPointComboBoxStyle}"
ItemTemplate="{StaticResource StudyPointComboItem}"
Tag="{Binding Meeting.BibleReadingClass2Name, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding Meeting.BibleReadingClass2StudyPoint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Number"/>
</StackPanel>
</Border>
</TabItem>
</TabControl>
The second is the tabControlAYFMStudents control but I have to keep my question body limited. So I have not shown it.
I don't expect anyone to read through all my code. So if you think I should remove this question then please advise me so. Or if you think I can ask it somewhere else.
I would just like to be able to get the rectangle border set as transparent (or red) based on the current combo item selected. And, ensure it is visible.
Phew! Thank you.

I decided during the night that using the code behind would be the simplest solution for my issues. I added a handler for the SelectionChanged event:
private void comboActiveStudentAssignmentType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
List<Border> borders = new List<Border>();
// The list of border (focus rectangles) matches the combo of assignment types
borders.Add(borderBibleReadingMain);
borders.Add(borderBibleReadingClass1);
borders.Add(borderBibleReadingClass2);
borders.Add(borderMainHallStudent1);
borders.Add(borderMainHallAssistant1);
borders.Add(borderMainHallStudent2);
borders.Add(borderMainHallAssistant2);
borders.Add(borderMainHallStudent3);
borders.Add(borderMainHallAssistant3);
borders.Add(borderClass1Student1);
borders.Add(borderClass1Assistant1);
borders.Add(borderClass1Student2);
borders.Add(borderClass1Assistant2);
borders.Add(borderClass1Student3);
borders.Add(borderClass1Assistant3);
borders.Add(borderClass2Student1);
borders.Add(borderClass2Assistant1);
borders.Add(borderClass2Student2);
borders.Add(borderClass2Assistant2);
borders.Add(borderClass2Student3);
borders.Add(borderClass2Assistant3);
// Loop through the borders
for(int iBorder = 0; iBorder < borders.Count; iBorder++)
{
// Is this border the active student assignment?
if (comboActiveStudentAssignmentType.SelectedIndex == iBorder)
{
// Yes, so use a red brush for the background
borders[iBorder].BorderBrush = Brushes.Red;
// Now we must ensure the correct tab item is visible
if(iBorder >= 0 && iBorder <= 2)
{
expandTFGW.IsExpanded = true;
if (iBorder == 0)
tabTFGWReadingMainHall.IsSelected = true;
else if (iBorder == 1)
tabTFGWReadingClass1.IsSelected = true;
else if (iBorder == 2)
tabTFGWReadingClass2.IsSelected = true;
}
else if (iBorder >= 3 && iBorder <= 8)
{
expandAYFM.IsExpanded = true;
tabAYFMStudentsMainHall.IsSelected = true;
if (iBorder == 3 || iBorder == 4)
tabMainHallItem1.IsSelected = true;
else if (iBorder == 5 || iBorder == 6)
tabMainHallItem2.IsSelected = true;
else if (iBorder == 7 || iBorder == 8)
tabMainHallItem3.IsSelected = true;
}
else if (iBorder >= 9 && iBorder <= 14)
{
expandAYFM.IsExpanded = true;
tabAYFMStudentsClass1.IsSelected = true;
if (iBorder == 9 || iBorder == 10)
tabClass1Item1.IsSelected = true;
else if (iBorder == 11 || iBorder == 12)
tabClass1Item2.IsSelected = true;
else if (iBorder == 13 || iBorder == 14)
tabClass1Item3.IsSelected = true;
}
else if (iBorder >= 15)
{
expandAYFM.IsExpanded = true;
tabAYFMStudentsClass2.IsSelected = true;
if (iBorder == 15 || iBorder == 16)
tabClass2Item1.IsSelected = true;
else if (iBorder == 17 || iBorder == 18)
tabClass2Item2.IsSelected = true;
else if (iBorder == 19 || iBorder == 20)
tabClass2Item3.IsSelected = true;
}
borders[iBorder].BringIntoView();
}
else
{
// No, so set the background to transparent so we can't see it.
borders[iBorder].BorderBrush = Brushes.Transparent;
}
}
}
}
This works fine. As you change the combo the correct Border is set to red and it's associated TabItem is set to Selected.
In addition, I set the associated Expander to expanded. This means I am 95% where I want to be. I can remove all of the DataTriggers from the XAML as it is all now managed in this one handler.
However, this call doesn't seem to work:
borders[iBorder].BringIntoView();
So even though the above will set the border properties and ensure it's tab item is selected, I can't ensure the control is visible if it is off-screen.
Update
This answer explains how tobring the rectangle into view:
var localBorderIndex = iBorder; // copy to avoid closure of loop variable
Dispatcher.InvokeAsync(() => borders[localBorderIndex].BringIntoView(),
DispatcherPriority.Background);

Related

C#/WPF Main Window freezes when loading data into datagrid

guys I am currently working on a existing WPF Application build with Prism build and .NET 4.6. I have added a new Page which contains a few User Controls. In one of the User Controls I need to load a csv with a few hundred rows(always under thousand) into a datagrid.
Every time I load the data into the datagrid the whole Application freezes for a few seconds. I have tried to do the loading async with the dispatcher but the application freezes while loading. I have also tried to do it with a task but then I always got an exception("only UI Thread is allowed to change the observable collection")
Below my current async implementation which did not work, any help or idea is highly appreciated.
Thank you
Product XAML
<UserControl x:Class="Module.UserControls.Products"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Module.UserControls"
xmlns:mvvm="http://prismlibrary.com/"
mvvm:ViewModelLocator.AutoWireViewModel="true"
mc:Ignorable="d"
d:DesignHeight="650" d:DesignWidth="800" Background="{DynamicResource Module.Background}" MaxHeight="1500">
<UserControl.Resources>
<Style TargetType="DataGridCell">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="TextBox">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="{DynamicResource Module.Textbox.Foreground}" />
<Setter Property="Margin" Value="5,5,5,5" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="Button">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Background" Value="{DynamicResource Module.Button.Background}" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style TargetType="ComboBox">
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
</UserControl.Resources>
<Grid>
<Grid Margin="0,20,0,0" Background="{DynamicResource Module.Block.Background}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0">
<TextBlock Text="Products" Style="{DynamicResource Module.H2}" />
</Label>
<ScrollViewer Grid.Column="0" Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="0,10,0,10" OverridesDefaultStyle="True" >
<ScrollViewer.Resources>
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background" Value="LightGray"/>
</Style>
</ScrollViewer.Resources>
<DockPanel HorizontalAlignment="Stretch">
<DataGrid AutoGenerateColumns = "False" IsReadOnly="False" ItemsSource="{Binding ProductCollection, UpdateSourceTrigger=PropertyChanged, IsAsync=True, Mode=TwoWay}" HorizontalAlignment="Center"
Width="Auto" HorizontalContentAlignment="Center">
<DataGrid.Resources>
<Style TargetType="DataGridCell">
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="16" />
<Setter Property="BorderBrush" Value="White" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header = "Position" Binding="{Binding Path=InProductPos, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="120" />
<DataGridTextColumn Header = "Layout Position" Binding="{Binding Path = InProductLayout, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="120"/>
<DataGridTextColumn Header = "Name" Binding="{Binding Path=InProductDescription, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="300"/>
<DataGridTextColumn Header = "Product Quantity" Binding="{Binding Path=InProductPieces, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="120"/>
<DataGridTextColumn Header = "Class" Binding="{Binding Path=InProductClass, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="120"/>
<DataGridTextColumn Header = "Product Part ID" Binding="{Binding Path=InProductPartID, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="120"/>
<DataGridTextColumn Header = "Number" Binding="{Binding Path=InProductNumber, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="120"/>
<DataGridTextColumn Header = "Part list Name" Binding="{Binding Path=InProductPartlistName, UpdateSourceTrigger=PropertyChanged}" Width="Auto" MinWidth="300"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>
Button XAML
<UserControl x:Class="Module.UserControls.ButtonRow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:resx="clr-namespace:Module.Properties"
xmlns:local="clr-namespace:Module.UserControls"
xmlns:mvvm="http://prismlibrary.com/"
mvvm:ViewModelLocator.AutoWireViewModel="true"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<Style TargetType="DataGridCell">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style TargetType="TextBox">
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="{DynamicResource Module.Textbox.Foreground}" />
<Setter Property="Margin" Value="5,5,5,5" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="StyleButtonWhite" TargetType="Button">
<Setter Property="Foreground" Value="White" />
</Style>
<Style x:Key="StyleButtonWhiteWhite" TargetType="Button">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{x:Null}" />
</Style>
<Style TargetType="Button">
<Setter Property="FontSize" Value="16" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Background" Value="{DynamicResource Module.Button.Background}" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style TargetType="ComboBox">
<Setter Property="Margin" Value="5,5,5,5" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,10">
<Button
Margin="12,0,12,0"
Command="{Binding LoadProductsCommand}"
Width="101" Height="40"
Style="{StaticResource StyleButtonWhite}"
Background="{DynamicResource Module.Button.Background}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Import Products" Foreground="White" />
</StackPanel>
</Button>
</StackPanel>
</Grid>
</UserControl>
Part of the ViewModel
public DelegateCommand LoadProductsCommand => new DelegateCommand(LoadProducts, () => true);
private ObservableCollection<Product> _productCollection;
public ObservableCollection<Products> ProductCollection
{
get => _productCollection;
set
{
_productCollection = value;
OnPropertyChanged();
}
}
public async void LoadProducts()
{
if (Dispatcher.CurrentDispatcher.CheckAccess())
{
await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
ProductCollection.AddRange(FileImport.ImportProducts());
});
}
}
Class of the collection
public string InProductPos
{
get => _inProductPos;
set
{
_inProductPos = value;
OnPropertyChanged();
}
}
public string InProductLayout
{
get => _inProductLayout;
set
{
_inProductLayout = value;
OnPropertyChanged();
}
}
public string InProductDescription
{
get => _inProductDescription;
set
{
_inProductDescription = value;
OnPropertyChanged();
}
}
public int InProductPieces
{
get => _inProductPieces;
set
{
_inProductPieces = value;
OnPropertyChanged();
}
}
public int InProductClass
{
get => _inProductClass;
set
{
_inProductClass = value;
OnPropertyChanged();
}
}
public int InProductPartID
{
get => _inProductPartID;
set
{
_inProductPartID = value;
OnPropertyChanged();
}
}
public string InProductNumber
{
get => _inProductNumber;
set
{
_inProductNumber = value;
OnPropertyChanged();
}
}
public string InProductPartlistName
{
get => _inProductPartlistName;
set
{
_inProductPartlistName = value;
OnPropertyChanged();
}
}
You may try something like shown below. It loads the Product collection in a background thread, and only adds them to the ObservableCollection in the UI thread.
public async Task LoadProducts()
{
var products = await Task.Run(() => FileImport.ImportProducts());
ProductCollection.AddRange(products);
}

WPF: Change the CornerRadius for only the last item in a ListBox

I've created a ListBox with round corners. I've also added a bottom border to all ListBoxItems except for the last one.
The ListBoxItems, however, have normal, square corners, so when hovering over or selecting the first or last items, you can see an overlap between the round ListBox corner and the square ListBoxItem corners.
I can't set CornerRadius the same way I set BorderThickness - I think that's because CornerRadius is a property of the Border property(?).
I can force ALL ListBoxItems to have all round corners which fixes the overlap, but then ALL ListBoxItems have round underlines & selections- which I'd rather not have. I only want those round corners on the bottom of the last item (and eventually top of the first item)
I would like to use a similar sort of trigger for setting CornerRadius that I do for setting BrushThickness.
Is there a way to set the corner radius of just the last item in a ListBox? (and eventually the first item)
In my test, I'm using the MaterialDesignTheme package from NuGet. Because that's non-standard, I'll add all my code here (also note: I'm new to WPF, so feel free to critique anything that looks off):
App.xaml:
<Application . . .
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml: (Note, if you uncomment the commented section, it styles all ListBoxItems like I would want only the last ListBoxItem styled)
<Window . . .
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}">
<Window.Resources>
<local:IsLastItemInContainerConverter x:Key="IsLastItemInContainerConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" Margin="5">
<ListBox x:Name="GameListBox"
BorderBrush="{DynamicResource MaterialDesignDivider}"
BorderThickness="1">
<ListBox.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">
<!--<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="0 0 10 10"/>
</Style>
</Style.Resources>-->
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource IsLastItemInContainerConverter}}" Value="False">
<Setter Property="BorderThickness" Value="0 0 0 1" />
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignDivider}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource IsLastItemInContainerConverter}}" Value="True">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem>
<TextBlock> Plain
</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock> Old
</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock> ListBox
</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock> Full of junk
</TextBlock>
</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
</Window>
...and in MainWindow.xaml.cs, I have defined the converter to find the last item:
public class IsLastItemInContainerConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
DependencyObject item = (DependencyObject)value;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
return ic.ItemContainerGenerator.IndexFromContainer(item)
== ic.Items.Count - 1;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
First of all, please understand that the MaterialDesignTheme package was not used in my code.
Items.cs
Instead of using Converter, I added a model class.
public class Items
{
public string Name { get; set; }
public bool IsFirst { get; set; }
public bool IsLast { get; set; }
}
App.xaml
I defined styles of ListBoxItem, ListBox like below.
<Style TargetType="{x:Type ListBoxItem}" x:Key="listboxitem">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="border"
Background="White"
BorderBrush="#AAAAAA"
BorderThickness="1 1 1 0"
CornerRadius="0">
<TextBlock Text="{Binding Name}" Foreground="Black" FontSize="13" FontWeight="Normal"
Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsFirst}" Value="True">
<Setter TargetName="border" Property="CornerRadius" Value="10 10 0 0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsLast}" Value="True">
<Setter TargetName="border" Property="CornerRadius" Value="0 0 10 10"/>
<Setter TargetName="border" Property="BorderThickness" Value="1 1 1 1"/>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#666666"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="border" Property="Background" Value="#666666"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListBox}" x:Key="listbox">
<Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="200"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource listboxitem}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border Background="Transparent"
BorderBrush="#AAAAAA"
BorderThickness="0 0 0 0"
CornerRadius="10">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml
<ListBox x:Name="lbx" Style="{StaticResource listbox}"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lbx.ItemsSource = GetItems();
}
private List<Items> GetItems()
{
List<Items> source = new List<Items>();
source.Add(new Items { Name = "Plain", IsFirst = true });
source.Add(new Items { Name = "Old" });
source.Add(new Items { Name = "ListBox" });
source.Add(new Items { Name = "Full of junk", IsLast = true }); ;
return source;
}
}
It will be shown like this..

How bind validation to RadioButton WPF

I have some problem with Radiobutton binding to show validation.
I create two RadioButtons
<StackPanel Grid.Column="2" Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Top" Margin="0,13,0,0">
<RadioButton IsChecked="{Binding EndUser, Mode=TwoWay}" Content="End User" />
<RadioButton IsChecked="{Binding AppDeveloper, Mode=TwoWay}" Margin="15,0,0,0" Content="App Developer"/>
</StackPanel>
for my logic I should take 1 name from 2.
I write the logic
[Required]
public string Role
{
get => role;
set
{
Set(ref role, value);
RaisePropertyChanged("EndUser");
RaisePropertyChanged("AppDeveloper");
}
}
public bool EndUser
{
get => Role.Contains("EndUser");
set => Role = "EndUser";
}
public bool AppDeveloper
{
get => Role.Contains("AppDeveloper");
set => Role = "AppDeveloper";
}
the problem is how show [Required] in a form, and if i choose 1 of them, the validation will be true (validation work right if it's need i show the validation code)
I find this Validation Rule for Radio buttons wpf ,but this example doesn't work for me, broke all logic(doesn't send to me anything) and doesn't mark my field.
how write the
<Binding.ValidationRules>
<DataErrorValidationRule />
</Binding.ValidationRules>
for radiobutton fields like in TextBox and mark it red?
my button which disable if the fields is not valid
<Button x:Name="SignInButton" Command="{Binding SignInCommand}" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="2" Content="Sign In" >
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="MidnightBlue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" CornerRadius="10">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Height="23" Margin="24,5,24,4"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=Password}" Value="False"/>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=Login}" Value="False"/>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=Role}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF280895"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
in MultiDataTrigger i write the rule, if Validation.HasError for the field the button is disable
You could for example put a Border around the RadioButtons and use a Style with a DataTrigger that binds to the Role property:
<Border Grid.Column="2" Grid.Row="1" BorderBrush="Red" Margin="0,13,0,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<RadioButton IsChecked="{Binding EndUser, Mode=TwoWay}" Content="End User" />
<RadioButton IsChecked="{Binding AppDeveloper, Mode=TwoWay}" Margin="15,0,0,0" Content="App Developer"/>
</StackPanel>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding Role}" Value="{x:Null}">
<Setter Property="BorderThickness" Value="1" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
You should then make the setter of the Role private and make sure to raise the PropertyChanged event for the involved properties:
private string role;
[Required]
public string Role
{
get => role;
private set
{
role = value;
RaisePropertyChanged("Role");
}
}
public bool EndUser
{
get => Role == "EndUser";
set
{
Role = "EndUser";
RaisePropertyChanged("EndUser");
RaisePropertyChanged("AppDeveloper");
}
}
public bool AppDeveloper
{
get => Role == "AppDeveloper";
set
{
Role = "AppDeveloper";
RaisePropertyChanged("AppDeveloper");
RaisePropertyChanged("EndUser");
}
}

DataAnnotations OrderBy Length Errors

This method returns property errors (dataannotations). What I want to do is to return those errors sorted by length (ValidationMessage.Length). The list is sorted, but the problem is in the view, it shows them disordered. Can you please help me? Thank you
public void Validate(object currentInstance, string propertyName)
{
if (_validationErrors.ContainsKey(propertyName))
{
_validationErrors.Remove(propertyName);
}
var propertyInfo = currentInstance.GetType().GetProperty(propertyName);
var propertyValue = propertyInfo.GetValue(currentInstance, null);
var validationAttributes = propertyInfo.GetCustomAttributes(true).OfType<ValidationAttribute>();
var validationErrors =
validationAttributes
.Select(
x => new CustomErrorType
{
ValidationMessage = x.FormatErrorMessage(string.Empty),
Severity = x.IsValid(propertyValue) ? Severity.SUCCESS : Severity.ERROR
}
).ToList().OrderBy(x => x.ValidationMessage.Length);;
if (validationErrors.Any(x => x.Severity == Severity.ERROR))
{
_validationErrors.Add(propertyName, validationErrors);
}
}
.xaml
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground"
Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.WARNING}">
<Setter Property="Foreground"
Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.SUCCESS}">
<Setter Property="Foreground"
Value="DarkGreen" />
<Setter Property="FontWeight"
Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
You could bind to a CollectionViewSource that sorts the validation messages:
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
<StackPanel.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="ErrorContent" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</StackPanel.Resources>
<AdornedElementPlaceholder x:Name="textBox" />
<ItemsControl ItemsSource="{Binding Source={StaticResource cvs}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.WARNING}">
<Setter Property="Foreground"
Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding ErrorContent.Severity}"
Value="{x:Static customEnums:Severity.SUCCESS}">
<Setter Property="Foreground"
Value="DarkGreen" />
<Setter Property="FontWeight"
Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>

How to change custom control's background color based on its property

I have simple custom control that shows a message to user (something like browser's Info bar).
I have added a Boolean Dependency Property that indicate an error message. If flag is set the background color of control should be red otherwise yellow.
Here is style for the control(in Themes\Generic.xaml):
<Style TargetType="{x:Type local:InfoBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:InfoBar}">
<ControlTemplate.Triggers>
<Trigger Property="IsError" Value="True" >
<Setter Property="Background" Value="LightPink" />
</Trigger>
<Trigger Property="IsError" Value="False" >
<Setter Property="Background" Value="LightYellow" />
</Trigger>
</ControlTemplate.Triggers>
<Grid Margin="4,0,4,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{TemplateBinding Message}" Padding="5" FontWeight="Normal" TextWrapping="Wrap" Grid.Column="0"/>
<Button x:Name="PART_CloseButton" Grid.Column="1" VerticalAlignment="Top" >
<Button.Template>
<ControlTemplate>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="Transparent">
<Image Height="16" Width="16" Source="/QOffice.Common.Controls;component/Images/icons/Close.png" />
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is The control itself:
[TemplatePart(Name = PART_CloseButton, Type = typeof(ButtonBase))]
public class InfoBar : Control
{
private const string PART_CloseButton = "PART_CloseButton";
static InfoBar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(InfoBar), new FrameworkPropertyMetadata(typeof(InfoBar)));
}
#region CloseButton
private ButtonBase _closeButton;
/// <summary>
/// Gets or sets the CloseButton template part.
/// </summary>
private ButtonBase CloseButton
{
get
{
return _closeButton;
}
set
{
if (_closeButton != null)
{
_closeButton.Click -= OnButtonClick;
}
_closeButton = value;
if (_closeButton != null)
{
_closeButton.Click += OnButtonClick;
}
}
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
this.Visibility = System.Windows.Visibility.Collapsed;
}
#endregion
public override void OnApplyTemplate()
{
CloseButton = GetTemplateChild(PART_CloseButton) as ButtonBase;
}
#region DependencyProperty Message of InfoBar
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(InfoBar),
new UIPropertyMetadata());
#endregion
#region DependencyProperty IsError of InfoBar
public bool IsError
{
get { return (bool)GetValue(IsErrorProperty); }
set { SetValue(IsErrorProperty, value); }
}
public static readonly DependencyProperty IsErrorProperty =
DependencyProperty.Register("IsError", typeof(bool), typeof(InfoBar),
new UIPropertyMetadata());
#endregion
}
As you can see I have defined a property IsError and a trigger to set the background of the control.
But the background is always transparent. Other than that the control if functional.
What is wrong?
It seems that your Custom Control is not setting Background Color properly even if I add Background color manually. I am not sure why this is, hopefully someone can elaborate. I did fix your issue though by changing the color of the Grid in your style using:
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:InfoBar}}, Path=IsError}" Value="True">
<Setter Property="Background" Value="LightPink" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:InfoBar}}, Path=IsError}" Value="False">
<Setter Property="Background" Value="LightYellow" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
This triggers the background color of the grid based on the IsError value in your InfoBar control.
You give your Control a Background but no child is using it. Two possible solutions:
TemplateBinding
<Grid Margin="4,0,4,0" Background="{TemplateBinding Background}">
DataTrigger with TargetName
<ControlTemplate TargetType="{x:Type local:InfoBar}">
<Grid Name="grid" Margin="4,0,4,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
...
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsError" Value="True" >
<Setter TargetName="grid" Property="Background" Value="LightPink" />
</Trigger>
<Trigger Property="IsError" Value="False" >
<Setter TargetName="grid" Property="Background" Value="LightYellow" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
In this solution you have to change the order: ControlTemplate.Triggers after Grid declaration.
Try this (Since IsError is DP in your InfoBar and not property of your ControlTemplate)
<DataTrigger Property="{Binding IsError, RelativeSource={RelativeSource
Mode=TemplatedParent}" Value="True">
<Setter Property="Background" Value="LightPink" />
</DataTrigger>
<DataTrigger Property="{Binding IsError, RelativeSource={RelativeSource
Mode=TemplatedParent}" Value="False">
<Setter Property="Background" Value="LightYellow" />
</DataTrigger>

Categories