WPF Opacity Mask at fixed location - wpf

I need to get rid of a part of a component ( make it totally transparent ) , a small bar at the bottom of it actually.
It's a fixed size something like 25-30px but the problem is this component will be resized a lot so i can't just put an image as opacity mask. ( which will look bad at different sizes )
Even if the component is 300x300 or 1000x200 , i need to get bottom 25px disappear somehow.
I searched about opacity mask&drawing brush but no luck , can't find a way to dock it at the bottom of component.
By the way , not sure if it matters but the component I'm talking about is WPFChromium browser control.
Is there a way to achieve this by opacity mask or something like viewbox etc?
Thanks in advance!

You could use a LinearGradientBrush as OpacityMask for the Control and Bind the Offset to the ActualHeight of the Control and then subtract 25 from the value and divide it by the ActualHeight in order to get the value in %. This should give you a transparent part of 25px at the Bottom
<WebBrowser Name="webBrowser">
<WebBrowser.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFFF0000"
Offset="{Binding ElementName=webBrowser,
Path=ActualHeight,
Converter={StaticResource OffsetConverter},
ConverterParameter=25}"/>
<GradientStop Color="#00000000"
Offset="{Binding ElementName=webBrowser,
Path=ActualHeight,
Converter={StaticResource OffsetConverter},
ConverterParameter=25}"/>
</LinearGradientBrush>
</WebBrowser.OpacityMask>
</WebBrowser>
The OffsetConverter
public class OffsetConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double height = (double)value;
double subract = System.Convert.ToDouble(parameter.ToString());
double opacityMaskHeight = height - subract;
return opacityMaskHeight / height;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

Set a property in XAML to a function

I need to set the property of cotrol which is a dependent of another property of its parent.
I try to explain better my problem with an example. I want to create a toggle switch button that animates a "slider" element into it. The dimensions of the toggle switch is being defined when the usercontrol is inserted into the application window. I want the slider be sized half larger than the switch case. So if the control is large 100, the slider should be 50, or if large 250, the slider should be 125. Then I need a sort of call to a function or something similar:
<UserControl>
<Border Name="switchCase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Border Name="slider" Width="**Container.Width/2**" ></Border>
</Border>
</UserControl>
Is there any possibilities to achieve this ??
Thanks in advance
Paolo
Yes you need databinding with a converter, such as the following example
xmlns:conv="clr-namespace:MyConverters.Converters"
.......
<UserControl.Resources>
<conv:WidthConvertercs x:Key="widthConv"></conv:WidthConvertercs>
</UserControl.Resources>
<Border Name="switchCase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Border Name="slider" Width="{Binding ElementName=switchCase, Path=ActualWidth, Converter={StaticResource widthConv}}" Background="DarkMagenta"></Border>
</Border>
Your converter class would be
[ValueConversion(typeof(double), typeof(double))]
class WidthConvertercs : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double withPar = (double)value;
return withPar/2.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I hope this helps
Out of the box it's not supported by XAML. You can only bind to Properties.
You can write a converter which do the calculation (or you can use
MathConverter)
You can do the calculation in the code behind in event
handlers
If you are following the MVVM pattern you can do the
calculation in the ViewModel (altough it will introduce view
related concepts to the ViewModels which is not always good...)
You can write your own Binding extension

Rounded rectangle in Silverlight

