In the following code part is Part class's object which is inherited from ScatterViewItem class (Microsoft.Surface.Presentation.Controls). part.Content is Path geometry:
GeometryGroup gGroup = new GeometryGroup
{
FillRule = FillRule.Nonzero
};
foreach(KeyValuePair<int, Part> kv in Layers)
{
geom = part.ShadowPath.RenderedGeometry.Clone();
geom.Transform = new TranslateTransform(0, 0);
gGroup.Children.Add(geom);
}
When I comment geom.Transform = new TranslateTransform(0, 0); line, it shows with right scale but when I uncomment it, although I am not changing anything, it scales paths:
I want to know how to fix it.
#Clemens was right about initial transformation values: there was matrixTransform with automatically calculated values by WPF. When I assigned new transformation values, it totally removed old values. I just picked that matrix and updated some values and it worked fine.
Related
I am creating a Path dynamically and I've noticed that the bounding Rect of the shape is far larger than the path itself, like in the picture bellow.
Is it possible to alter those bounds? Unfortunatelly the PathGeometry.Bounds property is read-only.
ps: If it helps, I am interested in narrowing the bounds so I can set RenderTransformOrigin of the path to new Point(0.5,0.5) to Rotate (RotateTransform) that path around itself.
Creation of PathFigure
I am defining 4 points and then I create three LineSegments and the ArcSegment. I've created a Circle struct to use as a guide to calculate those points.
private PathFigure CreateFigure()
{
var lineAB = new LineSegment(pointB, true);
var arcBC = new ArcSegment(pointC, new Size(_outerCircle.Radius, _outerCircle.Radius), 0, false, SweepDirection.Clockwise, true);
var lineCD = new LineSegment(pointD, true);
return new PathFigure(pointA, new List<PathSegment> { lineAB, arcBC, lineCD }, true);
}
Since it's impossible to alter the bounds after they're defined, adding the Path as a child to a Grid or a Border and setting Path.Stretch="Fill" would force the bounds to be filled by the path during the layout pass.
I have a simple handler that adds an ellipse to an empty Silverlight canvas
private void UCLoaded(object sender, RoutedEventArgs e)
{
var geometry = MakeElipse(20, 15, new Point(100, 100));
var ellipsePath = new Path
{
Data = geometry,
Fill = new SolidColorBrush(Colors.DarkGray),
StrokeThickness = 4,
Stroke = new SolidColorBrush(Colors.Gray)
};
LayoutRoot.Children.Add(ellipsePath);
//
var duplicateEllipsePath = new Path();
//duplicateEllipsePath.Data = ellipsePath.Data;
duplicateEllipsePath.Data = geometry;
duplicateEllipsePath.Fill = ellipsePath.Fill;
duplicateEllipsePath.StrokeThickness = ellipsePath.StrokeThickness;
duplicateEllipsePath.Stroke = ellipsePath.Stroke;
LayoutRoot.Children.Add(duplicateEllipsePath);
}
The first ellipse, ellipsePath, is fine and renders as expected. But the line duplicateEllipsePath.Data = ellipsePath.Data or the alternative duplicateEllipsePath.Data = geometry each throw the System.ArgumentException "Value does not fall within the expected range". How can it be in range once, and out-of-range immediately afterwards? What is the correct way of duplicating a path in code like this?
It looks like the only way to clone a path is to do so manually. To quote this answer from Yi-Lun Luo:
The Data property is actually a Geometry. While not noticeable in Silverlight, A Geometry actually relies on an underlying system resource (because it needs to draw something). If you need to draw another Geometry, you'll need another system resource. So you must clone it before you assign it to a new Path. In WPF, we do have a Clone method on Geometry, unfortunately this is not supported in Silverlight. So you have to manually do the clone.
Another post above Yi-Lun's claims to contain reflective code to clone a geometry, and the same code seems to appear here, although the latter is more clearly formatted. However, in your case, it seems overkill to use a method such as this. The geometry you use is created by your MakeElipse [sic] method. Extracting the common code to generate the geometries into a method seems about the best way to proceed here.
The error message 'Value does not fall within the expected range' is a bit misleading. I don't see anything 'out of range', given that the exact same object was supposedly in range for your first ellipse. I can't say exactly why this error message is reported, but I can speculate. Silverlight is implemented in native code, and I believe that because the native code can't throw exceptions it instead returns numeric error codes. Perhaps there's a limited number of error codes and the one for 'Value does not fall within the expected range' was the one chosen for this error?
I need to take snapshots of Control in time and store them in one FixedDocument. Problem is that VisualBrush is somehow "lazy" and do not evaluate itself by adding it to document. When I finaly create the document, all pages contains the same (last) state of Control. While VisualBrush cannot be Freezed, is there any other chance to do it? I would like to have more snapshots on one page so generate document page by page isn't solution for me. Aswel as converting VisualBrush to Bitmap (I want to keep it in vectors). In short - I need to somehow Freeze() VisualBrush
for(;;)
{
FixedPage page = new FixedPage();
...
Rectangle rec = new Rectangle();
...
rec.Fill = vb;
page.Children.Add(rec);
PageContent content = new PageContent();
((IAddChild)content).AddChild(page);
doc.Pages.Add(content);
}
I used serialization:
string svb = XamlWriter.Save(vb.CloneCurrentValue());
// Replace all "Name" attributes (I don't need them already and deserialization would crash on them) with "Tag" - not best practice but it's fast :)
svb = svb.Replace("Name", "Tag");
rect.Fill((VisualBrush)XamlReader.Parse(svb));
EDIT
Better way is to save Visual as XPS document and then take the Visual back. (De)serialization has some problems with SharedSizeGroups and many other "reference like" things.
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
control.InvalidateArrange();
UpdateLayout();
writer.Write(control);
Visual capture = doc.GetFixedDocumentSequence().DocumentPaginator.GetPage(0).Visual;
//Create my grid and child controls
var layoutRoot = new System.Windows.Controls.Grid
{
Background = new SolidColorBrush(Colors.Blue),
Name = "layaoutRoot1",
Height = 400.0,
VerticalAlignment = System.Windows.VerticalAlignment.Stretch,
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch
};
layoutRoot.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = new GridLength(1, GridUnitType.Auto)
});
layoutRoot.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = new GridLength(1, GridUnitType.Star)
});
var myImage = new Image
{
Source = new BitmapImage(new Uri(#"C:\Path\to\Image\img.png")),
Stretch = Stretch.UniformToFill,
Margin = new Thickness(3),
Width = 50.0,
Height = 50.0,
};
var textBlocklbl = new TextBlock
{
Text = "Label Here",
FontFamily = new FontFamily("Arial"),
FontSize = 14.0,
FontWeight = FontWeights.Bold,
Margin = new Thickness(3)
};
layoutRoot.Children.Add(myImage);
layoutRoot.Children.Add(textBlocklbl);
System.Windows.Controls.Grid.SetColumn(myImage, 0);
System.Windows.Controls.Grid.SetColumn(textBlocklbl, 1);
grid1.Children.Add(layoutRoot); //grid1 is placed on the MainWindow
Storyboard myStorboard = new Storyboard();
DoubleAnimation myDoubleAnimation = new DoubleAnimation();
myDoubleAnimation.From = 0.0;
myDoubleAnimation.To = 300.0;
myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(3));
myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
//myDoubleAnimation.AutoReverse = true;
myStorboard.Children.Add(myDoubleAnimation);
Storyboard.SetTargetName(myDoubleAnimation, layoutRoot.Name);
Storyboard.SetTargetProperty(myDoubleAnimation, new PropertyPath(System.Windows.Controls.Grid.HeightProperty));
myStorboard.Begin();
Your animation cannot find source for animation. Setting TargetName will not work until you put animation into logical tree. Use StoryBoard.SetTarget(layoutRoot) instead of StoryBoard.SetTargetName(layoutRoot.Name).
Here is what you could do to resolve it:
1) First of all you could debug it. You just launch the code in Visual Studio in debug mode and keep looking into Output window until you see error there. I believe you will see it there soon - then you can fix it. There is still a chance that you won't see any errors.
2) Second, you should try to debug it again. Take your code, paste it into clean solution. Still doesn't work? Great! Keep removing parts of the code which doesn't look related to the issue. VerticalAlighnment, HorizontalAlignment, do you really need them? Can the issue be reproducible without those properties? Remove them. Less code - easier to debug. Ok, finally you have 10 lines of code but it still doesn't work. Take a sample from MSDN which is working and looks as close to your as possible and find the difference. You can even replace parts of your code with MSDN one to see whether it will help or not.
3) And ok, it still doesn't work, you haven't found any samples and there is no way for you to debug it - you need community help. Ask question correctly. If you are a professional developer then probably you've seen it many times - users coming with only one statement it doesn't work. You have to test/debug/fix it completely on your own. But those are your clients, they will pay you money eventually. This is free community which is driven only on behalf of our interest. If we are not interested to answer your question - we will never answer it. So ask a question which worth answering. Pasting the code which doesn't work is not enough. You should also provide an information regarding the part which doesn't work. And what have you tried to fix it yourself. If we will think that you're trying to exploit us - we won't answer your question.
Thanks for your time.
UPDATE Ok, just tried to debug it. Exactly as I said. Your code gives an exception that layoutRoot cannot be found. The exact message is: No applicable name scope exists to resolve the name 'layaoutRoot1'.. You just had to run your own code in VS, exactly as you said.
I am working on a charting control where I am plotting the "analysis range," which is just two vertical lines on the chart. A problem arises when I want to change the analysis range, because I don't know how to remove only the two analysis range lines, so I end up clearing the chart and plotting the actual data values and whatnot again. Is there a way to tag these UI elements (i.e. analysis range is a gridline UI element) so that I can remove them specifically? I suppose I could save the "index" of the UI element somewhere and delete these, but I am wondering if there is a cleaner way of doing this. Thanks a lot.
All UIElements have a UID which is a string. You could set the UID of the range lines to something predictable. Keep in Mind that UID must be unique. Then when you need to remove only the gridlines, you iterate through the Children collection gathering a list of the UI elements that need to be removed, then remove them.
Something like this:
Canvas c = new Canvas();
c.Children.Add( new UIElement() { Uid = "Line1" } );
c.Children.Add( new UIElement() { Uid = "Line2" } );
c.Children.Add( new UIElement() { Uid = "Line3" } );
c.Children.Add( new UIElement() { Uid = "Text1" } ); //This is added as a sample
List<UIElement> itemstoremove = new List<UIElement>();
foreach (UIElement ui in c.Children)
{
if (ui.Uid.StartsWith("Line"))
{
itemstoremove.Add(ui);
}
}
foreach (UIElement ui in itemstoremove)
{
c.Children.Remove(ui);
}
That should work. A quick test of this code in debug shows the Children count at 1, with only the UIElement with Uid of Text1 present in the list.
When you add the two lines to the Canvas, can't you hold a reference to the two lines. That way, when you need to redraw them, just do a Canvas.Children.Remove(line1) and Canvas.Children.Remove(line2). Then update your references for the lines and re-add them to the Canvas. You could even just update the X and Y values of the lines themselves rather than removing and re-adding them. This way, the Chart would just move the lines.
But, basically the key is to maintain a reference to the lines after adding them to the Canvas.