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;
}
}
Related
I have a TabControl with many TabItems inside.
I am trying to make TabControl's width equal to Max of its TabItems Width(same for Height).
I cannot set it explicitly, because my program is multilanguage, and so depending on language selected, I need different widths to all of my labels(they all are in "auto").
Is there a way to do in only in XAML?
Edit :
I found a part of solution in order to do it in the code : When I open my window I do the following :
List<TabItem> list = new List<TabItem>();
list.AddRange(tabControl.Items.OfType<TabItem>());
foreach (TabItem tabItem in list)
{
tabControl.SelectedItem=tabItem;
tabControl.UpdateLayout();
tabItem.UpdateLayout();
System.Threading.Thread.Sleep(250);
maxWidth = Math.Max(maxWidth, tabItem.ActualWidth);
maxHeight = Math.Max(maxHeight, tabItem.ActualHeight);
}
tabControl.Width = maxWidth;
tabControl.Height = maxHeight;
It seems to be a working solution, but I don't understand why tabItem.ActualWidth and tabItem.ActualHeight are always set to 0?
To understand better, I put a screenshot of my window :
When I open my window, I already know in which language it may be opened, so the TabItems dimensions don't change after the windows is loaded.
Edit 2 :
Here is concretely what I mean by "size of TabItems differ regarding used language, this is not the best example as here difference is small, but I would like to apply that to all TabContols of my project to be sure I never met the problem if I make changes in future, or even add another languages :
In Russian :
In English :
I finally found a solution, maybe not the best, but it is working perfectly, and didn't find any other solution.
It is not possible to do the job when initializing the window, as the contents has not been loaded yet (MyWindow.Show()), so I had to use the SizeChanged event of TabControl.
On my ViewModel I set two parameters TabControlWidth and TabControlHeight.
private double tabControlWidth = 0;
public double TabControlWidth
{
get { return tabControlWidth; }
set
{
if (this.tabControlWidth != value)
{
tabControlWidth = value;
this.NotifyPropertyChanged("TabControlWidth");
}
}
}
private double tabControlHeight = 0;
public double TabControlHeight
{
get { return tabControlHeight; }
set
{
if (this.tabControlHeight != value)
{
tabControlHeight = value;
this.NotifyPropertyChanged("TabControlHeight");
}
}
}
in my file MyWindow.xaml.cs I add a bool property to check when I checked all TabItems(obviously, this property is set to false when initializing the window) :
bool allTabItemsChecked { get; set; }
Finally, I need to add to my TabControl two things :
Bind Width and Height to my properties in ViewModel.
Add event SizeChanged
SizeChanged="TabControlSizeChanged" MinHeight="{Binding TabControlHeight}" MinWidth="{Binding TabControlWidth}"
(excuse me, I don't manage to display the last line as code? often happens after I use "-" or "*")
Finally I make the SizeChanged function as following :
private void TabControlSizeChanged(object sender, SizeChangedEventArgs e)
{
if(this.allTabItemsChecked==false)
{
int index = tabControl.SelectedIndex;
List<TabItem> list = new List<TabItem>();
list.AddRange(tabControl.Items.OfType<TabItem>());
if (index < list.Count()-1)
{
tabControl.SelectedIndex = index + 1;
}
else
{
this.allTabItemsChecked = true;
tabControl.SelectedIndex = 0;
}
contexte.TabControlWidth = Math.Max(tabControl.ActualWidth, contexte.TabControlWidth);
contexte.TabControlHeight = Math.Max(tabControl.ActualHeight, contexte.TabControlHeight);
}
}
When we display the window, event is automatically launched.
I will then change TabControl.SelectedIndex to the next TabItem, and update TabControl dimensions to the bigger one.
When I reached the last index, I just set SelectedIndex to 0, then set allTabItemsChecked to true.
I have a simple listview that shows client name and age. The list needs to be scrolled and I made the rows' background alternate colour (white and blue). But if the cell containing the client's age is 18, I want to highlight in orange and I want to highlight in red if the age is negative (to signal there is an error).
It all works fine until I start scrolling. At that point everything is messed up amd the orange/red background is not applied correctly.
The adapter code is below. While debugging I noticed that the variable position changes value at each iteration. For example if I initially show only 8 rows, after scrolling I see that position goes to 9, 10... then 5, 4... I understand it might be because it is reusing rows but how can I make it work as expected? I hope someone can help since I tried many time but still didn't succeed. Thank you.
class MyListViewAdapter : BaseAdapter<dbClient>
{
public List<dbClient> mItems;
private Context mContext;
private int mRowLayout;
private string[] mAlternatingColors;
// Default constructor
public MyListViewAdapter(Context context, List<dbClient> items, int rowLayout)
{
mItems = items;
mContext = context;
mRowLayout = rowLayout;
mAlternatingColors = new string[] { "#F2F2F2", "#00bfff" };
}
// Tells how many rows are in the dataset
public override int Count
{
get { return mItems.Count; }
}
// Return a row identifier
public override long GetItemId(int position)
{
return position;
}
// Return the data associated with a particular row
public override dbClient this[int position]
{
get { return mItems[position]; }
}
// Return a view for each row
public override View GetView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
if(row == null)
{
row = LayoutInflater.From(mContext).Inflate(Resource.Layout.listview_row, null, false);
}
row.SetBackgroundColor(Color.ParseColor(mAlternatingColors[position % mAlternatingColors.Length]));
TextView txtName = row.FindViewById<TextView>(Resource.Id.nameView);
txtName.Text = mItems[position].Name;
TextView txtAge = row.FindViewById<TextView>(Resource.Id.ageView);
txtAge.Text = mItems[position].Age.ToString();
// highlight if aged 18
if(txtAge.Text == "18")
{ txtAge.SetBackgroundColor(Color.Orange); }
// signale there is error in the age reported
if(txtAge.Text.Contains("-"))
{ txtAge.SetBackgroundColor(Color.Red); }
return row;
}
private Color GetColorFromInteger(int color)
{
return Color.Rgb(Color.GetRedComponent(color), Color.GetGreenComponent(color), Color.GetBlueComponent(color));
}
}
It all works fine until I start scrolling. At that point everything is messed up amd the orange/red background is not applied correctly.
You're right that it reuses rows, so in your code, you changed color for different reasons and you didn't change it back to your original color. Just need to change part of your code in GetView for example like this:
// highlight if aged 18
if (txtAge.Text == "18")
{ txtAge.SetBackgroundColor(Color.Orange); }
// signale there is error in the age reported
else if (txtAge.Text.Contains("-"))
{ txtAge.SetBackgroundColor(Color.Red); }
// set back to default color
else
{
txtAge.SetBackgroundColor(Color.Black);
}
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
I have a question about the ProcessBar on C#,
How would I add the value of 1 to a label if the method used when passing an item within a list box was successful ?
I have a method like this
private static Form1 f1 = Application.OpenForms["Form1"] as Form1;
public static void GroupList1(processBar bar)
{
f1.listBox1.Items.Add("User1");
bar.Value = 100;
}
public static void GroupList2(processBar bar2)
{
f1.listBox1.Items.Add("User2");
bar.Value = 100;
} // Etc, etc - up to GroupList6
I would also like to have a label that tells me how many user's were successfully added (using the bar), I was thinking of adding a method like this :
if (bar.Value = 100)
{
f1.label1.Text = "" + 1;
}
Inside of my GroupList1/2 method, but the label always appears as the value 1 .
This method within the main form of my code loads a separate label :
for(int i = 0; int i < listBox1.Items.Count; i++)
{
label2.Text = i.ToString();
}
So, I would like label 1 to increase by 1 if the user has been loaded into my list box successfully, how would I do this ?
Obviously this isn't actually the code I'm using within my program, a method is used if the selected index changes (which is why I want to increase by 1, to ensure the user parsed the method successfully), but the question still remains as described, thanks.
I dont quite understand well what are you trying to achieve, but if you only look in increasing the value of such label then you can easily do this by
if (bar.Value = 100)
{
f1.label1.Text = ""+(int.Parse(f1.label1.Text)+1);
}
or even a better way to initialize the string if for some reason the Text of the label is not an integer
if (bar.Value == 100)
{
int value;
if(!int.TryParse(f1.label1.Text,out value))
{
f1.label1.Text = "1";
}
else
{
f1.label1.Text = ""+(value+1);
}
}
but the best way to do this is to keep track of the value in a separate variable and just update the content of the label.
It's a bad idea to store the actual count in the label's Text property, instead, create a variable:
private static int count;
Now, change your code to something like this:
if (bar.Value = 100)
{
// Add 1
count += 1;
// Update the UI
f1.label1.Text = count.ToString();
}
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