I'm trying to create a rectangle in Silverlight where the corners are rounded. However, I do not explicitly specify the width and the height of the rectangle, which means it adapts to the size of the Grid which contains it (the size of the grid depends on the screen resolution amongst other things, and is not known before hand).
I'd like the RadiusX and RadiusY properties to be percentages of the rectangle's width and height respectively. What would be the cleanest way of doing this? Is there a XAML-only way of doing it (without resorting to code-behind)?
Two files below to download I used for testing this http://dl.dropbox.com/u/8679840/SilverlightApplication1.zip
Best way for reuse is to create a Type converter like
<Grid x:Name="LayoutRoot" Background="White">
<Rectangle x:Name="rectangle"
Width="200" Height="200"
RadiusX="{Binding Width, ElementName=rectangle, Converter={StaticResource myConverter}, ConverterParameter=.1}"
RadiusY="{Binding Height, ElementName=rectangle, Converter={StaticResource myConverter}, ConverterParameter=.1}"
/>
</Grid>
and the code behind
namespace SilverlightApplication1
{
public class PercentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return System.Convert.ToDouble(value) * System.Convert.ToDouble(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
While Justin King's answer works if the Width and Height are known before hand, it doesn't work if they're not set, and the parent control dynamically lays the rectangle out. Unfortunately, in Silverlight, you cannot use Binding with Converters on ActualWidth and ActualHeight, as they are calculated properties. What this means is that when ActualWidth and ActualHeight change, a property changed event is not raised internally, so the binding wouldn't propagate the changes to the source.
Essentially, at this point, the only option is to subscribe to the LayoutUpdated event and calculate and set the RadiusX and RadiusY properties in code-behind.

How can I define a Silverlight Color by color component in XAML?

I'm trying to set the color of my Silverlight control as a slightly transparent version of a color defined in my ResourceDictionary used by my application.
To do this, my strategy was to break the color into components, such that I could grab the RGB values, then set my own alpha value on top of that to get my semi-transparent color.
The ResourceDictionary looks something like:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:Byte x:Key="PrimaryLightColorAlphaValue">#FF</system:Byte>
<system:Byte x:Key="PrimaryLightColorRedValue">#DB</system:Byte>
<system:Byte x:Key="PrimaryLightColorGreenValue">#E5</system:Byte>
<system:Byte x:Key="PrimaryLightColorBlueValue">#F1</system:Byte>
<Color x:Name="PrimaryLightColor" A="{StaticResource PrimaryLightColorAlphaValue}"
R="{StaticResource PrimaryLightColorRedValue}"
G="{StaticResource PrimaryLightColorGreenValue}"
B="{StaticResource PrimaryLightColorBlueValue}" />
<SolidColorBrush x:Name="PrimaryLightColorBrush" Color="{StaticResource PrimaryLightColor}" />
....
Then my color would be used in my application by referencing the color or it's components.
....
<Border Background="{StaticResource PrimaryLightColorBrush}" />
....
<LinearColorKeyFrame KeyTime="00:00:00">
<LinearColorKeyFrame.Value>
<Color A="#CC"
R="{StaticResource PrimaryLightColorBrushRedValue}"
G="{StaticResource PrimaryLightColorBrushGreenValue}"
B="{StaticResource PrimaryLightColorBrushBlueValue}" />
</LinearColorKeyFrame.Value>
</LinearColorKeyFrame>
....
My Problem:
Silverlight XAML apparently doesn't natively support the Byte type: thus my color parts defined in the ResourceDictionary never loads and throws a "type 'Byte' not found" error.
So how can I achieve the equivalent of breaking up those A, R, G, B color values into bytes, except not using bytes? (Using string & type conversion?) Or perhaps theres a better way of defining a color, inheriting/using it, then overriding its alpha value? The catch is I need to achieve this exclusively via XAML.
Any ideas?
Here's how you could do it with a converter that changes alpha.
This is the converter:
public class ChangeAlphaConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var color = (Color) value;
color.A = byte.Parse((string) parameter);
return color;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This is how you would use it:
<UserControl.Resources>
<Color x:Key="BaseColor">#fff</Color>
<SilverlightTests:ChangeAlphaConverter x:Key="ChangeAlpha"/>
</UserControl.Resources>
...
<Border>
<Border.Background>
<SolidColorBrush Color="{Binding Source={StaticResource BaseColor}, Converter={StaticResource ChangeAlpha}, ConverterParameter=100}"/>
</Border.Background>
</Border>
In the example we set the border's background to solid white (from BaseColor) and set the color's alpha to 100 (decimal)

WPF Window Background ImageBrush not tiling

I have a window with a background image. The image may change at runtime which really should not matter for this.
I want the image to be fixed to the top left (which it is) and not scale (which is also correct. But I need the image to repeat (tile) when the window is made larger than the image. I am doing ...
What am i missing?
TIA
You need to set the TileMode property as well as the Viewport and ViewportUnits:
For example:
<Window.Background>
<ImageBrush ImageSource="myImage.png"
Viewport="0,0,300,300"
ViewportUnits="Absolute"
TileMode="Tile"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top" />
</Window.Background>
Note: the second 2 segments of the Viewport attribute indicate the desired size of each repetition. If you want to display the entire image, these should be the width and height of the image.
Example output:
Edit in response to comments
If you don't know the size of the image to specify in the Viewport property, you can use a Binding with an IValueConverter to calculate it from the image. I am convinced there must be a better way of doing this, but I haven't found one yet!
XAML:
<Window.Resources>
<local:Converter x:Key="Converter" />
</Window.Resources>
<Window.Background>
<ImageBrush ImageSource="myImage.png"
ViewportUnits="Absolute"
TileMode="Tile"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top"
Viewport="{Binding ImageSource, RelativeSource={RelativeSource Self}, Converter={StaticResource Converter}}"/>
</Window.Background>
Value converter:
public class Converter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var source = (ImageSource)value;
return new Rect(0,0,source.Width, source.Height);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
If you would like the entire solution in c#
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(#"c:\your\image\source.gif"));
brush.TileMode = TileMode.Tile;
brush.ViewportUnits = BrushMappingMode.Absolute;
brush.Viewport = new Rect(0, 0, brush.ImageSource.Width, brush.ImageSource.Height);
MainWindow1.Background = brush;

TwoWay MultiBinding with read-only properties

How to skip updating some of the sub-bindings of a MultiBinding? I have defined in code-behind (I had some troubles making it in XAML and I don't think it matters - after all code-behind is not less expressive then XAML) a MultiBinding which takes two read-only properties and one normal property to produce a single value. In case of ConvertBack the read-only properties are not modified (they sustain their value) and only the normal property is changed.
While defining the MultiBinding the entire MultiBinding was set to TwoWay however particular sub-bindings where set appropriate (first two to OneWay and the third two TwoWay).
The problem occurs in a my own control. However for the sake of presentation I simplified it to a smaller control. The control presented in this example is a Slider-like control allowing to select a value in [0.0; 1.0] range. The selected value is represented by the thumb and exposed as a DependencyProperty.
Basically the control is build by a 1 row x 3 column Grid where the thumb is in the middle column. To correctly position the thumb left column must be assigned width corresponding to selected position. However this width depends also on the actual width of the entire control and actual width of the thumb itself (this is because the position is given as a relative value in [0.0; 1.0] range).
When the thumb is moved the position should be updated appropriately however the thumb width and control width obviously do not change.
The code works as expected however when run in IDE during thumb moving Output window is cluttered with exceptions information as reported when MultiBinding tries to set value to those two read-only properties. I suspect it is not harmful however it is somewhat annoying and misleading. And also it means that the code does something else then I wanted it to do as I didn't want to set those properties (this matters in case they were not read-only and this would actually modify them).
MultiBinding documentation in Remarks section mentions that individual sub-bindings are allowed to override the MultiBinding mode value but it doesn't seem to work.
Maybe this could be solved somehow by expressing the dependency on the control and thumb widths (the read-only properties) somehow differently. For example registering to their notifications separately and enforcing update upon their change. However it does not seem natural to me. MultiBinding does on the other hand as after all left column width does depend on those three properties.
Here is the example XAML code.
<UserControl x:Class="WpfTest.ExampleUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="leftColumn" />
<ColumnDefinition x:Name="thumbColumn" Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Rectangle used in the left column for better visualization. -->
<Rectangle Grid.Column="0">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Black" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<!-- Thumb representing the Position property. -->
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" />
<!-- Rectangle used in the right column for better visualization. -->
<Rectangle Grid.Column="2">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Black" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>
And here is the corresponding code-behind
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfTest
{
public partial class ExampleUserControl : UserControl
{
#region PositionConverter
private class PositionConverter : IMultiValueConverter
{
public PositionConverter(ExampleUserControl owner)
{
this.owner = owner;
}
#region IMultiValueConverter Members
public object Convert(
object[] values,
Type targetType,
object parameter,
CultureInfo culture)
{
double thisActualWidth = (double)values[0];
double thumbActualWidth = (double)values[1];
double position = (double)values[2];
double availableWidth = thisActualWidth - thumbActualWidth;
double leftColumnWidth = availableWidth * position;
return new GridLength(leftColumnWidth);
}
public object[] ConvertBack(
object value,
Type[] targetTypes,
object parameter,
CultureInfo culture)
{
double thisActualWidth = owner.ActualWidth;
double thumbActualWidth = owner.thumbColumn.ActualWidth;
GridLength leftColumnWidth = (GridLength)value;
double availableWidth = thisActualWidth - thumbActualWidth;
double position;
if (availableWidth == 0.0)
position = 0.0;
else
position = leftColumnWidth.Value / availableWidth;
return new object[] {
thisActualWidth, thumbActualWidth, position
};
}
#endregion
private readonly ExampleUserControl owner;
}
#endregion
public ExampleUserControl()
{
InitializeComponent();
MultiBinding leftColumnWidthBinding = new MultiBinding()
{
Bindings =
{
new Binding()
{
Source = this,
Path = new PropertyPath("ActualWidth"),
Mode = BindingMode.OneWay
},
new Binding()
{
Source = thumbColumn,
Path = new PropertyPath("ActualWidth"),
Mode = BindingMode.OneWay
},
new Binding()
{
Source = this,
Path = new PropertyPath("Position"),
Mode = BindingMode.TwoWay
}
},
Mode = BindingMode.TwoWay,
Converter = new PositionConverter(this)
};
leftColumn.SetBinding(
ColumnDefinition.WidthProperty, leftColumnWidthBinding);
}
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register(
"Position",
typeof(double),
typeof(ExampleUserControl),
new FrameworkPropertyMetadata(0.5)
);
public double Position
{
get
{
return (double)GetValue(PositionProperty);
}
set
{
SetValue(PositionProperty, value);
}
}
}
}
Finally I found the solution myself. Actually it is in the documentation - I don't know how I missed that but I paid dearly (in wasted time) for it.
According to the documentation ConvertBack ought to return Binding.DoNothing on positions on which no value is to be set (in particular there were OneWay binding is desired). Another special value is DependencyProperty.UnsetValue.
This is not a complete solution as now IMultiValueConverter implementation must know where to return a special value. However I think most reasonable cases are covered by this solution.
It looks like MultiBinding doesn't work right. I've seen some unexpected behavior (something like yours) before in my practice. Also you can insert breakpoints or some tracing in converter and you can find some funny things about which converters and when are called.
So, if its possible, you should avoid using MultiBinding. E.g. you can add special property in your view model that will set value of your mutable property in its setter and return needed value using all three your properties in its getter. Its something like a MultiValueConverter inside a property =).
Hope it helps.

Resources