How bind validation to RadioButton WPF - c#

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");
}
}

Related

RadioButton with custom image and text change at runtime

I have a custom RadioButton style with an Image and 2 TextBlocks.
<Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}" >
<Setter Property="Background" Value="#32353B" />
<Setter Property="Margin" Value="10,5"/>
<Setter Property="Height" Value="45" />
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="1" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<Image Source="{TemplateBinding Button.Tag}" HorizontalAlignment="Left" Stretch="Uniform" Width="45" IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE" FontSize="18" FontFamily="Arial">Strona</TextBlock>
<TextBlock Foreground="#52555C" FontSize="12" FontFamily="Arial"> Login</TextBlock>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#282B2E"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E" Opacity="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
When I create a new button at runtime, setting the Style and I want to change the Image and the text for every single RadioButton. Right now I am thinking about using a Tag for that.
RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
radioButton.Style = (Style)Resources["ToggleButton_Chose"];
radioButton.Tag = new BitmapImage(new Uri("https://www.google.com/favicon.ico"));
Is there any other method to set this?
I will have around 100 of the RadioButtons and any of them should get different images and texts.
Custom Control
If you want to create a ToggleButton that requires additional (bindable) properties, you could create a custom control with dependency properties for the image and texts based on the ToggleButton type. Create a new type AdvancedToggleButton that derives from ToggleButton.
public class AdvancedToggleButton : ToggleButton
{
static AdvancedToggleButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AdvancedToggleButton), new FrameworkPropertyMetadata(typeof(AdvancedToggleButton)));
}
public ImageSource ImageSource
{
get => (ImageSource) GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value);
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
nameof(ImageSource), typeof(ImageSource), typeof(AdvancedToggleButton));
public string FirstText
{
get => (string) GetValue(FirstTextProperty);
set => SetValue(FirstTextProperty, value);
}
public static readonly DependencyProperty FirstTextProperty = DependencyProperty.Register(
nameof(FirstText), typeof(string), typeof(AdvancedToggleButton));
public string SecondText
{
get => (string) GetValue(SecondTextProperty);
set => SetValue(SecondTextProperty, value);
}
public static readonly DependencyProperty SecondTextProperty = DependencyProperty.Register(
nameof(SecondText), typeof(string), typeof(AdvancedToggleButton));
}
Then you can create a default implicit style (by omitting the x:Key) in your application resources or another resource dictionary in scope, so the style is applied automatically.
<Style TargetType="{x:Type local:AdvancedToggleButton}">
<Setter Property="Background"
Value="#32353B" />
<Setter Property="Margin"
Value="10,5" />
<Setter Property="Height"
Value="45" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Padding"
Value="1" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AdvancedToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left"
Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center" />
<Image Source="{TemplateBinding ImageSource}"
HorizontalAlignment="Left"
Stretch="Uniform"
Width="45"
IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE"
FontSize="18"
FontFamily="Arial"
Text="{TemplateBinding FirstText}"/>
<TextBlock Foreground="#52555C"
FontSize="12"
FontFamily="Arial"
Text="{TemplateBinding SecondText}"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Background"
Value="#282B2E" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E"
Opacity="0.5" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Note, that I removed the duplicate setter for HorizontalAlignment. The Image binds its Source to the ImageSource property and the TextBlocks bind to FirstText and SecondText respectively. You can either define the AdvancedToggleButton in XAML or code.
<local:AdvancedToggleButton ImageSource="\Resources/Check.jpg"
FirstText="Strona"
SecondText="Login"/>
RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
radioButton.ImageSource = new BitmapImage(new Uri("https://www.google.com/favicon.ico"));
radioButton.FirstText = "Strona";
radioButton.SecondText = "Login"
Useful resources on custom controls:
Control authoring overview
Dependency properties overview
Attached Properties
An alternative without creating a custom control is to create a set of attached properties.
public static class ToggleButtonProperties
{
public static ImageSource GetImageSource(DependencyObject dependencyObject)
{
return (ImageSource) dependencyObject.GetValue(ImageSourceProperty);
}
public static void SetImageSource(DependencyObject dependencyObject, ImageSource value)
{
dependencyObject.SetValue(ImageSourceProperty, value);
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.RegisterAttached(
"ImageSource", typeof(ImageSource), typeof(ToggleButtonProperties));
public static string GetFirstText(DependencyObject dependencyObject)
{
return (string) dependencyObject.GetValue(FirstTextProperty);
}
public static void SetFirstText(DependencyObject dependencyObject, string value)
{
dependencyObject.SetValue(FirstTextProperty, value);
}
public static readonly DependencyProperty FirstTextProperty = DependencyProperty.RegisterAttached(
"FirstText", typeof(string), typeof(ToggleButtonProperties));
public static string GetSecondText(DependencyObject dependencyObject)
{
return (string) dependencyObject.GetValue(SecondTextProperty);
}
public static void SetSecondText(DependencyObject dependencyObject, string value)
{
dependencyObject.SetValue(SecondTextProperty, value);
}
public static readonly DependencyProperty SecondTextProperty = DependencyProperty.RegisterAttached(
"SecondText", typeof(string), typeof(ToggleButtonProperties));
}
These properties can be bound in the control template using parentheses, which is the binding syntax for attached properties and RelativeSource to the parent ToggleButton.
<Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}">
<Setter Property="Background"
Value="#32353B" />
<Setter Property="Margin"
Value="10,5" />
<Setter Property="Height"
Value="45" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Padding"
Value="1" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left"
Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center" />
<Image Source="{Binding (local:ToggleButtonProperties.ImageSource), RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Left"
Stretch="Uniform"
Width="45"
IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE"
FontSize="18"
FontFamily="Arial"
Text="{Binding (local:ToggleButtonProperties.FirstText), RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Foreground="#52555C"
FontSize="12"
FontFamily="Arial"
Text="{Binding (local:ToggleButtonProperties.SecondText), RelativeSource={RelativeSource TemplatedParent}}"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Background"
Value="#282B2E" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E"
Opacity="0.5" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Assigning or binding the attached properties is done via the static class.
<ToggleButton Style="{StaticResource ToggleButton_Chose}"
local:ToggleButtonProperties.ImageSource="\Resources/Check.jpg"
local:ToggleButtonProperties.FirstText="Strona"
local:ToggleButtonProperties.SecondText="Login"/>
RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
ToggleButtonProperties.SetImageSource(radioButton, new BitmapImage(new Uri("https://www.google.com/favicon.ico")));
ToggleButtonProperties.SetFirstText(radioButton, "Strona");
ToggleButtonProperties.SetSecondText(radioButton, "Login");
Useful resources on attached properties:
Attached Properties Overview
But is ther any other method to sett this? I will have around 100 of the RadioButtons and any of there should get other image and text.
Create a simple data type:
public class ButtonContent
{
public string Strona { get; set; }
public string Login { get; set; }
public object ImageSource { get; set; }
}
This type will be used to pass the button data into the context.
Therefore, we will slightly change the style:
<Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}" >
<Setter Property="Background" Value="#32353B" />
<Setter Property="Margin" Value="10,5"/>
<Setter Property="Height" Value="45" />
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="1" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<Image Source="{Binding ImageSource}"
HorizontalAlignment="Left" Stretch="Uniform" Width="45" IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE" FontSize="18" FontFamily="Arial"
Text="{Binding Strona}"/>
<TextBlock Foreground="#52555C" FontSize="12" FontFamily="Arial"
Text="{Binding Login}"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#282B2E"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E" Opacity="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
For a set of buttons use ItemsControl:
<DataTemplate x:Key="itemTemplate" DataType="{x:Type local:ButtonContent}">
<ToggleButton Style="{DynamicResource ToggleButton_Chose}"/>
</DataTemplate>
<ItemsControl x:Name="itemsControl"
ItemTemplate="{DynamicResource itemTemplate}">
</ItemsControl>
Create an observable collection with data and pass it to the ItemsControl source:
public partial class MainWindow : Window
{
private readonly ObservableCollection<ButtonContent> ButtonContents
= new ObservableCollection<ButtonContent>();
public MainWindow()
{
InitializeComponent();
itemsControl.ItemsSource = ButtonContents;
ButtonContents.Add(new ButtonContent() { Strona = "Strona1", Login = "Login1", ImageSource = "Image/block.png" });
ButtonContents.Add(new ButtonContent() { Strona = "Strona2", Login = "Login2", ImageSource = "Image/block.png" });
ButtonContents.Add(new ButtonContent() { Strona = "Strona3", Login = "Login3", ImageSource = "Image/block.png" });
ButtonContents.Add(new ButtonContent() { Strona = "Strona4", Login = "Login4", ImageSource = "Image/block.png" });
}
}

