WPF: Determine if a Panel is visible to the user - wpf

I have a WPF usercontrol (myGraphicControl) in a tab (WPF application).
When the form size changes, I redraw the graph in myGraphicControl.
Since the redrawing operation is a I need to do it only the control in in the visible tab.
How the WPF (user)control can detect if it's "visible" actually or not?
PS.
by Visible I mean that user can see it.
say, if a Visible TextBox is located in the currently invisible tab, this textBox is not visible by the user.

I don't believe there is a quick-fix solution here, but you may be able to do something using UIElement.InputHitTest(Point).
You could make a call similar to
//get the coordinates of the top left corner of the child control within
//the parent
var childTopLeft = childControl.TranslatePoint(new Point(), parentControl);
//check whether or not the child control is returned when you request the element
//at that coordinate through hit testing
var isVisible = (parentControl.InputHitTest(childTopLeft) == childControl);
However, I should point out that I haven't tried this myself, and that it probably won't work in the following scenarios:
Transparent items - generally, transparent backgrounds cause hit testing of a control to pass to the parent
Partially occluded items - you can only hit-test one point at a time, so if only part of your child control is visible you will have to check the correct point

I've found that while Steve's method generally works, it works much more reliably if you get a point from somewhere in the middle of the child control. I'm guessing that maybe layout rounding somewhere along the way makes the InputHitTest check somewhat inexact. So, change his first line to the following and you're golden:
var childTopLeft = childControl.TranslatePoint(new Point(childControl.RenderSize.Width/2, childControl.RenderSize.Height/2), parentControl);

Maybe UIElement.IsVisible will be helpful? It works for tab contents well.
Anyway you can use a solution described here.
I have one more solution. The current implementation of TabControl removes inactive tabs from visual tree. So, another way to determine whether your element is visible is to find PresentationSource. It will be null for elements of inactive tabs.

Related

Access WPF DataGrid after making element visible

Currently I am stuck with a problem that is simple on the first sight. Its about automated GUI testing.
I want to make a row/cell of a WPF DatGrid completely visible by scrolling using ScrollIntoView(row) and then accessing the row/cell directly after. Unfortunately scrolling in ScrollViewer seems to happen asynchronously. This means I need to wait for the scrolling to finish before accessing the row/cell. For this purpose I found the ScrollChanged event I can subscribe.
There is only one detail I can not solve: If the row/cell I want to access is already visible (and no scrolling is necessary) I do not get that event and the algorithm gets stuck. I was not able to find a reliable way to predict if a call to ScrollIntoView(row) actually scrolls.
Any idea how to solve this?
To make sure layout is updated call UIElement.UpdateLayout after you ScrollIntoView and before you want to use item. Quoting MSDN it
Ensures that all visual child elements of this element are properly updated for layout.

WPF Printing multiple pages from a single View Model

I am currently a little bit troubled by the following problem. I have a user interface which basically shows a graphic (a canvas made of Lines, Circles, ... these are all WPF objects). Depending on the selection a user makes in the menu, some items get deleted and some get added. So the basic image looks the same, but a few modifications are made.
The user has the possibility to select - say - 10 different "pages" by clicking a Next/Previous Button.
I am using MVVM Light and my ViewModel contains all the items of the graphic (all Lines, ...).
Now I would like to print that graphic to multiple pages. The first page should contain the graphic with changes from page 1, the second page contains the graphic with changes from page 2 and so on. The actual number of pages is dynamic. I track this with a property CurrentPage and a property PagesTotal.
Whenever I push the "Next" button, this causes a command to be executed which will change the variable CurrentPage and also makes sure that the correct items are displayed.
Now I would like to print this but this is where I'm stuck. I dont' mind leaving the MVVM zone and doing some dirty work in code-behind but I would refuse to draw everything again like in the old GDI days.
Any ideas are really welcome.
Create a UserControl containing your display logic (you graphic, for instance). Grab you ViewModel list and project then in UserControls, setting each ViewModel as each UserControl's DataContext.
Force each one to render calling Measure with infinite value and then Arrange with the resulting DesiredHeight and Width. Then follow the procedures to print WPF visuals (link link link).
Essentially, this should be quite simple if and only if your views work independently; i.e. your ViewModel doesn't contain UiElements that are placed into your View.
Simple solution is to basically print your visual root. If need be encapsulate your Views in a user control first.
PrintDialog printDlg = new PrintDialog();
UserControl1 uc = new UserControl1();
printDlg.PrintVisual(uc, "User Control Printing.");
Reference
Alright, I have to admin that I now switched back to doing the printing through code only. I would have really liked doing it "WPF-style" but handling the multiple pages issue was just too much trouble.
Anyway, there is still one issue regarding the printout left but this will be another question.

WPF Modal dialog goes in background

I have problem with an application where modal WPF dialog occasionally goes behind the main application window. This hapens when I click button on the dialog which does some processing and updates controls (through binding) in the main application window. When it goes in background - clicking anywhere in the application brings it back into foreground.
var dialog = LoadDialogWindowThroughMEF();
dialog.Owner = Application.Current != null ? Application.Current.MainWindow : null;
dialog.ShowInTaskbar = false;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
return dialog.ShowDialog();
The above code shows how I open modal window. This happens very rarely.
Does anybody know what could be the problem?
I'm not sure I have an answer for you, but I can share some of my thoughts:
Every time I've encountered this type of problem, it happened because the Owner wasn't set properly. So, I'd try to not set the Owner and see if that makes the problem reproducible. You need to be absolutely sure that Owner is set to the correct parent window at all times1. You might also want to check that it is the actual MainWindow of your application that are supposed to be the parent. I think that most of the time it is beneficial to be explicit2 in your code. In this case that means that it is better to assign the known parent (maybe you have a reference to the parent somewhere that you could use), rather than relying on the Application.Current to provide you with that reference. Doing so will put you in control of the assignment to Owner. It could even make it possible to get rid of the ?: operator since you would have the means to control the reference even during unit testing.
I also want you to make sure that the code that is actually updating the parent window doesn't in any way force focus to a specific control on the parent window, or anything like that. (As long as the correct parent is set as Owner, I don't see this as a likely problem.)
I hope this helps you, but I understand if it doesn't. The fact that your dialog reappears when you click the parent window disproves some (or all!) of my points...
1 Except when running your unit tests, but that's a completely different matter.
2 As in the first meaning of the word according to wiktionary.org/wiki/explicit, and as opposed to implicit.

