I'm styling a simple graphical object in Silverlight, which is shown as a red rectangle with some white text on it; when the mouse hovers the rectangle, I want to show an overlay rectangle which fades from red to transparent, so that the text is only partially visible. My problem is, the LinearGradientBrush I'm using does not go from red to transparent but from red to some kind of semi-transparent white! I've reproduced the problem as follows:
<Grid x:Name="LayoutRoot" Background="Red">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid>
<Grid.Background>
<LinearGradientBrush>
<GradientStop Color="Transparent" Offset="0"/>
<GradientStop Color="Red" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
</Grid>
In this case, you can easily see that the upper part of the grid is slightly lighter than the lower part, even though I've used the Transparent color constant. The result is identical if I use #00FFFFFF, while if I use #00xxxxxx, where xxxxxx is the RGB code of any color, the hue of the upper rectangle changes according to the color! Shouldn't the first two digits of the code represent the alpha channel? Why doesn't "00" mean full transparency? Any hint is appreciated.
Related
I would like to create a custom border around a content, that the upper and lower side of the border is not continuous, it should have a style like bracket as below. (inside the "bracket border" the content should be placed e.g. grid, stackpanel, etc.)
Note that the height of the right and the left border can be changed depending on the content's height, whereas the top and the bottom should have a standard width.
[ sample content ]
In order to achieve this, I separated the view in a 3 columns grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22px"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="22px"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="Blue" BorderThickness="6px 6px 0px 6px"></Border>
<Border Grid.Column="1" BorderBrush="Transparent" BorderThickness="6px">
<!--Place for the actual content of the border-->
<TextBlock Text="Test" FontSize="15"></TextBlock>
</Border>
<Border Grid.Column="2" BorderBrush="Blue" BorderThickness="0px 6px 6px 6px"></Border>
</Grid>
Is there another approach for achieving this style?
One possible solution is to write your own Border based on a Decorator.
An implementation (for a different border) can be found in How can I draw a border with squared corners in wpf?
One simple trick is try setting some LinearGradientBrush for the BorderBrush. If your text has a fixed width, it will look best at all time. However when the text's width may change, the horizontal lines (at 2 ends) will shrink/extend at some ratio. That's because we set some Offset for the GradientStop and it's pity that this Offset can only be set based on some ratio (from 0 to 1) with the width of the whole Brush (which is exactly the width of the Border when the background is stretched). Note that the MappingMode cannot change this behavior, it just works for StartPoint and EndPoint.
Here is the pure XAML code:
<Border VerticalAlignment="Center" HorizontalAlignment="Center"
BorderThickness="3" Padding="5,0,5,0">
<TextBlock Text="Square bracket border here" FontSize="30"
HorizontalAlignment="Center"/>
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,1">
<GradientStop Offset="0.03" Color="Blue"/>
<GradientStop Offset="0.03" Color="Transparent"/>
<GradientStop Offset="0.97" Color="Transparent"/>
<GradientStop Offset="0.97" Color="Blue"/>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
You can change the Offset of the first 2 GradientStops to what you want, the Offset of the remaining GradentStops should be the subtraction of the first's Offset from 1.
If using some code behind, you can pinpoint exactly the length of the horizontal lines (at the 2 ends). That way we need some Binding between the Offset and the ActualWidth of the Border. Next we need some Converter here, this Converter will convert the ActualWidth and the desired exact length to the correct ratio. So when the text width changes, the length of the horizontal lines will always be some fixed value.
You can try the following XAML code:
<Grid>
<Border BorderBrush="Black" BorderThickness="1"/>
<TextBlock Text="sample content"/>
<Border BorderBrush="White" BorderThickness="0,1,0,1" Margin="8,0,8,0"/>
</Grid>
Second border's color "White" can be replaced with the actual background color. "Transparent" color will not help.
Thanks,
RDV
I'm working on a custom control and I have a VisualBrush with the Visual property as such:
<VisualBrush.Visual>
<Grid>
<Ellipse Stretch="Uniform" Stroke="OrangeRed" StrokeThickness="1">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5">
<GradientStop Color="Yellow" Offset="0" />
<GradientStop Color="Orange" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse Stretch="Uniform">
<Ellipse.Fill>
<RadialGradientBrush GradientOrigin="0.5, 0.05" RadiusX=".7" RadiusY=".5" >
<GradientStop Color="White" Offset=".10" />
<GradientStop Color="Transparent" Offset="1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
</VisualBrush.Visual>
When the Stretch property is set to Uniform instead of using the RadialGradientBrush I created, it somehow creates a SolidColorBrush using the Stroke color. However, when I explicitly set the Width and Height properties of the Ellipse instead of using the Stretch property, I get the gradient I expected.
Any ideas of what's going weird here?
EDIT: I just observed the behavior occurs when I enclose the Ellipse inside of a ViewBox.
I'm not sure what you're using the ViewBox for in your above example, but I think some of the strange behaviour you're seeing is a result of not setting the size on the root element for your VisualBrush.
In the documentation for VisualBrush.Visual, there's a paragraph that mentions sizing (added emphasis):
When you define a new Visual for a VisualBrush and that Visual is a
UIElement (such as a panel or control), the layout system runs on the
UIElement and its child elements when the AutoLayoutContent property
is set to true. However, the root UIElement is essentially isolated
from the rest of the system; styles, storyboards, and external layout
dictated by the parent where the brush is applied cannot permeate this
boundary. Therefore, you should explicitly specify the size of the
root UIElement, because its only parent is the VisualBrush and
therefore it cannot automatically size itself to the area being
painted. For more information about layout in Windows Presentation
Foundation (WPF), see the Layout.
If I run your code above, my ellipse is completely filled with the stroke colour, presumably because the the layout is calculated with the smallest value for the ellipse, so the stroke is large enough to cover ellipse content/fill (which is then upscaled to the fill the viewport so it looks like the stroke is the fill colour).
If I give the root element some arbitrary size on which to base the layout <Grid Width="100" Height="100">...</grid> I start to see the stroke and fill colours, rendered with relative sizes.
<Grid Name="maingrid">
<Viewbox Stretch="Fill" Height="Auto">
<dp:DockablePanel x:Name="dockPanel" Width="980" Height="710" Margin="0,60,0,0" VerticalAlignment="Top" HorizontalAlignment="Center">
<dp:DockablePanel.DockPanelCollection>
<dp:ChildPanel StripTitle="" Dock="Left" ParentName="Window1" Eve_MenuItemClicked="menuAssets_GotMouseCapture" ScrollViewer.VerticalScrollBarVisibility="Hidden" >
<dp:ChildPanel.OpacityMask>
<LinearGradientBrush EndPoint="0.131,0.161" StartPoint="0.143,0.163">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</dp:ChildPanel.OpacityMask>
<dp:ChildPanel.Effect>
<DropShadowEffect BlurRadius="6" Color="#FF000000" Direction="-60" Opacity="0.68" ShadowDepth="0"/>
</dp:ChildPanel.Effect>
</dp:ChildPanel>
</dp:DockablePanel.DockPanelCollection>
<dp:DockablePanel.CenterContent>
<TextBlock></TextBlock>
</dp:DockablePanel.CenterContent>
</dp:DockablePanel>
</Viewbox>
where works ok on my system where my screen resolution is 1024 and 768,but the font (menu panel font ) gets stretched on running this exe on another system (with monitor screen resolution (1280 ,768) or (1366,768) ,please provide me a solution regarding this
When you expect your application to properly run in full-screen mode, you need to think about a real layout.
If your application has to run on systems with different aspect ratios like 4:3 (1024x768), 5:3 (1280x768) and 16:9 (1366x768), you can not simply put everything in a ViewBox and expect it to fill the entire screen and at the same time keep the aspect ratio.
Either you incorporate a layout that allows for different aspect ratios, or you simply don't fill the screen. Consider dropping your top-level ViewBox and read about the layout options offered by e.g. Grid.
<Viewbox Stretch="Uniform" Height="Auto">
I changed the ControrTemplate of a button, gave it a border with CornerRadius and BevelBitmpaEffect. Looks decent enough for the minimal effort. Put it on a colored background and then the problem becomes apperent.
The corner where the lightangle is coming from there is a white snip with a right angle corner. Possibly coming from the light effect but very obvious with a cornerRadius. I don't suppose I could do anything about it ( aside from the obvious like not using a cornerRadius )?
EDIT:
This code should generate the same problem for you
<Style x:Key="TabButtons" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="8"
SnapsToDevicePixels="True"
Name="BtnBorder"
Width="80"
Height="35"
BorderThickness="1"
BorderBrush="DarkBlue">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="LightBlue" Offset="0" />
<GradientStop Color="DarkBlue" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<Border.BitmapEffect>
<BevelBitmapEffect BevelWidth="5"
EdgeProfile="CurvedOut"
LightAngle="135"
Relief="0.1"
Smoothness="1" />
</Border.BitmapEffect>
<ContentPresenter Name="BtnContent" VerticalAlignment="Center" HorizontalAlignment="Center" ContentSource="Content" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Use this button on a LightBlue Background for example
<Border Background="LightBlue" >
<Button Style={StaticResource TabButtons} >Test</Button>
</Border>
I can't imagine what the problem exactly looks like, but here is one suggestion - try setting the SnapsToDevicePixels to true for the affected button or perhaps the entire window.
The problem might stem from the fact that WPF by default wants to do device independent rendering. Which causes control edges to slightly "bleed" to adjacent pixels due to width/height and DPI/pixels not aligning exactly. Setting SnapsToDevicePixels to true, will force WPF to render exactly for the device pixels and the "bleeding" effect will dissapear...
EDIT:
I tried out the code you provided and I guess the "white snip" is the white(ish) pixels around the corners?
Well the problem here stems from antialiasing, which also lightens the pixels inside the corners. Because you use a dark tone, those lighter pixels stand out and the button doesn't look so nice.
I'm not sure if this can be considered a bug. You would think that antialising will do it's magic on the pixels that make up the corner and outside of the corner, but every pixel inside of the corner should be colored with the background color.
Then again this might be by design, that antialiasing has to mess with the pixels inside the corner as well otherwise you don't get the required effect...
Anyway there are two workarounds:
1) Give the border the following property RenderOptions.EdgeMode="Aliased". This will turn off antialiasing (0% white(ish) pixels), but the controll will look very jaggedy.
2) Use two borders. The outer border will be defined just like you have defined your current border, only set the Background property to just DarkBlue. Then for this outer border create another border as a child element. For this inner border set CornerRadius=7, RenderOptions.EdgeMode="Aliased" and define the Background with the LinearGradientBrush. The inner border will contain the ContentPresenter. The code will look like this:
<Border CornerRadius=8 Background=DarkBlue>
<Border CornerRadius=7 RenderOptions.EdgeMode="Aliased">
<Border.Background>
...
</Border.Background>
<ContentPresenter />
</Border>
</Border>
It won't get rid of the white pixels a 100%, but this is the best compromise I think. With the naked eye I couldn't see any white pixels, but when I zoomed in with the magnifier tool I counted only 2 pixels per corner and only at the bottom corners. Before it was something like 8 pixels per corner. At least the outer border is still antialiased and the control doesn't look jaggedy.
The BevelBitmapEffect didn't make any difference to the appearance of the control so I just ommited it.
My question is similar to this one.
However, the answer in that thread does not work for me, because I'm using the LinearGradientBrush for an opacity mask.
I've got a grid with three rows:
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
The content of the grid should be semi-transparent at the top and the bottom of the grid (the first and third row).
I understand I can't use clipping, because that does not allow for adjustable opacity. So I guess I have to go with opacity masks. They take images or brushes as content, the latter of which makes more sense for this problem, but raises the question of how to set the offset of the GradientBrush to the absolute value of the gridrow's height.
Also, I'm using the MVVM pattern. Binding the offset does not work because it doesn't derive from FrameworkElement. The only solution I can see right now would be to bind the complete brush to my viewmodel, and adjust it every time the grid's size changes. Not too nice, is there a more elegant solution?
What I'd like to do is something like this (RowDefinitions and content skipped)
pseudo code:
<Grid>
<Grid.OpacityMask>
<LinearGradientBrush StartPoint="0.5,1" EndPoint="0.5,0">
<LinearGradientBrush.GradientStops>
<GradientStop AbsoluteOffset="50" Color="#7F000000" />
<GradientStop AbsoluteOffset="*" Color="#00000000" />
<GradientStop AbsoluteOffset="50" Color="#7F000000" />
</LinearGradientBrush.GradientStops>
</Grid.OpacityMask>
</Grid>
I can't split the grid or anything like that, because the content spans the three rows. DrawingBrush would solve it nicely I guess, but that's not living in the SL house.
I'm using Silverlight 3.