Bind AngleProperty of the image in code - c#

UPDATE
I want to add an image to my canvas programmatically. I want to bind X, Y coords and Angle of the Image. X and Y binding works fine, but the Angle doesn't. Here is the code:
public void AddNewImage()
{
Image newImage = new Image
{
Source = new BitmapImage(new Uri(imagePath))
};
These two work fine
Binding binding1 = new Binding("X");
binding1.Mode = BindingMode.TwoWay;
binding1.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(newImage, Canvas.LeftProperty, binding1);
Binding binding2 = new Binding("Y");
binding2.Mode = BindingMode.TwoWay;
binding2.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(newImage, Canvas.TopProperty, binding2);
That's why I use a group
TransformGroup tg = new TransformGroup();
tg.Children.Add(new TranslateTransform(xTranslate,yTranslate));
tg.Children.Add(new RotateTransform());
newImage.RenderTransform = tg;
This one doesn't work
Binding binding3 = new Binding("Angle");
binding3.Mode = BindingMode.TwoWay;
binding3.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(tg, RotateTransform.AngleProperty, binding3);
MainCanvas.Children.Add(newImage);
}
When I change X and Y - image moves, but when I change angle - nothing happens.
What do I do wrong?

You're setting the binding on the TransformGroup, which has no Angle property. Instead of that you should set the binding directly on the RotateTransform.
var rotateTransform = new RotateTransform();
var tg = new TransformGroup();
tg.Children.Add(rotateTransform);
newImage.RenderTransform = tg;
var binding3 = new Binding("Angle");
...
BindingOperations.SetBinding(rotateTransform, RotateTransform.AngleProperty, binding3);

Related

Line with its length self-adjusting to parent-Panel's ActualWidth property (data-binding)

I have a vertical StackPanel and I want it to contain Line-child with .X2 value binded to parent-StackPanel's ActualWidth. Something like:
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Vertical;
(...)
Line line = new Line();
line.X1 = 0;
line.X2 = <Some binding to sp.ActualWidth>;
// line.Y1 = line.Y2 - will it set it's value to 0 by default?
line.StrokeThickness = 2;
(...)
sp.Children.Add(line);
Should I use INotifyPropertyChanged interface? Is this the most proper way?
INotifyPropertyChanged is not necessary.
Just create a binding and setbinding to X2Property of the line.
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Vertical;
Line line = new Line();
line.X1 = 0;
var bind = new Binding()
{
Source = sp,
Path = new PropertyPath("ActualWidth")
};
BindingOperations.SetBinding(line, Line.X2Property, bind);
line.StrokeThickness = 2;
line.Stroke=Brushes.Black;
line.Margin=new Thickness(10);
sp.Children.Add(line);
An alternative to a Line with a bound X2 property and a StrokeThickness of 2 would be a Rectangle with a Height of 2. Its width is automatically set to the StackPanel width.
var sp = new StackPanel
{
Orientation = Orientation.Vertical
};
...
var line = new Rectangle
{
Height = 2,
Fill = Brushes.Black
};
sp.Children.Add(line);

How can I prevent Silverlight from converting TransformGroup to MatrixTransform?

I have created a TransformGroup with multiple Transforms and assigned this to an Image's RenderTransform property:
TransformGroup group = new TransformGroup();
ScaleTransform st = new ScaleTransform();
group.Children.Add(st);
TranslateTransform tt = new TranslateTransform();
group.Children.Add(tt);
image.RenderTransform = group;
If I want to get an individual transform from this group in WPF, I can do this:
TranslateTransform tt = (TranslateTransform)((TransformGroup)image.RenderTransform).Children.First(tr => tr is TranslateTransform);
However, in Silverlight, this TransformGroup is apparently implicitly converted to a MatrixTransform, because running the above code gives the error:
Unable to cast object of type 'System.Windows.Media.MatrixTransform' to type 'System.Windows.Media.TransformGroup'.
Is there any way to avoid the conversion from TransformGroup to MatrixTransform? If not, how can I recover the individual Transforms from the MatrixTransform?
D'oh! I had accidentally tried to use the TransformGroup before assigning it, so image.RenderTransform was still set to the default of MatrixTransform.
In case anyone needed to use a MatrixTransform to perform a ScaleTransform followed by a TranslateTransform, given these transforms:
ScaleTransform st = new ScaleTransform();
TranslateTransform tt = new TranslateTransform();
The following two options should provide equivalent results:
Using TransformGroup:
TransformGroup group = new TransformGroup();
group.Children.Add(st);
group.Children.Add(tt);
image.RenderTransform = group;
Using MatrixTransform:
MatrixTransform mt = new MatrixTransform
{
Matrix = new Matrix
{
M11 = st.ScaleX,
M22 = st.ScaleY,
OffsetX = tt.X,
OffsetY = tt.Y
}
};
image.RenderTransform = mt;

