Set specific style on user control based on custom DP [duplicate] - c#

I have a requirement to change a button's style based on a value in the data. It looks like a StyleSelector would work perfectly but there doesn't seem to be a way to set one for a button.
Is there a way to set a button style dynamically from data? Maybe even a pure XAML approach?

A more general way to accomplish the same thing:
SomeView.xaml
<UserControl>
<UserControl.Resources>
<converters:BooleanToStyleConverter x:Key="MyButtonStyleConverter"
TrueStyle="{StaticResource AmazingButtonStyle}"
FalseStyle="{StaticResource BoringButtonStyle}"/>
</UserControl.Resources>
<Grid>
<Button Style={Binding IsAmazingButton, Converter={StaticResource MyButtonStyleConverter}}/>
</Grid>
</UserControl>
BooleanToStyleConverter.cs
public class BooleanToStyleConverter : IValueConverter
{
public Style TrueStyle { get; set; }
public Style FalseStyle { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool && (bool) value)
{
return TrueStyle;
}
return FalseStyle;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This converter works in any view with any kind of control using whatever style you choose as long as you are binding to a Boolean property in your ViewModel to control the style switching. Easy to adapt it to other binding requirements though. This works in a DataTemplate as well.

You could place your Button Styles in a Resource Dictionary and bind the Style for the Button and use a Converter
ButtonStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Background" Value="Green"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</ResourceDictionary>
Then for the Button that has this requirement you bind Style to the property of interest
<Button ...
Style="{Binding Path=MyDataProperty,
Converter={StaticResource ButtonStyleConverter}}"/>
And in the Converter you load the ButtonStyles Resource Dictionary and return the desired Style based on the value
public class ButtonStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Uri resourceLocater = new Uri("/YourNameSpace;component/ButtonStyles.xaml", System.UriKind.Relative);
ResourceDictionary resourceDictionary = (ResourceDictionary)Application.LoadComponent(resourceLocater);
if (value.ToString() == "Some Value")
{
return resourceDictionary["ButtonStyle1"] as Style;
}
return resourceDictionary["ButtonStyle2"] as Style;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

WPF Multibuilding 2 Values to the same Property

I have a table, and when the user would like to mark a row. he can click on button and mark it.
so I wrote a converter for the property, that if its true it return color(yellow)
and if false it return white,
however it Deletes the default style when user select a row in the table.
I was thinking of using Multibuilding once for mark, and one for selected.
however I am not understand what the syntax should be in WPF.
attaching the code I wrote,
will appreciate a code examples.
WPF:
<Style TargetType="syncfusion:GridCell" x:Key="ComCell">
<Setter Property="Foreground" Value="{Binding COMPort , Converter={StaticResource CVconverters } }" />
<Setter Property="Background" Value="{Binding isBookMarked, Converter={StaticResource BookMarkConverter}}"></Setter>
</Style>
C#:
public class BookMarkConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string valStr = value.ToString();
if (valStr == "True")
{
return Brushes.Yellow;
}
else
{
}
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Instead of returning Brushes.White from the converter, you could return Binding.DoNothing from the converter if you simply don't want to change the background.
You could also base your Style on the default one:
<Style TargetType="syncfusion:GridCell" x:Key="ComCell"
BasedOn="{StaticResource {x:Type syncfusion:GridCell}}">

Is it possible to bind a property to itself through a converter in WPF?

