Tab in Image with hotspots - c#

I have a problem which I don't know a work around for.
I'm creating a program for the company I work. The program has to write down a deliverypage for sendings we transport because right now the company has over 100 excel files, for every delivery one.
What I got now is a picturebox with an Image and that image has 16 hotspots.
with hotspots I mean Rectangles(Bounds) to know which part on the image the user has clicked on because that part needs to be selected. so far no problem. but my only problem is when hitting the Tab button.
How can I switch from Rectangle to rectangle on the image to the other Rectangle when hitting the tab button. because because there is no tabstop on a rectangle bound.
I already tried adding custom controls using tabstop and that worked great but then I had this problem that the data that has to be written in the content is limited to the area. so I need to paint it directly on the image so using controls is no option for me.
Hope my description is clear enough and else feel free to ask.

I Managed to solve my own problem with underlaying code
protected override bool ProcessDialogKey(Keys keyData)
{
int selectionIndex = pBoundsCollection.IndexOf(pSelection);
if (keyData == Keys.Tab)
{
while (selectionIndex++ <= pBoundsCollection.Count)
{
if (selectionIndex >= pBoundsCollection.Count)
{
selectionIndex = 0;
pSelection = (CMRField)pBoundsCollection[selectionIndex];
Refresh();
break;
}
if (((CMRField)pBoundsCollection[selectionIndex]).IsSelectable)
{
pSelection = (CMRField)pBoundsCollection[selectionIndex];
Refresh();
return false;
}
}
}
return base.ProcessDialogKey(keyData);
}

Related

WPF, ScrollViewer consuming touch before longpress

I'm kinda new to this WPF world.
And I'm kinda confused on why my scrollview is consuming my touch event.
My current situation is this:
I have this ScrollViewer with an ItemsControl. In this ItemsControl I'm using a Wrappanel to show a number of Rectangles. My ScrollViewer should make it possible to scroll vertically to show the Rectangles that are wrapped downward.
On each of these Rectangles I made a CustomBehaviour with all kinds of handlers.
One of these handlers is a 'creatively' made way to handle LongPressGestures.
The problem is as follows, before my longpress is detected by the behaviour, my ScrollViewer is capturing my TouchDevice in its PreviewTouchMove handler.
How can I prevent my ScrollViewer from capturing my TouchDevice too early?
How can I make sure that I can scroll my ScrollViewer and do my LongPress, DoubleClick (which still works), SingleClick (which still works as well) and other gestures I might add to this custom behaviour?
I've found similar questions on stackoverflow and google that I just not figure out for my specific case.
Something with a CustomThumb <-- This link solves the problem by making a CustomThumb. Can I somehow re-use this for my behaviour? By capturing the TouchDevice early in my behaviour handlers?
If all things fail. Is there an alternative to this ScrollViewer & CustomBehaviour combination?
EDIT:
In the meantime. I retried the CustomThumb-method. I got the longpress to work now from my CustomBehaviour while the UIElements (with that behaviour) are located on a ScrollViewer.
However the scroll functionality of the ScrollViewer still does not work.
The bounty I added will also be awarded to the person that helps me get that to work properly again (since the answer should lie in the same direction as this CustomThumb solution).
I had a similar issue before. I could not use scrolling, touch and style in the same time. At the end I developped a custom ScrollViewer. It is easier than you think, since you only need to watch / change some basic methods.
In your case you can check whether the user pressed on an empy surface or a list item. If it is a list item, you need to check whether it was a short press (so, touchup also occured after touchdown) or a long one.
Scrolling can be configured with PanningMode. It allows you to scroll with finger all over the usercontrol.
Here is my version of scrollviewer. It turns scrolling mode off when user pressed a button and turned on afterwards.
public class ScrollViewerWithTouch : ScrollViewer
{
/// <summary>
/// Original panning mode.
/// </summary>
private PanningMode panningMode;
/// <summary>
/// Set panning mode only once.
/// </summary>
private bool panningModeSet;
/// <summary>
/// Initializes static members of the <see cref="ScrollViewerWithTouch"/> class.
/// </summary>
static ScrollViewerWithTouch()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ScrollViewerWithTouch), new FrameworkPropertyMetadata(typeof(ScrollViewerWithTouch)));
}
protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e)
{
base.OnManipulationCompleted(e);
// set it back
this.PanningMode = this.panningMode;
}
protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
{
// figure out what has the user touched
var result = VisualTreeHelper.HitTest(this, e.ManipulationOrigin);
if (result != null && result.VisualHit != null)
{
var hasButtonParent = this.HasButtonParent(result.VisualHit);
// if user touched a button then turn off panning mode, let style bubble down, in other case let it scroll
this.PanningMode = hasButtonParent ? PanningMode.None : this.panningMode;
}
base.OnManipulationStarted(e);
}
protected override void OnTouchDown(TouchEventArgs e)
{
// store panning mode or set it back to it's original state. OnManipulationCompleted does not do it every time, so we need to set it once more.
if (this.panningModeSet == false)
{
this.panningMode = this.PanningMode;
this.panningModeSet = true;
}
else
{
this.PanningMode = this.panningMode;
}
base.OnTouchDown(e);
}
private bool HasButtonParent(DependencyObject obj)
{
var parent = VisualTreeHelper.GetParent(obj);
if ((parent != null) && (parent is ButtonBase) == false)
{
return HasButtonParent(parent);
}
return parent != null;
}
}

