Using TransformToVisual in Silverlight to position HTML elements - silverlight

I'm in the process of developing a Silverlight custom control that "hosts" a Flash instance. The way that you do this, of course, is to position the HTML element in question over your Silverlight instance, as described, say, here. The problem I'm running into is that when I use the GeneralTransform.Transform() method to get the absolute coordinates of my control, so that I can position the HTML element correctly, the Point object that's returned always has .X=0 and .Y=0.
public void InitControl(string id)
{
GeneralTransform gt = this.TransformToVisual(Application.Current.RootVisual);
Point localPos = gt.Transform(new Point(_htmlControlLeft, _htmlControlTop));
// Create the containing DIV tag.
HtmlDocument doc = HtmlPage.Document;
divHost = doc.CreateElement("div");
divHost.SetAttribute("id", System.Guid.NewGuid().ToString());
divHost.SetStyleAttribute("position", "absolute");
divHost.SetStyleAttribute("left", localPos.X.ToString() + "px"); // always 0
divHost.SetStyleAttribute("top", localPos.Y.ToString() + "px"); // always 0
Debug.WriteLine("x,y=" + localPos.X.ToString() + "," + localPos.Y.ToString());
divHost.SetStyleAttribute("width", Width.ToString() + "px");
divHost.SetStyleAttribute("height", Height.ToString() + "px");
divHost.SetStyleAttribute("z-index", _htmlZIndex.ToString());
}
I haven't been able to find great documentation on the GeneralTransform.Transform() method, but it seems like I'm using it correctly. Any thoughts on what I'm doing wrong?
Edit 4/28/09: I still haven't found an answer, but I AM using the Transform() method properly. The problem only shows up if I call the InitControl() method during the Page.Loaded event. If I wait a few seconds, then call it (say) from a Button_Click event, the same code works fine. According to the SL docs, everything should be laid out appropriately by the time the Page.Loaded event fires, but clearly that's not the case.
I should also note that every once in a while, the code above works perfectly fine, even when it's called from the Page.Loaded event. Huh.
My workaround so far, for what it's worth, is hide the control for a couple seconds after the form loads, then show it. It's an ugly hack, but unless anyone has any better ideas...?

This is due to RootVisual having a 0,0 x/y axis. Try using the next element within the tree, typically it's layoutRoot by default (assuming you haven't changed its name etc). That or utilise FindName() to ensure you have a direct reference to the said layer you are wanting to position the iFrame over.
Scott Barnes / Rich Platforms Product Manager / Microsoft.

Related

OxyPlot performance issue on larg data in WPF on InvalidatePlot

I'm using OxyPlot in my wpf application as line recorder. It's like the LiveDemo example.
On a larg visible data set, I get some UI performance issues and may the whole application could freez. It seems to be PlotModel.InvalidatePlot which is called with to many points to often, but I didn't found a better way.
In deep:
Using OxyPlot 2.0.0
I code all in the PlotModel. The Xaml PlotView is only binding to the PlotModel.
I cyclical collect data in a thread an put them in a DataSource (List of List which are ItemSoure for the LineSeries)
I have a class which calculates cyclical in a thread the presentation for x and y axis and a bit more. After all this stuff, it calls PlotModel.InvalidatePlot.
If I
have more than 100 k points on the display (no matter if in multiple LineSeries or not)
and add 1 DataPoint per LineSeries every 500 ms
and call PlotModel.InvalidatePlot every 200 ms
not only the PlotView has performance issues, also the window is very slow in reaction, even if I call PlotModel.InvalidatePlot (false).
My goal
My goal would be that the Windo / Application is working normally. It should not hang up because of a line recorder. The best would be if it has no performance issues, but I'm skeptical.
What I have found or tested
OxyPlot has Performance guidelines. I'm using ItemsSource with DataPoints. I have also tried adding them directly to the LineSeris.Points, but then the Plot doesn’t refresh anyway (even with an ObservableCollection), so I have to call PlotModel.InvalidatePlot, what results in the same effect. I cannot bind to a defined LineSeries in Xaml because I don’t know how much Lines will be there. Maybe I missed something on adding the points directly?
I have also found a Github issue 1286 which is describing a related problem, but this workaround is slower in my tests.
I have also checked the time which is elapsed on the call of PlotModel.InvalidatePlot, but the count of points does not affect it.
I have checked the UI thread and it seems it have trouble to handle this large set of points
If I zoom in to the plot and display under 20 k Points it looks so
Question:
Is there a way to handle this better, except to call PlotModel.InvalidatePlot much less?
Restrictions:
I also must Update Axis and Annotations. So, I think I will not come around to call PlotModel.InvalidatePlot.
I have found that using the OxyPlot Windows Forms implementation and then displaying it using Windows Form integration in WPF gives much better performance.
e.g.
var plotView = new OxyPlot.WindowsForms.PlotView();
plotView.Model = Plot;
var host = new System.Windows.Forms.Integration.WindowsFormsHost();
host.Child = plotView;
PlotContainer = host;
Where 'Plot' is the PlotModel you call InvalidatePlot() on.
And then in your XAML:
<ContentControl Content="{Binding PlotContainer}"/>
Or however else you want to use your WindowsFormsHost.
I have a similar problem and found that you can use a Decimator in LineSeries. It is documented in the examples: LineSeriesExamples.cs
The usage is like this:
public static PlotModel WithXDecimator()
{
var model = new PlotModel { Title = "LineSeries with X Decimator" };
var s1 = CreateSeriesSuitableForDecimation();
s1.Decimator = Decimator.Decimate;
model.Series.Add(s1);
return model;
}
This may solve the problem on my side, and I hope it helps others too. Unfortunately it is not documented in the documentation
For the moment I ended up with calculating the time for calling InvalidatePlot for the next time. I calculate it with the method given in this answer, wich returns the number of visible points. This rededuce the performance issue, but dosent fix the block on the UI Thread on calling InvalidatePlot.

