Is it possible to alter the bounds of a WPF element? - wpf

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.

Related

WPF TranslateTransform is ScaleTransforming

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.

LoaderMax: setting array as a container (ImageLoader)

So, I have a LoaderMax instance loading images from various URLs. I want to add all loaded images to an array.
Here's my code:
var photosArray:Array = new Array(5);
var imageLoadingQueue:LoaderMax = new LoaderMax({name:"mainQueue", onComplete:completeHandler});
for (var g:uint=0; g<5; g++)
{
imageLoadingQueue.append(new ImageLoader("/img" + g + ".jpg", {name:"photo", container:photosArray[g], noCache:false, smoothing:true, width:126, height:126, scaleMode:"proportionalOutside"}));
}
imageLoadingQueue.load();
private function completeHandler(e:LoaderEvent):void
{
trace("finished loading pictures!");
//the next two lines will return an error (saying that photosArray[1] is null)
stage.addChild(photosArray[1]);
photosArray[1].x = 250;
}
A few problems:
If I set the container of the image being loaded to the Array, it won't work. I'm not being able to access the image inside the array because it says it's null.
If I set the container of the image being loaded to "this" (using the container property when appending a new ImageLoader) and, on the completeHandler, set my array equal to event.target.content, it kinda works (but it's not the ideal). The problem is that, by doing so, the images are appearing on the stage as they are loaded, and I do no want them to do so.
Any help would be heavily appreciated.
Thanks!!
David is correct, but I also wanted to mention that the LoaderMax's "content" is actually an array of all of its children's content, so you could just use that for simplicity. Keep in mind that ImageLoaders automatically create a Sprite (technically called a "ContentDisplay") to drop the image into so you probably don't need to create ANOTHER Sprite (a container for the container).
var photos:Array = imageLoadingQueue.content;
stage.addChild(photos[1]);
The other nice thing is that it creates the ContentDisplay Sprites immediately, even before any content is loaded into them, so you can place them and size them however you want while (or before or after) loading occurs.
The container needs to be a DisplayObjectContainer. ImageLoader will try to add the image to the container using addChild(), so obviously this won't work with an empty array. Create a new Sprite for each image and add it into the array first:
for (var g:uint=0; g<5; g++)
{
photosArray[g] = new Sprite();
imageLoadingQueue.append(new ImageLoader("/img" + g + ".jpg", {name:"photo", container:photosArray[g], noCache:false, smoothing:true, width:126, height:126, scaleMode:"proportionalOutside"}));
}

Cloning a PathGeometry in Silverlight / WPF

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?

Snapshots of Control in time using VisualBrush stored in one Fixed(Flow)Document

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;

The DoubleAnimation doesn't work

//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.

Resources