Lets say you have an HScrollBar. Also lets say you might want to cancel scrolling and return to a previous value. You are at scroll bar value of 3 for example. Now a user clicks on the scroll bar to advance one value (largechange and smallchange are both set to 1). You handle the scroll event as follows:
// Global sheet number containing current sheet number
int sheetNum;
private void hsbSheet_Scroll(object sender, ScrollEventArgs e)
{
// Process only the EndScroll Event
if (e.Type == ScrollEventType.EndScroll)
{
// Validation
if (!checkValidation())
{
// Rollback Scroll Value
hsbSheet.Value = sheetNum;
hsbSheet.Update();
// Exit
return;
}
// Process Scroll
processScroll();
}
}
If there is a validation error, the scroll value goes back to the previous value. In this example, user was trying to set value of scroll bar to 4. But since there was a validation error, scroll value is back at 3 again. Now if he clicks on the scroll bar again, the value is now 5! Now here comes my question, how do you stop the value from accumulating that way after having been reset back to 3? In another words, when he clicks on the scroll bar again to increment, it should have been 4 again.
Thanks before hand.
Update #1
I am trying everything:
...
// Rollback Scroll Value
hsbSheet.Value = sheetNum;
hsbSheet.Refresh();
hsbSheet.ResetText();
hsbSheet.Update();
...
And it still has the same issue...
hsbSheet.Value = sheetNum;
You cannot update the Value property in the Scroll event handler, it will very quickly be overwritten again when the event handler returns. You are supposed to use the e.NewValue property to override where it is going to go. Like this:
if (!checkValidation())
{
e.NewValue = sheetNum; // or e.OldValue
return;
}
Related
I am modifying the value of a DGV cell programmatically. For some reason, this is clearing the SelectedRows property of the DGV. Here is my code:
1: public static void WriteDGVCellValueByRowAndCol<T>(DataGridView dgv, int row, int column, T newValue)
2: {
3: dgv.Rows[row].Cells[column].Value = newValue;
4: }
Seems pretty straightforward, I thought. Unfortunately, this is what happens when I trace through using a break point and the debugger:
Before line 3 executes, debugger shows dgv.SelectedRows.Count == 1
After line 3 executes, debugger shows dgv.SelectedRows.Count == 0
The DGV itself remains intact, and the cell value is getting updated, but the SelectedRows property is being emptied so that its Count is 0. Why would this happen, and what might I do to prevent it?
Edit
This only appears to occur after also changing the selected row programmatically from another form. E.g., if I set the current Rows[n].Selected = false; and then set Rows[n+1].Selected = true;, then the above function fails in the way described. However, if I do not perform this selection modification first, no error occurs (the SelectedRows property remains intact).
For the meantime, I'm working around this issue successfully with the following update to the function, but it does not seem like it should be necessary, so I'd love any help figuring out what is causing this strange effect.
1: // Workaround
2: public static void WriteDGVCellValueByRowAndCol<T>(DataGridView dgv, int row, int column, T newValue)
3: {
4: dgv.Rows[row].Cells[column].Value = newValue;
5: dev.Rows[row].Selected = true;
6: }
For purposes of clarity, here is the function being used to modify the selection programmatically:
private void SelectNext()
{
int currentIndex = myDGV.SelectedRows[0].Index;
int newIndex = currentIndex;
if (currentIndex == myDGV.RowCount - 1)
{
Log("The last row was already selected. Wrapping to the first row.");
newIndex = 0;
}
else newIndex++;
myDGV.Rows[currentIndex].Selected = false;
myDGV.Rows[newIndex].Selected = true;
}
This function is being passed to another form using Func<...> and works as expected, except for this SelectedRows side effect.
I have a gtk entry right below the scrolled window which has the default focus , left and right keys move the cursor in the entry ,I am able to catch the key press events for up and down arrow keys but don't know how to scroll the scrolled window, referred many websites none of them were clear or explained only in parts.
Below are some of the pages I went through:
https://mail.gnome.org/archives/gtk-devel-list/2002-February/msg00104.html
https://developer.gnome.org/gtkmm-tutorial/stable/sec-keyboardevents-overview.html.en
tried using gtk_scrolled_window_set_vadjustment() couldn't get it working.
The official page says GTK_SCROLL_STEP_UP is deprecated but doesn't say what should be used instead.
Every answer would be very appreciated.Thanks
bool Method::cb_MPWindow(GtkWidget *wgt, GdkEventKey *event, MethodSelect *ms)
{
if(event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_Down)
{
g_signal_emit_by_name(ms->ScrolledWindow, "scroll-child",(event->keyval == GDK_KEY_Up)?GTK_SCROLL_STEP_UP:GTK_SCROLL_STEP_DOWN);
//The above line works in gtk 3.14.5 but crashes the app in 3.24.5
return TRUE;
}
return FALSE;
}
In order to scroll the window with the keyboard, you need to:
Get the scrolled window's vertical or horizontal adjustment with gtk_scrolled_window_get_vadjustment() or gtk_scrolled_window_get_hadjustment().
From the adjustment object, get the following properties: value (the current scroll position), step-increment (how much to scroll by line), and page-increment (how much to scroll by page).
Then, according to the key that was pressed, you add or subtract the increment to value and then set the new value with gtk_adjustment_set_value().
The the window will scroll when change when you set the value. Typically the line increment is used when navigating with the arrow keys, while the page increment when using the Page Up/Down keys. You add them when scrolling down, and subtract while scrolling down. It is worth noting that the increments change dynamically based on the window size, so you do not need to set them manually.
Here is my code (in C). First setting up the callback:
// Create a scrolled window
GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
// Get the vertical adjustment object
GtkAdjustment *page_vertical_adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolled_window));
// Connect to the key press event
g_signal_connect(
GTK_SCROLLED_WINDOW(scrolled_window),
"key-press-event",
G_CALLBACK(keyboard_scrolling),
page_vertical_adjustment
);
And then the callback function:
void keyboard_scrolling(GtkScrolledWindow *widget, GdkEventKey event, GtkAdjustment *adjustment)
{
// Get the vertical position of the page
gdouble position = gtk_adjustment_get_value(adjustment);
// Get the scrolling increments
gdouble step = gtk_adjustment_get_step_increment(adjustment); // Amount to be scrolled by the arrows (roughly a line)
gdouble page = gtk_adjustment_get_page_increment(adjustment); // Amount to be scrolled by the Page keys (roughly the visible area)
// printf("step: %f, page: %f, key: %d\n", step, page, event.keyval);
// Check which key was pressed
switch (event.keyval)
{
case GDK_KEY_Page_Down:
gtk_adjustment_set_value(adjustment, position + page);
break;
case GDK_KEY_Page_Up:
gtk_adjustment_set_value(adjustment, position - page);
break;
case GDK_KEY_Down:
gtk_adjustment_set_value(adjustment, position + step);
break;
case GDK_KEY_Up:
gtk_adjustment_set_value(adjustment, position - step);
break;
default:
break;
}
}
For convenience, here is a list of keyboard macros that GTK accepts: https://github.com/GNOME/gtk/blob/main/gdk/gdkkeysyms.h
I'm implementing some drag drop features in one my controls inheriting from a datagridview. Basically I'm dragging a row from somewhere in the DGV and dropping it somewhere else, reordering the rows. I've run into a problem though. If the DGV is too large such that there's a scrollbar, how can I have the DGV scroll up or down while the user is in the middle of a dragdrop?
I know how to get the current mouse position and also get the position of the dgv rectangle and such. So, I can easily find out if i'm in the top or bottom half of the rectangle... I just need a way to programmatically scroll the dgv. I'd prefer if I don't have to keep changing the selected cell to do this.
Any suggestions?
Thanks
Isaac
Well, since this is a datagridview... Sorry for the 'winforms' in the question... but I could just do this.. scrolling up or down one row.
Scroll up:
this.FirstDisplayedScrollingRowIndex = this.FirstDisplayedScrollingRowIndex - 1
Scroll Down:
this.FirstDisplayedScrollingRowIndex = this.FirstDisplayedScrollingRowIndex + 1;
You've gotta make sure to check that the numbers don't go out of bounds though.
you can do this by setting HorizontalScrollingOffset / VerticalScrollingOffset of the DataGridView
to set HorizontalScrollingOffset
dataGridView1.HorizontalScrollingOffset = dataGridView1.HorizontalScrollingOffset + 10;
check
DataGridView.HorizontalScrollingOffset Property
and
for VerticalScrollingOffset you can use Reflection
include namespace System.Reflection
PropertyInfo verticalOffset = dataGridView1.GetType().GetProperty("VerticalOffset", BindingFlags.NonPublic | BindingFlags.Instance);
verticalOffset.SetValue(this.dataGridView1, 10, null);
dgv.FirstDisplayedScrollingRowIndex = dgv.RowCount - 1;
You may do that using WinAPI by sending message to the control telling it to scroll up or down.
Here is the code, I hope it helps:
private const int WM_SCROLL = 276; // Horizontal scroll
private const int WM_VSCROLL = 277; // Vertical scroll
private const int SB_LINEUP = 0; // Scrolls one line up
private const int SB_LINELEFT = 0;// Scrolls one cell left
private const int SB_LINEDOWN = 1; // Scrolls one line down
private const int SB_LINERIGHT = 1;// Scrolls one cell right
private const int SB_PAGEUP = 2; // Scrolls one page up
private const int SB_PAGELEFT = 2;// Scrolls one page left
private const int SB_PAGEDOWN = 3; // Scrolls one page down
private const int SB_PAGERIGTH = 3; // Scrolls one page right
private const int SB_PAGETOP = 6; // Scrolls to the upper left
private const int SB_LEFT = 6; // Scrolls to the left
private const int SB_PAGEBOTTOM = 7; // Scrolls to the upper right
private const int SB_RIGHT = 7; // Scrolls to the right
private const int SB_ENDSCROLL = 8; // Ends scroll
[DllImport("user32.dll",CharSet=CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg,IntPtr wParam, IntPtr lParam);
Now assuming you have a textbox control on your form. You can move it with:
SendMessage(textBox1.Handle,WM_VSCROLL,(IntPtr)SB_PAGEUP,IntPtr.Zero); //ScrollUp
SendMessage(textBox1.Handle,WM_VSCROLL,(IntPtr)SB_PAGEDOWN,IntPtr.Zero); //ScrollDown
If that classic general solution doesn't work for you. You may want to look at FirstDisplayedScrollingRowIndex Property and change it regarding your mouse position during dragging.
You need to implement the DragOver event. Check if the mouse is located close to the top or the bottom of the control (use PointToClient). When it is, enable a timer with an interval of ~200 msec. In the Tick event handler scroll the DGV by a row. Disable the timer when the mouse isn't close and after DoDragDrop returns. The user can now easily and intuitively scroll the grid just be hovering near the ends.
Answer using MaxEcho's answer as a base. You can override the OnScroll event of the DataGridView. The eventArgs in this method contain the first visible line number. You can pass this line number to the other DataGridView, and set the FirstDisplayedScrollRowIndex to cause it to scroll to that position.
The only issue is somewhat cosmetic, if you scroll quite fast, the second datagridview pauses updating/animating and blinks to the active line once your scrolling slows.
class DGVSubclass : DataGridView
{
public Action<ScrollEventArgs> delOnScroll;
....
protected override void OnScroll(ScrollEventArgs e)
{
base.OnScroll(e);
if (e.ScrollOrientation == ScrollOrientation.VerticalScroll &&
delOnScroll != null)
delOnScroll.Invoke(e);
}
public void setSrcoll(ScrollEventArgs e)
{
//ScrollEventArgs.NewValue is a line number
this.FirstDisplayedScrollingRowIndex = e.NewValue;
}
public void bindScroll(DGV3_Broker dgv)
{
delOnScroll = dgv.setSrcoll;
}
}
dgvTwo.bindScroll(dgvOne); //DGVTwo Scrollbar controls both,
I want to change the direction of my marquee on changeDirection button click.
My code for changing direction is :
private void changeDirection_click(object sender, RoutedEventArgs e)
{
if (_marqueeType == MarqueeType.RightToLeft)
{
_marqueeType = MarqueeType.LeftToRight;
StartMarqueeing(_marqueeType);
}
else if (_marqueeType == MarqueeType.LeftToRight)
{
_marqueeType = MarqueeType.RightToLeft;
StartMarqueeing(_marqueeType);
}
}
And code for start marquee is :
public void StartMarqueeing(MarqueeType marqueeType)
{
double height = canMain.ActualHeight - marqueeList.ActualHeight;
marqueeList.Margin = new Thickness(0, 0, 0, 0);
doubleAnimation.From = -marqueeList.ActualWidth;
doubleAnimation.To = canMain.ActualWidth;
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
doubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(_marqueeTimeInSeconds));
if (marqueeType == MarqueeType.RightToLeft)
{
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(Canvas.Right)"));
_storyBoard.Children.Add(doubleAnimation);
_storyBoard.Begin(marqueeList, true);
}
else if (marqueeType == MarqueeType.LeftToRight)
{
Storyboard.SetTargetProperty(doubleAnimation, new PropertyPath("(Canvas.Left)"));
_storyBoard.Children.Add(doubleAnimation);
_storyBoard.Begin(marqueeList, true);
}
}
Now here I am able to change the direction from Right to Left only first time.
But when I am change it from Left to Right it’s not changing the marquee position Left to Right.
It looks like you left out a _storyboard = new Storyboard(), perhaps at the top of the StartMarqueeing method.
From what I see it appears that every call to StartMarqueeing will add an additional DoubleAnimation to the storyboard, then start it again. So all the old DoubleAnimations will be recreated, and it looks like they take precedence.
Try creating a new Storyboard object each time, not just re-using it and adding to its children collection.
Update
Oh, now I see the problem. You should not be setting both (Canvas.Left) and (Canvas.Right). Use only one of the two: That's all you need anyway, and using both will give the Canvas conflicting instructions. Traditionally people use (Canvas.Left). I think that's what Canvas selects, which is what is causing your asymmetry.
You may wonder why I say you are using both when you don't think your two animations every run at the same time. Actually they do: The first animation runs then holds the value on the animated property until it is removed or bumped off by another animation. If the second animation then runs and modifies a different property it doesn't bump off the first animation so the first animation's value is still present.
The bottom line is, using (Canvas.Left) on both animations should fix it as long as you are using the default HandoffBehavior.SnapshotAndReplace.
I have a WPF Canvas with some Ellipse objects on it (displayed as circles). Each circle is from a collection class instance which is actually a custom hole pattern class. Each pattern has a certain number of circles, and each circle then gets added to the canvas using an iteration over the collection using the code below.
So, the canvas is populated with a bunch of circles and each circle belongs to a certain pattern instance. You can see a screenshot here: http://twitpic.com/1f2ci/full
Now I want to add the ability to click on a circle on the canvas, and be able to determine the collection it belongs to, so that I can then do some more work on the selected pattern to which that circle belongs.
public void DrawHoles()
{
// Iterate over each HolePattern in the HolePatterns collection...
foreach (HolePattern HolePattern in HolePatterns)
{
// Now iterate over each Hole in the HoleList of the current HolePattern...
// This code adds the HoleEntity, HoleDecorator, and HoleLabel to the canvas
foreach (Hole Hole in HolePattern.HoleList)
{
Hole.CanvasX = SketchX0 + (Hole.AbsX * _ZoomScale);
Hole.CanvasY = SketchY0 - (Hole.AbsY * _ZoomScale);
canvas1.Children.Add(Hole.HoleEntity);
}
}
}
All FrameworkElements have a Tag property which is of type object that can be used to hold arbitrary information. You could assign the HolePattern to the Tag property and easily use that later to get the associated collection.
i.e.:
...
Hole.HoleEntity.Tag = HolePattern as object;
canvas1.Children.Add(Hole.HoleEntity);
later on in the click event:
event(object sender,....)
{
Ellipse e = sender as Ellipse;
HolePattern hp = e.Tag as HolePattern;
...
}
So you probably already read my reply where I said I had it working. And it does work perfectly, (except that it requires great precision with the mouse), but I want to ask this: is it really smart to add an event handler to EVERY ellipse that gets added to a canvas? Now I don't know what kind of memory bog that could be, or maybe it is a piece of cake for WPF and Windows to handle.
In a practical case, I guess there would be not more that 30-50 holes even on a screen that had multiple patterns, but still; FIFTY event handlers? It just seems scary. And actually, each "Hole" is visually represented by two concentric circles and a text label (see the screenshow here: http://twitpic.com/1f2ci/full ), and I know the user would expect to be able to click on any one of those elements to select a hole. That means an event handler on 3 elements for every hole. Now we could be talking about 100 or more event handlers.
It seems like there should be a solution where you could have just one event handler on the Canvas and read the element reference under the mouse, then work off of that to get the .Tag property of that elment, and so on.
I thought I'd post my final and more refined solution in case it helps anyone else.
void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int ClickMargin = 2;// Adjust here as desired. Span is in both directions of selected point.
var ClickMarginPointList = new Collection<Point>();
Point ClickedPoint = e.GetPosition(canvas1);
Point ClickMarginPoint=new Point();
for (int x = -1 * ClickMargin; x <= ClickMargin; x++)
{
for (int y = -1 * ClickMargin; y <= ClickMargin; y++)
{
ClickMarginPoint.X = ClickedPoint.X + x;
ClickMarginPoint.Y = ClickedPoint.Y + y;
ClickMarginPointList.Add(ClickMarginPoint);
}
}
foreach (Point p in ClickMarginPointList)
{
HitTestResult SelectedCanvasItem = System.Windows.Media.VisualTreeHelper.HitTest(canvas1, p);
if (SelectedCanvasItem.VisualHit.GetType().BaseType == typeof(Shape))
{
var SelectedShapeTag = SelectedCanvasItem.VisualHit.GetValue(Shape.TagProperty);
if (SelectedShapeTag!=null && SelectedShapeTag.GetType().BaseType == typeof(Hole))
{
Hole SelectedHole = (Hole)SelectedShapeTag;
SetActivePattern(SelectedHole.ParentPattern);
SelectedHole.ParentPattern.CurrentHole = SelectedHole;
return; //Get out, we're done.
}
}
}
}