How to work around a Windows ListView issue? - winforms

If the listview (in Details mode) is scrolled down and then you resize the listview, and if you resize columns in the Resize event or even Layout event, the contents get corrupted badly.
To reproduce, make a new C# project and put this code in there:
private void Form1_Load(object sender, EventArgs e)
{
ListView lv = new ListView();
lv.Size = ClientSize;
lv.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
lv.Columns.Add(new ColumnHeader());
lv.Columns.Add(new ColumnHeader());
lv.View = View.Details;
lv.FullRowSelect = true;
lv.Resize += lv_Resize;
Controls.Add(lv);
string[] s = new string[2];
s[0] = "hrd";
s[1] = "igwegmg";
for (int i = 0; i < 20; i++)
{
lv.Items.Add(new ListViewItem(s));
}
lv_Resize(lv, null);
}
private void lv_Resize(object sender, EventArgs e)
{
ListView lv = (ListView)sender;
lv.Columns[1].Width = lv.ClientSize.Width - lv.Columns[0].Width;
}
Run it, scroll the list down a bit, resize the form to show all items by dragging the bottom of the form downwards.
Resize it smaller to where the scroll bar shows up, then it gets worse.
Or instead of resizing just maximize and restore the form.
Notice also there's a bunch (depending on how much you scrolled) of empty items at the top of the listview, you cant click on them. To restore it to normal you have to do one of two things.
Repopulate the items (not good - I have too many items and I lose my scroll position).
manually if you resize the form to where the scrollbar pops up, then you scroll the scroll bar to the bottom, then move your mouse off the scroll bar, and then back on top of the scrollbar you will see the scrollbar resize itself, at which point you can drag the scrollbar to the top, and then its back to normal.
If you RedrawItems, then at least you can see all the items again, but you still get the blank items at the top. Doing a begininvoke to a function that calls RedrawItems after a resize/layout event doesn't always work.
Any ideas on this bug? I really don't want to use any other controls or third party software.

Noticed other ListView's in my app not exhibiting the bug. Found out its because they were resizing columns inside the Form's Resize event.
Workaround: Dont Resize columns in the ListView.Resize() but do it in the Form.Resize() event.
I'll leave this open a littelwhile incase some of you already been researching for a workaround to use ListView.Resize()

Related

How to dynamically resize a label inside a WPF application?

I need to know how I could dynamically resize a label in a WPF application.
I've already found a sample in this article which has already achieved both dragging and resizing a label at the same time.
I dug into the code, and to make it short, I found out that inside OnMouseMove event of the label, it checks the mouse cursor shape and if it was Hand it will do the dragging and if it was either of the resizing arrows it will do the resizing correspondingly.
Check it out. you'll see.
In this certain example I couldn't manage to find out how the cursor shape changes to resizing arrows when the mouse is hovered on the label's border.
So
I either need to find out 'how I could change the mouse cursor shape to resizing arrows when hovered on a label's border', OR to find a new approach to resize a label, dynamically.
Changing the cursor is done via the this.Cursor property.
I opened the code in the article and saw how they do it...
In the OnMouseMove the cursor is changed if the left mouse button is NOT clicked:
Point currentLocation = e.MouseDevice.GetPosition(wnd);
......
......
const int dragHandleWidth = 3;
var bottomHandle = new Rect(0, height - dragHandleWidth, width, dragHandleWidth);
var rightHandle = new Rect(width - dragHandleWidth, 0, dragHandleWidth, height);
Point relativeLocation = wnd.TranslatePoint(currentLocation, this);
if (rightHandle.Contains(relativeLocation))
{
this.Cursor = Cursors.SizeWE;
}
else if (bottomHandle.Contains(relativeLocation))
{
this.Cursor = Cursors.SizeNS;
}
else
{
this.Cursor = Cursors.Hand;
}
In other words, they check if the current mouse location is within 3 px of the bottom or right border, if it is, they change the cursor accordingly...
You can easily change this logic to suite your needs....

WPF Thumb DragDelta moving across monitors