Coded UI - How do we find if something exists when IsVisible, Exists, TryGetClickablePoint etc all return true when I can't see the control?

I am testing a WPF application and am not privy to it's exact workings but I am finding many instances where I need to find if a control is shown. All the traditional answers on this on Stack Overflow and MS forums etc say to use one of the following ...
IsVisible,
Exists,
TryGetClickablePoint,
State (e.g. OffScreen
The problem is that for this system, many controls return true for all of those even when the control cannot be seen! They also return a point with co-ordinates (-1, -1, -1, -1) whether the control is visible or not.
The only thing I have had any success with is using a try catch finally. I try to click on the control and if that fails, I go in to the catch block. That takes 60 seconds to time out though and I am getting intermittent issues with tests that run 9 times out of 10. Maybe the constant use of try catch is causing performance issues.
Is there an approach that actually works when all the standard approaches fail? I have noticed lots of other people asking these question are also testing WPF. Is there something WPF developers are doing to hide controls that makes CodedUI think they are still present and visible etc. Are they just behind something?
Many thanks in advance.
The solution was two-fold. Firstly I had to find the element and this was not working properly with my recorded steps. The element was buried too deeply in the system under test which is WPF (XAML). Secondly I had to prove I had found the element and for this I can't use TryGetClickablePoint, Exists, Top or Width. None of them seemed to work properly at all for my element. I had to use State.
public void Assert_MyElementShown()
{
#region Variable Declarations
WpfCustom uISurfaceCustom = this.UISysUnderTestClientShWindow.UIItemCustom1.UISurfaceCustom;
WpfCustom uIYAxisLabelsCustom = new WpfCustom();
#endregion
//Find the Element using it's Container and SearchProperties
uIYAxisLabelsCustom.Container = uISurfaceCustom;
uIYAxisLabelsCustom.SearchProperties[WpfControl.PropertyNames.ClassName] = "Uia.AxisLabelControl";
uIYAxisLabelsCustom.SearchProperties[WpfControl.PropertyNames.AutomationId] = "YAxisLabels";
//Use the State to find if it's on screen or not
var state = uIYAxisLabelsCustom.State;
if (state == Microsoft.VisualStudio.TestTools.UITest.Extension.ControlStates.Default)
{
//Element is visible, do stuff here!
}
else if (state == Microsoft.VisualStudio.TestTools.UITest.Extension.ControlStates.Offscreen)
{
//The control may exist, it may have location on screen and may even
//appear to be clickable according to coded ui framework but is is NOT
//shown on the screen.
}
}
You can try this approach for your application..if control properties are showing true for viable than we can go for height and width.Means if control is not visible in UI and but still all properties are showing true than check control height and width must be in -ve number.Than we can keep a assertion like
If control.height<0
Not visible in UI

Esri Silverlight control Pan/Zoom from code

I have trouble getting Map behave properly when calling ZoomToResolution and PanTo
I need to be able to Zoom into specific coordinate and center map.
The only way I got it working is by removing animations:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
Otherwise if I make call like this:
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
It does one or another (i.e. pan or zoom, but not both). If (after animation) I call this code second time (map already zoomed or panned to needed position/level) - it does second part.
Tried this:
control.MapControl.ZoomToResolution(ZoomLevel, MapPoint());
Same issue, internally it calls above commands
So, my only workaround right now is to set Zoom/Pan duration to 0. And it makes for bad UX when using mouse.
I also tried something like this:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
this.MapControl.ZoomDuration = new TimeSpan(750);
this.MapControl.PanDuration = new TimeSpan(750);
Which seems to be working, but then mouse interaction becomes "crazy". Mouse scroll will make map jump and zoom to random places.
Is there known solution?
The problem is the second operation replaces the previous one. You would have to wait for one to complete before starting the next one. But that probably doesn't give the effect you want.
Instead zoom to an extent, and you'll get the desired behavior. If you don't have the extent but only center and resolution, you can create one using the following:
var zoomToExtent = new Envelope(point.X - resolution * MapControl.ActualWidth/2, point.Y, point.X + resolution * MapControl.ActualWidth/2, point.Y);
Btw it's a little confusing in your code that you call your resolution "ZoomLevel". I assume this is a map resolution, and not a level number right? The esri map control doesn't deal with service-specific levels, but is agnostic to the data's levels and uses a more generic "units per pixels" resolution value.

TransformToVisual returns incorrect result only for the first time

I am trying to use the TransformToVisual method to get an Image element's absolute coordinates like this:
var transform = img.TransformToVisual( this ); // this is the current page
Point absolutePosition = transform.Transform( new Point(0, 0) );
For the first time I use it, this code returns an incorrect value (it is always 155,255). However, it always returns the correct coordinates afterwards.
This code runs in a Tap event handler, so the UI is already completely rendered when it is being executed. I tried calling InvalidateMeasure and InvalidateArrange (both on the Image element and on the page), but it did not help (I don't know if those calls have any connection whatsoever with TransformToVisual).
What could be the problem?
UPDATE: If I pin lock my phone and then unlock it, then the above issue does not happen.
UPDATE2: The position seems to be where the upper left hand corner of the image would be if it was at the center of the page. (The image's size is fixed 170x260. I get the coordinates 155,255. 2*155 + 170 = 480, 2*255 + 260 = 770 (okay the second one is not exactly 800, but it is suspicious anyway. The Application bar and the System tray are both hidden.))
UPDATE3: The problem occurs on the main page of my app. However, if I navigate to another page, then come back to the main page, it also works correctly.

Microsoft Surface: Adding IdentityTag to TagVisualizer shows a cross-hair. Why?

in my Surface application happens this:
When I put an IdentityTag onto my TagVisualizer, a white cross-hair appears. This TagVisualizer adds no TagVisualization when adding a Tag, it just calls some methods in its "VisualizationAdded"-Event.
In my other TagVisualizers before there were no cross-hair but they always had Visualizations added like this in the initialization of the TagVisualizer: tagDef.Source = new Uri("something.xaml", UriKind.Relative);
But how can I ged rid of this cross-hair?
I cannot find anything about it.
By the way, it looks like this: http://img80.imageshack.us/img80/4728/crosshairc.png
http://img80.imageshack.us/img80/4728/crosshairc.png'/>
I've just run into the same problem because I didn't want a TagVisualization to display when I put a tag down (I wanted some items to be displayed in an already displayed librarystack). I solved it by setting the source of the ByteTagDefinition to null
ByteTagVisualizationDefinition tvBlue = new ByteTagVisualizationDefinition();
tvBlue.Value = 02;
tvBlue.Source = null;
MainTagVisualizer.Definitions.Add(tvBlue);
This gets rid of the crosshair - and I assume will work for IdentityTags, although I have not tried.
the crosshairs are used as the default visualization if you dont specify a custom source. we did this in order to let developers get the layout & configuration working without having to first define the visualization. a crosshair was selected as a default visual because it can be helpful in validating your physical offset properties
-robert (former PM for the Surface controls)

Resources