WP7/8 changing InputScope dynamically after an event using C#

I'm making an app for Windows Phone, I've been trying for ages to get the InputScope of the main text box to change when the orientation is changed to landscape (so that the keyboard takes up less space in landscape without the autocorrect bar), and back again.
I experimented with a second text box and hiding the others upon an orientation change, but this did not work neatly. :P
Try as I might I can't get this to work and can find no way to change the InputScope value after the OrientationChangedEvent argument, which has worked nicely in changing the position of the elements of the page around orientations.
I'm still fairly new to developing apps with C# and XAML, and I hope there's a nice easy way to set the InputScope of my text box that one of you awesome people could show me!
-EDIT : Here's the event handler, everything inside there works absolutely fine, but any way I try to add anything to do with InputScope does not work :(
private void MainPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
if ((e.Orientation & PageOrientation.Portrait) == (PageOrientation.Portrait))
{
//Portrait
PlaceholderText.FontSize = 29.333;
PlaceholderText.Padding = new Thickness (0,0,0,0);
MainTweet.FontSize = 29.333;
MainTweet.Padding = new Thickness (12,8,12,8);
Counter.Margin = new Thickness (0,212,28,0);
}
else
{
//Landscape
PlaceholderText.FontSize = 23;
PlaceholderText.Padding = new Thickness (8,0,0,0);
MainTweet.FontSize = 22;
MainTweet.Padding = new Thickness (16,8,180,0);
Counter.Margin = new Thickness (0,-18,28,0);
}
}
MainTweet.Text is the textbox that the keyboard is focusing on by default, when the page is changed to landscape I'd love to be able to change this from the "Search" InputScope to another, probably "URL". The stuff currently in there rearranges elements on the page nicely when the orientation is changed, I appreciate it might not look that neat...
There are multiple "orientation" states in the enumeration - not just Portrait and Landscape. The following worked for me to change the scope (on Windows Phone 7.5 emulator):
if (e.Orientation == PageOrientation.Landscape
|| e.Orientation == PageOrientation.LandscapeRight
|| e.Orientation == PageOrientation.LandscapeLeft)
{
InputScope inputScope = new InputScope();
InputScopeName inputScopeName = new InputScopeName();
inputScopeName.NameValue= InputScopeNameValue.Url;
inputScope.Names.Add(inputScopeName);
textBox1.InputScope = inputScope;
}
So you'd drop that into your MainPage_OrientationChanged event handler.

.NET Form Treeview Node Width & Custom Drawn Text (Cannot set node bounds)

I have a treeview in a windows form. I recently added code to custom draw the text so that I can fiddle with placement, bold text, etc. Everything is great except it appears that when I make my changes the node bounds don't get updated so the horizontal scrollbar gets confused into thinking it doesn't need to be shown. I am guessing this is the case because when I click a node on tree I see a box that I think is the node bounds and it is too small.
In my OnDrawText method, I'd love to be able to change the Bounds on the Node member of DrawTreeNodeEventArgs, but it is readonly.
I've searched for a solution for a painfully long time now to no avail. Setting the Bounds of the treeview itself doesn't make the scrollbar appear. I am using TextRenderer.DrawText() in my OnDrawText method, if that makes a difference.
Any help greatly appreciated!
If anyone thinks seeing the code would help, I'll add it.
For the benefit of anyone else out there searching, I'll provide my hackorama (thanks Hans!) for reference.
When I build my tree initially, I now pad blanks onto the end of the .Text of the nodes that I will be modifying in my ...OnDrawText(object sender, DrawTreeNodeEventArgs e) method. In this method, I use TextRenderer.DrawText to customize the look of the node, however the rectangle that corresponds to the customized stuff doesn't get taken into account by whatever is figuring out my scrollbar size. The scrollbar size is still determined by the original text. Since the original text is (massively) padded with blanks, the scrollbar gets drawn appropriately.
I am not sure how this will hold up over time, but it works for now.
Edit on 11/12/12:
To hide tooltips, ignore some WndProc messages. The WM_VSCROLL part is to reduce flicker when scrolling. The other cases were related to tooltips, but I don't recall exactly which one did what. I think that Notify might be the only one you need, but I thought I'd add the whole method just in case.
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
//case WindowsConstants.WM_HSCROLL:
case WindowsConstants.WM_VSCROLL:
{
var nfy = m.WParam.ToInt32() & 0xFFFF;
if (nfy == WindowsConstants.SB_THUMBTRACK)
{
currentMsgCount++;
if (currentMsgCount % skipMsgCount == 0)
base.WndProc(ref m);
return;
}
if (nfy == WindowsConstants.SB_ENDSCROLL)
currentMsgCount = 0;
base.WndProc(ref m);
}
break;
case WindowsConstants.MouseLeave:
case WindowsConstants.NcMouseLeave:
case WindowsConstants.MouseHover:
case WindowsConstants.NcMouseHover:
case WindowsConstants.Notify:
break;
default:
base.WndProc(ref m);
break;
}
}
public const int NcMouseHover = 0x2a0;
public const int MouseHover = 0x2a1;
public const int NcMouseLeave = 0x2a2;
public const int MouseLeave = 0x2a3;
public const int Notify = 0x4e;