I have a Popup control that I added a thumb to so I can drag it around the screen. The thumb's DragDelta event was overloaded with this:
private static void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Thumb thumb = (Thumb)sender;
Popup popup = thumb.Tag as Popup;
if (popup != null)
{
popup.HorizontalOffset += e.HorizontalChange;
popup.VerticalOffset += e.VerticalChange;
}
}
The Dragging works perfectly (I used the Dragging example from here: http://www.codeproject.com/Articles/43636/WPF-A-search), except for when the popup reaches the end of the monitor and crosses over to the other (dual monitor setup). For instance if I have the popup open on the Left monitor and start dragging it right, when the right border of it touches the edge of the monitor it's movement is erratic and starts moving all around until I move further right and it displays on the other monitor.
I debugged through this scenario, and this is a numerical example of basically what happens:
At edge of screen:
HorizontalOffset = 600
HorizontalChange = 1
Move Right:
HorizontalOffset = 601
HorizontalChange = -800
HorizontalOffset = -199
HorizontalChange = 401
HorizontalOffset = 200
HorizontalChange = -150
Which gives this weird strobe effect of the popup while it moves to the other monitor; Is there something I need to do to get it to transition smoothly across monitors?
I still haven't figured out how to un-bind pop-ups to a screen, but I was able to accomplish what I needed by using the Window control instead. I made WindowStyle.None so there was no border and overloaded the MouseLeftButtonDown event with a delegate to the DragMove() method so they can be dragged around the screen. This allowed me to have very similar look and feel as the pop-ups, but able to drag it around the screen with no flicker.

Is there a way to ensure that a Silverlight ScrollViewer keeps the top item completely visible

In Silverlight 4, is there a way that whenever you page down a ScrollViewer (i.e click the scroll bar in the area adjacent to the thumb) whatever item is at the top is completely visible. I still need it to scroll smoothly when the thumb is dragged or the mouse wheel is used.
My client doesn't like that an item is cut in half when he pages down the list because it is cut in half both when its at the top and when its at the bottom. I suggested some sort of integral scroll, and he didn't like it. He wants it to still scroll smoothly unless paging up or down.
Edits
Here's the catch. The items are not the same size. So I have to detect the item that is at the top of the scroll viewer and Scroll it into view. Is there an easy way to do this?
The first thing you need to do is dig out the vertical scroll bar from the internals of the ScrollViewer. You can do this with the help of VisualTreeHelper. There are a number of little chunks of code in various blogs the make its use even easier. I recommend this VisualTreeEnumeration (but I would wouldn't I). With that extensions class in place you can get the vertical scroll bar with:
ScrolBar vertSB = someScrollViewer.Descendents()
.OfType<ScrollBar>()
.FirstOrDefault(sb => sb.Name = "VerticalScrollBar");
Now you can attach to is Scroll event and determine the type of scroll that occured like this:
vertSB.Scroll += (s, args) =>
{
if (args.ScrollEventType == ScrollEventType.LargeDecrement
|| args.ScrollEventType == ScrollEventType.LargeIncrement)
{
// using args.NewValue determine the correct Integral value and assign
// using someScrollViewer.ScrollToVerticalOffset
}
};

How to implement my own scrolling in windows form

I have an MdiClient derived from Form and I use the surface of this control for GDI+ drawing. I run into troubles implementing my own scrolling for this control. I set both AutoScroll and AutoSize properties to false and try to use form's own horizontal/vertical scrollbars instead of placing my own. Observed form's behavior is quite confusing. To begin with there are two properties (A) HScroll and (B) HorizontalScroll that also allows access to Visible attribute.
I ended up setting HorizontalScroll.Visible = true and leaving HScroll = false (same for vertical) but am curious why there are two of them. Documentation implies that both control visibility of horizontal scroll bar but they do not appear to access the same data. Besides, it looks like HScroll is being reset on every paint. At the moment I ignore existence of HScroll/VScroll. Is it OK for my application?
What is more critical for me is the ability control placement of the thumb on scroll bars. I set VerticalScroll attributes Minimum = 0, Maximum = 100, and Value = 50 but when form is displayed thumb is positioned at the start of scrollbar not in the middle. Why? Also when user clicks on horizontal scrollbar an event handler for horizontal scrolling is invoked but meanwhile form has already reset VerticalScroll.Value to 0 (without raising vertical scroll event). What is going on?
I probably don't understand how framework expects me to implement what I need. Can someone shed some light.
Credit goes to LarsTech who pointed me towards good solution. Setting large AutoScrollMinSize automatically does whatever needs to be done enabling and controlling form scrollbars. There is one potential trap to watch for. Be aware that programmatic attempts to set AutoScrollPosition will be ignored until form is shown. So if you want your form to open with scrollbars not in the default (0,0) position then place your code inside form_shown event handler.
Just set the AutoScrollMinSize to your desired canvas.
Quick example:
using System.Drawing;
using System.Drawing.Drawing2D;
private void Form1_Load(object sender, EventArgs e)
{
this.AutoScrollMinSize = new Size(1200, 1200);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(SystemColors.Window);
using (Matrix mx = new Matrix(1, 0, 0, 1, this.AutoScrollPosition.X, this.AutoScrollPosition.Y))
{
e.Graphics.Transform = mx;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(250, 250, 100, 100));
}
}
See these links: Understanding Windows Forms AutoScroll and How to back-track the mouse to the virtual page.

Silverlight. Fix needed. Dragging stack panel item to the right moves it underneath other items

I have stack panel with custom controls in it. I attach standard MouseDragElementBehavior to each item.
When I drag to the right the item moves underneath the other items.
What would be a viable solution to create better user experience - to show better visual cue - how the item moves and where is it going to be dropped.
After a bit of tinkering I realised that nothing can be dragged within stack panel to the right not being coverd by other elements .. unless you drag the very right item..
What I did to resolve it:
Created a visual cue (half transparent shape of a generic item to represnt it during the drag operation)
Made the cue invisible (width=0) and keep it always as the very last element of the stack panel children
Subscribed the stack panel to mouse left button up, down, move
Emulated drag/drop with code
Once the drag initated I turn the cue to visible and set its translate transform to the current mouse coordinates
Adjust translate transform on every mouse move event
On drop I hide the cue again and rearrange items in a way I want.
To stress again - whatever way you do - you have to manipulate with the last element in StackPanel.Children collection....
If the MouseDragElementBehavior doesn't work the way you need it to, you could always descend from the class and customize it to your needs. For example:
public class DragBehvaior : MouseDragElementBehavior
{
public DragBehvaior()
{
this.DragBegun += new MouseEventHandler(DragBehvaior_DragBegun);
}
void DragBehvaior_DragBegun(object sender, MouseEventArgs e)
{
var element = this.AssociatedObject as UIElement;
// Calculate the correct ZIndex here.
Canvas.SetZIndex(element, 100);
}
}

Resources