Using DoubleAnimation code behind in WinRT (XAML)

I have some problem. This is my code behind:
var s = new Storyboard();
var dAnimation = new DoubleAnimation();
dAnimation.RepeatBehavior = RepeatBehavior.Forever;
dAnimation.AutoReverse = true;
dAnimation.EnableDependentAnimation = true;
dAnimation.From = 200;
dAnimation.To = 300;
Storyboard.SetTarget(dAnimation, viewBox);
Storyboard.SetTargetProperty(dAnimation, "Width");
dAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(2000));
s.Children.Add(dAnimation);
s.Begin();
viewBox is on Canvas and has just a few property: Canvas.Left, Canvas.Top, Height and Width. Code from codebehind working great with Opacity, but not working with Width or Height. Codebehind work when user pointer is entered. I want do it without triggers. Found some solutions for Silverlight and WPF:
WinRT/Metro Animation in code-behind
they are not working. I dont undersstand why it work with opacity and didn't work with Width and Height Any ideas?
You cannot animate Width with a DoubleAnimation, you need to use a DoubleAnimationUsingKeyFrames.
var animation = new DoubleAnimationUsingKeyFrames();
var frame = new EasingDoubleKeyFrame { KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(2000)), Value = 300});
animation.KeyFrames.Add(frame);
Storyboard.SetTargetProperty(animation, "(FrameworkElement.Width)");
Storyboard.SetTarget(animation, viewBox);
Storyboard.Children.Add(animation);
I didn't check ViewBox,but I can with DoubleAnimation for Grid.
Code is like below
var s = new Storyboard();
var widthAnim = new DoubleAnimation()
{
To = w,
Duration=new Duration(TimeSpan.FromMilliseconds(duration))
};
widthAnim.EnableDependentAnimation = true;
Storyboard.SetTarget(widthAnim,elem);
Storyboard.SetTargetProperty(widthAnim,"Width");
s.Children.Add(widthAnim);
s.Begin();

How to call Binding function from a method?

I have a Slider Binded to the ScaleX of a Line, like the following code:
/* Graphics on Canvas */
Line lR = new Line();
lR.X1 = 0;
lR.Y1 = 0;
lR.X2 = 150;
lR.Y2 = 150;
lR.Stroke = new SolidColorBrush(Colors.Blue);
lR.StrokeThickness = 2;
/* declare ScaleTransformation */
ScaleTransform lRSt = new ScaleTransform();
TransformGroup lRTran = new TransformGroup();
lRTran.Children.Add(lRSt);
lR.RenderTransform = lRTran;
Binding sliderRBind1 = new Binding();
sliderRBind1.Source = sliderR;
sliderRBind1.Path = new PropertyPath("Value");
BindingOperations.SetBinding(lRSt, ScaleTransform.ScaleXProperty, sliderRBind1);
BindingOperations.SetBinding(lRSt, ScaleTransform.ScaleYProperty, sliderRBind1);
/* Slider - Slider should be placed outside the canvas to prevent being redrawn */
Slider sliderR = new Slider();
sliderR.Minimum = 1;
sliderR.Maximum = 3;
sliderR.Value = 1;
sliderR.TickPlacement = TickPlacement.BottomRight;
sliderR.TickFrequency = 0.2;
sliderR.IsSnapToTickEnabled = true;
Code works fine. But if I move the ScaleTransform to a method, the Binding is lost.
/* Graphics on Canvas */
Line lR = new Line();
lR.X1 = 0;
lR.Y1 = 0;
lR.X2 = 150;
lR.Y2 = 150;
lR.Stroke = new SolidColorBrush(Colors.Blue);
lR.StrokeThickness = 2;
/* declare ScaleTransformation */
ScaleTransform lRSt = new ScaleTransform();
TransformGroup lRTran = new TransformGroup();
lRTran.Children.Add(lRSt);
lR.RenderTransform = lRTran;
LineSliderR(lR);
/* Slider - Slider should be placed outside the canvas to prevent being redrawn */
Slider sliderR = new Slider();
sliderR.Minimum = 1;
sliderR.Maximum = 3;
sliderR.Value = 1;
sliderR.TickPlacement = TickPlacement.BottomRight;
sliderR.TickFrequency = 0.2;
sliderR.IsSnapToTickEnabled = true;
public void LineSliderR(Line lRSt)
{
Binding sliderRBind1 = new Binding();
sliderRBind1.Source = sliderR;
sliderRBind1.Path = new PropertyPath("Value");
BindingOperations.SetBinding(lRSt, ScaleTransform.ScaleXProperty, sliderRBind1);
BindingOperations.SetBinding(lRSt, ScaleTransform.ScaleYProperty, sliderRBind1);
}
Why did the Binding fail if it's called to a separate method?
The two codes are (to me) identical. Both contain four same, identical, "things":
a Line, a ScaleTransform, a Binding, and a Slider.
The first code has all 4 "things" in a function. The second code has the Binding call from a separate function. Both compiles without error: However, the Binding (between Line and Slider) works OK on the first code, but not the second. I am trying get the second code to work because I have many Line's and I don't want to repeat myself.
In your second code block, inside the method LineSliderR, lRSt is an object of type Line, while in your first block it is of type ScaleTransform.
If you want your second block to behave like the first, your method LineSliderR, should accept a ScaleTransform' as parameter:
public void LineSliderR(ScaleTransform lRSt)

