C# WPF) DropShadowEffect is sometimes missing when dynamically adding a control - c#

foreach (DataRow dr in dt.Rows)
{
Rectangle rectangle_timeline = new Rectangle();
rectangle_timeline.Height = 19;
rectangle_timeline.Cursor = Cursors.Hand;
rectangle_timeline.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
Grid_Timeline.Children.Add(rectangle_timeline);
}
I dynamically add a Rectangle with above simple code as shown image.
However, sometimes, randomly, there're rectangles without DropShadowEffect like yellow rectangles and 1 blue rectangle at the lowest.
As you see the code, if a rectangle is added, the code for DropShadowEffect should have to be worked.
I'm wondering why this is happening.
Thank you !
XAML code added-
<Grid x:Name="Grid_Timeline" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" Width="1159" HorizontalAlignment="Left" VerticalAlignment="Top" SnapsToDevicePixels="True">
</Grid>
Minimal code to re-produce is added-
private void Window_Loaded(object sender, RoutedEventArgs e)
{
int count_each_category = 0;
string current_therapeutic_category = String.Empty;
foreach (DataRow dr_test in dt.Rows)
{
if (dr_test != null)
{
if (current_category == String.Empty)
{
count_each_category++;
current_category = dr_test["category"].ToString();
}
else
{
if (current_category == dr_test["category"].ToString())
{
// empty
}
else
{
count_each_category++;
current_category = dr_test["category"].ToString();
}
}
Rectangle rectangle_test = new Rectangle();
rectangle_test.HorizontalAlignment = HorizontalAlignment.Left;
rectangle_test.VerticalAlignment = VerticalAlignment.Top;
rectangle_test.Margin = new Thickness(119 + (((Convert.ToDateTime(dr_test["date"]) - DateTime.Today.AddYears(-10)).TotalDays) * 0.27), count_each_category * 50, 0, 0);
rectangle_test.Width = 0.27 * Convert.ToInt32(dr_test["howlong"]);
rectangle_test.Height = 19;
rectangle_test.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
rectangle_test.Fill = Brushes.LightGreen;
Grid_Timeline.Children.Add(rectangle_test);
}
}
}
XAML code of WPF Window
<Window x:Class="OperationSWdummy.Green_Timeline"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OperationSWdummy"
mc:Ignorable="d"
Title="Green_Timeline" Width="1165" Background="White" ResizeMode="CanMinimize" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" SnapsToDevicePixels="True" Loaded="Window_Loaded">
<Grid x:Name="Grid_Timeline" ScrollViewer.VerticalScrollBarVisibility="Auto" UseLayoutRounding="True" Width="1159" HorizontalAlignment="Left" VerticalAlignment="Top" SnapsToDevicePixels="True">
</Grid>
</Window>
Raw data of dt (DataTable)
date datetime
category NVARCHAR
howlong int
date category howlong
2015-01-25 HHH 60
2014-04-03 AAA 60
2015-01-25 DDD 60
2014-04-03 UUU 60
2015-01-25 CCC 60
2015-11-07 PPP 30
2015-01-25 TTT 60
2015-11-07 MMM 30
2015-02-22 MMM 30
2015-11-07 VVV 8
Result of above minimal code
Another minimal code to create rectangles randomly-
for (int k = 0; k < 191; k++)
{
Rectangle rec_test = new Rectangle();
rec_test.Margin = new Thickness(random_margin.Next(100, 1000), 29 * (k % 10), 0, 0);
rec_test.Width = random_width.Next(10, 40);
rec_test.HorizontalAlignment = HorizontalAlignment.Left;
rec_test.VerticalAlignment = VerticalAlignment.Top;
rec_test.Height = 14;
rec_test.Cursor = Cursors.Hand;
rec_test.Effect = new DropShadowEffect
{
Color = new Color { A = 255, R = 0, G = 0, B = 0 },
Direction = 315,
ShadowDepth = 5,
Opacity = 1
};
rec_test.Fill = Brushes.Green;
Grid_Timeline.Children.Add(rec_test);
}

Have you tried to use Canvas to place your rectangles?
Having Canvas to place your shapes such as rectangles, circles, or even graphical paths with random locations is better than placing on fixed layout container such as Grid or StackPanel.
It is quite known that if you just placed shapes with effects such as DropShadow on fixed layout (with arbitrary X,Y locations) such as Grid and StackPanel, your shapes may be clipped. Because the shapes edges will always be checked for pixel boundaries at render time, and often this is caused by overlapped rendering calculations when it tries to recalculate edges of fixed layout controls.

Could be a driver problem with your graphics hardware. Try disabling hardware acceleration and see if it helps.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;
}
}
In general I advise against using the DropShadowEffect as it will have a significantly negative impact on rendering performance.

Related

Popup Not Showing

