Here's a little Brownian motion demo in WPF:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Threading;
namespace WpfBrownianMotion
{
static class MiscUtils
{
public static double Clamp(this double n, int low, double high)
{
return Math.Min(Math.Max(n, low), high);
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Width = 500;
Height = 500;
var canvas = new Canvas();
Content = canvas;
var transform = new TranslateTransform(250, 250);
var circle = new Ellipse()
{
Width = 25,
Height = 25,
Fill = Brushes.PowderBlue,
RenderTransform = transform
};
canvas.Children.Add(circle);
var random = new Random();
var thread =
new Thread(
() =>
{
while (true)
{
Dispatcher.Invoke(
DispatcherPriority.Normal,
(ThreadStart)delegate()
{
transform.X += -1 + random.Next(3);
transform.Y += -1 + random.Next(3);
transform.X = transform.X.Clamp(0, 499);
transform.Y = transform.Y.Clamp(0, 499);
});
}
});
thread.Start();
Closed += (s, e) => thread.Abort();
}
}
}
My question is this. In cases like this where the standard WPF animation facility isn't being used, is the above approach using Thread and Dispatcher the recommended approach to take?
In general, I sometimes have states that need to be updated and rendered and the animation facility isn't a good fit. So I need a way to do the update and rendering in a separate thread. Just wondering if the above approach is the Right Thing.
Clemens in the comments above suggested using DispatcherTimer. Indeed, this does simplify the code quite a bit. Here's a version taking that approach:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace WpfBrownianMotion
{
static class MiscUtils
{
public static double Clamp(this double n, int low, double high)
{
return Math.Min(Math.Max(n, low), high);
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Width = 500;
Height = 500;
var canvas = new Canvas();
Content = canvas;
var transform = new TranslateTransform(250, 250);
var circle = new Ellipse()
{
Width = 25,
Height = 25,
Fill = Brushes.PowderBlue,
RenderTransform = transform
};
canvas.Children.Add(circle);
var random = new Random();
var timer = new DispatcherTimer();
timer.Tick += (s, e) =>
{
transform.X += -1 + random.Next(3);
transform.Y += -1 + random.Next(3);
transform.X = transform.X.Clamp(0, 499);
transform.Y = transform.Y.Clamp(0, 499);
};
timer.Start();
}
}
}
Related
Buttons very close to each other.
Component Code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
namespace CustomExplorer.Components
{
public partial class RunningApps : TableLayoutPanel
{
public RunningApps()
{
InitializeComponent();
}
public RunningApps(IContainer container)
{
container.Add(this);
InitializeComponent();
this.ColumnStyles.Clear();
this.RowStyles.Clear();
this.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
this.HandleCreated += new EventHandler(this.Form_Load);
}
private void Form_Load(object sender, EventArgs e)
{
// Process list and spawn buttons
List<WindowData> ar = WindowsTasks.GetActiveTasks();
this.ColumnCount = ar.Count;
this.MinimumSize = new Size(50 * ar.Count, 54);
this.Size = new Size(50 * ar.Count, 54);
for (int i = 0; i < ar.Count; i++)
{
this.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100 / this.ColumnCount));
Button button = new Button();
WindowData w = ar[i];
// Setup Button Style
button.Name = string.Format("Button_{0:D}", i);
button.Text = string.Format("Button_{0:D}", i);
button.BackColor = Color.Transparent;
button.FlatStyle = FlatStyle.Flat;
button.FlatAppearance.BorderSize = 0;
// Icon
button.Image = w.icon.ToBitmap();
button.ImageAlign = ContentAlignment.MiddleCenter;
// Sizes
button.MinimumSize = new Size(50, 54);
button.Margin = new Padding(5);
button.Size = new Size(50, 54);
// Utils
this.Controls.Add(button, i, 0);
}
}
}
}
The way buttons are exposed:
The function gives the running process that have "Graphics", add them all to Array ar. Then the for loop adds columns for each of the buttons and them other for loop add the buttons with the image
Tried all that I could found. Margins, Paddings and stuff seems to be only applied at the first Button.
The component is imported to a tablelayout at main Component
I just want to create multiple web control using c# in visual studio. I have written the code for that but it create only once, I think it show the control created at the last in the loop.
Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MDMScreenSharing
{
public partial class Form2 : Form
{
private List<Skybound.Gecko.GeckoWebBrowser> geckowebbrouser;
public Form2()
{
InitializeComponent();
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
this.AutoSize = true;
this.Padding = new Padding(0, 0, 20, 20);
this.StartPosition = FormStartPosition.CenterScreen;
}
private void Form2_Load(object sender, EventArgs e)
{
int inputNumber =5;
geckowebbrouser = new List<Skybound.Gecko.GeckoWebBrowser>();
for (int i = 1; i <= inputNumber; i++)
{
int j = 1;
String wbname = "br" + i;
Skybound.Gecko.GeckoWebBrowser gw = new Skybound.Gecko.GeckoWebBrowser();
gw.Width = 200;
gw.Height = 200;
gw.Parent = panel1;
gw.Name = wbname;
gw.Location = new Point(gw.Width, panel1.Bottom + (i * 30));
gw.Navigate("http://192.168.1.162:8080");
geckowebbrouser.Add(gw);
this.Controls.Add(gw);
j = j*gw.Width;
}
}
}
}
This will show the web control created at the last. I think the program should be more dynamic for that. What am I doing wrong?
The form output, that show only once.
with the new addition of code i have code that one of you provided. the code is
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MDMScreenSharing
{
public partial class Form2 : Form
{
private List<Skybound.Gecko.GeckoWebBrowser> geckowebbrouser;
public Form2()
{
InitializeComponent();
// this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
// this.AutoSize = true;
//this.Padding = new Padding(0, 0, 20, 20);
// this.StartPosition = FormStartPosition.CenterScreen;
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(602, 395);
this.flowLayoutPanel1.TabIndex = 0;
}
private void Form2_Load(object sender, EventArgs e)
{
int inputNumber =4;
geckowebbrouser = new List<Skybound.Gecko.GeckoWebBrowser>();
for (int i = 1; i <= inputNumber; i++)
{
String wbname = "br" + i;
Skybound.Gecko.GeckoWebBrowser gw = new Skybound.Gecko.GeckoWebBrowser();
gw.Parent = flowLayoutPanel1;
gw.Width = 200;
gw.Height = 200;
gw.Name = wbname;
gw.Navigate("http://192.168.1.162:8080");
geckowebbrouser.Add(gw);
flowLayoutPanel1.Controls.Add(gw);
}
}
}
}
but the problem in this case only one browser window navigate the page.
like this
as you can see.
I think better approach will be using FlowLoayoutPanel so that you don't have to handle the position of each new control.
Go like this:
Add the FlowLayoutPanel through visual designer from toolbox.
Then on your designer.cs you should automatically have:
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(602, 395);
this.flowLayoutPanel1.TabIndex = 0;
FormLoad event:
private void Form1_Load ( object sender, EventArgs e )
{
int inputNumber = 5;
geckowebbrouser = new List<Skybound.Gecko.GeckoWebBrowser>();
for ( int i = 1; i <= inputNumber; i++ )
{
int j = 1;
String wbname = "br" + i;
var gw = new Skybound.Gecko.GeckoWebBrowser();
gw.Width = 300;
gw.Height = 300;
gw.Name = wbname;
geckowebbrouser.Add(gw);
flowLayoutPanel1.Controls.Add(gw);
gw.Navigate("http://www.google.com");
}
}
Update
The only difference from the WebBrowser implementation is that you need to call the Navigate method after you've added the control to the panel. Check the updated code above.
I have tested with default Skybound.Gecko.GeckoWebBrowser control too, and it's working just fine too:
I am initiating me into programming thanks to Stack Overflow.
The game I'm doing in c #, consists of several bees flying around the desktop, which I have to give Click and SCORE is going to increase by a certain time.
To which I did the following:
Create a list PictureBox dynamically (at runtime): OK
Load PictureBox with these GIF images randomly: OK
![Bee Games][1]
In this part I was stuck:
Place these randomly PictureBox (in the bottom of the form).
Make the PictureBox move randomly (more or less marked routes).
each PictureBox Click Event, Change the PictureBox image and hide (visible = false), and increase in SCORE + 1.
I need help please.
My code is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace MiPrimerJuego
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int x = 20;
int y = 600;
List<System.Windows.Forms.PictureBox> objeto = new List<PictureBox>();
for (int i = 0; i < 10; i++, x += 90)
{
PictureBox pBox = new PictureBox();
pBox.Height = 80;
pBox.Width = 50;
pBox.Location = new System.Drawing.Point(x, y);
objeto.Add(pBox);
pBox.SizeMode = PictureBoxSizeMode.StretchImage;
Controls.Add(pBox);
var rand = new Random();
var files = Directory.GetFiles(Application.StartupPath + #"/Images", "*.gif");
pBox.Image = System.Drawing.Bitmap.FromFile(files[rand.Next(files.Length)]);
}
}
}
}
Declare rand and files outside the for-loop and objeto outside the function/void as a Private member variable:
private List<PictureBox> objeto = new List<PictureBox>();
private void button1_Click(object sender, EventArgs e)
{
var files = Directory.GetFiles(Application.StartupPath + #"/Images", "*.gif");
int x = 20;
int y = 600;
var rand = new Random();
for (int i = 0; i < 10; i++)
{
x += 90;
PictureBox pBox = new PictureBox();
pBox.Height = 80;
pBox.Width = 50;
pBox.Location = new System.Drawing.Point(x, y);
objeto.Add(pBox);
pBox.SizeMode = PictureBoxSizeMode.StretchImage;
Controls.Add(pBox);
pBox.Image = System.Drawing.Bitmap.FromFile(files[rand.Next(files.Length)]);
}
}
}
Sorry, I did not explain properly.
What I do is this:
1 Make the PictureBox move randomly from the bottom of the form (more or less marked routes).
2 In the Click event of each PictureBox, Changing the PictureBox image and hide (visible = false), and increase in SCORE + 1.
My code is as follows:
using System;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Windows.Forms;
using System.IO;
namespaceMiPrimerJuego
{
publicpartialclassForm1 : Form
{
public Form1()
{
InitializeComponent();
}
privatevoid button1_Click(object sender, EventArgs e)
{
int x = 20;
int y = 600;
List<System.Windows.Forms.PictureBox>objeto = newList<PictureBox>();
for (inti = 0; i< 10; i++, x += 90)
{
PictureBoxpBox = newPictureBox();
pBox.Height = 80;
pBox.Width = 50;
pBox.Location = newSystem.Drawing.Point(x, y);
objeto.Add(pBox);
pBox.SizeMode = PictureBoxSizeMode.StretchImage;
Controls.Add(pBox);
var rand = newRandom();
var files = Directory.GetFiles(Application.StartupPath + #"/Images", "*.gif");
pBox.Image = System.Drawing.Bitmap.FromFile(files[rand.Next(files.Length)]);
}
}
}
}
I'm trying to make a custom ContentControl that takes on the shape of a Polyogon with rounded corners. For some reason when I set the Clip property on the Control, nothing shows up. Any help is appreciated...
PageHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Controls;
namespace DtcInvoicer.Controls
{
public class PageHost:UserControl
{
#region public ImageSource Icon;
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ImageSource), typeof(PageHost));
public ImageSource Icon
{
get { return GetValue(IconProperty) as ImageSource; }
set { SetValue(IconProperty, value); }
}
#endregion
#region public string Title;
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(PageHost));
public string Title
{
get { return GetValue(TitleProperty).ToString(); }
set { SetValue(TitleProperty, value); }
}
#endregion
#region public double Radius;
public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(PageHost));
public double Radius
{
get { return (double)GetValue(RadiusProperty); }
set
{
SetValue(RadiusProperty, value);
DoClip();
}
}
#endregion
public PageHost()
{
Loaded += new RoutedEventHandler(PageHost_Loaded);
SizeChanged += new SizeChangedEventHandler(PageHost_SizeChanged);
}
#region Event Handlers
private void PageHost_Loaded(object sender, RoutedEventArgs e)
{
DoClip();
}
private void PageHost_SizeChanged(object sender, SizeChangedEventArgs e)
{
DoClip();
}
#endregion
private void DoClip()
{
Polygon p = new Polygon()
{
Points = new PointCollection()
{
new Point(0, 0),
new Point(ActualWidth - 30, 0),
new Point(ActualWidth, 30),
new Point(ActualWidth, ActualHeight),
new Point(0, ActualHeight)
}
};
Geometry g1 = new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight), Radius, Radius);
Geometry g2 = p.RenderedGeometry;
// Clip = g1; this works, the control shows up with the rounded corners
// Clip = g2; this does not work, nothing shows up
// this is what I want to do, I want to combine the two geometries
// but this does not work either
Clip = new CombinedGeometry(GeometryCombineMode.Intersect, g1, g2);
}
}
}
HomePage.xaml
<control:PageHost x:Class="DtcInvoicer.Pages.HomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:control="clr-namespace:DtcInvoicer.Controls"
Width="500" Height="250" Radius="20" Background="Aqua">
</control:PageHost>
Setting the clip to a RenderedGeometry fails in this case because the RenderedGeometry has not yet been actually rendered, and is thus not available. For regular geometries, use this in DoClip:
Dispatcher.BeginInvoke(DispatcherPriority.Background, new ThreadStart(delegate
{
Clip = new CombinedGeometry(GeometryCombineMode.Intersect, g1, g2);
}));
With your RenderedGeometry, you would need to add it to the visual tree somewhere and then use its Loaded event before you set the Clip region, which would be hard. Try using a regular Geometry instead of a RenderedGeometry, with the same points, such as A path geometry. Here's an example of where I draw a triangle using a PathGeometry:
double leftPoint = cellRect.Right - 12;
if (leftPoint < cellRect.Left)
leftPoint = cellRect.Left;
double topPoint = cellRect.Top + (cellRect.Height - 4.0) / 2;
if (topPoint < cellRect.Top)
topPoint = cellRect.Top;
double rightPoint = leftPoint + 7;
if (rightPoint > cellRect.Right)
rightPoint = cellRect.Right;
double bottomPoint = topPoint + 4;
if (bottomPoint > cellRect.Bottom)
bottomPoint = cellRect.Bottom;
double middlePoint = leftPoint + 3;
if (middlePoint > cellRect.Right)
middlePoint = cellRect.Right;
PathFigure figure = new PathFigure();
figure.StartPoint = new Point(middlePoint, bottomPoint);
PathFigureCollection figCollection = new PathFigureCollection();
figCollection.Add(figure);
PathSegmentCollection segCollection = new PathSegmentCollection();
LineSegment topSeg = new LineSegment();
topSeg.Point = new Point(rightPoint, topPoint);
segCollection.Add(topSeg);
LineSegment midRightSeg = new LineSegment();
midRightSeg.Point = new Point(leftPoint, topPoint);
segCollection.Add(midRightSeg);
LineSegment midLeftSeg = new LineSegment();
midLeftSeg.Point = new Point(middlePoint+1, bottomPoint);
segCollection.Add(midLeftSeg);
figure.Segments = segCollection;
PathGeometry geo = new PathGeometry();
geo.Figures = figCollection;
I have a WPF canvas on which I'm dynamically creating objects from code. These objects are being transformed by setting the RenderTransform property, and an animation needs to be applied one of those transforms. Currently, I can't get properties of any transform to animate (although no exception gets raised and the animation appears to run - the completed event gets raised).
In addition, if the animation system is stressed, sometimes the Storyboard.Completed event is never raised.
All the examples I've come accross animate the transforms from XAML. MSDN documentation suggests that the x:Name property of a transform must be set for it to be animatable, but I haven't found a working way to set it from code.
Any ideas?
Here's the full code listing that reproduces the problem:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace AnimationCompletedTest {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
Canvas panel;
public MainWindow() {
InitializeComponent();
MouseDown += DoDynamicAnimation;
Content = panel = new Canvas();
}
void DoDynamicAnimation(object sender, MouseButtonEventArgs args) {
for (int i = 0; i < 12; ++i) {
var e = new Ellipse {
Width = 16,
Height = 16,
Fill = SystemColors.HighlightBrush
};
Canvas.SetLeft(e, Mouse.GetPosition(this).X);
Canvas.SetTop(e, Mouse.GetPosition(this).Y);
var tg = new TransformGroup();
var translation = new TranslateTransform(30, 0);
tg.Children.Add(translation);
tg.Children.Add(new RotateTransform(i * 30));
e.RenderTransform = tg;
panel.Children.Add(e);
var s = new Storyboard();
Storyboard.SetTarget(s, translation);
Storyboard.SetTargetProperty(s, new PropertyPath(TranslateTransform.XProperty));
s.Children.Add(
new DoubleAnimation(3, 100, new Duration(new TimeSpan(0, 0, 0, 1, 0))) {
EasingFunction = new PowerEase {EasingMode = EasingMode.EaseOut}
});
s.Completed +=
(sndr, evtArgs) => {
Debug.WriteLine("Animation {0} completed {1}", s.GetHashCode(), Stopwatch.GetTimestamp());
panel.Children.Remove(e);
};
Debug.WriteLine("Animation {0} started {1}", s.GetHashCode(), Stopwatch.GetTimestamp());
s.Begin();
}
}
[STAThread]
public static void Main() {
var app = new Application();
app.Run(new MainWindow());
}
}
}
Leave out the Storyboard:
var T = new TranslateTransform(40, 0);
Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 0));
DoubleAnimation anim = new DoubleAnimation(30, duration);
T.BeginAnimation(TranslateTransform.YProperty, anim);
(small fix for syntax)
Seems that after a bit of Googling I solved the problem myself. Many thanks to MSDN documentation and a post in MSDN forums by Antares19.
In summary:
For a Freezable object (like TranslateTransform) to be targetable by an Storyboard, it must have a registered name. This can be done by calling FrameworkElement.RegisterName(..).
I added the Storyboard object to the ResourceDictionary of the same Framework element to which I registered the TranslateTransform. This can be done by calling ResourceDictionary.Add(..)
Here's the updated code, which now animates nicely, and registers/deregisters the added resources:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace AnimationCompletedTest {
public partial class MainWindow : Window {
Canvas panel;
public MainWindow() {
InitializeComponent();
MouseDown += DoDynamicAnimation;
Content = panel = new Canvas();
}
void DoDynamicAnimation(object sender, MouseButtonEventArgs args) {
for (int i = 0; i < 12; ++i) {
var e = new Ellipse { Width = 16, Height = 16, Fill = SystemColors.HighlightBrush };
Canvas.SetLeft(e, Mouse.GetPosition(this).X);
Canvas.SetTop(e, Mouse.GetPosition(this).Y);
var tg = new TransformGroup();
var translation = new TranslateTransform(30, 0);
var translationName = "myTranslation" + translation.GetHashCode();
RegisterName(translationName, translation);
tg.Children.Add(translation);
tg.Children.Add(new RotateTransform(i * 30));
e.RenderTransform = tg;
panel.Children.Add(e);
var anim = new DoubleAnimation(3, 100, new Duration(new TimeSpan(0, 0, 0, 1, 0))) {
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseOut }
};
var s = new Storyboard();
Storyboard.SetTargetName(s, translationName);
Storyboard.SetTargetProperty(s, new PropertyPath(TranslateTransform.YProperty));
var storyboardName = "s" + s.GetHashCode();
Resources.Add(storyboardName, s);
s.Children.Add(anim);
s.Completed +=
(sndr, evtArgs) => {
panel.Children.Remove(e);
Resources.Remove(storyboardName);
UnregisterName(translationName);
};
s.Begin();
}
}
[STAThread]
public static void Main() {
var app = new Application();
app.Run(new MainWindow());
}
}
}
I have a Solution using XAML / C# Combo. In your XAML file define:
<UserControl.RenderTransform>
<TranslateTransform x:Name="panelTrans" Y="0"></TranslateTransform>
</UserControl.RenderTransform>
This will give you the ability to do the following in the C# code:
Storyboard.SetTargetName(mFlyInDA, "panelTrans");
Storyboard.SetTargetProperty(mFlyInDA, new PropertyPath("Y"));
The rest is business as usual. Create a DoubleAnimation, set its properties, add it as a child to your Storyboard, call the Begin function on the story board.
No need to register the transform or add the storyboard to the resource collection if the target is the FrameworkElement you want to animate. You can use Blend to generate the PropertyPath syntax.
frameworkElement.RenderTransform = new TransformGroup
{
Children =
{
new ScaleTransform(),
new SkewTransform(),
new RotateTransform(),
translate
}
};
translate.X = 100.0;
var animation = new DoubleAnimation(0.0, TimeSpan.FromSeconds(1));
var sb = new Storyboard();
sb.Children.Add(animation);
Storyboard.SetTarget(animation, frameworkElement);
Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"));
sb.Begin();