4

I'm having trouble with my c# UWP App.

<Grid VerticalAlignment="Bottom" Height="40" Grid.Row="3" Margin="0,0,0,-1">
  <Grid.Background>
    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#00000000" Offset="0"/>
        <GradientStop Color="{StaticResource stdBackgroundColor}" Offset="1"/>
    </LinearGradientBrush>
  </Grid.Background>
</Grid>

This code snippet should show a transparent overlay at the bottom of the page, but instead it creates a blackish overlay, like it is adding transparent values on top of the existing background (which is also of Color stdBackgroundColor) and therefore looks quite strange (see image below)

Overlay problem

How can I achieve an transparent overlay to content (such as ScrollViewer) without this effect?

Markus Hütter
  • 7,796
  • 1
  • 36
  • 63
user3079834
  • 2,009
  • 2
  • 31
  • 63
  • That looks a little funny and I'm not sure I'm visualizing your intended output right. Since you have your alpha set at 0 on your first offset and I'm not sure what stdBackgroundColor is, do you have an example of what your desired effect would be? – Chris W. Feb 01 '16 at 16:36
  • The expected output in the picture should be, that nothing is visible, but everything is the same `Color` (in this case it's stdBackgroundColor, which is #FFa1a1a1). When there is content behind the Grid from above, that content should slowly fade into the background color (stdBackgroundColor). – user3079834 Feb 01 '16 at 17:16

1 Answers1

6

Disclaimer, I'm at work and I don't have access to a platform I can test this code in, but from what I've looked up it should work.

The underlying problem is one of color blending, namely that we want to smoothly blend from full alpha to any arbitrary color. The problem with starting with Color.Transparent is that you'll get a band of white while the alpha channel progresses more than the other components. A similar problem occurs with #00000000 as that has a band of black. The solution is to start with a version of your color that has the opacity set to 0. How we go about this is slightly different based on which version of XAML based application you are developing--and I find it difficult to keep them straight. In UWP we don't have an opacity mask and we can't create a gradient on opacity alone. The only thing left is to use an IValueConverter.

The Color property in GradientStop is a Color and not a Brush, as that would overly complicate the rendering system. So the first step we need is an IValueConverter that takes any Color and strips out the alpha value:

public class TransparentColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
                          object parameter, string language)
    {
        Color convert = (Color) value; // Color is a struct, so we cast
        return Color.FromArgb(0, convert.R, convert.G, convert.B);
    }

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

The next challenge is to create a binding that we can use this color against. The binding object allows us to specify a Source, which can be any object we pass in. ElementName won't work because resources are referenced by key, not name.

<LinearGradientBrush x:Key="OpacityGradientBrush" StartPoint="0,0" EndPoint="0,1">
  <GradientStop Color="{Binding Source={StaticResource stdBackgroundColor},
                       Converter={StaticResource TransparentColorConverter}}" Offset="0"/>
  <GradientStop Color="{StaticResource stdBackgroundColor}" Offset="1"/>
</LinearGradientBrush>

Of course, don't forget to put the TransparentColorConverter in your Resources so you can reference it here. Personally, I would prefer not to have to use converters, but in this case I think it is warranted.

NOTE: We use Source in the binding so we can reference a static resource. If we had a property we could bind to, we could use a Path instead.


The answer below is valid for WPF, but not UWP. I wish Microsoft would stop redefining the same concept in incompatible ways, but this is the world we live in.

Correcting my answer based on this answer

You want to change your LinearGradientBrush to use the Opacity property rather than the color #00000000. The problem is that it is interpolating from basically black to your desired color. To get the affect you want you'll need to set up an opacity mask brush like this:

<LinearGradientBrush x:Key="OpacityGradientBrush" StartPoint="0,0" EndPoint="0,1">
  <GradientStop Color="#00FFFFFF" Offset="0"/>
  <GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>

Then in the code you need to apply the opacity mask to, you reference it like this:

<Grid VerticalAlignment="Bottom" Height="40" Grid.Row="3"
    Background="{StaticResource stdBackgroundColor}"
    OpacityMask="{StaticResource OpacityGradientBrush} Margin="0,0,0,-1"/>

The problem is that if you want the blend to only affect the opacity, the rest of the color has to be the same. You'll get a band of your starting color fading in and changing to the ending color otherwise.

Community
  • 1
  • 1
Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57
  • This sounds reasonable, but unfortunately I get an Error, that `The property 'Opacity' was not found in type 'GradientStop'. `, so how did you implement `Opacity` in `LinearGradientBrush`? – user3079834 Feb 03 '16 at 09:26
  • I've changed the code to provide the same effect, but not use opacity in the gradient stop. Hopefully this should work for you. The comments about color blending still apply and I haven't changed those. – Berin Loritsch Feb 03 '16 at 13:58
  • 1
    unfortunatelly the answer, you linked to is for WPF only, but not c# UWP apps. In UWP there is no `OpacityMask` (yet) – user3079834 Feb 05 '16 at 13:36
  • The only thing I can think now is to use a ValueConverter to take your color and remove the alpha (setting alpha to 0 and copying the RGB values to the new color). That of course would only apply to the start value. Why can't Microsoft just have one API? It's really hard to keep straight what features are in which flavor of XAML. – Berin Loritsch Feb 05 '16 at 14:02
  • For OpacityMask specifically, i didnt watch the video yet but there might be a way now since this is on Channel9: How to apply an Opacity Mask to an image in Universal apps using Direct2D https://channel9.msdn.com/Blogs/OneCode/How-to-apply-an-Opacity-Mask-to-an-image-in-Universal-apps-using-Direct2D – Dmitry Lyalin Sep 04 '16 at 19:32