Doesn't disable the button if the textbox invalid

I have the email text block with validation
<TextBox x:Name="email" Style="{StaticResource emaliStyle}" Grid.Column="2" Grid.Row="2">
<TextBox.Text>
<Binding Mode="TwoWay" Path="Email" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<DataErrorValidationRule ValidatesOnTargetUpdated="False"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Style TargetType="TextBox" x:Key="emaliStyle">
<Setter Property="Width" Value="220"/>
<Setter Property="Height" Value="30"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="Padding" Value="6,1,1,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="10"
SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter x:Name="LoginValidation" Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Bottom" Foreground="Maroon" FontSize="8pt"
Text="{Binding ElementName=email, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
</TextBlock>
<AdornedElementPlaceholder Name="email" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="DarkRed" />
<Setter Property="BorderThickness" Value="2" />
</Trigger>
</Style.Triggers>
</Style>
the validation work and show me the message which I write in viewModel( it doesn't matter)
next step it blocks the button if the field is invalid.
my button and style for it
<Button x:Name="SignInButton"
Style="{StaticResource SignInButton}"
Command="{Binding SignInCommand}"
CommandParameter="{Binding ElementName=This}"
Content="Sign In"/>
<Style TargetType="Button" x:Key="SignInButton">
<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="20" Margin="26,10"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers >
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Validation.HasError), ElementName=email}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="True"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LimeGreen"/>
</Trigger>
</Style.Triggers>
</Style>
I write the Multitrigger(later add other textbox fields) to disable my button, but it isn't work.
I try write all with element name Email doesn't help. What i miss?
because the error message will show and the field border mark red, bu isn't disable
Your BlockButton() method should return false whenever you want to disable the Button that is bound to the SignInCommand. There is no reason to set the IsEnabled property in a style when you bind to a command.
For the status of the command and the Button to get refeshed, you should raise the CanExecuteChanged event for the command whenever the validation status changes. Most ICommand implementations include a RaiseCanExecuteChanged() method or similar that lets you do this:
private bool _isEmailValid;
private bool BlockButton()
{
return _isEmailValid && IsAppDeveloper == false && IsEndUser == false;
}
public string this[string columnName]
{
get
{
string error = String.Empty;
switch (columnName)
{
case "Email":
string s = ValidateModelProperty(Email, columnName);
_isEmailValid = string.IsNullOrEmpty(s);
SignInCommand.RaiseCanExecuteChanged();
return s;
}
return error;
}
}
private string ValidateModelProperty(object value, string propertyName)
{
ICollection<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(this, null, null) { MemberName = propertyName };
if (!Validator.TryValidateProperty(value, validationContext, validationResults))
foreach (ValidationResult validationResult in validationResults)
return validationResult.ErrorMessage;
return null;
}