Load image into picturebox only when needed/visible

I'm programming a little application that shows thumbnails of images. All the displayed images are in the same directory, each Image is inside it's own groupbox with a few labels and a checkbox. All the group boxes get added to a flowlayoutpanel. The problem is, that the amount of images may get pretty large and I'm concerned that memory usage / performance might get a little out of hand if I load all images even if they're not yet visible.
Is there a way to load only images that are currently visible to the user? My first thought is storing the location of my boxes and determine which images to load depending on the scroll position, or is there an easier way to determine if a picturebox/groupbox is currently visible?
Ideally what you should be doing is creating buffer logic rather than hiding 1 image and showing the other. It's a much better idea to have a couple buffers loading the images before you show them and have a fixed number of actual fields showing images rather than a new set per image.
But if your solution requires that, try to create a custom user control.
Try something like this:
public class customUserControl : UserControl
{
//Store image as a Uri rather than an Image
private Uri StoredImagePath;
public class PictureBoxAdv : PictureBox
{
public PictureBoxAdv()
{
this.VisibleChanged +=new EventHandler(VisibleChanged);
}
}
public Uri Image
{
get { return StoredImagePath; }
set
{
StoredImagePath = value;
if (this.Visible && StoredImagePath != null)
{
this.Image = Image.FromFile(StoredImagePath.AbsolutePath);
}
}
}
public void VisibleChanged(object sender, EventArgs e)
{
//When becomes visible, restore image, invisible, nullify.
if (this.Visible && StoredImagePath != null)
{
this.Image = Image.FromFile(StoredImagePath.AbsolutePath);
}
else
{
this.Image = null;
}
}
}

How to scroll in a fixed-size ToolStripDropDown

I am using a ToolStripDropDown control to implement the dropdown portion of a custom ComboBox-like control. In order to be visually appealing, I am imposing a MaximumSize on the dropdown and manually specifying the width of each ToolStripButton within it - the result is a popup which is the same width as the control that activates it, with a cap on the height of the height of the dropdown portion.
Example (simplified):
ToolStripDropDown dropDown = new ToolStripDropDown();
dropDown.MaximumSize = new Size(200, 100);
dropDown.RenderMode = ToolStripRenderMode.System;
dropDown.AutoSize = true;
for (int i = 0; i < 50; i++) {
ToolStripButton dropDownItem = (ToolStripButton)dropDown.Items.Add("Item " + i);
dropDownItem.AutoSize = false;
dropDownItem.Size = new Size(200, 20);
}
dropDown.Show(owningControl, new Point(0, owningControl.Height - 1));
As you can see, the constraints on the popup's size are applied, however the up/down scroll buttons are not displayed and there seems to be no way to make them appear. There do not appear to be any methods or properties within ToolStripDropDown regarding the scrolling offset or a mechanism to scroll a particular item into view (such as EnsureVisible() on ListViewItem).
How, then, can I get the dropdown to scroll? Any method would be sufficient, be it a scroll bar, scroll buttons or even the mouse-wheel.
(Incidentally, I have tried many times to make similar controls using a Form for the dropdown portion - despite trying dozens of solutions to prevent the popup from stealing focus or gaining focus when its controls are clicked, this seems to be a dead end. I have also ruled out using ToolStripControlHost, whose hosted control can still take focus away from the form that opened it.)
Finally cracked this one. It occurred to me that ContextMenuStrip and ToolStripDropDownMenu are capable of the auto-scrolling behaviour which their base class, ToolStripDropDown, cannot provide. Initially, I avoided these alternative controls because they usually add a wide margin. This can be removed via ShowImageMargin and ShowCheckMargin. Even after doing this, a small (approx 5px) margin remains. This can be removed by overriding the DefaultPadding property:
public class MyDropDown : ToolStripDropDownMenu {
protected override Padding DefaultPadding {
get { return Padding.Empty; }
}
public MyDropDown() {
ShowImageMargin = ShowCheckMargin = false;
RenderMode = ToolStripRenderMode.System;
MaximumSize = new Size(200, 150);
}
}
// adding items and calling Show() remains the same as in the question
This results in a popup window which can contain any type of ToolStrip item, enforces MaximumSize, has no margin and, most importantly, does not steal focus and cannot receive focus.
This is your nemesis:
internal virtual bool RequiresScrollButtons
{
get
{
return false;
}
set
{
}
}
It is internal, you cannot override it. You can revive your approach of using a Form by fixing the focus stealing behavior. Paste this into the form class:
protected override bool ShowWithoutActivation
{
get { return true; }
}

Categories