Change style of a button at runtime in wpf with multibinding - c#

I am trying to change the style of a button at runtime depending on a bool property. In addition to that, I am using the mahapps.metro framework.
My Code:
The view:
<UserControl.Resources>
<Style TargetType="{x:Type Button}" x:Key="firstStyle">
<Setter Property= "Style" Value="{StaticResource SquareButtonStyle}"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="secondStyle">
<Setter Property="Style" Value="{StaticResource AccentedSquareButtonStyle}"/>
</Style>
<conv:ButtonMultiConverter x:Key="styleConvert"/>
</UserControl.Resources>
The button:
<Button>
<Button.Style>
<MultiBinding Converter="{StaticResource styleConvert}">
<Binding Path="ValueFilter" />
<Binding Source="{StaticResource firstStyle}" />
<Binding Source="{StaticResource secondStyle}" />
</MultiBinding>
</Button.Style>
<iconPacks:PackIconModern Width="30" Height="15" Kind="edit"/>
</Button>
The converter:
public class ButtonMultiConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var firstStyle = values[1] as Style;
var secondStyle = values[2] as Style;
if (values[0] is bool)
return (bool)values[0] ? secondStyle : firstStyle;
return firstStyle;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
With this code I get always the following exception:
"style object is not allowed to affect the style property of the object to which it applies"
What should I change on the xaml code to prevent this exception.

The firstStyle and secondStyle resources are invalid. As the error message says, a Style applied to an element can not have a Setter that sets the Style property of that element.
They are however also redundant. Use SquareButtonStyle and AccentedSquareButtonStyle directly:
<Button.Style>
<MultiBinding Converter="{StaticResource styleConvert}">
<Binding Path="ValueFilter"/>
<Binding Source="{StaticResource SquareButtonStyle}"/>
<Binding Source="{StaticResource AccentedSquareButtonStyle}"/>
</MultiBinding>
</Button.Style>
If for any reason you really need firstStyle and secondStyle, declare them like this:
<Style x:Key="firstStyle" TargetType="Button"
BasedOn="{StaticResource SquareButtonStyle}"/>
<Style x:Key="secondStyle" TargetType="Button"
BasedOn="{StaticResource AccentedSquareButtonStyle}"/>

Related

Changing Background Color of DataGridCell via IValueConverter

I am using a WPF DataGrid with dynamic columns. The colums and binding are generated in code behind which is working fine.
Now I want to change the background color of the DataGrid cell depending on data
Therefore I created a IValueConverter
public class ValueToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is DataGridCell dgc)
{
var content = dgc.Content;
var header = dgc.Column.Header;
var index = dgc.Column.DisplayIndex;
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
Using it that way:
<UserControl.Resources>
<converters:ValueToBrushConverter x:Key="ValueToBrushConverter"/>
<Style x:Key="CellStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ValueToBrushConverter}}" />
</Style>
</UserControl.Resources>
<DataGrid Grid.Row="2" x:Name="PartsGrid" AutoGenerateColumns="False" IsReadOnly="True" CanUserSortColumns="True"
BorderBrush="Black" Margin="20 10 0 10"
CellStyle="{StaticResource CellStyle}"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.CacheLengthUnit="Item"
EnableColumnVirtualization = "True"
EnableRowVirtualization = "True"
>
</DataGrid>
Unfortunately I cannot get the showen Value of the gridcell inside the converter. Header and DisplayIndex are the, but content is null.
So whats the proper way to get the value of the gridcell inside the IValueConverter?
Using a Multibinding Converter solved it:
<Style x:Key="CellStyle" TargetType="DataGridCell">
<Setter Property="Background" >
<Setter.Value>
<MultiBinding Converter="{StaticResource ValueToBrushConverterMulti}" >
<Binding Path="." RelativeSource="{RelativeSource Self}"/>
<Binding Path="." RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=DataGridColumn}" />
</MultiBinding>
</Setter.Value>
</Setter>

Change predefined style inside a control in xaml

