WPF shape different opacity for stroke and fill - c#

This is a very basic question.
I want to be able to add a shape defining different opacity for fill and for stroke.
If I add this:
Ellipse e = new Ellipse();
e.Width = e.Height = 150;
e.Stroke = Brushes.Aqua;
e.Fill = Brushes.Chartreuse;
e.StrokeThickness = 20;
e.Opacity = .25;
plotCanvas.Children.Add(e);
I can only set 1 opacity. Instead I would like the fill to be 0.25 opaque and the stroke to be 1.0 opaque.
Thank you
Patrick

Setting the Opacity on the Ellipse will set the opacity for the entire control. What you want to do is create dedicated Brushes for Fill and Stroke, and control the opacity on the Brushes, i.e. :
SolidColorBrush strokeBrush = new SolidColorBrush(Colors.Aqua);
strokeBrush.Opacity = .25d;
Alternatively, you could control the Alpha channel of the brush:
SolidColorBrush strokeBrush = new SolidColorBrush(Color.FromArgb(/*a, r, g, b*/));

<Ellipse Stroke="Red" Width="200" Height="100" StrokeThickness="5">
<Ellipse.Fill>
<SolidColorBrush Color="Green" Opacity=".25"></SolidColorBrush>
</Ellipse.Fill>
</Ellipse>
Or in C# you can set the fill to a new SolidColorBrush with the desired opacity for the Opacity property.

You can't set the opacity twice for a single Shape object. Insteaf of setting the opacity twice you can add a Border to your Ellipse:
<Canvas x:Name="MyCanvas" Width="1000" Height="1000" Background="White">
<Border BorderBrush="Black" Opacity="1" BorderThickness="10" CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}">
<Ellipse Height="150" Width="150" Fill="Black" Opacity="0.25"></Ellipse>
</Border>
But since the Border is a rectangle which encloses the ellipse, you also need to set the cornerradius

Related

Cover an image with black and reveal parts of the image using mouse

So I am creating a "Map Viewer" for a program I am working on. Basically I want to display a map and have it resize the image to fit in the grid. For this I am using Viewbox to hold the image and resize it. I was attempting to use this code to reveal the map but it does not center the circle on the mouse and it does not retain the revealed portions of the map (it ONLY reveals where the mouse is and not where it has been).
Here is the XAML:
<Viewbox x:Name="MapHolder" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="1" Grid.RowSpan="7" Margin="25,5,25,15">
<Image x:Name="SelectedMap" Source="/wizard_dungeon.jpg" Stretch="Uniform" MouseMove="SelectedMap_MouseMove" >
<Image.OpacityMask>
<VisualBrush Stretch="None" >
<VisualBrush.Visual>
<Ellipse Width="400" Height="400" StrokeThickness="1" Fill="Black"/>
</VisualBrush.Visual>
<VisualBrush.RelativeTransform>
<TransformGroup>
<TranslateTransform x:Name="OpacityFilterTransform" X="1" Y="1"/>
</TransformGroup>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Image.OpacityMask>
</Image>
</Viewbox>
And the code-behind:
private void SelectedMap_MouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(this);
var height = MapHolder.ActualHeight;
var width = MapHolder.ActualWidth;
// with the position values, interpolate a TranslateTransform for the opacity mask
var transX = position.X / width;
var transY = position.Y / height;
OpacityFilterTransform.X = transX - 0.5;
OpacityFilterTransform.Y = transY - 0.5;
}
I want there to basically be a Image under a Black screen and I can erase the black screen to reveal the image in the areas I have erased.

WPF Canvas Fill

