Updated the code to give you a better idea of what's happening and to allow the data to load faster.
I have still not seen anything posted online that answers this question.
I have a MS Chart control on a C# application (Visual Studio 2010 Express, if that makes a difference). Data loads at a slow realtime rate (once per second). My intent is to display 6 minutes of data at a time, with the ability to scroll to another page of data if necessary. I want the data to fill the chart from the left hand side. As the data is added to the chart, the scroll bar and X axis legends also move over so that I can only see the latest data point. I have to scroll to the left to see earlier data -- and the the scroll bar jumps back when the new data point is added. I want the scroll bar to stay in one place (left edge) unless I move it. I have a 1 second timer on my form and new data is added with every time cycle.
I hope this is sufficient. Any help?
Code to Initialize the Chart control:
DateTime startTime = DateTime.Now;
DateTime endTime = startTime.AddMinutes(6);
DateTime maxTime = startTime.AddMinutes(24);
// Bind the chart to the list.
chartAssociateProductivity.DataSource = Globals.listKohlsPerformanceDataSource;
chartAssociateProductivity.ChartAreas["ChartArea1"].CursorX.AutoScroll = true; // enable autoscroll
chartAssociateProductivity.ChartAreas["ChartArea1"].CursorX.IsUserEnabled = false;
chartAssociateProductivity.ChartAreas["ChartArea1"].CursorX.IsUserSelectionEnabled = false;
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.Minimum = startTime.ToOADate();
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.Maximum = endTime.ToOADate();
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.Zoomable = true;
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScrollBar.IsPositionedInside = true;
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.Zoom(startTime.ToOADate(), 6, DateTimeIntervalType.Minutes);
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.Position = 0;// startTime.ToOADate();
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.MinorTickMark.Enabled = true;
// disable zoom-reset button (only scrollbar's arrows are available)
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
// set scrollbar small change to blockSize (e.g. 100)
chartAssociateProductivity.ChartAreas["ChartArea1"].AxisX.ScaleView.SmallScrollSize = 100;
Code to Add the Data (for the example, I will add constant data):
private void timer1_Tick(object sender, EventArgs e)
{
listPerformanceDataSource.Add(new PerformanceRecord(0, 248));
}
The data structure:
public class PerformanceRecord
{
int bagCount, goal;
public PerformanceRecord(int bagCount, int goal)
{
this.bagCount = bagCount;
this.goal = goal;
}
public int BagCount
{
get { return bagCount; }
set { bagCount = value; }
}
public int Goal
{
get { return goal; }
set { goal = value; }
}
}
// Create a list.
public static List<PerformanceRecord> listPerformanceDataSource = new List<PerformanceRecord>();
ChartArea chartArea=new ChartArea();
chartArea = chart1.ChartAreas[series.ChartArea];
int scrollBarVal=chartArea.AxisX.ScaleView.Position;
you have to make a chartArea instance and use its axis position
Related
I am trying to reproduce the operation of the Control Expander WPF, or as shown in the menu of Outlook, Vertical Web Menu etc., since in WindowsForms this control does not exist. Here I leave the sample code: Menu_Expader.zip link GoogleDrive.
I have managed to do it using the following controls:
Panels
FlowLayoutPanel
1 Time Control
Button Vectors
Labels Vectors ...
This works perfectly, but it happens that to each panel I must establish a
Maximum Size and Minimum Size therefore every time I add an item inside I must modify the size of the panel where I add it, and the item are very close to each other is a bit annoying for the user's vision.
Example this is what I currently have:
EDIT
Code Sample:
// The state of an expanding or collapsing panel.
private enum ExpandState
{
Expanded,
Expanding,
Collapsing,
Collapsed,
}
// The expanding panels' current states.
private ExpandState[] ExpandStates;
// The Panels to expand and collapse.
private Panel[] ExpandPanels;
// The expand/collapse buttons.
private Button[] ExpandButtons;
// Initialize.
private void Form1_Load(object sender, EventArgs e)
{
// Initialize the arrays.
ExpandStates = new ExpandState[]
{
ExpandState.Expanded,
ExpandState.Expanded,
ExpandState.Expanded,
};
ExpandPanels = new Panel[]
{
panModule1,
panModule2,
panModule3,
};
ExpandButtons = new Button[]
{
btnExpand1,
btnExpand2,
btnExpand3,
};
// Set expander button Tag properties to give indexes
// into these arrays and display expanded images.
for (int i = 0; i < ExpandButtons.Length; i++)
{
ExpandButtons[i].Tag = i;
ExpandButtons[i].Image = Properties.Resources.expander_down;
}
}
// Start expanding.
private void btnExpander_Click(object sender, EventArgs e)
{
// Get the button.
Button btn = sender as Button;
int index = (int)btn.Tag;
// Get this panel's current expand
// state and set its new state.
ExpandState old_state = ExpandStates[index];
if ((old_state == ExpandState.Collapsed) ||
(old_state == ExpandState.Collapsing))
{
// Was collapsed/collapsing. Start expanding.
ExpandStates[index] = ExpandState.Expanding;
ExpandButtons[index].Image = Properties.Resources.expander_up;
}
else
{
// Was expanded/expanding. Start collapsing.
ExpandStates[index] = ExpandState.Collapsing;
ExpandButtons[index].Image = Properties.Resources.expander_down;
}
// Make sure the timer is enabled.
tmrExpand.Enabled = true;
}
// The number of pixels expanded per timer Tick.
private const int ExpansionPerTick = 7;
// Expand or collapse any panels that need it.
private void tmrExpand_Tick(object sender, EventArgs e)
{
// Determines whether we need more adjustments.
bool not_done = false;
for (int i = 0; i < ExpandPanels.Length; i++)
{
// See if this panel needs adjustment.
if (ExpandStates[i] == ExpandState.Expanding)
{
// Expand.
Panel pan = ExpandPanels[i];
int new_height = pan.Height + ExpansionPerTick;
if (new_height >= pan.MaximumSize.Height)
{
// This one is done.
new_height = pan.MaximumSize.Height;
}
else
{
// This one is not done.
not_done = true;
}
// Set the new height.
pan.Height = new_height;
}
else if (ExpandStates[i] == ExpandState.Collapsing)
{
// Collapse.
Panel pan = ExpandPanels[i];
int new_height = pan.Height - ExpansionPerTick;
if (new_height <= pan.MinimumSize.Height)
{
// This one is done.
new_height = pan.MinimumSize.Height;
}
else
{
// This one is not done.
not_done = true;
}
// Set the new height.
pan.Height = new_height;
}
}
// If we are done, disable the timer.
tmrExpand.Enabled = not_done;
}
I want to get a result similar to this - Bootstrap Menu Accordion:
Imitate that operation panels expand according to the quantity of item that it contains as long as it does not protrude from the screen, in which case it will show the scroll bar. I know there are software that provide custom controls like DVexpress, DotNetBar Suite among others, but they are Licensed Software I do not want to use it illegally pirate. Can you help me optimize it or create it in another way?
Environment: Visual Studio 2010 & .NET NetFramework 4.
The original question I made it in StackOverFlow in Spanish.
Modulo (Module)
Menu Principal (Main menu)
Mantenimientos (Maintenance)
Procesos (Processes)
Consultas (Queries)
Reportes (Reports)
Note: If someone speaks Spanish and English and can do a better translation, please edit the question. (Excuse the advertising on the image, I recorded the screen with a software trial version).
Just a quick one.
I have a graph that has the possibility to display 9 different series, data comes in through textboxes from user and populates these respective series.
The graph is linked to a checkedlistbox and the items that are checked in the listbox enable their respective series on the chart. Only 2 series may be enabled at any one time, which works without a problem using the code below:
private void chListBoxChartSeries_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.NewValue == CheckState.Checked && chListBoxChartSeries.CheckedItems.Count >= 2)
{
e.NewValue = CheckState.Unchecked;
}
}
public void saveChartSeries()
{
//placeholder variable to relate between checklist item and chart series
string seriesName;
for (int index = 0; index < chListBoxChartSeries.Items.Count; ++index)
{
seriesName = chListBoxChartSeries.Items[index].ToString();
if (chListBoxChartSeries.CheckedItems.Contains(chListBoxChartSeries.Items[index]))
{
main.chartVitals.Series[seriesName].Enabled = true;
}
else
{
main.chartVitals.Series[seriesName].Enabled = false;
}
}
}
There's one thing I do want to do following this, I would like whichever series are enabled to be set to a colour each (first series red, second series blue for example). I'm struggling to find an efficient way to do this, but I imagine it involves setting the first of the two indexes to one colour (red) and the second to another colour (blue). I figure I can do this using the existing for-loop in the saveChartSeries() function, something like this:
public void saveChartSeries()
{
//placeholder variable to relate between checklist item and chart series
string seriesName;
for (int index = 0; index < chListBoxChartSeries.Items.Count; ++index)
{
seriesName = chListBoxChartSeries.Items[index].ToString();
if (chListBoxChartSeries.CheckedItems.Contains(chListBoxChartSeries.Items[index]))
{
main.chartVitals.Series[seriesName].Enabled = true;
if (main.chartVitals.Series[seriesName].Enabled == true)
{
//set series color to Color.Red
//if there is already a red series, set to Color.Blue
}
}
else
{
main.chartVitals.Series[seriesName].Enabled = false;
}
}
}
This is about as much as I can get so far, if anyone can offer a next step, or if I'm over-complicating it and there's a simpler way, I'd really appreciate someone pointing it out!
If I understand you correctly, you want to color each visible series with a color from a fixed list.
That will involve changing subsequent series' colors whenever you enable or disable a previous series, right?
Here is a function that will do that:
void colorSeries(Chart chart)
{
List<Color> seriescolors = new List<Color>
{ Color.Khaki, Color.Brown, Color.CornflowerBlue,
Color.DarkCyan, Color.ForestGreen, Color.Gold, Color.HotPink, Color.Indigo};
int co = 0;
foreach (Series s in chart.Series)
if (s.Enabled) s.Color = seriescolors[co++];
}
You would call it each time you enable or disable a Series.
You also wrote: if I'm over-complicating it and I figure I can do this using the existing for-loop. Hmm. In my opion you are both overcomplicating it and also setting a completely wrong priority.
Do not try to fit something in an 'existing loop'; instead keep things simple and call a function to take care of the display colors after you have processed the user actions.
Try to 'separate concerns', and always aim at creating small and self-sufficient routines!
I am using the Chart class in Visual Studio 2013 to visualize some of my data. However, my data quickly spawns many series and it's very important to have them all in one chart. I limited the legend area to 20% of the complete chart area, and so I pretty much cannot display more than 7-8 legend items when I stretch my chart to its maximum size. The control just puts ... after it runs out of space for legend items.
Instead of it just writing ..., is it somehow possible to add a scrollbar to the legend and be able to see all of the items? I am aware that I can implement my own legend in some way, but I would like to squeeze the most out of what the Chart class has to offer. I would also like to add checkboxes next to each legend item which would indicate whether the series should be hidden on the chart or not. Is this possible to do without my own legend implementation?
Additionally, I would also like to have a menu expand on right click on a legend item with a few options, but that's completely optional. Scrollbar and checkboxes are my main problem now.
Thanks.
General idea: You have to create two charts. One is main and second for legend only. You will have same series style if series order will be the same.
For showing pop up on right click on legend item:
Connect ContextMenu (ContextMenuStrip class in toolbox) to your legend's chart.
For showing hiding series from legend:
You have to implement MouseClick event handler and check what object is under mouse cursor using math (GetChildAtPoint() method doesn't work for legend items). Equation: is series_index = control_relative_mouse_y / c_legendItemHeight where c_legendItemHeight is value you provide to compute controls height (height of single legend item).
You have to configure your legend chart to contain LegendStyle to Row, MaximumAutoSize to 100, Docking to Left, IsTextAutoFit to false and IsEquallySpacedItems to true.
You have define 3 columns in your legend (one for series style, second for checkbox and third for series name). Use series CustomProperties to keep visibility state. In check column use this custom property (Text = "#CUSTOMPROPERTY(...)") to show check state. Chart does not support auto sizing. You can do it manually. During series load set your chart height to calculated value. This value equals to _stock.Shares.Count * c_legendItemHeight + 9. Where: _stock.Shares.Count is number of items in legend, c_legendItemHeight constant height of item (integer value, numbers grater then 18 seems to work for me), 9 (seems to be constant). I know it is not nice but I cannot find any better solution. I've added 502 series in my example and it worked fine. Make sure that you don't have any margin in your chart because otherwise you will be not able to calculate series number correctly.
For "many series in legend" problem:
Put your legend chart into a panel with AutoScroll property turned on. Set panels and legends height using expression from above description.
Source code:
public partial class Form1 : Form
{
private const int c_legendItemHeight = 20;
private const string c_checkCustomPropertyName = "CHECK";
private const string c_checkedString = "✔"; // see http://www.edlazorvfx.com/ysu/html/ascii.html for more
private const string c_uncheckedString = "✘";
private Stock _stock;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_stock = Stock.Load();
// mainChart
mainChart.Legends.Clear();
foreach (Share share in _stock.Shares)
{
Series series = mainChart.Series.Add(share.Name);
series.ChartType = SeriesChartType.Line;
foreach (ShareQuotation shareQuotation in share.Quotations)
{
series.Points.AddXY(shareQuotation.Date.ToString(), shareQuotation.Close);
}
}
// LegendChart
Legend legend = legendChart.Legends[0];
legendChart.Series.Clear();
legend.IsTextAutoFit = false;
legend.IsEquallySpacedItems = true;
legend.MaximumAutoSize = 100;
legend.Docking = Docking.Left;
legend.LegendStyle = LegendStyle.Column;
legend.Position.Auto = true;
legend.Position.Width = 100;
legend.Position.Height = 100;
legend.CellColumns[1].Text = "#CUSTOMPROPERTY(" +c_checkCustomPropertyName+ ")";
foreach (Share share in _stock.Shares)
{
Series series = legendChart.Series.Add(share.Name);
series.SetCustomProperty(c_checkCustomPropertyName,c_checkedString);
}
legendChart.Height = _stock.Shares.Count * c_legendItemHeight + 9; // 9 - seems to be constant value
legendPanel.Height = legendChart.Height;
}
private void legendChart_MouseClick(object sender, MouseEventArgs e)
{
Point mousePosition = legendChart.PointToClient(Control.MousePosition);
int seriesNo = mousePosition.Y / c_legendItemHeight;
Series series = legendChart.Series[seriesNo]; // TODO - check if not out of range
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// check uncheck series
if (series.GetCustomProperty(c_checkCustomPropertyName) == c_checkedString)
{
// if checked
// uncheck
series.SetCustomProperty(c_checkCustomPropertyName, c_uncheckedString);
series.CustomProperties = series.CustomProperties; // workaround - trigger change - is this a bug?
// hide in mainChart
mainChart.Series[seriesNo].Enabled = false;
}
else
{
// if unchecked
legendChart.Series[seriesNo].SetCustomProperty(c_checkCustomPropertyName, c_checkedString);
series.CustomProperties = series.CustomProperties; // workaround - trigger change - is this a bug?
// show in mainChart
mainChart.Series[seriesNo].Enabled = true;
}
}
}
private void contextMenu_Opening(object sender, CancelEventArgs e)
{
Point mousePosition = legendChart.PointToClient(Control.MousePosition);
int seriesNo = mousePosition.Y / c_legendItemHeight;
Series series = legendChart.Series[seriesNo]; // TODO - check if not out of range
contextMenu.Items.Clear();
string state = series.GetCustomProperty(c_checkCustomPropertyName) == c_checkedString ? "visible" : "hidden";
contextMenu.Items.Add("&Some strange action for " + state + " item named " + series.Name);
contextMenu.Items.Add("&Another action ...");
}
}
Result should look like this:
I have a System.Windows.Forms.Panel with some content.
I am trying to programmatically scroll the panel (vertically) either up or down.
I have tried setting the AutoScrollPosition property to a new Point on the panel but that doesn't seem to do it.
I have the AutoScroll property set to true.
I even tried to set the VerticalScroll.Value twice as suggested here, but that doesn't seem to work either.
This is what I am currently doing:
//I have tried passing both positive and negative values.
panel.AutoScrollPosition = new Point(5, 10);
The X and Y values on AutoScrollPosition remain 0 and 0.
Any help or direction on this would be greatly appreciated it.
Thanks in advance,
Marwan
Here is a solution. I guess you can scroll your Panel by arbitrary position using Win32 however there is a simple trick to help you achieve your requirement here:
public void ScrollToBottom(Panel p){
using (Control c = new Control() { Parent = p, Dock = DockStyle.Bottom })
{
p.ScrollControlIntoView(c);
c.Parent = null;
}
}
//use the code
ScrollToBottom(yourPanel);
Or use extension method for convenience:
public static class PanelExtension {
public static void ScrollToBottom(this Panel p){
using (Control c = new Control() { Parent = p, Dock = DockStyle.Bottom })
{
p.ScrollControlIntoView(c);
c.Parent = null;
}
}
}
//Use the code
yourPanel.ScrollToBottom();
UPDATE
If you want to set the exact position, modifying the code above a little can help:
//This can help you control the scrollbar with scrolling up and down.
//The position is a little special.
//Position for scrolling up should be negative.
//Position for scrolling down should be positive
public static class PanelExtension {
public static void ScrollDown(this Panel p, int pos)
{
//pos passed in should be positive
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + pos })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p, int pos)
{
//pos passed in should be negative
using (Control c = new Control() { Parent = p, Height = 1, Top = pos})
{
p.ScrollControlIntoView(c);
}
}
}
//use the code, suppose you have 2 buttons, up and down to control the scrollbar instead of clicking directly on the scrollbar arrows.
int i = 0;
private void buttonUp_Click(object sender, EventArgs e)
{
if (i >= 0) i = -1;
yourPanel.ScrollUp(i--);
}
private void buttonDown_Click(object sender, EventArgs e)
{
if (i < 0) i = 0;
yourPanel.ScrollDown(i++);
}
Another solution you may want to use is using Panel.VerticalScroll.Value. However I think you need more research to make it work as you expect. Because I can see once changing the Value, the scrollbar position and control position don't sync well. Notice that Panel.VerticalScroll.Value should be between Panel.VerticalScroll.Minimum and Panel.VerticalScroll.Maximum.
This surprisingly works! NOTE THE MINUS SIGN in the code. There is strange behavior in setting scroll position. If you set the position to exact value (50), it goes negative when you read it next time (-50). So you have to invert it before setting new scroll value.
Scroll down:
private void ButtonScrollDown_OnClick(object sender, EventArgs e)
{
Point current = yourScrollPanel.AutoScrollPosition;
Point scrolled = new Point(current.X, -current.Y + 10);
yourScrollPanel.AutoScrollPosition = scrolled;
}
Scroll up similarly, (-current.Y - 10)
If you have a class that derives from Panel, then call these two protected methods to scroll the panel:
// The bottom is off screen; scroll down. These coordinates must be negative or zero.
SetDisplayRectLocation(0, AutoScrollPosition.Y - item.BoundingRect.Bottom + ClientRectangle.Bottom);
AdjustFormScrollbars(true);
In my example, item.BoundingRect.Bottom is the Y coordinate of the bottom of a thumbnail, and I need to scroll the panel down so that the whole thumbnail is visible.
#King King's solution of creating a temporary Control just so that scrolling could be done seemed "heavy" to me. And #Hans Passant's suggestion of setting AutoScrollMinSize and AutoScrollPosition didn't work for me.
Leave AutoScroll to its default value of 'true'.
Try this:-
panel.ScrollControlIntoView(childcontrol);
This should work. childcontrol is the particular control that you want to show in your display area.
Setting the value of the HorizontalScroll property and then using the method ScrollControlIntoView works for me:
lpanel.HorizontalScroll.Value = 100;
lpanel.ScrollControlIntoView(lpanel);
Use #King King Answered Code and if you want to hide horizontal and vertical scroll bar, just apply the below code in the constructor or initialization.
yourPanel.AutoScroll = false;
yourPanel.HorizontalScroll.Maximum = 0;
yourPanel.HorizontalScroll.Visible = false;
yourPanel.VerticalScroll.Maximum = 0;
yourPanel.VerticalScroll.Visible = false;
yourPanel.AutoScroll = true;
I had an issue where I couldnt get my panel to scroll back to top . I tried many things to try and get the panel to scroll back to the top after populating it with many controls.
Nomatter what I did it always put the VScroll bar to the bottom.
After exhaustive testing I found it was because my controls had the TabStop property set to true (default on user controls) was causing the issue.
Setting TabStop to false fixed it.
Create an control that sits slightly outside the visible area (so -1 at the top and clientsize+1 ) and then call ScrollControlIntoView:
public static class PanelExtension {
public static void ScrollDown(this Panel p)
{
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + 1 })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p )
{
using (Control c = new Control() { Parent = p, Height = 1, Top = -1})
{
p.ScrollControlIntoView(c);
}
}
}
//use the code, suppose you have 2 buttons, up and down to control the scrollbar instead of clicking directly on the scrollbar arrows.
private void buttonUp_Click(object sender, EventArgs e)
{
yourPanel.ScrollUp();
}
private void buttonDown_Click(object sender, EventArgs e)
{
yourPanel.ScrollDown();
}
with yourpanel.SetAutoScrollMargin(1, 1); you can set very fine scrolling steps and then take a timer to call the srolling when buttons are down
I am trying to implement a way to move back the scroll bar of a datagrid back to a previous position following this sample http://www.codeproject.com/Articles/109531/Controlling-and-Viewing-the-ScrollBar-Positions-of but this method always returns null making it impossible to create automation. Does any have an idea why this would always return null?
public static IScrollProvider GetScrollProvider(DataGrid grid)
{
var p = FrameworkElementAutomationPeer.FromElement(grid)
?? FrameworkElementAutomationPeer.CreatePeerForElement(grid);
return p.GetPattern(PatternInterface.Scroll) as IScrollProvider;
}
Ancient history, but at least current version seems to work liek charm in all cases.
//p.GetPattern(PatternInterface.Scroll) as IScrollProvider;
{System.Windows.Automation.Peers.DataGridAutomationPeer}
[System.Windows.Automation.Peers.DataGridAutomationPeer]:
{System.Windows.Automation.Peers.DataGridAutomationPeer}
HorizontallyScrollable: true
HorizontalScrollPercent: 0.0
HorizontalViewSize: 81.44329896907216
VerticallyScrollable: true
VerticalScrollPercent: 29.062733871459486
VerticalViewSize: 2.9625
I know its a old post, but in case someone gets here with the same problem, (Like me), here is how i solved it:
For some reason, when you the the scrollbar of the DataGrid, if the user didn't scroll yet, it will inform that the maximum position is 0, even if it isn't. So i saved the maximum value before updating and restore it after:
First i create a class to save those values. It could be variables, but a class seemed more organized:
public class ScrollInfo
{
public double HorizontalPosition;
public double HorizontalMaximum;
public double VerticalPosition;
public double VerticalMaximum;
}
Before i update the ItemSource property i use this method to save Scroll info:
public static ScrollInfo GetScrollInfo(this DataGrid grid)
{
ScrollInfo oInfo = new ScrollInfo();
ScrollBar sbHorizontal = grid.GetScrollbar(ScrollMode.Horizontal);
oInfo.HorizontalMaximum = sbHorizontal.Maximum;
oInfo.HorizontalPosition = sbHorizontal.Value;
ScrollBar sbVertical = grid.GetScrollbar(ScrollMode.Vertical);
oInfo.VerticalMaximum = sbVertical.Maximum;
oInfo.VerticalPosition = sbVertical.Value;
return oInfo;
}
And then i call this method to set this info back to the grid after the update:
public static void SetScrollPosition(this DataGrid grid, ScrollInfo info)
{
if (info.HorizontalPosition > 0)
{
ScrollBar sbHorizontal = grid.GetScrollbar(ScrollMode.Horizontal);
sbHorizontal.Maximum = info.HorizontalMaximum;
grid.Scroll(ScrollMode.Horizontal, info.HorizontalPosition);
}
if (info.VerticalPosition > 0)
{
ScrollBar sbVertical = grid.GetScrollbar(ScrollMode.Vertical);
sbVertical.Maximum = info.VerticalMaximum;
grid.Scroll(ScrollMode.Vertical, info.VerticalPosition);
}
}
I even create a UpdateItemSource method to make things easier:
public static void UpdateItemSource(this DataGrid grid, IEnumerable itemSource)
{
ScrollInfo oInfo = grid.GetScrollInfo();
grid.ItemsSource = itemSource;
grid.SetScrollPosition(oInfo);
}
Other methods I use, wich i guess i take from the same post the user who post the question did:
public static void Scroll(this DataGrid grid, ScrollMode mode, double position)
{
// Get the scrollbar and convert the position to percent.
var scrollBar = grid.GetScrollbar(mode);
double positionPct = ((position / scrollBar.Maximum) * 100);
// Scroll to a specfic percentage of the scrollbar.
grid.ScrollToPercent(mode, positionPct);
}
public static void ScrollToPercent(this DataGrid grid, ScrollMode mode, double percent)
{
// Fix the percentage.
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
// Get the scroll provider.
var scrollProvider = GetScrollProvider(grid);
// Scroll.
switch (mode)
{
case ScrollMode.Vertical:
scrollProvider.SetScrollPercent( System.Windows.Automation.ScrollPatternIdentifiers.NoScroll, percent);
break;
case ScrollMode.Horizontal:
scrollProvider.SetScrollPercent(percent, System.Windows.Automation.ScrollPatternIdentifiers.NoScroll);
break;
}
}