Use Binding to connect Ellipses with a Line

I use the following Bindings to connect two Ellipses with a line:
Line l = new Line();
l.Stroke = Brushes.Green;
l.StrokeThickness = 3;
Binding x1 = new Binding(); x1.Path = new PropertyPath(Canvas.LeftProperty);
Binding y1 = new Binding(); y1.Path = new PropertyPath(Canvas.TopProperty);
Binding x2 = new Binding(); x2.Path = new PropertyPath(Canvas.LeftProperty);
Binding y2 = new Binding(); y2.Path = new PropertyPath(Canvas.TopProperty);
x1.Source = y1.Source = e;
x2.Source = y2.Source = e1;
l.SetBinding(Line.X1Property, x1);
l.SetBinding(Line.Y1Property, y1);
l.SetBinding(Line.X2Property, x2);
l.SetBinding(Line.Y2Property, y2);
Dependencies.Children.Add(l);
This works great, but the problem is, the lines are drawn on the left top of the Ellipse. I'd like to use the center of the Ellipse. Therefore I would have to add Ellipse#width / 2 to the x property. But how can I do that?
You can use IValueConverter to change/transform values while Binding.
Here is something I cooked up:
Canvas Dependencies = new Canvas();
Ellipse e1 = new Ellipse() { Width = 200, Height = 200, Stroke = Brushes.Red, StrokeThickness = 1 };
Ellipse e2 = new Ellipse() { Width = 200, Height = 200, Stroke = Brushes.Red, StrokeThickness = 1 };
Line l = new Line();
l.Stroke = Brushes.Green;
l.StrokeThickness = 3;
Binding x1 = new Binding(); x1.Path = new PropertyPath(Canvas.LeftProperty); x1.Converter = new MyConverter(); x1.ConverterParameter = e1;
Binding y1 = new Binding(); y1.Path = new PropertyPath(Canvas.TopProperty); y1.Converter = new MyConverter(); y1.ConverterParameter = e1;
Binding x2 = new Binding(); x2.Path = new PropertyPath(Canvas.LeftProperty); x2.Converter = new MyConverter(); x2.ConverterParameter = e2;
Binding y2 = new Binding(); y2.Path = new PropertyPath(Canvas.TopProperty); y2.Converter = new MyConverter(); y2.ConverterParameter = e2;
x1.Source = y1.Source = e1;
x2.Source = y2.Source = e2;
l.SetBinding(Line.X1Property, x1);
l.SetBinding(Line.Y1Property, y1);
l.SetBinding(Line.X2Property, x2);
l.SetBinding(Line.Y2Property, y2);
Dependencies.Children.Add(e1);
Dependencies.Children.Add(e2);
Dependencies.Children.Add(l);
SizeChangedEventHandler act = (Object s, SizeChangedEventArgs args) =>
{
BindingOperations.GetBindingExpressionBase(l, Line.X1Property).UpdateTarget();
BindingOperations.GetBindingExpressionBase(l, Line.Y1Property).UpdateTarget();
BindingOperations.GetBindingExpressionBase(l, Line.X2Property).UpdateTarget();
BindingOperations.GetBindingExpressionBase(l, Line.Y2Property).UpdateTarget();
};
e1.SizeChanged += act;
e2.SizeChanged += act;
Canvas.SetLeft(e1, 200);
Canvas.SetTop(e1, 200);
Canvas.SetLeft(e2, 500);
Canvas.SetTop(e2, 500);
Grid2.Children.Add(Dependencies);
Converter:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Ellipse e = parameter as Ellipse;
Double d = (Double)value;
return d + (e.ActualWidth / 2);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Ellipse e = parameter as Ellipse;
Double d = (Double)value;
return d - (e.ActualWidth / 2);
}
}
Note that the converter considers Ellipse.Width only. You will need to modify it to make it work correctly.
Your binding now depends on two properties, the Canvas.Left (or Canvas.Top), and the Ellipse.ActualWidth (or height). To achieve this you can use a multibinding. See the following examples:
http://www.switchonthecode.com/tutorials/wpf-tutorial-using-multibindings
However, there are other, possibly simpler alternatives. You could use a render transform to translate your ellipses by an X position which is half of its width and a Y position that is half of its height, to centre your ellipse at the position given by Canvas.Left and canvas.Top
Regards, Colin E.

Categories

Resources