I currently have a WPF windows with a Canvas is 600 x 400. Is it possible to scale or automatically zoom in so that the lines take up as much as the 600x600 as possible?
<Border>
<Canvas x:Name="cMap" Width="600" Height="400">
<Line X1="5" Y1="5" X2 ="10" Y2="10" StrokeThickness="2" Stroke="Black"/>
<Line X1="10" Y1="10" X2 ="15" Y2="25" StrokeThickness="2" Stroke="Black"/>
</Canvas>
</Border>
My intention will be to add lines programmatically via code instead of XAML.
Thanks.
Not sure what is your exact usecase, but you could probably benefit by using ViewBox:
<Border>
<Viewbox Stretch="Uniform">
<Canvas x:Name="cMap" Width="15" Height="25">
<Canvas.LayoutTransform>
<ScaleTransform />
</Canvas.LayoutTransform>
<Line X1="5" Y1="5" X2 ="10" Y2="10" StrokeThickness="2" Stroke="Black"/>
<Line X1="10" Y1="10" X2 ="15" Y2="25" StrokeThickness="2" Stroke="Black"/>
</Canvas>
</Viewbox>
</Border>
Hope this helps you!
To draw lines in code you shoud do something like this:
Line line = new Line();
Thickness thickness = new Thickness(101,-11,362,250);
line.Margin = thickness;
line.Visibility = System.Windows.Visibility.Visible;
line.StrokeThickness = 4;
line.Stroke = System.Windows.Media.Brushes.Black;
line.X1 = 10;
line.X2 = 40;
line.Y1 = 70;
line.Y2 = 70;
and don't forget to add:
myCanvas.Children.Add(line);
to put those line in some place
from: Drawing lines in code using C# and WPF
To resize your canvas please read this:
Canvas is the only panel element that has no inherent layout
characteristics. A Canvas has default Height and Width properties of
zero, unless it is the child of an element that automatically sizes
its child elements. Child elements of a Canvas are never resized, they
are just positioned at their designated coordinates. This provides
flexibility for situations in which inherent sizing constraints or
alignment are not needed or wanted. For cases in which you want child
content to be automatically resized and aligned, it is usually best to
use a Grid element.
So as a solution you would make it inside a GRID or using the following code:
public class CanvasAutoSize : Canvas
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size constraint)
{
base.MeasureOverride(constraint);
double width = base
.InternalChildren
.OfType<UIElement>()
.Max(i => i.DesiredSize.Width + (double)i.GetValue(Canvas.LeftProperty));
double height = base
.InternalChildren
.OfType<UIElement>()
.Max(i => i.DesiredSize.Height + (double)i.GetValue(Canvas.TopProperty));
return new Size(width, height);
}
}
at your XAML:
<local:CanvasAutoSize VerticalAlignment="Top" HorizontalAlignment="Left"></local:CanvasAutoSize>
from: WPF: How to make canvas auto-resize?

Template doesnt work in Content Control in C#

Here is my xaml
<Canvas x:Name="DesignArea">
<ContentControl
Name="DesignerItem"
Width="100"
Height="100"
Canvas.Top="100"
Canvas.Left="100"
Template="{StaticResource DesignerItemTemplate}">
<Ellipse Fill="Blue" IsHitTestVisible="False"/>
</ContentControl>
<ContentControl Width="130"
MinWidth="50"
Height="130"
MinHeight="50"
Canvas.Top="150"
Canvas.Left="150"
Template="{StaticResource DesignerItemTemplate}">
<Path Fill="Blue"
Data="M 0,5 5,0 10,5 5,10 Z"
Stretch="Fill"
IsHitTestVisible="False"/>
</ContentControl>
</Canvas>
As you can see that the content control use the template just fine with all of it functionality
But I want to do it through C# like this
ContentControl ct = new ContentControl();
ControlTemplate Temp;
Temp = (ControlTemplate)this.FindResource("DesignerItemTemplate");
ct.Template = Temp;
Ellipse ell = new Ellipse();
ell.Fill = new SolidColorBrush(Colors.Black);
ell.Width = 100;
ell.Height = 100;
ell.IsHitTestVisible = false;
ct.Content = ell;
DesignArea.Children.Add(ct);
The Black Ellipse in C# did show up the grid template like the xaml shape
But I can't move, drag drop, resize or rotate Like the content control in the canvas What happen?
Oh I see why
Turn out I need to set the properties of the Canvas too in order to make it work
Canvas.SetLeft(ct, 300);
Canvas.SetTop(ct, 300);
ct.Width = 100;
ct.Height = 100;

Using VisualBrush as OpacityMask

