Using more than 144 adorners - wpf

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();
}
}

Related

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.

How to handle events generated by Grid Splitter in WPF?

I want an event handler that handles the event when the grid splitter is being moved, Im not sure if there is one, if not, I guess I can generated an event when the size of the rows are changed?
Thanks.
You could do the rows changing size, but GridSplitter itself is a Thumb and so has its own events such as DragStarted and DragCompleted. More details here.
Edit: If you make the GridSplitter focusable and allow it to be moved with the keyboard, read the answer by Benlitz for more information.
I didn't tested, but I'm pretty sure that the currently accepted answer from AresAvatar won't work if you're resizing the rows/columns using keyboard arrows (by giving the focus to the grid splitter). This is a rare but possible case that you should anticipate in your application.
When the grid splitter is moved (either by drag'n'drop or using keyboard arrows), it changes the Width/Height dependency properties of the ColumnDefinitions/RowDefinitions of the grid. You can easily register a handler on this property change:
var heightDescriptor = DependencyPropertyDescriptor.FromProperty(RowDefinition.HeightProperty, typeof(ItemsControl));
heightDescriptor.AddValueChanged(myGrid.RowDefinitions[0], HeightChanged);
(This snippet will for instance track size change in the first row of the grid).
Then you can handle resize in an handler that will work in every case.
private void HeightChanged(object sender, EventArgs e)
{
// TODO: handle row resize
}
Generally, it is really not advised to rely on the user input action (mouse dragging, keyboard inputs...) to handle a logical or visual actions/events, since there are almost always several ways to do the same actions using different inputs (mouse, keyboards, touchscreen, ease-of-use tools...).
Use DragDelta. Be aware that if you want to change the size, use as the Actual property as the current size like ActualWidth instead of Width.

WPF: Determine if a Panel is visible to the user

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.

WPF - Virtualising WrapPanel

does anyone have a functioning virtualising WrapPanel I can use in a WPF application?
I have downloaded and tried the implementation at http://virtualwrappanel.codeplex.com/. However, I get the following exception:
"Layout measurement override of element 'MyNamespace.VirtualizingWrapPanel' should not return PositiveInfinity as its DesiredSize, even if Infinity is passed in as available size."
This is when trying to apply the wrappanel to a ListBox
This is probably a bug that you might be able to fix yourself. Look for the MeasureOverride method. It always seem to return the availableSize wich was passed to the method. As the exception states you must not return availableSize when it contains double.PositiveInfinity. So try this:
if(availableSize.Width == double.PositiveInfinity || availableSize.Height == double.PositiveInfinity)
{
return Size.Empty;
}
// all the measureoverride code comes here
return availableSize;
I haven't looked at the implementation in details. But who knows, you might be able to get a away with this if the panel doesn't save state between MeasureOverride and ArrangeOverride (wich it shouldn't if it is well implemented).
That problem is probably occuring because you have your listbox inside another control, such as a stack panel or scroll viewer, which allows the listbox to grow to whatever size it likes. While the virtual wrap panel shouldn't give an error in this case, it does explain the performance problem.
Even using one of Microsoft's own virtualising panels won't fix the performance issues in this case because the virtualisation is defeated. Since the listbox can grow to whatever size it likes, it does so and draws all the items even if they're not on screen... hence the virtualisation doesn't apply.
If you ensure your listbox isn't inside one of these sorts of containers, you should find the virtualisation starts working performance improves significantly.

WPF Undo Redo Property System to highlight in red color if value has changed

I have a following requirement for a very complex UI. (Complex here means there are lot of controls in the form [approximately 100]). I am using MVVM (if my problem requires it to slightly go away from MVVM I am ok with it)
My question is for Editable ComboBox and TextBox. But I would say I like to hear a common algorithm which will fit all controls.
Requirement 1 : The user edits the content and goes to next control, the color of the control/text should become red.
Requirement 2 : When the user comes back to the previously edited control and enters the value which was initially present, the color of the control/text should become back to black.
I know the requirement is tough and I have been breaking my head to design a generic algorithm using which I can store the previous value and call a function to change the color of control.
To just give you all an idea, --> I tried storing 2 properties for every TextBox like Default_Text and Text. But since the number of properties are huge, the memory footprint is very huge. Also maintaining so many properties is very tough.
--> I tried adding a Dictionary to every ViewModel to store what values have got changed. But here the problem I faced was giving unique keys to all the controls in my application, which is not very helpful
--> I had even thought and tried about subclassing controls like TextBox, ComboBox and overriding some methods to suit my requirement, but sadly I failed miserabley when I started adding validations and all.
So here I am stuck with designing a generic WPF property system/algorithm to handle all undo redo functionality, changing styles of controls,etc!!!
It will be really great if you experts can guide me in right direction and also help me in developing such an algorithm/system. A sample illustration will be nice though!!!
I found an answer to the above problem. I used attached behavior for this. More details on this link Function call from XAML from StackOverFlow.
When I databind, I store the initial value of the DataBound variable in the Tag property by using Binding=OneWay. Then I have written a attached behaviour for LostFocus event. Whenever the user enters a control and then goes to other control, it fires LostFocus event and calls my attached behaviour. In this, I check whether the value is equal to the value in Tag. If it is same, I display in black else I display in red.
Attached Behaviour rocks in WPF. I can achieve anything from that cleanly without code cluttering!!!!
Another alternative is to use some "dirty" tracking in your models (or viewmodels) and bind to a properties isdirty (and convert it to a color).

Resources