WPF MVVM changing property under controltemplate programmatically - wpf

I want to change Border Background programmatically depends on WeekType property. It has All, Even and Odd property. Color of my items are in Border's Background property. So when property is All I want them to stay LightGray, but when All or Even I want to change color of this border
XAML:
<Style TargetType="{x:Type summary:SummaryHourUnitItem}">
<Setter Property="StartTime" Value="{Binding StartTime}" />
<Setter Property="EndTime" Value="{Binding EndTime}" />
<Setter Property="WeekType" Value="{Binding WeekType}" />
<Setter Property="SubGroup" Value="{Binding SubGroup}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type summary:SummaryHourUnitItem}">
<Border Width="Auto" BorderThickness="1,1,1,1" BorderBrush="Black" Background="LightGray"
Margin="0" Padding="3,1.5,0,1.5" >
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="9" />
<Setter Property="Foreground" Value="Black" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
SummaryHourUnitItem class:
public class SummaryHourUnitItem : ButtonBase
{
public static readonly DependencyProperty StartTimeProperty =
SummaryTimeSlotPanel.StartTimeProperty.AddOwner(typeof(SummaryHourUnitItem));
public static readonly DependencyProperty EndTimeProperty =
SummaryTimeSlotPanel.EndTimeProperty.AddOwner(typeof(SummaryHourUnitItem));
public static readonly DependencyProperty WeekTypeProperty =
SummaryTimeSlotPanel.WeekTypeProperty.AddOwner(typeof(SummaryHourUnitItem));
public static readonly DependencyProperty SubGroupProperty =
SummaryTimeSlotPanel.SubGroupProperty.AddOwner(typeof(SummaryHourUnitItem));
static SummaryHourUnitItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SummaryHourUnitItem),
new FrameworkPropertyMetadata(typeof(SummaryHourUnitItem)));
}
public bool StartTime
{
get => (bool) GetValue(StartTimeProperty);
set => SetValue(StartTimeProperty, value);
}
public bool EndTime
{
get => (bool) GetValue(EndTimeProperty);
set => SetValue(EndTimeProperty, value);
}
public WeekType WeekType
{
get => (WeekType) GetValue(WeekTypeProperty);
set
{
SetValue(WeekTypeProperty, value);
}
}
public SubGroup SubGroup
{
get => (SubGroup)GetValue(SubGroupProperty);
set => SetValue(SubGroupProperty, value);
}
}
How can I achieve that?

You must to use a Style Trigger.
<Border Width="Auto" BorderThickness="1,1,1,1" BorderBrush="Black" Background="LightGray"
Margin="0" Padding="3,1.5,0,1.5" >
<Border.Style>
<Style BasedOn="{StaticResource {x:Type Border}}" TargetType="{x:Type Border}">
<Setter Property="Background" Value="LightGray" />
<Style.Triggers>
<DataTrigger Binding="{Binding WeekType}" Value="XXXX">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="FontSize" Value="9" />
<Setter Property="Foreground" Value="Black" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
I hope it works for you.

Related

Problem finding AncestorType in ControlTemplate.Triggers

Im trying to make a Clip'ped ToggleButton to spin itself, not its Content, upon Checked event.
I have my AttachedProperty like this:
public class Extensions
{
public static readonly DependencyProperty RotationAngleCheckedProperty = DependencyProperty.RegisterAttached(
"RotationAngleChecked",
typeof(double),
typeof(Extensions),
new PropertyMetadata(0.0));
public static readonly DependencyProperty RotationAngleUncheckedProperty = DependencyProperty.RegisterAttached(
"RotationAngleUnchecked",
typeof(double),
typeof(Extensions),
new PropertyMetadata(0.0));
public static void SetRotationAngleUnchecked(UIElement element, double value)
{
element.SetValue(RotationAngleUncheckedProperty, value);
}
public static double GetRotationAngleUnchecked(UIElement element)
{
return (double)element.GetValue(RotationAngleUncheckedProperty);
}
public static void SetRotationAngleChecked(UIElement element, double value)
{
element.SetValue(RotationAngleCheckedProperty, value);
}
public static double GetRotationAngleChecked(UIElement element)
{
return (double)element.GetValue(RotationAngleCheckedProperty);
}
}
Style for the ToggleButton uses ControlTemplate.Triggers to change RotateTransform.Angle based upon Binding:
<Style x:Key="StyleToggleButtonTriangleS" TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="Clip" Value="{StaticResource GeometryGroupTriangleClipS}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="Border" CornerRadius="3" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center">
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="{StaticResource BrushForegroundBlue}"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="{Binding Path=(ext:Extensions.RotationAngleChecked), RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Background" Value="{StaticResource BrushForegroundMain}"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform Angle="{Binding Path=(ext:Extensions.RotationAngleUnchecked), RelativeSource={RelativeSource AncestorType=ToggleButton}}"/>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ToggleButton used like this:
<!-- xmlns:ext="clr-namespace:MyProject.Extensions" -->
<ToggleButton x:Name="ToggleButtonSpeedSelection" Grid.Row="1" Grid.Column="0" Margin="38"
VerticalAlignment="Top" HorizontalAlignment="Left"
ext:Extensions.RotationAngleChecked="-45"
ext:Extensions.RotationAngleUnchecked="135"
Style="{StaticResource StyleToggleButtonTriangleS}">
</ToggleButton>
Now my issue is that it actually works, ToggleButton spins, but Output window shows an error:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.Primitives.ToggleButton',
AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target
element is 'RotateTransform' (HashCode=54095401); target property is
'Angle' (type 'Double')
How can I fix that error?