I want to set OpacityMask to a control, but I need to build that mask dynamically.
Here is how it should look:
The width and height of the whole (red) rectangle is dynamic, based on width and height of parent control. But I need to place two small rectangles (static width and height) in top left and top right corner, as shown on image. So how can I make this happen?
I tried this code, but it doesn't work: (nothing is displayed at all)
<Border BorderBrush="#80FFFFFF" BorderThickness="1" CornerRadius="5">
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Height="2">
<Border Background="Transparent" Width="12" VerticalAlignment="Stretch" HorizontalAlignment="Left" />
<Border Background="Black" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<Border Background="Transparent" Width="12" VerticalAlignment="Stretch" HorizontalAlignment="Right" />
</StackPanel>
<Border Background="Black" />
</DockPanel>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
</Border>
Is it even valid to use VisualBrush this way (as a OpacityMask)?
If I understand your question correctly you want those Black squares in you image to be transparent?
Update: Uploaded sample project here: http://www.mediafire.com/?5tfkd1cxwfq0rct
I think the problem is that the Panel inside the VisualBrush won't stretch. You could get the desired effect by Binding the Width and Height of whatever Panel you use to the ActualWidth and ActualHeight of the Border
<Border Name="border" BorderBrush="Red" BorderThickness="1" CornerRadius="5">
<Border.OpacityMask>
<VisualBrush>
<VisualBrush.Visual>
<Grid Width="{Binding ElementName=border, Path=ActualWidth}"
Height="{Binding ElementName=border, Path=ActualHeight}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Transparent" Grid.Column="0"/>
<Rectangle Fill="Black" Grid.Column="1"/>
<Rectangle Fill="Transparent" Grid.Column="2"/>
<Rectangle Fill="Black" Grid.Row="1" Grid.ColumnSpan="3"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Border.OpacityMask>
<Grid>
<TextBlock Text="Testing OpacityMask with a rather long string................." Grid.ZIndex="3"/>
<Rectangle Fill="Green"/>
</Grid>
</Border>
Update Again
The DropShadowEffect for the Decorator Child of the Border seems to push the OpacityMask for the Border both Verticaly and Horizontaly. And what's even worse is that it seems to stack, so in your example when you have three DropShadowEffects for three nested Decorators, the sum of the BlurRadius is 45 (20+15+10) so the OpacityMask is pushed by a value of 45 (at least it looks like this is whats going on, but it's a little hard to tell..). You could try to compensate for this by increasing the ColumnDefinition Widths and RowDefinition Heights but I think it'll be hard to find a dynamic solution.
A better approach to your problem may be to use Border.Clip but that doesn't come easy either.
Point1: 0, 2
Point2: 12, 2
Point3: 12, 0
Point4: Width of Border - 12, 0
Point5: Width of Border - 12, 2
Point5: Width of Border, 2
Point6: Width of Border, Height of Border
Point7: 0, Height of Border
Update 3
Came up with a better solution that doesn't require so many Bindings. Create a custom class that derives from Border and override GetLayoutClip. This works both in Designer and Runtime. To increase flexibility of ClippedBorder you could introduce some Dependency Properties to use instead of the hardcoded 2 and 12. New sample app here: http://www.mediafire.com/?9i13rrqpbmzdbvs
public class ClippedBorder : Border
{
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures = new PathFigureCollection();
//Point1: 0, 2
PathFigure pathFigure = new PathFigure();
pathFigure.StartPoint = new Point(0, 2);
//Point2: 12, 2
LineSegment lineSegment1 = new LineSegment();
lineSegment1.Point = new Point(12, 2);
//Point3: 12, 0
LineSegment lineSegment2 = new LineSegment();
lineSegment2.Point = new Point(12, 0);
//Point4: Width of Border - 12, 0
LineSegment lineSegment3 = new LineSegment();
lineSegment3.Point = new Point(this.ActualWidth-12, 0);
//Point5: Width of Border - 12, 2
LineSegment lineSegment4 = new LineSegment();
lineSegment4.Point = new Point(this.ActualWidth-12, 2);
//Point5: Width of Border, 2
LineSegment lineSegment5 = new LineSegment();
lineSegment5.Point = new Point(this.ActualWidth, 2);
//Point6: Width of Border, Height of Border
LineSegment lineSegment6 = new LineSegment();
lineSegment6.Point = new Point(this.ActualWidth, this.ActualHeight);
//Point7: 0, Height of Border
LineSegment lineSegment7 = new LineSegment();
lineSegment7.Point = new Point(0, this.ActualHeight);
pathFigure.Segments.Add(lineSegment1);
pathFigure.Segments.Add(lineSegment2);
pathFigure.Segments.Add(lineSegment3);
pathFigure.Segments.Add(lineSegment4);
pathFigure.Segments.Add(lineSegment5);
pathFigure.Segments.Add(lineSegment6);
pathFigure.Segments.Add(lineSegment7);
pathGeometry.Figures.Add(pathFigure);
return pathGeometry;
}
}

Border around image in c#

How to have a black border around image in c#.the image exists inside a wrap panel
Just add a border to the Image:
<toolkit:WrapPanel x:Name="wp">
<Border BorderBrush="Black" BorderThickness="5" >
<Image Source="myimage.png" />
</Border>
</toolkit:WrapPanel>
Or add it to the WrapPanel in code:
var b = new Border
{
BorderBrush = new SolidColorBrush(Colors.Black),
BorderThickness = new Thickness(5)
};
var bi = new BitmapImage
{
UriSource = new Uri("/myimage.png", UriKind.Relative)
};
b.Child = new Image {Source = bi};
wp.Children.Add(b);
Use a border element and configure it and set the background to a imagebrush with your image as source.
HereĀ“s some XAML:
<Border BorderBrush="Black">
<Border.Background>
<ImageBrush ImageSource="<Your Image>"/>
</Border.Background>
</Border>
You can also define a CornerRadius on the Border to make rounded corners. This will also apply to the image.

Categories

Resources