locked
How to bind to SystemColors in Silverlight? RRS feed

  • Question

  • I am not sure what will be the best techinique. I set references to system colors in App.xaml.cs.

    App.Current.Resources.Add("ActiveBorderColor ", SystemColors.ActiveBorderColor); 

    and bind it this way:

    <TextBlock Text="TextBlock" Foreground="{StaticResource ActiveBorderColor}" /> 

    It works but I am getting error that the 'ActiveBorderColor' resource could not be resolved. It is not perfect solution. I am wondering what is the correct way to do it. Any ideas are highly appreciated. Thank you.

    Wednesday, April 27, 2011 5:53 PM

Answers

  • Here are two ways to approach this. First if you're going to add the resource via code-behind ensure:

    1. The resource name doesn't contain the extra space at the end. That's the first way you could receive that error.
    2. Place that code in the StartUp handler in the App.xaml.cs code-behind.

    The second way would involve defining this resource in XAML which is the most typical, however there are a few steps you'll need to follow. Since you can't define a static class in XAML, you'll need to create a wrapper class like so:

    public class SystemColorsWrapper
    {
      public Color ActiveBorderColor
      {
        get
        {
          return SystemColors.ActiveBorderColor;
        }
      }
    }

    This class exposes the property you're after but also leaves the door open to implement other system colors as you see fit. Next, you're now able to define this in your App.xaml like so:

    <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 xmlns:local="clr-namespace:SilverlightApplication1"
                 x:Class="SilverlightApplication1.App">
      <Application.Resources>
        <local:SystemColorsWrapper x:Key="SystemColors" />
      </Application.Resources>
    </Application>

    The wrapper class is returning a color but you'll need a brush for the TextBlock foreground, so here's the converter for that:

    public class ColorToBrushConverter : IValueConverter
    {
      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
        // get the color
        Color systemColor = (Color)value;
    
        // return a brush
        return new SolidColorBrush(systemColor);
      }
    
      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
        throw new NotImplementedException();
      }
    }

    Finally, your binding on the TextBlock would be:

    <UserControl.Resources>
      <local:ColorToBrushConverter x:Key="colorConverter" />
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot">
      <TextBlock FontSize="48" Text="TextBlock" Foreground="{Binding ActiveBorderColor, Source={StaticResource SystemColors}, Converter={StaticResource colorConverter}}" />
    </Grid>

    So yes, this does involve some steps but it is a flexible approach and a more standard way to go about it.

    I hope this helps.

    Brice

    Wednesday, April 27, 2011 10:51 PM
  • No bother at all. I took your code and successfully ran the following sample:

    <UserControl.Resources>
      <local:ColorToBrushConverter x:Key="colorConverter" />
    
      <Style x:Key="AVV_TextBlockStyle" TargetType="TextBlock">
        <Setter Property="FontSize" Value="60" />
      </Style>
    
      <Style x:Key="AVV_TextBlockStyleWhiteSys" TargetType="TextBlock" BasedOn="{StaticResource AVV_TextBlockStyle}">
        <Setter Property="Foreground" Value="{Binding WindowColor, Source={StaticResource SystemColors}, Converter={StaticResource colorConverter}}"/>
      </Style>
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot">
      <TextBlock Text="TextBlock" Style="{StaticResource AVV_TextBlockStyleWhiteSys}" />
    </Grid>

    The styles in this example inherit and execute with success. I modified my SystemColorsWrapper class to rename the ActiveBorderColor property to WindowColor to match your example. The error message you have Cannot set read-only property however makes me think about the SystemColorsWrapper class since it has that read-only property named WindowColor. Are you trying to modify that property by any chance?

    Brice

    Thursday, April 28, 2011 9:35 AM
  • Yeah, it works for me. I posted my solution on my web site, try this out:

    http://www.bricemason.com/wp-content/uploads/2011/04/SilverlightApplication1.zip

    You had mentioned previously that this had worked for you, whatever has changed since then should be your debugging target. Give this a go and see how I have it working.

    I hope this gets you rolling.

    Brice

    Thursday, April 28, 2011 1:16 PM
  • Wow, strange. For what it's worth, I'm running Visual Studio 2010 Premium SP 1. As far as environment, the solution I posted doesn't even require any special assemblies, it should all be there for you. Just for the heck of it, can you delete the Bin and obj folders in the solution I sent to you and rebuild?

    Brice

    Thursday, April 28, 2011 2:56 PM