On a tap event I would like to show a popup all within code behind, but my popup is not displaying?
void PopupDisplay_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
if (sender != null)
{
p = new Popup
{
Width = 480,
Height = 580,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
VerticalAlignment = System.Windows.VerticalAlignment.Center
};
Border b = new Border();
b.BorderBrush = new SolidColorBrush(Colors.Gray);
b.BorderThickness = new Thickness(2);
b.Margin = new Thickness(10, 10, 10, 10);
p.Child = b;
p.IsOpen = true;
}
}
Think you're trying to Popup over a top-level control like a Pivot which is very buggy.
See Popup with Pivots
If it was a Grid, it would pop up without problem. To fix this you will have to add it to the same visual level as the Pivot like so:
<Grid x:Name="ContentPanel" Margin="0,0,0,0">
<phone:Pivot x:Name="MainDisplay">
<!-- more code -->
</phone:Pivot>
</Grid>
Then in your code-behind
// I made with a thickness of 100, so we can see the border better
Popup p;
p = new Popup
{
Width = 480,
Height = 580,
VerticalOffset = 0
};
Border b = new Border();
b.BorderBrush = new SolidColorBrush(Colors.Red);
b.BorderThickness = new Thickness(100);
b.Margin = new Thickness(10, 10, 10, 10);
b.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
b.VerticalAlignment = System.Windows.VerticalAlignment.Stretch;
p.Child = b;
// add it to the same level as the pivot to over ride pivot
this.ContentPanel.Children.Add(p);
p.IsOpen = true;

How to Add Rectangle one after other vertically on Canvas in WPF using C#?

I have a Canvas in my WPF application. I am adding the Rectangle on button click. The Width is Fixed but, Height is value entered by user in TextBox/GridCell.
When i add rectangle on Canvas with specifying Height. it adds the rectangle but, it doesnt appear one after other. Any idea?
In .xaml.cs:
int width=200;
Reactangle rect;
static int val=0;
Protected void Add()
{
rect = new Rectangle();
rect.Stroke = Brushes.Red;
rect.StrokeThickness = 1;
rect.Height = Convert.ToInt32(txtheight.Text);
rect.Width = width;
Canvas.SetLeft(rect,100);
Canvas.SetTop(rect,rect.Height);
rect.Tag = val;
canvasboard.Children.Add(rect);
val=val+1;
}
This adds Rectangle but not Exactly one after other on canvas.
<Canvas Name="canvasboard" Background="White" Margin="2">
</Canvas>
<TextBox Name="txtheight" Width="150"/>
Note: I cant use WrapPanel or StackPanel for this form. and want to make changes in existing code.
Help Appreciated!
If all your adding is sequential elements vertically into this Canvas, you can do it without having to add a new variable in class scope as well.
private void Add() {
rect = new Rectangle {
Stroke = Brushes.Red,
StrokeThickness = 1,
Height = Convert.ToDouble(txtheight.Text),
Width = width
};
Canvas.SetLeft(rect, 100);
double canvasTop = 0.0;
if (canvasboard.Children.Count > 0) {
var lastChildIndex = canvasboard.Children.Count - 1;
var lastChild = canvasboard.Children[lastChildIndex] as FrameworkElement;
if (lastChild != null)
canvasTop = Canvas.GetTop(lastChild) + lastChild.Height + 1;
}
Canvas.SetTop(rect, canvasTop);
rect.Tag = val++;
canvasboard.Children.Add(rect);
}
Try storing a local variable that maintains the combined height of all Rectangles:
private double _top = 0;
protected void Add()
{
var rect = new Rectangle();
rect.Stroke = Brushes.Red;
rect.StrokeThickness = 1;
rect.Height = double.Parse(txtheight.Text);
rect.Width = 20;
Canvas.SetLeft(rect, 100);
Canvas.SetTop(rect, _top);
_top += rect.Height;
rect.Tag = val;
canvasboard.Children.Add(rect);
val = val + 1;
}

Drawing Multiple Rectangles on a Canvas in C#

I'm trying to draw a row of rectangles across my Canvas. When I run the following code, I only get one rectangle, even though my canvas element says it has 12 children.
Dimensions is a class with 2 integer properties, Height and Width. The canvas I am drawing this is on 400px by 600px.
Dimensions windowDimensions = new Dimensions()
{
Width = (int)cvsGameWindow.Width,
Height = (int)cvsGameWindow.Height
};
//init rectangles
for (int i = 0; i < windowDimensions.Width; i+=50)
{
Rectangle rect = new Rectangle(); //create the rectangle
rect.StrokeThickness = 1; //border to 1 stroke thick
rect.Stroke = _blackBrush; //border color to black
rect.Width = 50;
rect.Height = 50;
rect.Name = "box" + i.ToString();
Canvas.SetLeft(rect,i * 50);
_rectangles.Add(rect);
}
foreach (var rect in _rectangles)
{
cvsGameWindow.Children.Add(rect);
}
and the private members declared at the top of my code:
private SolidColorBrush _blackBrush = new SolidColorBrush(Colors.Black);
private SolidColorBrush _redBrush = new SolidColorBrush(Colors.Red);
private SolidColorBrush _greenBrush = new SolidColorBrush(Colors.Green);
private SolidColorBrush _blueBrush = new SolidColorBrush(Colors.Blue);
private List<Rectangle> _rectangles = new List<Rectangle>();
This is the culprit:
Canvas.SetLeft(rect,i * 50);
On the first loop, with i=0, you're setting Canvas.Left = 0; Since your for loop is doing i+=50, on the second loop i will be 50, so you'll be setting Canvas.Left = 2500. You said your Canvas is 400x600, so your rectangles are off-screen.
The simplest fix: use Canvas.SetLeft(rect, i) - since i is increasing in increments of 50.