WPF: Change Progress-Bar color according its value and View Model property

Ok so i have this property in my View Model:
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged(); // I also try OnPropertyChanged("IsChecked");
}
}
And as you can see this implement INotifyPropertyChanged.
When this property is changing i can see the it notify and get the correct value.
This is my Progress-Bar:
<ProgressBar Name="progressBarColumn"
Value="{Binding Progress, UpdateSourceTrigger=PropertyChanged}"
Width="{Binding Path=Width, ElementName=ProgressCell}"
Style="{StaticResource CustomProgressBar}" />
And my Progress-Bar style:
<Style x:Key="CustomProgressBar" TargetType="ProgressBar">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar" >
<Grid x:Name="Root">
<Border Name="PART_Track"
CornerRadius="0"
Background="{DynamicResource ProgressBackgroundColor}"
BorderBrush="{DynamicResource ProgressBackgroundColor}"
BorderThickness="1" />
<Border Name="PART_Indicator"
CornerRadius="0"
Background="{DynamicResource ProgressBarFillColor}"
BorderBrush="{DynamicResource ProgressBarFillColor}"
BorderThickness="1"
HorizontalAlignment="Left" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Value="100" Binding="{Binding Path=Value, RelativeSource={RelativeSource AncestorType=ProgressBar}}">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Value="False" Binding="{Binding IsChecked}">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter TargetName="Root" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270" />
</Setter.Value>
</Setter>
<Setter TargetName="Root" Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}"/>
<Setter TargetName="Root" Property="Height" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So this is the things that currently not working:
When my Progress-Bar reach the value of 100% i want its Background color will change so i add this DataTrigger:
<DataTrigger Value="100" Binding="{Binding Path=Value, RelativeSource={RelativeSource AncestorType=ProgressBar}}">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
So this not effect at all over my Pregress-Bar color when its reach 100%.
I even also try:
<DataTrigger Value="100" Binding="{Binding Path=Value, RelativeSource={RelativeSource Self}}">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
When my View Model IsChecked property is set to false i want that my Progress-Bar Background color will change to Red - also this is not working.
I try to search many solutions but again and again same results - my Progress-Bar``color not changing at all.
EDIT:
<Style x:Key="CustomProgressBar2" TargetType="ProgressBar">
<Style.Triggers>
<Trigger Value="100" Property="Value">
<Setter Property="Background" Value="{DynamicResource ProgressBarFillCompleteColor}"/>
<Setter Property="Foreground" Value="Transparent"/>
</Trigger>
<DataTrigger Value="False" Binding="{Binding IsChecked}">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar" >
<Grid x:Name="Root">
<Border Name="PART_Track"
CornerRadius="0"
Background="{DynamicResource ProgressBorderBrushColor}"
BorderBrush="{DynamicResource ProgressBorderBrushColor}"
BorderThickness="1" />
<Border Name="PART_Indicator"
CornerRadius="0"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding Background}"
BorderThickness="1"
HorizontalAlignment="Left" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
First of all you need properly implement INotifyPropertyChanged interface.
public event PropertyChangedEventHandler PropertyChanged;
private int _Progress;
private int _Progress;
public int Progress
{
get {
return _Progress;
}
set
{
_Progress = value;
PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
}
}
Then change your style. It will be better to use Style.Triggers instead
<Style.Triggers>
<Trigger Value="100" Property="Value">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="Pink"/>
</Trigger>
<DataTrigger Value="False" Binding="{Binding IsChecked}">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
And modify background binding on your barders inside template
<Border Name="PART_Track"
CornerRadius="0"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding Background}"
BorderThickness="1" />
<Border Name="PART_Indicator"
CornerRadius="0"
Background="{TemplateBinding Foreground}"
BorderBrush="{TemplateBinding Foreground}"
BorderThickness="1"
HorizontalAlignment="Left" />
It will be enough to change Background when progress will be 100, but when progress will be more than 100 ( if you need it) foreground and background will be switched to default value. If you need to change color for progress >= 100 then you need ValueConverter instead of triggers.
EDIT:
Full solution.
<Window x:Class="WpfApplication3.ProgressWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
Title="ProgressWindow" Height="200" Width="400">
<Window.Resources>
<SolidColorBrush x:Key="ProgressBarFillCompleteColor" Color="Green"/>
<SolidColorBrush x:Key="ProgressBorderBrushColor" Color="Gray"/>
<Style x:Key="CustomProgressBar" TargetType="ProgressBar">
<Setter Property="Foreground" Value="AliceBlue"/>
<Setter Property="Background" Value="{DynamicResource ProgressBorderBrushColor}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar" >
<Grid x:Name="Root">
<Border Name="PART_Track"
CornerRadius="0"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding Background}"
BorderThickness="1" />
<Border Name="PART_Indicator"
CornerRadius="0"
Background="{TemplateBinding Foreground}"
BorderBrush="{TemplateBinding Foreground}"
BorderThickness="1"
HorizontalAlignment="Left" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter TargetName="Root" Property="LayoutTransform">
<Setter.Value>
<RotateTransform Angle="270" />
</Setter.Value>
</Setter>
<Setter TargetName="Root" Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}"/>
<Setter TargetName="Root" Property="Height" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Value="False" Binding="{Binding IsChecked}">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<Trigger Value="100" Property="Value">
<Setter Property="Background" Value="Green"/>
<Setter Property="Foreground" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<DockPanel>
<ProgressBar Name="progressBarColumn" DockPanel.Dock="Top" Height="20" Minimum="0" Maximum="100"
Value="{Binding Progress, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource CustomProgressBar}" />
<StackPanel>
<Button Content="Start" Click="Button_Click"/>
<CheckBox Content="Check me" IsChecked="{Binding IsChecked}"/>
</StackPanel>
</DockPanel>
</Window>
View and viewmodel
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for ProgressWindow.xaml
/// </summary>
public partial class ProgressWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ProgressWindow()
{
InitializeComponent();
DataContext = this;
}
private int _Progress;
public int Progress
{
get
{
return _Progress;
}
set
{
_Progress = value;
PropertyChanged(this, new PropertyChangedEventArgs("Progress"));
}
}
private bool _IsChecked;
public bool IsChecked
{
get
{
return _IsChecked;
}
set
{
_IsChecked = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
Progress = 0;
await Task.Run(async () =>
{
var value = 0;
while (value < 100)
{
await Task.Delay(500);
await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
Progress += 10;
}));
}
});
}
}
}