Using more than 144 adorners

It appears that the maximum number of adorners that work without any breakage is 144.
I have a ScrollView with a bunch of objects, and many of them come with adorners. The first 144 adorners are positioned correctly, but the rest are not. Note that it is an exceptional situation when there are so many; usually there are exactly zero adorners. Nevertheless, I'd like this to work properly even on that exceptional occasion.
Leaving aside how this arbitrary (and very low) limit makes me feel, are there any practical work-arounds for this bug?
At this time there is no known way of doing this.
Which is just as well, because I found the performance to be poor; simply subclassing my Image control that was supposed to display the adorner, and drawing the overlay in the OnRender, worked much better (and unlike WinForms, the visual can extend beyond the logical boundary of the control).
Here is the scenario under which I managed to implement a workout for this problem:
I have a number of textboxes that are linked to an Excel document.
The textboxes take a numerical value. They are set to invalidate on data errors in the xaml code. A data error occurs if the number is < 1, or null.
I placed an AdornerDecorator around the textbox (so that the red invalidation border appears correctly over the textbox).
In Excel, you can alter all the textboxes at the same time - but, as the OP found, if you manage to invalidate over 144 text boxes at once, the adorner decorator starts playing up, offsetting the position of the borders (the very thing it was designed to fix in the first place).
I tried a number of different solutions including invalidating the layout, however none of these worked for the situation I was facing.
Using Snoop, I found that if I refresh the textbox manually, the adorner then placed itself correctly. So, I decided to call an update to the layout from each individual textbox that needed the adorner. I did this by listening for OnValueUpdated on the textboxes. If the new value it was updating to happened to be an invalid value, I force an "UpdateLayout()" for the textbox (I only wanted to do this for invalid values as forcing an update impacts performance, and I don't want to do that every time the value changes).
In this way, regardless of the number of cells I wanted to change at once, the adorner decorator was always displayed in the correct position (except for the very last textbox to be evaluated which, despite my best efforts, is always ever so slightly misaligned).
This might be way late to the party here, but this seemed to solve the problem for me. I kept a list of the adorners that I had added to the adorner layer (called m_adorners), and in the root control where my adorners were contained, I attach to the LayoutUpdated event. Here's event handler:
private void OnLayoutUpdated(object sender, EventArgs e)
{
if (m_adorners.Any(a => !a.IsArrangeValid &&
a.Parent != null))
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
adornerLayer.InvalidateArrange();
}
}

I need advice on how to design/implement this custom panel

I've written a custom panel which displays its children docked either vertically or horizontally, separated by moving splitters in between. Since the Grid panel offers much of this functionality out-of-the-box, I just inherited from it.
To create the layout, upon Loaded is fired I do the following:
1) Read how many children it has and create the appropiate number of rows/colums.
2) Position every existing children in the corresponding row/colum.
3) Create, position and add a GridSplitter for every child.
This approach looks and works fine, but it opens the door to a lot of problems:
Since it's added a GridSplitter for each child, there are twice the number of expected children. If someone added 3 elements to it, Children.Count would return 6.
User could insert/remove things at the wrong place.
It just throws an exception when this Grid is used as the ItemsPanel for an ItemsControl, since in this case WPF (not Silverlight) does not allow direct children manipulation.
These 3 cases are the ones I've already tested, but I'm pretty sure a lot more would arise depending on what the user does with it.
As it turns out, this class must be regarded as 'implementation details', so the real question is, what control should I put in front of the user?
It sounds like it should be a Panel, but I can't control the Children property since it's not virtual, and there's also the ItemsControl which I think could be a good candidate, but I really don't know.
I'd much appreciate any kind of advice or some directions to do this the right way.
Thanks in advance.
You see using just grid you leave yourself with an imperative way of adding items only. As in
myCustomGrid1.AddMyItem(***), Grids simply don't have ItemsSource property. ItemsControls do - so if you need support for declarative items sources i.e. myControl.ItemsSource = {Binding ...} you're going to derive your control from ItemsControl. This is not a two liner - making your ItemsPanel Children writable is a big challange - there's no simple way of doing that.
This is all about a small thing overlooked during the Grid's design - splitters shouldn't have been added to Children collection, as Children are visulaizations of your BOs while spliiters are just formatting elements.
Here's what I would do.
Forget about ItemsSource & items altogether - it's aint worht the hassle. The only way to add/remove items to your control will be AddResiazableItem/RemoveResizbleItem. Calls will add items and splitter (for the middle items), extend the number of rows/cols of your grid depeneding on its orientation, set Grid.Row/Grid.Column attached properties for your visual children. You can keep your actual objects internally to support Orientation change.
If at any stage you'll want to bind your control to IEnumerable source - just create an attached behavior, which will iterate through the items and call AddResiazableItem within a loop.
Cheers.
P.S. To moderators - the editor seems to get broken, lads. I cant see the second item.
P.S.S. Got it fixed after a few tries.

Resources