Get default theme based forground for custom control

1 Answer 848 Views
GridView
Marco
Top achievements
Rank 1
Iron
Iron
Iron
Marco asked on 31 Aug 2021, 12:12 AM

Hello,

i had a custom user control which i use in a GridView Column. The foreground should change by a binded value and a IValueConverter. The "Bad" state is red and the normal state is the default theme color.

The result should look like this (But it does only with a dirty hack -> use 2 idendical textblock and hide one or the other):

BUT i dont know how to get the default foreground (yellow unselected and white selected)

I tried:

  • return null in the IValueConverter -> No Color/Brush at all
  • Use a Multibinding and return the given second Binding: <Binding Source="{x:Static telerik:CrystalPalette.Palette}" Path="MarkerColor"/> -> Select state is not respected
  • Use a Multibinding to a "Helper" Element and return the value <Binding ElementName="ColorHelper" Path="Foreground"/>
    <TextBlock Name="ColorHelper" Foreground="{telerik:CrystalResource ResourceKey={x:Static telerik:CrystalResourceKey.MarkerBrush}}"> </TextBlock> -> Select state is not respected

1 Answer, 1 is accepted

Sort by
1
Accepted
Vladimir Stoyanov
Telerik team
answered on 02 Sep 2021, 10:58 AM

Hello Marco,

Thank you for the shared picture. 

My current understanding is that you have modified the MarkeColor of the Crystal theme to Orange and you would like to change the Foreground inside the cells of a specific column depending on a condition, but otherwise leave the default/selected Foreground colors. Feel free to correct me, if I am wrong and elaborate on the scenario. 

I am attaching a sample project to demonstrate a possible way to achieve the desired result. It involves creating a Style targeting GridViewCell and conditionally setting the Foreground inside a DataTrigger. This will take precedence over the default colors. 

Can you check out the shared sample project and let me know, if it helps?

Regards,
Vladimir Stoyanov
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Marco
Top achievements
Rank 1
Iron
Iron
Iron
commented on 05 Sep 2021, 12:56 AM

Thank you very much! This solved my problem(s).

Is there a way for doing this with a ValueConverter for more complex use cases? I thought of something like "return null" for the default color, but it doesn't work.

regards
Marco

Vladimir Stoyanov
Telerik team
commented on 08 Sep 2021, 10:55 AM

I am glad to hear that you found the sample project helpful. While I am uncertain of the exact scenario, you can also use an IValueConverter and a binding for the value of the Foreground. I am attaching back the sample project modified to demonstrate this approach. 

If that is not what you had in mind, can you elaborate on your scenario? If you find it possible, you can update the sample project in order to demonstrate the idea and send it back.

Marco
Top achievements
Rank 1
Iron
Iron
Iron
commented on 08 Sep 2021, 12:20 PM

Hello Vladimir,

thank you again for your efforts! Your second example does not match my scenario.

concrete example:

The user can define a custom color-table on which value which color should be used as fore- or background.

I need something like this (very simplified and adapted to your example):

    public class Converter : IValueConverter
    {
        Dictionary<int, Brush> colorDict = new Dictionary<int, Brush>()
        {
            {1880, new SolidColorBrush(Colors.BlueViolet)},
            {1890, new SolidColorBrush(Colors.Aquamarine)},
            {1900, new SolidColorBrush(Colors.Fuchsia)}
        };

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value is DateTime est)
            {
                var color = colorDict.OrderBy(x => x.Key).FirstOrDefault(x => x.Key > est.Year);
                if (!color.Equals(default(KeyValuePair<int, Brush>)))
                    return color.Value;
            }

            return default; // <- ??? how do i get the default foreground color?
            return null; // <- doesnt work
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

I dont think I can build this or even more complex requirements up as xaml trigger.

regards marco

Vladimir Stoyanov
Telerik team
commented on 13 Sep 2021, 07:50 AM

In the demonstrated scenario, you can return a SolidColorBrush with the color from the palette that is used in the scenario. Here is some sample code:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if(value is DateTime est)
            {
                var color = colorDict.OrderBy(x => x.Key).FirstOrDefault(x => x.Key > est.Year);
                if (!color.Equals(default(KeyValuePair<int, Brush>)))
                    return color.Value;
            }

            return new SolidColorBrush(CrystalPalette.Palette.MarkerColor);
            //return null; // <- doesnt work
        }

I hope you find this helpful. 

Marco
Top achievements
Rank 1
Iron
Iron
Iron
commented on 19 Sep 2021, 01:23 AM

Hello,

and again, thank you for your efforts. The Problem is, that the user can chose their Theme freely or set the app in DarkMode, even special Themes for some controls. With your solution i had to consider this every time for every converter with every theme/mode/variation.

For now i helped me out with a hybrid approach:

I build a bool variable which breaks the complex calculation down to true or false. Than i use a trigger like you given in your example.

In my simplified example it will look something like this:

ViewModel (pseudo code):

public bool IsInRange { get { return Value > colorDict.Min() && Value < colorDict.Max(); } }

The Converter stays mainly the same, but the default-case should never be occur and its now an MultiConverter to transfer indiviual colorDictionaries:

internal class ColorConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values[0] is double value && values[1] is ColorDict colorDict)
        {
            foreach (var color in colorDict)
            {
                if (value > color.Value)
                    return new SolidColorBrush(color.BackgroundColor);
            }
        }

        // Should never occur
        return new SolidColorBrush(Colors.DeepPink);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

The Xaml Trigger:

<Style x:Key="ColorColumnStyle" TargetType="telerik:GridViewCell" >
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsInRange}" Value="True">
            <Setter Property="Background">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource ColorConverter}">
                        <Binding Path="Value" />
                        <Binding Path="colorDict"/>
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
</Style>
regards marco
Vladimir Stoyanov
Telerik team
commented on 21 Sep 2021, 10:33 AM

I am glad to hear that you have achieved what you were going for. Indeed creating a custom property and using a MultiBinding is a nice approach. 

Thank you for sharing your implementation. 

Marco
Top achievements
Rank 1
Iron
Iron
Iron
commented on 22 Sep 2021, 10:02 PM

So finally i came up with a nearly "perfect" solution.

I define per theme the marker color and return it in the value converters.

When the theme changes:

Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary()
{
    Source = new Uri($@"/MyApplication;component/Styles/{themeName}Styles.xaml", UriKind.RelativeOrAbsolute)
});
Per theme i got these styles (e.g. Windows8):

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
    xmlns:actions="clr-namespace:Infrastructure.Actions;assembly=Infrastructure">

    <SolidColorBrush x:Key="BasicColorBrush" Color="{Binding Source={x:Static telerik:Windows8Palette.Palette}, Path=BasicColor}"/>
    <SolidColorBrush x:Key="MainColorBrush" Color="{Binding Source={x:Static telerik:Windows8Palette.Palette}, Path=MainColor}"/>
    <SolidColorBrush x:Key="MarkerColorBrush" Color="{Binding Source={x:Static telerik:Windows8Palette.Palette}, Path=MarkerColor}"/>    

</ResourceDictionary>

And finally in the ValueConverter i can use:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    if(value is DateTime est)
    {
        var color = colorDict.OrderBy(x => x.Key).FirstOrDefault(x => x.Key > est.Year);
        if (!color.Equals(default(KeyValuePair<int, Brush>)))
            return color.Value;
        }

    return Application.Current.TryFindResource("MarkerColorBrush");
}

Tags
GridView
Asked by
Marco
Top achievements
Rank 1
Iron
Iron
Iron
Answers by
Vladimir Stoyanov
Telerik team
Share this question
or