Unable to bind SelectedItem of ListView when using ControlTemplate to define ListViewItem

I've defined a ListViewItem style as shown below
<Style x:Key="BaseContentStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ListViewItem Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
FontWeight="{TemplateBinding FontWeight}">
<ContentPresenter/>
</ListViewItem>
<ControlTemplate.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="#ebebeb"/>
</Trigger>
<DataTrigger Binding="{Binding ListViewPosition}" Value="Conference">
<Setter Property="Content" Value="{Binding Conference}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Background" Value="#192942"/>
</DataTrigger>
<DataTrigger Binding="{Binding ListViewPosition}" Value="Division">
<Setter Property="Content" Value="{Binding Division}"/>
<Setter Property="BorderBrush" Value="LightGray"/>
<Setter Property="BorderThickness" Value="1.5"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
<DataTrigger Binding="{Binding ListViewPosition}" Value="Team">
<Setter Property="ContentTemplate" Value="{StaticResource MyDataTemplate}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I've defined my ListView as follows
<ListView ItemsSource="{Binding AmericanAthleticTeams}"
SelectedItem="{Binding Path=SelectedTeam, Mode=TwoWay}"
ItemContainerStyle="{StaticResource BaseContentStyle}">
This is the DataTemplate used for the ContentTemplate in the Style template
<DataTemplate x:Key="MyDataTemplate" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Logo, Converter={StaticResource imageConverter}}"
RenderOptions.BitmapScalingMode="Fant"
Grid.Column="0"
Height="15"
Width="15"
HorizontalAlignment="Left"
Margin="5 0 0 0"/>
<TextBlock Text="{Binding Name}"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
And here is the ViewModel serving as the View's DataContext
class NCAAFBTeamsViewModel : ViewModelBase
{
public List<NCAAFBTeamViewModel> AmericanAthleticTeams { get; set; }
private NCAAFBTeamViewModel team;
public NCAAFBTeamsViewModel(List<NCAAFBTeamViewModel> americanAthleticTeams)
{
AmericanAthleticTeams = americanAthleticTeams;
}
public NCAAFBTeamViewModel SelectedTeam
{
get { return team; }
set
{
team = value;
OnPropertyChanged();
}
}
}
The problem I'm running into is the binding for the SelectedItem property of the ListView doesn't work as intended. In particular, when I click on one of the displayed ListViewItems, the trigger isn't invoked for the bound SelectedTeam property in the ViewModel. I believe the issue resides with the ControlTemplate of my Style and that perhaps I'm not bubbling up high enough. I've scoured Google and unfortunately wasn't able to discover anything with regards to issues encountered with using a ControlTemplate to define ListViewItems and the impact it has on the SelectedItem property. Any help would be greatly appreciated!

