Binding Canvas Programmatically, Loading and Clearing them - c#

I am building an application like a noteBook in Landscape.
I have two canvas next to each other and I'm changing the Background inside them.
Every time I write on the two canvas and move to another 2 backgrounds, I need to save what I wrote on the two canvas and reload it once I move to these backgrounds once again, and clear them. And do the same for all the backgrounds.
<Grid x:Name="RenderedGrid" Width="1140" Height="770">
<ScrollViewer x:Name="DScrollViewer" MaxZoomFactor="2.0" MinZoomFactor="1.0" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" ZoomMode="Disabled">
<StackPanel Orientation="Horizontal">
<Canvas x:Name="inkCanvas0" HorizontalAlignment="Left" Width="570">
<Canvas.Background>
<ImageBrush x:Name="DImage0" ImageSource="{Binding}"/>
</Canvas.Background>
</Canvas>
<Canvas x:Name="inkCanvas1" HorizontalAlignment="Right" Width="570">
<Canvas.Background>
<ImageBrush x:Name="DImage1" ImageSource="{Binding}"/>
</Canvas.Background>
</Canvas>
</StackPanel>
</ScrollViewer>
</Grid>
I need to bind the Canvas in a way to this class instead of inkCanvas0 & inkCanvas1:
public class MyCanvas
{
public Canvas inkCanvas { get; set; }
}
List<MyCanvas> CanvasList = new List<MyCanvas>();
This is how I am creating Empty Canvas and storing them in CanvasList:
for (int i = 0; i < 24; i++)
{
Canvas canvas = new Canvas();
canvas.Name = "inkCanvas" + i;
MyCanvas mc = new MyCanvas();
mc.inkCanvas = canvas;
CanvasList.Add(mc);
}
I need to Bind these Canvas to the its corresponding page (inkCanvas0 to page0)
but I'm getting an exception in Xaml when I try to bind the Name of the Canvas.
<Canvas x:Name="{Binding}" HorizontalAlignment="Left" Width="570">
Exception: Catastrophic failure The name already exists in the tree.
How can I solve this? Is my logic of creating and loading the Canvas correct?

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?

Rectangle is not being drawn