I have the following style in App.xaml:
<Style TargetType="{x:Type Button}">
<Style.Resources>
<DataTemplate x:Key="Unpressed">
<Image Stretch="Uniform" Source="Img/button1.png"/>
</DataTemplate>
<DataTemplate x:Key="Pressed">
<Image Stretch="Uniform" Source="Img/button1_press.png"/>
</DataTemplate>
</Style.Resources>
<Setter Property="ContentTemplate" Value="{StaticResource Unpressed}"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource Pressed}"/>
</Trigger>
</Style.Triggers>
</Style>
I want to change the data template from my button, something like this:
<Button x:Name="Button2">
<Style>
<Style.Resources>
<DataTemplate x:Key="Unpressed">
<Image Stretch="Uniform" Source="../Img/button2.png"/>
</DataTemplate>
<DataTemplate x:Key="Pressed">
<Image Stretch="Uniform" Source="../Img/button2_press.png"/>
</DataTemplate>
</Style.Resources>
</Style>
</Button>
So basically, all my buttons have the same style but each button have an unique image, I need to change the DataTemplate of the style individually for each button
This is my proposed solution:
For the style:
<Application.Resources>
<local:ImgToDisplayConverter x:Key="ImgToDisplayConverter"/>
<local:ImgPressedToDisplayConverter x:Key="ImgPressedToDisplayConverter"/>
<Style TargetType="Image" x:Key="PressedButtonImageStyle">
<Setter Property="Source">
<Setter.Value>
<MultiBinding Converter="{StaticResource ImgToDisplayConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource AncestorType=Button}"/>
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsPressed, RelativeSource={RelativeSource AncestorType=Button}}" Value="true">
<Setter Property="Source">
<Setter.Value>
<MultiBinding Converter="{StaticResource ImgPressedToDisplayConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource AncestorType=Button}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Application.Resources>
For the Control:
<Button Tag="button1" Width="100" Height="100" HorizontalAlignment="Left">
<ContentControl>
<Image Stretch="Uniform" Style="{StaticResource PressedButtonImageStyle}" IsHitTestVisible="False"/>
</ContentControl>
</Button>
The Converters:
class ImgToDisplayConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string image = values[0].ToString();
string resourceName = String.Format("pack://application:,,,/{0}.png", image);
return new BitmapImage(new Uri(resourceName));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
class ImgPressedToDisplayConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string image = values[0].ToString();
string resourceName = String.Format("pack://application:,,,/{0}_pressed.png", image);
return new BitmapImage(new Uri(resourceName));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can change the code depending on your needs.
I use this simple code and it works well(if you want to release the button from being pressed ,set timer).I hope this will help you..
xaml code
<Button Name="male" Height="318" Width="352" Click="male_Click" Margin="236,120,1332,642">
<Button.Background>
<ImageBrush ImageSource="pack://application:,,,/Imagesrc/logotype/USER_MALE.png" ></ImageBrush>
</Button.Background>
</Button>
pressed code
public void male_Click(object sender, RoutedEventArgs e)
{
System.Windows.Controls.Image img = new System.Windows.Controls.Image();
img.Source = new BitmapImage(new Uri(#"pack://application:,,,/maindocket;component/Imagesrc/logotype/USER_MALE_SELECTED.png"));
male.Content = img;
}

Cant set Style in Trigger wpf

i want to change style when toggle button checked
<ToggleButton.Style>
<Style TargetType="ToggleButton" BasedOn="{StaticResource ToggleButtonPrimary}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ButtonNude, Path=IsChecked}" Value="True">
<Setter Property="Style" Value="{StaticResource ToggleButtonDanger}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
but my code not work and app crash
As suggested by #clemens , the right way would be to have template and apply in controlTemplate with TargetName
There is one more (may be ugly) way:
On view:
<StackPanel>
<StackPanel.Resources>
<local:Myconverter x:Key="MyConverter" />
<Style TargetType="ToggleButton" x:Key="ToggleButtonPrimary">
<Setter Property="Background" Value="blue" />
</Style>
<Style TargetType="ToggleButton" x:Key="ToggleButtonDanger">
<Setter Property="Background" Value="Red" />
</Style>
</StackPanel.Resources>
<CheckBox Margin="20" x:Name="chk" />
<ToggleButton Width="100" Height="100" >
<ToggleButton.Style>
<MultiBinding Converter="{StaticResource MyConverter}">
<MultiBinding.Bindings>
<Binding ElementName="chk" Path="IsChecked"/>
<Binding RelativeSource="{RelativeSource AncestorType=StackPanel}"/>
</MultiBinding.Bindings>
</MultiBinding>
</ToggleButton.Style>
</ToggleButton>
</StackPanel>
Converter:
public class Myconverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)values[0])
{
return (values[1] as FrameworkElement).Resources["ToggleButtonDanger"];
}
else
return (values[1] as FrameworkElement).Resources["ToggleButtonPrimary"];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Hope that helps.