After change wpf button IsMouseOver trigger color not work

this is my button code
public class MetroButton : Button
{
public static readonly DependencyProperty MoseOverBrushProperty;
public static readonly DependencyProperty PressedBrushProperty;
public MetroButton():base()
{
var resource = new ResourceDictionary
{
Source = new Uri("/Parking.Component.Ui;component/Styles/ButtonMetro.xaml",
UriKind.RelativeOrAbsolute)
};
Style = resource["ButtonMetro"] as Style;
//SetResourceReference(StyleProperty, Style);
}
static MetroButton()
{
MoseOverBrushProperty = DependencyProperty.Register("MoseOverBrush", typeof(Brush), typeof(MetroButton));
PressedBrushProperty = DependencyProperty.Register("PressedBrush", typeof(Brush), typeof(MetroButton));
}
public Brush MoseOverBrush
{
get { return (Brush)base.GetValue(MoseOverBrushProperty); }
set { base.SetValue(MoseOverBrushProperty, value); }
}
public Brush PressedBrush
{
get { return (Brush)base.GetValue(PressedBrushProperty); }
set { base.SetValue(PressedBrushProperty, value); }
}
}
and I use this style for my button
<Style x:Key="ButtonMetro" TargetType="{ x:Type LochalUI:MetroButton}">
<Setter Property="Foreground" Value="White" />
<Setter Property="MoseOverBrush" Value="#FF3F62FD"/>
<Setter Property="PressedBrush" Value="#FF000099"/>
<Setter Property="Background" Value="#FF6B9AFF"/>
<Setter Property="FontSize" Value="15" />
<Setter Property="FontFamily" Value="B Yekan" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type LochalUI:MetroButton}">
<Border x:Name="border" CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,0,0,0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding Path=PressedBrush , RelativeSource={RelativeSource Self}}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but the problem is there when i put color for my button background like below code:
<UI:MetroButton HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="132" Height="107" Background="#FF09CD00" >
<Grid>
<Label Content="تنظیمات" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5,5,5,12" Foreground="White" Margin="5"/>
</Grid>
</UI:MetroButton>
the IsMouseOver changer color and IsPressed Triggers not work.
(I don't want use static resource in my setters)
Changing other properties has no effect just changing background made this problem.
I found the answer problem was in 2 place:
first one when we use trigger in
<ControlTemplate.Triggers/>
you have sure you set your setter set property on the currect object
and the secend one is in the binding we have change
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource Self}}"
to
Value="{Binding Path=MoseOverBrush , RelativeSource={RelativeSource TemplatedParent}}"
because MoseOverBrush is parrent property not the ControlTemplate property