I am trying to make a drawing program in WPF but I ran into some problems.
I can succesfully draw lines (yeeey) but I also want the ability to fill out space (my method is stacking alot of recntagles with the same starting point).
but when I switch my radiobutton from "line" to "fill" it doesnt draw anything.
-- drawing (boolean) is used to create the starting point of filling
Code
private void cnvs_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && linemode.IsChecked == true)
{
Line line = new Line();
drawing = true;
line.Stroke = currentbrush;
line.X1 = currentPoint.X - 9;
line.Y1 = currentPoint.Y - 9;
line.X2 = e.GetPosition(this).X - 9;
line.Y2 = e.GetPosition(this).Y - 9;
currentPoint = e.GetPosition(this);
cnvs.Children.Add(line);
} else if (cnvs.IsFocused == true)
{
if (!drawing) rectbeggining = e.GetPosition(this);
drawing = true;
Rectangle rect = new Rectangle();
rect.Stroke = currentbrush;
rect.StrokeThickness = 1;
rect.Height = (Math.Abs(e.GetPosition(this).Y) - Math.Abs(rectbeggining.Y));
rect.Width = (rectbeggining.X - e.GetPosition(this).X);
Canvas.SetLeft(rect, e.GetPosition(this).X);
Canvas.SetTop(rect, Math.Abs(e.GetPosition(this).Y));
cnvs.Children.Add(rect);
}
if(e.LeftButton == MouseButtonState.Released) drawing = false;
}
Xaml
<Grid>
<Border Name="CanvasBorder" BorderBrush="Black" BorderThickness="2" Margin="8,8,207,9"/>
<Canvas HorizontalAlignment="Left" Height="972" Margin="10,10,0,0" VerticalAlignment="Top" Width="1493" MouseDown="Canvas_MouseDown" MouseMove="cnvs_MouseMove" Name="cnvs">
<Canvas.Background>
<SolidColorBrush Color="White" Opacity="0"/>
</Canvas.Background>
</Canvas>
<ComboBox Name="selectcolor" Margin="1510,10,10,945" SelectedItem="Black" SelectionChanged="selectcolor_SelectionChanged" SelectedIndex="7">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="{Binding Name}" Width="16" Height="16" Margin="0,2,5,2" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Reset" HorizontalAlignment="Left" Margin="1510,939,0,0" VerticalAlignment="Top" Width="192" Click="Button_Click" Height="44"/>
<RadioButton GroupName="selectedMode" Name="linemode" Content="Line" HorizontalAlignment="Left" Margin="1510,232,0,0" VerticalAlignment="Top" IsChecked="True"/>
<RadioButton GroupName="selectedMode" Name="fillmode" Content="Fill" HorizontalAlignment="Left" Margin="1510,247,0,0" VerticalAlignment="Top"/>
<Label Content="Mode:" HorizontalAlignment="Left" Margin="1510,206,0,0" VerticalAlignment="Top"/>
</Grid>
I'm working on the map editor for generalstaff at the moment. You pick between line or area types of terrain, draw and when you finish a stroke it's processed.
Unless I've misunderstood your purpose that sounds pretty much like you're doing.
I essentially have an inkcanvas on top of a canvas.
It's rather more complicated than that. I bind viewmodels to an itemscontrol which has a canvas as it's itemspresenter and template data into the pieces of terrain.
I can bore at length on this stuff.
I use inkcanvas because it gives you smoother results than drawing directly on a canvas. I started off using a canvas like that but you get jiggly bits..
The strokeschanged event is used to tell when a stroke on the inkcanvas has finished.
Each stroke has a styluspointcollection StylusPoints and you can iterate through that to get the points.
Once you have the points, you can build a polygon.
I do that by templating and I bind the Points property of a Polygon to a Pointcollection from a viewmodel.
A polygon has a Fill property which you can set or bind to a solidcolorbrush.
And in that way you can draw any shape you like, then fill it with one colour.

How can I get the elements of a canvas?