I need to change value of a property on a trigger, and I need to do it according to the existing value with some parameter. So, I've tried to use xaml like this:
<Style.Triggers>
<Trigger ...>
<Setter Property="BorderBrush"
Value="{Binding Path=BorderBrush,
RelativeSource={RelativeSource Self},
Converter={StaticResource MyConverter},
ConverterParameter=ParamValue}" />
</Trigger>
</Style.Triggers>
And converter is simple:
[ValueConversion(typeof(SolidColorBrush), typeof(SolidColorBrush))]
public class MyConverter : IValueConverter
{
public object Convert(object existingValue, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//change the brush
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
But with such code existingValue parameter in MyConverter.Convert() is null.
So, is there a way to apply converter to a property using its own value? I can't pass it as ConverterParameter because that is used for another data needed by the converter.

Binding to a Control Template

I need to bind a button to a Control Template. The XAML looks something like this:
Button Template="{Binding Status, Converter={StaticResource StatustoTemplate}}"
The converter (StatustoTemplate) runs fine as the status (which is an integer, but happy for it to be a string) changes:
public class StatustoTemplate : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value==1)
{
return ControlTemplateName1;
}
if (value==2)
{
return ControlTemplateName2;
}
}
}
Now, in what format can I send back ControlTemplate1, or ControlTemplate2? Let us assume that ControlTemplate1 and ControlTemplate2 are valid Control Templates as defined in the XAML. I now that it needs to return a ControlTemplate - but how to set it up?
my preferred approach is to use a Style with DataTriggers to switch Template, without converters
<Style TargetType="Button" x:Key="StatusButton"> <!--set BasedOn if there is a base Style-->
<Style.Triggers>
<DataTrigger Binding="{Binding Status}" Value="1">
<Setter Property="Template" Value="{StaticResource ControlTemplateName1}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="2">
<Setter Property="Template" Value="{StaticResource ControlTemplateName2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
and then apply this Style:
<Button Style="{StaticResource StatusButton}"/>
It is not easy for converter to find resource defined in XAML. I usually define one control template which has both definitions and switch them using Visibility. The code is just a short sample.
XAML
<Window>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<ResourceDictionary>
<local:MyConverter x:Key="MyConverter"/>
<ControlTemplate x:Key="template">
<Grid>
<Grid Visibility="{Binding Status, Converter={StaticResource MyConverter}, ConverterParameter=1}">
<TextBox Text="1"/>
</Grid>
<Grid Visibility="{Binding Status, Converter={StaticResource MyConverter}, ConverterParameter=2}">
<TextBox Text="2"/>
</Grid>
</Grid>
</ControlTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Template="{StaticResource template}"/>
</Grid>
</Window>
Converter
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((string)parameter == "1") ? Visibility.Visible : Visibility.Collapsed;
}
// ConvertBack
}
I have a MarkupExtension that might work for you. It is getting xaml defined Resources by ResourceKey.
First: abstract class StyleRefExtension:
public abstract class StyleRefExtension : MarkupExtension
{
protected static ResourceDictionary RD;
public string ResourceKey { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return ProvideValue();
}
public object ProvideValue()
{
if (RD == null)
throw new Exception(
#"You should define RD before usage.
Please make it in static constructor of extending class!");
return RD[ResourceKey];
}
}
Second: class implementation as StyleRefExt
public class StyleRefExt : StyleRefExtension
{
static StyleRefExt()
{
RD = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/##YOUR_ASSEMBLYNAME##;component/Styles/##YOUR_RESOURCE_DICTIONARY_FILE##.xaml")
};
}
}
Just replace ##YOUR_ASSEMBLYNAME## with the name of your assembly and ##YOUR_RESOURCE_DICTIONARY_FILE## with the filename of your ResourceDictionary (that is located in folder Styles).
Your Converter should look like this:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value==1) {
StyleRefExt sr = new StyleRefExt {ResourceKey = "ControlTemplateName1"};
return sr.ProvideValue();
}
if (value==2) {
StyleRefExt sr = new StyleRefExt {ResourceKey = "ControlTemplateName2"};
return sr.ProvideValue();
}
}

Is there a way to bind the Enabled property of a button to a checkbox's Checked property?

I want to bind IsEnabled of Button in WPF as follows:
WPF Code:
<Button Content="TestButton" IsEnabled="{Binding ??}" />
C# code:
private MyObjectClass _Checked;
public MyObjectClass Checked
{
get { return _Checked; }
set
{
_Checked = value;
RaisePropertyChanged("Checked");
}
}
In WPF code above, I want the button to be enabled only when Checked object is not null. I know one way is to have a bool property in C# code which tells me whether the Checked object is null or not, and then bind it to IsEnabled property. I want to know if there is a way I can bind IsEnabled to Checked object directly?
Use DataTrigger and check for {x:Null} value of binding:
<Button Content="TestButton">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Checked}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Also, you can use IValueConverter which will return false if value is null otherwise true.
public class ObjectToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
bool returnValue = true;
if (value == DependencyProperty.UnsetValue || value == null)
{
returnValue = false;
}
return returnValue;
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
return Binding.DoNothing;
}
}
and bind in XAML:
<Button Content="TestButton"
IsEnabled="{Binding Checked,
Converter={StaticResource ObjectToBoolConverter}}" />
Ofcourse you need to declare instance of converter in your XAML for this.
You can use a converter to convert an object into a bool. Look into IValueConverter.
public class IsNotNullToBoolConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("Two-way binding not supported by IsNotNullToBoolConverter");
}
}
And your xaml would look like this:
<Window.Resources>
<local:IsNotNullToBoolConverter x:Key="IsNotNull" />
</Window.Resources>
...
<Button IsEnabled="{Binding Converter={StaticResource IsNotNull}}" />
I know this is an old issue but you can do this without an extra code. Just add the "TargetNullValue=false" to the binding.
IsEnabled="{Binding SomeProperty, TargetNullValue=false}"

WPF - Highlighting rows in listview with binded XML

First of all I apologise for my bad english.
I have xml binded to a listview, where each row stands for one person. I want row's background color blue or pink depending on sex element in xml. I've created style with triggers, but they seems to check only first xml node and all rows are colored same as 1st row.
Xml element sex is 0 for male, 1 for female.
One DataTrigger (second is similar):
<DataTrigger Binding="{Binding Source={StaticResource Data}, XPath=People/Person/Sex}" Value="0">
<Setter Property="Background" Value="{StaticResource MaleBrush}" />
</DataTrigger>
This is xml and style binding to listview (data is XmlDataProvider):
<ListView ... ItemsSource="{Binding Source={StaticResource Data}, XPath=People/Person}" ItemContainerStyle="{StaticResource SexStyle}">
And this is style header:
<Style x:Key="SexStyle" TargetType="{x:Type ListViewItem}">
Thanks for help!
You should use a ValueConverter for this.
You bind the background of the datatemplate to the gender.
Then you create a value converter class like this:
public sealed class GenderToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string) {
if (Convert.ToString(value) == "m") {
return Colors.Blue;
} else {
return Colors.Pink;
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This value converter you then add to your resources like this:
<local:GenderToBackgroundConverter x:Key="GenderToBackgroundConverter" />
And in your datatemplate:
<Stackpanel Background={Binding Sex, Converter={StaticResource GenderToBackgroundConverter}}">
</Stackpanel>

Categories

Resources