I have the same problem as the question stated in "Printing in Silverlight 4".
To get around the problem, I have tried to scale transform root of my visual tree before printing.
void document_PrintPage(object sender, PrintPageEventArgs e)
{
var renderScale = 1.0D;
if (LayoutRoot.ActualWidth > e.PrintableArea.Width)
renderScale = e.PrintableArea.Width/LayoutRoot.ActualWidth;
var scaleTransform = new ScaleTransform();
scaleTransform.ScaleX *= renderScale;
scaleTransform.ScaleY *= renderScale;
e.PageVisual = LayoutRoot;
e.PageVisual.RenderTransform = scaleTransform;
}
Now above code correctly prints out with silverlight visuals fit on a piece of paper.
The problem now is that LayoutRoot itself is now scaled down on the screen.
The question is, is there a way for me to create a clone of LayoutRoot before applying scale transform?
My walk-around is to applying the scale tranformation again after printing but I'd like to know if there is a way to clone visual tree
My goodness, thanks for the question. I had the same problem but tried to fiddle about with setting the dimensions of a container (that is already in the visual tree) to the printable area, which does not work, as another layout pass seems to be required. ScaleTransform does work here however instantly.
I'm fine with the "work around" by just doing a myContainer.ClearValue(FrameworkElement.RenderTransformProperty) in the EndPrint event. Trying to clone the visual tree will yield a plethora of other issues (I have lazy loading content etc).
Check out this link for details on silverlight object clone.
also just another idea would using xamlreader/writer to read the xaml string and creating an in-memory copy of the visual tree work.
for ex
If your xaml has button called originalbutton, using the code below you will have a copy of the button in readerLoadButton
// Save the Button to a string.
string savedButton = XamlWriter.Save(originalButton);
// Load the button
StringReader stringReader = new StringReader(savedButton);
XmlReader xmlReader = XmlReader.Create(stringReader);
Button readerLoadButton = (Button)XamlReader.Load(xmlReader);
Related
I create an Window and stylezed it with MahApps, i also created an UserControl. In my UserControl i create a method that populate some data in the UserControl elements.
In my window, i created a button that do the following:
EdicaoFisioterapeuta ed = new EdicaoFisioterapeuta();
ed.LoadContents("My Text");
when i debug the code, i see the elements of user control being populated, here is the code:
public void LoadContents(string text)
{
textBox1.Text = text;
lbl.Text = text;
}
After all the job, the textBox1 and the lbl does not get the content "My Text".
I create another Project and repeated the process, everything is working fine in the new project.
In my "old" project i removed MahApps, but ir doesnt get effect.
I know, it is so simple, but i cant find an solution for this trouble.
Oh man, i was instatiating the wrong class, it seems i need to sleep a little more.
I have a Grid containing several controls, the most important being three image controls.
I need to make a duplicate of this Grid. Serializing by saving the XAML in a MemoryStream doesnt seem to help because, obviously, when I set the source of the Image Control in the code behind at runtime, this change is not reflected in the XAML designer code. [Technically it is, but as
<Image.Source> System.Windows.Interop.InteropBitmap</Image.Source>
and I get some wierd exception]
So, how can I serialize my Image control?
More generally, how can I Clone my Grid control to reflect any changes to the UI that happened after the window loaded?
If I understood your question properly, I think you just need to create a clone of your grid at runtime. If so take a look at these threads
How can you clone a WPF object?
http://social.msdn.microsoft.com/Forums/en-HK/wpf/thread/e1a63ed2-a432-4c46-8f3b-4f172702cd7c
Use this function to clone an Object
public static T DeepClone<T>(T from)
{
using (MemoryStream s = new MemoryStream())
{
BinaryFormatter f = new BinaryFormatter();
f.Serialize(s, from);
s.Position = 0;
object clone = f.Deserialize(s);
return (T)clone;
}
}
above function was by Arcturus
I'm writing tests which will check correctness of Binding elements specified in XAML. They work so far, the only issue is that I do not know how to correctly force databinding to happen. Surprisingly it is not enough to simply set something in DataContext, binding won't happen until you show your control/window. Please not that I'm writing 'unit'-tests and I'd like to avoid showing any windows.
Take a look at following code:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
Here I'm creating a Window and putting an array as DataContext to that window. I'm binding Window.Title to Array.Length so I expect to see number 5 printed in console. But until I Show window (commented line) I will get empty string. If I uncomment that line then I will receive desired 5 in console output.
Is there any way I can make binding happen without showing a window? It is pretty annoying to look at ~20 windows while launching tests.
P.S.: I know I can make windows more transparent and etc, but I'm looking for more elegant solution.
UPDATE Code above is simplified version of what I really have. In real code I receive a View (some UIElement with bindings) and object ViewModel. I do not know which exactly binding there were set on View, but I still want all of them to be initialized.
UPDATE 2: Answering to the questions regarding what I test and I why. I do not intend to test that classes like Binding, BindingBase, etc are working as expected, I assume they are working. I'm trying to test that in all my XAML files I have written bindings correctly. Because bindings are stringly typed things, they are not verified during compilation and by default they cause only errors in output window, which I'm missing occasionally. So if we take my example from above and if we will made a typo there in binding: {Binding Lengthhh} then my tests will notify you that there is no property with name Lengthhh available for binding. So I have around 100 XAML files and for each XAML I have a test (3-5 lines of code) and after launching my tests I know for sure that there are no binding errors in my solution.
The bindings are updated by the dispatcher with the DispatcherPriority.DataBind - so if you wait for a dummy task with SystemIdle priority you are sure that any pending databinding is done.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
If you are trying to test correctness of your view, I suggest you test your view :-)
Why not run the UI from a unit test and write code that checks content of UI after changing data.
VS2010 does have GUI testing, or you could take a look at the code of tools such as Snoop.
Edit following comment:
If ALL you want to do is test a few simple bindings, try writing a static code test that runs as a post build event using reflection on view models and regular expressions on XAMLs. Add attributes on VM or use a config file so your test will know which view receives which View Model as DataContext. Compare property names and types in View Models with binding strings in View (automatically search XAML for these) and throw exception (thus failing build) if strings do not match.
If your bindings are more complex (converters, multibindings, ...) this may be a bit more complicated to implement.
I think you should first set the DataContext and then do the Binding, e.g.:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
I'm not sure if this is real solution for your general problem, but it works in this case.
I don't believe the Window's bindings will run without calling Show or ShowDialog, because that is the only way it gets associated with the UI message loop/dispatcher.
Your best bet would be to set it to be as least visible as possible, potentially using an extension method to clean things up:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
I had the same problem, and from sixlettervariables gave me an idea. It's very simple.
I am using WPF in WinForms application, so I use ElementHost control to host Wpf controls on WinForms control. To enforce WinForms control initialization you can just read value of Handle (which is actually Windows HWND) and this will force control to fully initialize itself including child ElementHost and all Wpf binding work.
I didn`t try to perform the same thing for pure Wpf control. But you can easily use ElementHost to initialize your Wpf controls like this:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS: Found, that everything work better, if you first set DataContext and only then force a Handle to be created (just like my example). But, I think, this is already the case for you, so should not be a problem.
Have you tryed to use the IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
Also check this out:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
My other question is why you trying to do a UNIT test on something that has been technically tested already? By the way I am not critising, just want to understand a little better.
Not sure, but maybe something like this will work?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();
in my custom control i have a ContainerVisual object and a DrawingVisual under it.
I override ArrangeOverride and calculate the rectangle that i want to draw in based on the given size and the control's padding.
after that i set my ContainerVisual object's transform to the upper left corner of the rectangle so that the methods that render the drawing would not have to take account of the rectangle and assume that the drawing origin is at point 0,0.
this does not work, and the drawing is displaced. if instead i set transform of the DrawingVisual object it works and the rectangle is displayed the way it is supposed to be.
i thought that if i set transform on the container, it will automatically be applied to the visuals under it. is that so?
thanks for any help
EDIT: Updated the source code to show complete code.
class MyControl : Control
{
private readonly ContainerVisual container = new ContainerVisual();
private readonly DrawingVisual drawing = new DrawingVisual();
private Rect rect;
private void RenderDrawing()
{
using (var c = drawing.RenderOpen())
{
var p = new Pen(new SolidColorBrush(Colors.Black), 1);
c.DrawRectangle(null, p, new Rect(0, 0, rect.Width, rect.Height));
}
}
protected override Size ArrangeOverride(Size s)
{
var h = Math.Max(0, s.Height - Padding.Top - Padding.Bottom);
var w = Math.Max(0, s.Width - Padding.Left - Padding.Right);
var r = new Rect(Padding.Left, Padding.Top, w, h);
if (rect != r)
{
rect = r;
container.Clip = new RectangleGeometry(rect);
container.Transform = new TranslateTransform(rect.Left, rect.Top);
// replace the line above with the following line to make it work
// drawing.Transform = new TranslateTransform(rect.Left, rect.Top);
RenderDrawing();
}
return s;
}
protected override Visual GetVisualChild(int index)
{
return container;
}
protected override Size MeasureOverride(Size s)
{
return new Size();
}
protected override int VisualChildrenCount
{
get { return 1; }
}
public MyControl()
{
container.Children.Add(drawing);
AddVisualChild(container);
}
}
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:c="clr-namespace:MyApp"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<c:MyControl Padding="20" />
</Grid>
</Window>
Explanation of strange clipping behavior
Now that you have posted your full source code I was finally able to see what you were seeing. Your problem isn't in the transform at all: It is in the clip!
If you comment out the container.Clip assignment statement, you get identical results no matter whether you put the transform on container or drawing
If you uncommented container.Clip assignment statement, the clipping region is perfectly centered on when the drawing is transformed, but when the container is transformed the clipping area is offset, so that only the lower and right lines of the rectangle were visible (and not all of those)
The reason this occurs is that the geometry specified for container.Clip is part of the container, so it is affected by container.Transform but not drawing.Transform:
This can be better understood by looking at the upper-left corners of the container, drawing, rectangle, and clip area relative to the upper-left corner of the window:
When you set the transform on the drawing:
Container is at (0,0) relative to window (null transform)
Clip area is at (20,20) relative to window (null transform + RectangleGeometry)
Drawing is at (20,20) relative to window (null transform + TranslateTransform)
Rectangle is at (20,20) relative to window (null transform + TranslateTransform + 0,0)
When you set the transform on the container:
Container is at (20,20) relative to window (TranslateTransform)
Clip area is at (40,40) relative to window (TranslateTransform + RectangleGeometry)
Drawing is at (20,20) relative to window (TranslateTransform + null transform)
Rectangle is at (20,20) relative to window (TranslateTransform + null transform + 0,0)
So your problem isn't that the transform isn't happening: It is that the transform is moving the clip area too, so the clip area no longer coincides with the rectangle and you can only see two sides of the rectangle.
Answer given for original code (retained because it has some useful explanation)
In fact, the code you posted never uses "container" so all you will see is a blank screen.
In your actual code you are using "container" incorrectly, preventing the events from occurring in the correct sequence to cause its Transform to be picked up and passed to the MIL layer.
Remember that when a Visual has a Transform set, it is not the visual itself but that Visual's visual parent that actually handles that transform. For example, if you render a page to XPS using ReachFramework or do hit testing, the Transform on the outermost Visual is ignored.
Your understanding is correct: If your visual tree is built following all the rules, it doesn't matter whether your transform is on your "container" or your "drawing".
Since you are using Control anyway, I'm curious why you don't just let the normal UIElement-based layout system handle your layout needs.
First update (retained for the same reason)
Thanks for the code correction. It is as I suspected: You are building your visual tree incorrectly. If you are using AddVisualChild you also must also override GetVisualChild and VisuaChildrenCount. This is because Visual does not store a list of children: It is up to the subclass (your class) to do this. What is happening is:
When you call AddVisualChild the container's transform is null so that is what is passed down to MILCore.
Later when you change the container's transform, it uses its parent pointer (that was set in AddVisualChild) to signal that its transform data must be refreshed. This update requires part of the visual tree to be scanned using GetVisualChild and VisualChildrenCount.
Since you didn't implement these methods this part of the update fails.
You say you are "new to WPF." Are you aware that you are playing with some of WPF's most low-level and esoteric features, ones that would never be used in a most ordinary WPF applications? It is equivalent to starting to learn programming using machine language. Normally you would use templates with Path, Rectangle, etc for this purpose. Sometimes you might go lower level and use a DrawingBrush with a DrawingGroup containing GeometryDrawings, etc. But you would almost never go all the way down to DrawingVisual and RenderOpen! The only time you would do that is when you have huge drawings consisting of millions of individual items and so you want to bypass all the layout and structure overhead of the higher layers for absolute maximum performance.
Manipulating the visual tree yourself (AddVisualChild, etc) is also an advanced feature. I always recommend people new to WPF stick with UIElement and above for the first few months, using Control with templates. I recommend they use Path and other shape subclasses for their drawings, and use VisualBrushes when advanced drawing effects are needed.
Hope this helps.
the problem is with the container.Clip. it should be
container.Clip = new RectangleGeometry(new Rect(0, 0, w, h));
Greetings!
I am currently working on a Silverlight project and I would like to animate a simple polygon shape (a trapezoid actually). Specifically, I woudld like to move two of the four points dynamically after some event happened. I need / want to resize and move one of the parallel sides to another position.
I admit I am rather new to Silverlight and have not found a source that could tell me wether it is even possible, not to mention how it can be done.
I have used animations before, so the general concept of storyboards and animations is not new to me. But how can I move the points of a polygon in an animation? Are there alternatives that have a similar optical effect (e.g. animating a path)?
Is there a PropertyPath I can use, similar to
P3AnimBack.SetValue(Storyboard.TargetPropertyProperty,
new PropertyPath("(Path.Data).
(PathGeometry.Figures)[0].(PathFigure.Segments)[0].
(BezierSegment.Point3)"));
, as found in a Point Animation in Silverlight 3 tutorial?
Thank you all in advance. :)
i don't know anything about Silverlight, or animations in .NET in general, but Charles Petzold did something similar:
Squaring the Circle (view animation in browser)
Animating Points and PointCollections in Silverlight (view in browser)
Animated Polyline Interpolations in Silverlight
As requested in a comment, I explain what I finally used to make my animation:
I follwed up on Animated Polyline Interpolations in Silverlight and more or less directly used this code - "stealing" the PointCollectionInterpolator.cs class.
Then I had my method to create the polygons I need and prepare animations:
private void CreatePolygon(TextBox txtbx, string prop, Color curcol)
{
PointCollectionInterpolator pci = new PointCollectionInterpolator();
pci.Points1 = new PointCollection() // Start Points
{
new Point(...),
new Point(...),
new Point(...),
new Point(...),
};
pci.Points2 = new PointCollection() // End Points
{
new Point(...),
new Point(...),
new Point(...),
new Point(...),
};
Polygon tmpply = new Polygon();
LayoutRoot.Children.Add(tmpply);
tmpply.Points = pci.InterpolatedPoints;
DoubleAnimation animpci = new DoubleAnimation();
animpci.Duration = someDuration;
animpci.From = 0.0;
animpci.To = 1.0;
Storyboard.SetTarget(animpci, pci);
Storyboard.SetTargetProperty(animpci, new PropertyPath("(Progress)"));
myStoryBoard.Children.Add(animpci);
}
And then in some random event handler, I start the animation. Additionally, so I can reuse the method, I moved the end points collection into the start points collection and update the interpolator with new end points. (Remember setting the progress to 0.0 ...) So each time the handler fires, the polygon seamlessly morphs into a new one.
private void SomeEventHandler(object sender, RoutedEventArgs e)
{
PointCollectionInterpolator polygonPCI =
this.referenceToPointCollectionInterpolator;
polygonPCI.Points1 = polygonPCI.Points2;
polygonPCI.Progress = 0.0;
polygonPCI.Points2 = getNewEndPoints();
myStoryBoard.Begin();
}
In retrospect, i would change the names from Points1 and Points2 to StartPoints and EndPoints resp.
Hope this helped. :)