All replies

  • Here are two ways to approach this. First if you're going to add the resource via code-behind ensure:

    1. The resource name doesn't contain the extra space at the end. That's the first way you could receive that error.
    2. Place that code in the StartUp handler in the App.xaml.cs code-behind.

    The second way would involve defining this resource in XAML which is the most typical, however there are a few steps you'll need to follow. Since you can't define a static class in XAML, you'll need to create a wrapper class like so:

    public class SystemColorsWrapper
    {
      public Color ActiveBorderColor
      {
        get
        {
          return SystemColors.ActiveBorderColor;
        }
      }
    }

    This class exposes the property you're after but also leaves the door open to implement other system colors as you see fit. Next, you're now able to define this in your App.xaml like so:

    <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 xmlns:local="clr-namespace:SilverlightApplication1"
                 x:Class="SilverlightApplication1.App">
      <Application.Resources>
        <local:SystemColorsWrapper x:Key="SystemColors" />
      </Application.Resources>
    </Application>

    The wrapper class is returning a color but you'll need a brush for the TextBlock foreground, so here's the converter for that:

    public class ColorToBrushConverter : IValueConverter
    {
      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
        // get the color
        Color systemColor = (Color)value;
    
        // return a brush
        return new SolidColorBrush(systemColor);
      }
    
      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
        throw new NotImplementedException();
      }
    }

    Finally, your binding on the TextBlock would be:

    <UserControl.Resources>
      <local:ColorToBrushConverter x:Key="colorConverter" />
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot">
      <TextBlock FontSize="48" Text="TextBlock" Foreground="{Binding ActiveBorderColor, Source={StaticResource SystemColors}, Converter={StaticResource colorConverter}}" />
    </Grid>

    So yes, this does involve some steps but it is a flexible approach and a more standard way to go about it.

    I hope this helps.

    Brice

    Wednesday, April 27, 2011 10:51 PM
  • Thank you for the great info. I am going to use the second approach. It works great.

    Thursday, April 28, 2011 12:21 AM
  • Bricemason, Sorry for bothering again, but I encounter an issue that I cannot overcome. I am wondering what can be the issue of getting the error while I apply systemcolor in styles using the second approach. For example, When I set the Foregroung property to the textblock in style like this:

    <Style x:Key="AVV_TextBlockStyleWhiteSys" TargetType="TextBlock" BasedOn="{StaticResource AVV_TextBlockStyle}">   
    	<Setter Property="Foreground" Value="{Binding WindowColor, Source={StaticResource SystemColors}, Converter={StaticResource colorConverter}}"/>
     </Style>



     

    Then, I am getting this error:
    System.Windows.Markup.XamlParseException occurred
      Message=Set property '' threw an exception. [Line: 11 Position: 41]
      LineNumber=11
      LinePosition=41
      StackTrace:
           at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
           at SilverlightSysColors.MainPage.InitializeComponent()
           at SilverlightSysColors.MainPage..ctor()
      InnerException: System.NotSupportedException
           Message=Cannot set read-only property ''.
           StackTrace:
                at MS.Internal.XamlMemberInfo.SetValue(Object target, Object value)
                at MS.Internal.XamlManagedRuntimeRPInvokes.SetValue(XamlTypeToken inType, XamlQualifiedObject& inObj, XamlPropertyToken inProperty, XamlQualifiedObject& inValue)
           InnerException:

    If I apply Foreground color directly to the texblock, then I don't get any error. It is a very strange, not sure how to fix it. Thank you in advance.

    Example:

    <TextBlock x:Name="txt" Text="Sample" Foreground="{Binding WindowTextColor, Source={StaticResource SystemColors}, Converter={StaticResource colorConverter}}"/>
    Thursday, April 28, 2011 4:43 AM
  • No bother at all. I took your code and successfully ran the following sample:

    <UserControl.Resources>
      <local:ColorToBrushConverter x:Key="colorConverter" />
    
      <Style x:Key="AVV_TextBlockStyle" TargetType="TextBlock">
        <Setter Property="FontSize" Value="60" />
      </Style>
    
      <Style x:Key="AVV_TextBlockStyleWhiteSys" TargetType="TextBlock" BasedOn="{StaticResource AVV_TextBlockStyle}">
        <Setter Property="Foreground" Value="{Binding WindowColor, Source={StaticResource SystemColors}, Converter={StaticResource colorConverter}}"/>
      </Style>
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot">
      <TextBlock Text="TextBlock" Style="{StaticResource AVV_TextBlockStyleWhiteSys}" />
    </Grid>

    The styles in this example inherit and execute with success. I modified my SystemColorsWrapper class to rename the ActiveBorderColor property to WindowColor to match your example. The error message you have Cannot set read-only property however makes me think about the SystemColorsWrapper class since it has that read-only property named WindowColor. Are you trying to modify that property by any chance?

    Brice

    Thursday, April 28, 2011 9:35 AM
  • Thank you! Did it work on your end? Did you debug it in VS? I have exactly the same setup and ggetting error. It is only working in Design view but when I run it then I am getting the error.

    I am not trying to modify WindowsColor property. I need to be able to apply it through the style because that style will be used for other texblocks. It is still not working for me. Not sure what else possible can be done.

    Thursday, April 28, 2011 12:20 PM
  • Yeah, it works for me. I posted my solution on my web site, try this out:

    http://www.bricemason.com/wp-content/uploads/2011/04/SilverlightApplication1.zip

    You had mentioned previously that this had worked for you, whatever has changed since then should be your debugging target. Give this a go and see how I have it working.

    I hope this gets you rolling.

    Brice

    Thursday, April 28, 2011 1:16 PM
  • Thank you for the attached solution. I highly appreaciate it. Now, I am totally lost. I tried to run it on my computer and getting the error. Visual Studio Just-In-Time debugger giving me error that 'An unhandled exception - Code: 4004. Messaage:System.Winods.Markup.XamlParseException:Set property" threw an exception. Runing debugger second time, I am getting the same error as before.

    Maybe, I am missing something in my environment. I have never had similar issues before. Do you know what can be the issue? It is working on you end but not on mine. Have no idea what this can be. What version of VS are you running?

    Thursday, April 28, 2011 1:37 PM
  • It worked for me only if I set foreground inside texblock not in a linked style.

    Thursday, April 28, 2011 2:27 PM
  • Wow, strange. For what it's worth, I'm running Visual Studio 2010 Premium SP 1. As far as environment, the solution I posted doesn't even require any special assemblies, it should all be there for you. Just for the heck of it, can you delete the Bin and obj folders in the solution I sent to you and rebuild?

    Brice

    Thursday, April 28, 2011 2:56 PM
  • I'm running VS 2010 Ultimate SP1. I deleted Bin and Obj folders. I am getting the same error. It is frustrating. It looks like I just have no other choice but assign foreground colors directly. It is so wrong. Thank you for your help and time.

    Thursday, April 28, 2011 3:12 PM
  • I forgot to mention that I am getting the same error while running the project through Blend.

    Thursday, April 28, 2011 3:19 PM
  • I just opened and successfully built and ran the project in Blend too. As a last bit of debuggery, is it possible for you to zip up and post something for me to try?

    Brice

    Thursday, April 28, 2011 3:34 PM
  • I uploaded my solution here: http://cid-0c29483cf3a6a14d.office.live.com/self.aspx/WPF%5E_Tests/SilverlightSysColors.zip

    I found a trick to solve it but it requires to add additional class 'SetterValueBindingHelper.cs' borrowed from the project you can find here: http://blogs.msdn.com/b/corrinab/archive/2010/04/12/9994045.aspx

    It works now if I set color values through that class. You will find a sample of how I did it in my solution. However, it is a bit more work that just you were able to run your project. I am not sure how you can possibly do that and I can't. Of course, I really interested to find out what is the issue and do not use additional class if it is not necessary. Please let me know if you will find the cause of this anomaly. Thank you in advance.

     

     

    Thursday, April 28, 2011 3:57 PM
  • It's still working on my end whether I use the helper class or not. Is there someone else out there that can test out these solutions to verify?

    Brice

    Monday, May 2, 2011 9:38 PM
  • Thank you for looking. Unfortunatelly, I don't have anything else, it is very strange that you can use system colors running the same app and I cannot. The only explaination I can think of is that we have different environments. I am not sure what this can be. Anyway, thank you a lot for your help. I highly appreciate it.

    Tuesday, May 3, 2011 1:14 AM