How can I get the elements of a canvas?
I have this:
<Canvas x:Name="can" HorizontalAlignment="Left" Height="502" Margin="436,0,0,0" VerticalAlignment="Top" Width="336" OpacityMask="#FFC52D2D">
<Canvas.Background>
<SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ActiveCaptionColorKey}}"/>
</Canvas.Background>
<Button x:Name="btn_twoThreads" Content="Two Threads" Height="32" Canvas.Left="195" Canvas.Top="460" Width="131" Click="btn_twoThreads_Click"/>
<Button x:Name="btn_oneThread" Content="One Thread" Height="32" Canvas.Left="10" Canvas.Top="460" Width="131" Click="btn_oneThread_Click"/>
<Rectangle Fill="#FFF4F4F5" Height="55" Canvas.Left="10" Stroke="Black" Canvas.Top="388" Width="316"/>
</Canvas>
As you can see there are some objects on this canvas in the XAML Code. I need to get the the Rectangle object's details:
Rectangle r;
r = can.Children[2] as Rectangle; //I know this probably doesn't retrieve the rectangle object, but hopefully you can see what I am trying to achieve.
if (r != null)
{
MessageBox.Show("It's a rectangle");
}
I know I could probably just access the Rectangle object by just giving it a variable name in the XAML, but the canvas object is being drawn to in various classes, and I don't want to pass the rectangle to every class if it is already contained within the canvas.
You could try this:
// to show that you'll get an enumerable of rectangles.
IEnumerable<Rectangle> rectangles = can.Children.OfType<Rectangle>();
foreach(var rect in rectangles)
{
// do something with the rectangle
}
Trace.WriteLine("Found " + rectangles.Count() + " rectangles");
The OfType<>() is very useful because it checks the type and only yields an item if it is the right type. (it's casted already)

How to rotate an image within a StackPanel or Grid in Silverlight

I have an image control sits inside a Grid control. I already have a button to enable zoom-in to this image. After zoom-in, the Horizontal/vertical scroll bars are displayed. And then I rotate the image contained grid, the image and the grid scroll bar are messed up. How should I incorporate both zoom-in and rotate for the image control? The following are the code that I am using in my project.
The image control zoom-in code I used (x is the image control):
if ((x as Image) != null) { x.Height = x.Height * 1.3; x.Width = x.Width * 1.3; }
The rotation code I used (x is the image control):
if ((x as Image) != null)
{
RotateTransform rotate = new RotateTransform(); rotate.Angle = rotateAngle;
rotate.CenterX = x.Width / 2;
rotate.CenterY = x.Height / 2;
x.RenderTransform = rotate;
};
The XAML is:
<ScrollViewer x:Name="scrollViewer" Height="480" Width="615"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ScrollViewer.Content>
<Grid x:Name="ImageGrid">
<StackPanel x:Name="ImageStackPanel">
<Image Source="..." VerticalAlignment="Center" Width="220" Height="170" ></Image>
</StackPanel>
</Grid>
</ScrollViewer.Content>
</ScrollViewer>
Does anybody have any existing code snippet that I can borrow to resolve this trick?
I think you need to use TransformGroup to use more than one transform at the time:
ScaleTransform myScaleTransform = new ScaleTransform();
myScaleTransform.ScaleY = 3;
RotateTransform myRotateTransform = new RotateTransform();
myRotateTransform.Angle = 45;
// Create a TransformGroup to contain the transforms
// and add the transforms to it.
TransformGroup myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
myTransformGroup.Children.Add(myRotateTransform);
// Associate the transforms to the image.
x.RenderTransform = myTransformGroup;
This may work for your needs:
<Image x:Name="image" Source="myImageSource" Stretch="Uniform"
HorizontalAlignment="Center" VerticalAlignment="Center"
RenderTransformOrigin="0.5, 0.5">
<Image.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="Rotate"/>
<ScaleTransform x:Name="Scale" />
</TransformGroup>
</Image.RenderTransform>
</Image>
code behind:
Rotate.Angle = 45;
Scale = 0.25;
You may be missing the LayoutTransformer from the Silverlight Toolkit, and the AnimationMediator from one of the Toolkit developers.
With the LayoutTransformer you can set its content to anything, not just images, and apply any transformation with it, and as opposed to the usual RenderTransform, it will affect layout and actual sizes.
I have a similar scenario and I use it like this:
<Grid>
<fs:AnimationMediator x:Name="RotateMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding Angle, ElementName=RotateTransform, Mode=TwoWay}" />
<fs:AnimationMediator x:Name="ScaleXMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding ScaleX, ElementName=ScaleTransform, Mode=TwoWay}" />
<fs:AnimationMediator x:Name="ScaleYMediator" LayoutTransformer="{Binding ElementName=LayoutTransformer}" AnimationValue="{Binding ScaleY, ElementName=ScaleTransform, Mode=TwoWay}" />
<tkt:LayoutTransformer x:Name="LayoutTransformer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<tkt:LayoutTransformer.LayoutTransform>
<TransformGroup>
<RotateTransform x:Name="RotateTransform" />
<ScaleTransform x:Name="ScaleTransform" />
</TransformGroup>
</tkt:LayoutTransformer.LayoutTransform>
<Image x:Name="MyImage" Source="mysource.png" Width="600" Height="800" />
</tkt:LayoutTransformer>
</Grid>
Because of the lack of MultiBinding you'd probably additionally have to manually handle the input value (from Slider controls etc) changed events and then set the AnimationValues of RotateMediator etc accordingly.

Categories

Resources