Cannot change the style of the selected item in a ListBox

I´m trying to change the focused/selected item of a ListBox. My application is based on this article.
At the moment I´m trying to set the ListBoxItem style via data templates:
<DataTemplate x:Key="ItemTemplate">
<TextBlock Text="{Binding}"
Foreground="Black"
FontFamily="Segoe UI"
FontSize="22"
HorizontalAlignment="Left"
Padding="15,10,0,0"
/>
</DataTemplate>
<DataTemplate x:Key="SelectedTemplate">
<TextBlock Text="{Binding}"
Foreground="Red"
FontFamily="Segoe UI"
FontSize="30"
HorizontalAlignment="Left"
Padding="15,10,0,0"
/>
</DataTemplate>
My idea was to switch between those templates using a trigger:
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
The ListBox looks like this:
<ListBox x:Name="valuesItemsCtrl"
BorderThickness="0"
ItemContainerStyle="{StaticResource ContainerStyle}"
Background="Transparent"
Tag="{Binding }">
<ListBox.AlternationCount>
<Binding>
<Binding.Path>Values.Count</Binding.Path>
</Binding>
</ListBox.AlternationCount>
<ListBox.ItemsSource>
<Binding>
<Binding.Path>Values</Binding.Path>
</Binding>
</ListBox.ItemsSource>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
At the end I add the template to another ListBox:
<ListBox x:Name="tumblersCtrl"
BorderThickness="0"
Background="Transparent"
ItemsSource="{Binding Tumblers, ElementName=thisCtrl}"
ItemTemplate="{StaticResource TumblerTemplate}">
</ListBox>
Thanks for any help or hint!
Use ItemTemplate and data triggers:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
Foreground="Black"
FontFamily="Segoe UI"
FontSize="22"
HorizontalAlignment="Left"
Padding="15,10,0,0"
x:Name="tbName"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="tbName" Property="Foreground" Value="Red"/>
<Setter TargetName="tbName" Property="FontSize" Value="30"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where bound data is a collection of:
public class ViewModel : ViewModelBase
{
public String Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
}
private String name;
public Boolean IsSelected
{
get { return isSelected; }
set
{
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
private Boolean isSelected;
}
Window code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new[]
{
new ViewModel { Name = "John", IsSelected = true },
new ViewModel { Name = "Mary" },
new ViewModel { Name = "Pater" },
new ViewModel { Name = "Jane" },
new ViewModel { Name = "James" },
};
}
}
If you want to change the templates use DataTemplateSelector.
Dismiss your ContainerStyle and instead set the ListBox.ItemsTemplateSelector to your custom datatemplateselector as static resource.
You can find a detailed example in this link.
EDIT :
According to your comment you don't need DataTemplate just set these two properties in the Trigger:
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="22" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="15,10,0,0" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="30" />
</Trigger>
</Style.Triggers>
</Style>

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