Strange behaviour after using the ScaleTransform on WP7

I have two Image controls on each other and I set the alpha channel of some pixels to zero, from the upper one(this is the colorful). But after I "zoom" (width the ScaleTransform), a "border" will be visible around the pixels that have set. Here is a screenshot:
Here is the code:
<Grid Name="grdPhotos">
<Image Stretch="None" Source="picture_grayscale.jpg" Name="photo1" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Image Stretch="None" Source="picture.jpg" Name="photo2" MouseLeftButtonDown="photo2_MouseLeftButtonDown" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
private void photo2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var photo = photo2.Source as WriteableBitmap; // A WriteableBitmap is created before from the Source BitmapImage
for (int x = 100; x < 200; x++)
{
for (int y = 100; y < 200; y++)
{
int index = Convert.ToInt32(photo.PixelWidth * y + x);
if (index > 0 && index < photo.Pixels.Length)
SetPixelAlphaChannel(ref photo.Pixels[index], 0);
}
}
var transform = new ScaleTransform { ScaleX = 2, ScaleY = 2 };
photo1.RenderTransform = photo2.RenderTransform = transform;
}
public void SetPixelAlphaChannel(ref int pixel, byte value)
{
var color = ColorFromPixel(pixel);
if (color.A == value)
return;
color.A = value;
pixel = ColorToPixel(color);
}
private Color ColorFromPixel(int pixel)
{
var argbBytes = BitConverter.GetBytes(pixel);
return new Color { A = argbBytes[3], R = argbBytes[2], G = argbBytes[1], B = argbBytes[0] };
}
private int ColorToPixel(Color color)
{
var argbBytes = new byte[] { color.B, color.G, color.R, color.A };
return BitConverter.ToInt32(argbBytes, 0);
}
Why is this? Or how can I implement a zoom functionality without this "border"? Thanks a lot.
When you zoom an image, the pixel values will be interpolated, this will result in pixels in the border are you are observing being the result of interpolating your transparent pixels with their non transparent neighbours. Unfortunately you cannot control the interpolation behaviour of render transforms. You are going to have to do this yourself, possibly via WriteableBitmap.

How to force text into a specific width?

I use a GridView to display my layout. To this GridView i manually add some RowDefinitions and in this RowDefinitions I add 1 Canvas containing 2 rectangles:
foreach (Method m in sourceFile.getMethods())
{
if (!m.getName().StartsWith("<") && !m.getName().EndsWith(">"))
{
RowDefinition row = new RowDefinition();
row.Height = GridLength.Auto;
MethodsContainer.RowDefinitions.Add(row);
Canvas c = new Canvas();
c.Width = width;
c.Height = height;
c.Tag = m;
Contacts.AddPreviewContactDownHandler(c, new ContactEventHandler(onContactDown));
Rectangle r1 = new Rectangle();
r1.Height = height;
r1.Width = m.getLoc() * (width / 1000);
Canvas.SetLeft(r1, 0);
Canvas.SetLeft(r1, 0);
r1.Fill = Brushes.Red;
Rectangle r2 = new Rectangle();
r2.Height = height;
r2.Width = width - r1.Width;
Canvas.SetTop(r2, 0);
Canvas.SetLeft(r2, r1.Width);
r2.Fill = Brushes.Blue;
c.Children.Add(r1);
c.Children.Add(r2);
Grid.SetRow(c, rowCounter);
MethodsContainer.Children.Add(c);
rowCounter++;
}
}
The canvas is 200px width and 30px height. Both rectangles fill the Canvas exactly. Now i want to add some text over this both Rectangles. But I don't know how long the text is. However I want to force that the text is always printed into this 200px. How can I achieve that?
Sounds like you could make use of a ViewBox. This will make your text stretch both horizontally and vertically. I assume that's what you want if I understood the question correctly. Example in xaml
<Canvas Width="200"
Height="30">
<Viewbox Width="200"
Height="30">
<TextBlock Text="Text that will fit in 200 Width"/>
</Viewbox>
</Canvas>
And in code behind it will be like
TextBlock textBlock = new TextBlock();
textBlock.Text = "Text that will fit in 200 Width";
Viewbox viewBox = new Viewbox();
viewBox.Width = width;
viewBox.Height = height;
viewBox.Child = textBlock;
c.Children.Add(viewBox);
var txt = new TextBlock();
txt.MaxWidth = 200;
// optionally you might want the text to wrap if too long
txt.TextWrapping = TextWrapping.Wrap;
By the way, it's not pixels in WPF, it's a device-independent unit (1/96th inch) measurement.

Categories

Resources