Best way to show a different Decimal Precision when TextBox is focused or not?

I have control, that is used by others. It has a TexBox that is binding to a decimal value. And since the Precision can vary, I am using a DependencyProperty and MultiBinding to specify it.
When the TextBox is not focused the number should be displayed with the specified Precision, but when it is focused the full number should be displayed.
Example:
Precision: 2
User Input: 29.333
TextBox not focused should show: 29.33
TextBox focused should show: 29.333
I have accomplished this by using a IMultibindingConverter, and the IsFocused property. But I don't know if this is the best approach.
My TexBox is defined like this
<UserControl.Resources>
<conv:ValuePrecisionConverter x:Key="ValuePrecisionConverter" />
</UserControl.Resources>
<TextBox x:Name="myTextBox">
<TextBox.Text>
<MultiBinding Converter="{StaticResource ValuePrecisionConverter}" Mode="TwoWay"
NotifyOnValidationError="true">
<Binding ElementName="parent" UpdateSourceTrigger="PropertyChanged" Path="Value" Mode="TwoWay" />
<Binding ElementName="parent" Path="Precision"/>
<Binding ElementName="parent" Path="AdditionalFormatting"/>
<Binding ElementName="myTextBox" Path="IsFocused" Mode="OneWay"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
My ValuePrecisionConver is defined like this:
public class ValuePrecisionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double? value = null;
converter = null;
value = System.Convert.ToDouble(values[0]);
precision = System.Convert.ToInt32(values[1]);
//Other code with 3rd parameter
if (values.Count() > 3)
{
bool isFocused = System.Convert.ToBoolean(values[3]);
if (isFocused)
return value.ToString();
}
/*Here I do the formating with the given precision*/
return formattedValue;
}
}
Is this the best way to accomplish what I need? Is it ok to use the IsFocused property like this?
Try with thisss.
<TextBox x:Name="myTextBox">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Text" Value="{Binding Value, StringFormat=n2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Text" Value="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
I would prefer you to go for Event Trigger with PreviewGotKeyboardFocus and PreviewLostKeyboardFocus... Set the value with a converter.. No need of MuiltBinding here...

My IconverterClass changing values to all my columns

I have a table with different columns I am showing two in this code :
<DataGridTextColumn Header="Allocated" Binding="{Binding Allocated}" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="Current_Phase" />
<Binding Path="Status" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Op10}" Header="WIP OP10" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="Current_Phase" />
<Binding Path="Status" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
The problem I have My IConverterClass is returning a background colour and is putting green for both, but my current phase is "op30"(my parameters value) but still is changing the colour of column op10. I am totally lost, pls helppppp.
My converter Class
object IMultiValueConverter.Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string type = values[0].ToString();
string status = values[1].ToString();
if (type == "op10" && status == "10")
{
return Brushes.Green;
}
else if ( type == "op30" && status == "30")
{
return Brushes.Green;
}
else
{
return DependencyProperty.UnsetValue;
}
}
Please Helppp, I dont know what to do.
I don't understand your problem:
Your converter returns green for "op10" and "op30" and you bind that converter to both columns and therefore it changes both columns to green. If you don't want to change the column op10, remove the binding to the converter.

Categories