WPF DataGrid Dynamic width cell highlighting

I have an application that's using the WPF Data Grid. That grid presents a set of test results. If the result of a test is out side the min and max allowed values I want to highlight that cell in red. I currently have it working, but am not quite happy with the highlighting.
Here's what it looks like currently:
Here's the desired look (via some image twiddling):
Notice the highlighting in the first example consumes the entire cell width. I'm hoping for the desired example where it only consumes as much space as the widest result with a little margin on both sides. Keep in mind, a result in any one cell could range between 0 and 1920K from one sample to the next. This is an edge case, but I want the highlighted area to grow and shrink as a result.
Just FYI, these results are updated on a configurable timer that triggers anywhere between 10 ms and 10 seconds depending on the user configuration.
Below is the code that generates the first example (sorry for the large amount of code). The interesting bits are DataGridCellStyle, ResultCellStyle and CellTemplate
The XAML
<Window x:Class="StackOverflow_HighlightCell.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StackOverflow_HighlightCell"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<ControlTemplate x:Key="CellTemplate" TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Margin="12,0,0,0" />
</Border>
</ControlTemplate>
<Style x:Key="DataGridCellStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="#707070" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="#CCCCCC" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ResultCellStyle" TargetType="DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="Template" Value="{StaticResource CellTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsResultOutOfBounds,
StringFormat={}{0:0.00}}"
Value="True">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGrid" BasedOn="{x:Null}">
<Setter Property="RowBackground" Value="#707070" />
<Setter Property="AutoGenerateColumns" Value="False" />
<Setter Property="IsReadOnly" Value="True" />
<Setter Property="Background" Value="#666666" />
<Setter Property="GridLinesVisibility" Value="None" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CanUserSortColumns" Value="False" />
<Setter Property="HeadersVisibility" Value="Column" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Foreground" Value="#CCCCCC" />
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed" />
<Setter Property="CellStyle" Value="{StaticResource DataGridCellStyle}" />
</Style>
<!-- A left justified DataGridTextColumn -->
<Style x:Key="ElementLeftJustified">
<Setter Property="TextBlock.HorizontalAlignment" Value="Left" />
<Setter Property="TextBlock.Margin" Value="15,0,5,0" />
</Style>
<!-- A right justified DataGridTextColumn -->
<Style x:Key="ElementRightJustified">
<Setter Property="TextBlock.HorizontalAlignment" Value="Right" />
<Setter Property="TextBlock.Margin" Value="0,0,5,0" />
</Style>
</Window.Resources>
<Grid Background="#FF666666">
<Border Margin="20" >
<DataGrid x:Name="_testSummaryGrid"
ItemsSource="{Binding TestResults}">
<DataGrid.Columns>
<DataGridTextColumn
MinWidth="75" Header="Test"
Binding="{Binding TestName}"
ElementStyle="{StaticResource ElementLeftJustified}" />
<DataGridTextColumn
MinWidth="75" Header="Min"
Binding="{Binding Min, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}" />
<DataGridTextColumn
MinWidth="75" Header="Result"
Binding="{Binding Result, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}"
CellStyle="{StaticResource ResultCellStyle}" />
<DataGridTextColumn
MinWidth="75" Header="Max"
Binding="{Binding Max, StringFormat={}{0:0.00}}"
ElementStyle="{StaticResource ElementRightJustified}" />
</DataGrid.Columns>
</DataGrid>
</Border>
</Grid>
</Window>
The View Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace StackOverflow_HighlightCell
{
public class ResultsViewModel : ViewModelBase
{
public ResultsViewModel()
{
TestResults = new ObservableCollection<TestResult>
{
{ new TestResult { TestGroup = "Circle",TestName = "Radius",
Min = 100, Max = 153, Result = 150} },
{ new TestResult { TestGroup = "Circle", TestName = "Min Radius",
Min = 0, Max = 90, Result = 97.59 } },
// And so on ...
};
}
public ObservableCollection<TestResult> TestResults { get; set; }
}
public class TestResult : ViewModelBase
{
public string TestGroup { get; set; }
public string TestName { get; set; }
public double Result { get; set; }
public double Min { get; set; }
public double Max { get; set; }
public bool IsResultOutOfBounds { get { return !(Result >= Min && Result <= Max); } }
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
I think this should do what you want:
<ControlTemplate x:Key="ResultCellTemplate" TargetType="{x:Type DataGridCell}">
<Border Background="{TemplateBinding Background}">
<Grid
Margin="12,0,0,0"
HorizontalAlignment="Right"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Result" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" x:Name="ContentPresenterBorder">
<ContentPresenter
/>
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--
That stringformat you had will have been ignored because the target
type isn't string.
-->
<DataTrigger Binding="{Binding IsResultOutOfBounds}" Value="True">
<Setter TargetName="ContentPresenterBorder" Property="Background" Value="Red" />
<Setter TargetName="ContentPresenterBorder" Property="TextElement.Foreground" Value="White" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="ResultCellStyle" TargetType="DataGridCell"
BasedOn="{StaticResource DataGridCellStyle}">
<Setter Property="Template" Value="{StaticResource ResultCellTemplate}" />
</Style>
...but don't forget to set Grid.IsSharedSizeScope="True" on the DataGrid. That works with SharedSizeGroup="Result" on the ColumnDefinition to ensure that any grid column named "Result" anywhere within the DataGrid will be dynamically sized to the same width.
<DataGrid
x:Name="_testSummaryGrid"
ItemsSource="{Binding TestResults}"
Grid.IsSharedSizeScope="True"
>
Excellent example by the way. Would've been better trimmed down to just two DataGrid columns, but I pasted it in, pressed F5, and it worked. And the important part wasn't hard to find.

WPF ComboboxItems with button

I have created my own combobox style along with stylign combobox items. The dropdown needs to display some items with buttons and others without my items are created dynamically as follows:
ComboBoxItem typeItem = new ComboBoxItem();
typeItem.Content = "Test";
typeItem.Tag = i;
MyCombobox.Items.Add(typeItem);
My question is how do I apply this style to some items and not others, my two comboboxitems are bellow?
<Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{DynamicResource ResourceKey=MinimumIteractSizeDips}" />
<Setter Property="MinWidth" Value="{DynamicResource ResourceKey=MinimumIteractSizeDips}" />
<Setter Property="Foreground" Value="Black"/>
</Style>
<Style x:Key="MyComboBoxItem" TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="MinHeight" Value="{DynamicResource ResourceKey=MinimumIteractSizeDips}" />
<Setter Property="MinWidth" Value="{DynamicResource ResourceKey=MinimumIteractSizeDips}" />
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Grid>
<Label Content="{TemplateBinding Content}" Foreground="Black" Background="White" Height="26" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0"/>
<Button Grid.Column="2" Grid.Row="0" Content="Settings" Height="23" HorizontalAlignment="Left" Margin="2" Name="PositionSettings" VerticalAlignment="Center" Width="57"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Check the StyleSelector class.
public class MyStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
var element = container as FrameworkElement;
var myVm = item as MyViewModel;
if(myVm.EditMode)
return element.FindResource("TemplateWithButtons") as DataTemplate
return element.FindResource("TemplateNormal") as DataTemplate
}
}
and in xaml, define a resource for your selector
<MyStyleSelector x:Key="myStyleSelector"/>
and apply that to your combobox.
<ComboBox StyleSelector="{StaticResource myStyleSelector}"/>
btw. x:Key with the same type as TargetType is redundant.
Final Note: You should not override the style for that. What you want is DataTemplate and the DataTemplate selector. Also you should not bind the Content(type object) to a labels text (type string), a ContentPresenter is the way to go here.

Resources