I'm trying to select all items in ListBox and made this extension method for this purpose:
public static void SetSelectedAllItems(this ListBox ctl)
{
for (int i = 0; i < ctl.Items.Count; i++)
{
ctl.SetSelected(i, true);
}
}
Problem is that if I have lots of items in the ListBox, it takes a long time to accomplish this task and I can watch how the ListBox is automatically scrolling down and selecting items.
Is there a way to temporary pause the update of a control, so that the task would finish faster? I tried using:
ctl.SuspendLayout();
for (int i = 0; i < ctl.Items.Count; i++)
...
ctl.ResumeLayout();
But that doesn't seem to do anything.
Call the BeginUpdate and EndUpdate methods to prevent the drawing/rendering of the control while properties on that control are being set.
Here is the revised code:
public static void SetSelectedAllItems(this ListBox ctl)
{
ctl.BeginUpdate();
for (int i = 0; i < ctl.Items.Count; i++)
{
ctl.SetSelected(i, true);
}
ctl.EndUpdate();
}
You said that you've tried calling SuspendLayout and ResumeLayout, but that only affects the control's layout events. This pair of methods is used when you want to change a control's position relative to other controls, like when you set the Size, Location, Anchor, or Dock properties.
Related
This questions has nothing in common with Reorder / move / dragdrop ListViewItems within the same ListView Control in C# Windows Forms because it will only work for large/small icons view mode!
I found some nice little code here:
http://snipplr.com/view/33427/
using System.Drawing;
using System.Windows.Forms;
namespace System.Windows.Forms // May need to set to something else
{
/// <summary>
/// A ListView with DragDrop reordering.
/// <see cref="http://support.microsoft.com/kb/822483/en-us"/>
/// </summary>
public class ListViewWithReordering : ListView
{
protected override void OnItemDrag(ItemDragEventArgs e)
{
base.OnItemDrag(e);
//Begins a drag-and-drop operation in the ListView control.
this.DoDragDrop(this.SelectedItems, DragDropEffects.Move);
}
protected override void OnDragEnter(DragEventArgs drgevent)
{
base.OnDragEnter(drgevent);
int len = drgevent.Data.GetFormats().Length - 1;
int i;
for (i = 0; i <= len; i++)
{
if (drgevent.Data.GetFormats()[i].Equals("System.Windows.Forms.ListView+SelectedListViewItemCollection"))
{
//The data from the drag source is moved to the target.
drgevent.Effect = DragDropEffects.Move;
}
}
}
protected override void OnDragDrop(DragEventArgs drgevent)
{
base.OnDragDrop(drgevent);
//Return if the items are not selected in the ListView control.
if (this.SelectedItems.Count == 0)
{
return;
}
//Returns the location of the mouse pointer in the ListView control.
Point cp = this.PointToClient(new Point(drgevent.X, drgevent.Y));
//Obtain the item that is located at the specified location of the mouse pointer.
ListViewItem dragToItem = this.GetItemAt(cp.X, cp.Y);
if (dragToItem == null)
{
return;
}
//Obtain the index of the item at the mouse pointer.
int dragIndex = dragToItem.Index;
ListViewItem[] sel = new ListViewItem[this.SelectedItems.Count];
for (int i = 0; i <= this.SelectedItems.Count - 1; i++)
{
sel[i] = this.SelectedItems[i];
}
for (int i = 0; i < sel.GetLength(0); i++)
{
//Obtain the ListViewItem to be dragged to the target location.
ListViewItem dragItem = sel[i];
int itemIndex = dragIndex;
if (itemIndex == dragItem.Index)
{
return;
}
if (dragItem.Index < itemIndex)
itemIndex++;
else
itemIndex = dragIndex + i;
//Insert the item at the mouse pointer.
ListViewItem insertItem = (ListViewItem)dragItem.Clone();
this.Items.Insert(itemIndex, insertItem);
//Removes the item from the initial location while
//the item is moved to the new location.
this.Items.Remove(dragItem);
}
}
}
}
which is based on / was adapted from http://www.codeproject.com/Articles/4576/Drag-and-Drop-ListView-row-reordering.
Unfortunately the code download doesn't work.
However - how is this code beeing used? Simply pasting it in my forms-class won't work. I supposed this code simply overrides all drag events from all listviews within a form?!
Sorry this is such a stupid question, but how do I use this code?
Someone has kindly taken the standard .NET ListView control and used something called Inheritance to make their own version with extra functionality. You should read up on it, it's a useful thing to know.
To get it to work in your project you need to add this class as described below.
I assume you're using Visual Studio, you haven't mentioned otherwise.
In Solution Explorer on the right hand side Right Click on your
solution and go to Add > Class.
Name the class whatever you like (ListViewWithReordering.cs would
make the most sense) and then open that file. Past all of this code
into the file.
Where it says namespace at the top you'll want to edit this to
match your own programs namespace.
Build your project.
In the Toolbox on the left where you normally see all of the
standard .NET controls there should be a section at the top that now
contains ListViewWithReordering.
Drag this control onto your project.
Done! Use it like any other ListView control. The rest should work automatically (as long as this class is indeed reliable).
I created a design example of drag and drop, download the project on the following link. Any questions ask again.
https://drive.google.com/file/d/0B21l6Fz0byBMSi1VTm52V2E4VVE/view?usp=sharing&resourcekey=0-jNA3sA85A6lygH3TyISstA
First add this class to your project.
Then, If you already have a listview from the forms designer, go to the InitializeComponent() method of your form constructor. There your listview is declared as:
private System.Windows.Forms.ListView listView1;
Change this declaration to:
private ListViewWithReordering listView1;
I have a WPF window with a maintabWindow and several tabitems.
It normally works fine and the layout is this:
but when I BEFORE add the following window:
the result is this:
So the problem is related with the tabControl/tabItem refresh.
This is fairly obvious but even more because if I move the window or pass with the mouse on the a tabItem they get refreshed one by one.
I searched and found that here is a solution: http://geekswithblogs.net/NewThingsILearned/archive/2008/08/25/refresh--update-wpf-controls.aspx
so I added:
this.MainTab.Refresh();
this.tabItem1.Refresh();
this.tabItem2.Refresh();
this.tabItem3.Refresh();
this.tabItem4.Refresh();
this.tabItem5.Refresh();
but that didn't change a thing.
Thanx for any help
Ok so in the end it has a quite a weird behavious. If I do
for (int i = 0; i < tbcMain.Items.Count; i++)
{
tbcMain.SelectedIndex = i;
tbcMain.UpdateLayout();
}
it works. But I have to set the 1st tabitem so if I add
tbcMain.SelectedIndex = 0;
it doesn't.
So the solution was put a sleep and it works again.
for (int i = 0; i < tbcMain.Items.Count; i++)
{
tbcMain.SelectedIndex = i;
tbcMain.UpdateLayout();
}
System.Threading.Thread.Sleep(250);
tbcMain.SelectedIndex = 0;
But that is not elegant at all. If anyone has a better solution pls let me know it.
Btw adding the tbcMain.SelectedIndex = 0; on the loaded event of the mainWindow is of no use.
You should be able to set your SelectedIndex first, and without having to include it in your loop:
tbcMain.SelectedIndex = 0;
Then, basing it off of your response, you should be able to either just do .UpdateLayout() on each of your TabItems:
MainTab.UpdateLayout();
tabItem1.UpdateLayout();
tabItem2.UpdateLayout();
tabItem3.UpdateLayout();
tabItem4.UpdateLayout();
tabItem5.UpdateLayout();
Or you should be able to do something like this in your loop:
MainTab.UpdateLayout();
for (int i = 0; i < tbcMain.Items.Count; i++)
{
TabItem tbi = (TabItem)this.FindControl("tabItem"+i);
tbi.UpdateLayout();
}
Updating/refreshing should have nothing to do with setting the selected one. Including the selection of the tab within the loop to i was your problem - not a race condition. Set the tbcMain.SelectedIndex = 0 outside of your loop and you should be fine. Sometimes, however, this doesn't work and you need to set it with Dispatcher:
Dispatcher.BeginInvoke((Action)(() => this.tbcMain.SelectedIndex = 0));
There's a write up/comments on a separate thread regarding why it needs to be sent to Dispatcher:
How to programmatically select a TabItem in WPF TabControl
Though, unfortunately for me, I had a similar issue where I was trying to refresh a ListView on a subtab. Neither .UpdateLayout(), nor .InvalidateVisual() (as I saw on this thread) worked. I just had to rebind my grid in the button event I was using on my main page, so that when the tab was clicked, it was refreshed manually. I added an x:Name property on the tab so I could call it using "dot" syntax, and it exposed the ListView. I simply added the DataTable of results back to that ListView's DataContext.
I have added buttons to a grid layout I created. Here is the code for that.
int nodeIndex = 0;
for (i = 0; i < usedRows; i++)
{
for (j = 0; j < cols; j++)
{
this.tableLayoutPanel1.Controls.Add(nodes[nodeIndex++], j, i);
}
}
Later on in the application I want to be able to change the color of a button at a specified position. Basically change the back round color of the button at position i,j. How would I get access to that specific button? I am using winforms. Is there something like
button = this.tableLayoutPanel1.Controls.GetChildAtPosition(j, i)
You can use something along these lines.
button = this.tableLayoutPanel1.GetControlFromPosition(j, i);
button.BackColor = Color.BLACK;
So first you want to able to find the control and conveniently there is a method called FindControl() that can do just that
MSDN link for reference: http://msdn.microsoft.com/en-us/library/system.web.ui.control.findcontrol(v=vs.110).aspx
Second you want to be able to change the color of the button once you find it.
For buttons you probably want to use the BackColor property.
Again MSDN link for reference:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.backcolor(v=vs.110).aspx
The trick is going to be finding the control and then working with that control as an object to change the color. Don't forget that you can cast the control to a button type once you find it, which should give you access to the BackColor property.
I am trying to get the text value of a "cell" inside of a GridView that is set as the view of a ListView. I do not want to get the SelectedItem of the ListView as that just returns my entire View Model (but not which property the cell refers to).
I am able to get the text value by responding to direct mouse events (up down or whatever) and if the value is a textblock, obviously I can use the text. This works great and as of right now this is my only solution, although its currently limited.
I would like to take it a step further and be able to click anywhere with in the cell area, navigate around to find the appropriate textblock and then use that value. I have tried a half million ways to do this but what seems logical doesn't seem to quite work out like it should.
Setup:
I have a dynamic GridView that creates its own columns and bindings based on data models that I pass to it. I am using a programmatic cell template (shown below) to have individual control over the cells, particularly so I can add a "border" to it making it actually separate out each cell. I have named the objects so I can access them easier when I'm navigating around the VisualTree.
Here is the Template Code. (Note that the content presenter originally was a textblock itself, but this was changed for later flexibility)
private DataTemplate GetCellTemplate(string bindingName)
{
StringBuilder builder = new StringBuilder();
builder.Append("<DataTemplate ");
builder.Append("xmlns='http://schemas.microsoft.com/winfx/");
builder.Append("2006/xaml/presentation' ");
builder.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
builder.Append("xmlns:local = 'clr-namespace:XXXXXXXX");
builder.Append(";assembly=XXXXXXXXX'>");
builder.Append("<Border Name=\"border\" BorderThickness=\"1,0,0,0\" BorderBrush=\"Gray\" Margin=\"-6,-3,-6,-3\">");
builder.Append("<Grid Margin=\"6,3,6,3\">");
builder.Append("<ContentPresenter Name=\"content\" HorizontalAlignment=\"Stretch\" Content=\"{Binding ");
builder.Append(string.Format("{0}", bindingName));
builder.Append("}\"/>");
builder.Append("</Grid>");
builder.Append("</Border>");
builder.Append("</DataTemplate>");
DataTemplate cellTemplate= (DataTemplate)XamlReader.Parse(builder.ToString());
return cellTemplate;
}
What I have Tried:
The logical approach for me was to react to a Mouse event. From the object that had the mouse event I would do either
A. Look at its children to find a textblock, or
B. Get its parent then look for child with a textblock.
My assumption is that if I click in white space I'm clicking in a container that has my textblock. So far the two things that come up are a Border and a Rectangle (if I don't click the text itself). A. Returns absolutely nothing except for the recangle and the border. When I do B i can find textblocks but they are every single text block in the entire row.
So what I try to do from that is get all textblocks, then go backwards till I find which one has a IsMouseOver property as true. It turns out none of these objects EVER have a IsMouseOver except the content presenter for the entire row. So this seems to indicate to me is that the whitespace in the cells does not actually contain the textblock.
What I find is that when I click on the Border and start looking at children, I eventually get to a container that has a rectangle (the rectangle I click) and a grid row view presenter. The presenter shows all of the objects inside the row (hence why i would get all textblocks when i do this recursive scan).
Here is some of the code used to do this to get an idea of what i'm doing. I have written about 10 different versions of this same recursive code generally attempting to find who has the Mouse over it and is related to a textbox.
private void OnPreviewMouseUp(object sender, MouseButtonEventArgs e)
{
object original = e.OriginalSource;
if (original is TextBlock)
{
this.valueTextBlock.Text = ((TextBlock)original).Text;
}
else if (original is FrameworkElement)
{
var result = GetAllNestedChildren<Border>(VisualTreeHelper.GetParent((DependencyObject)original)).Where(x => x.Name == "border").Where(x => HasAChildWithMouse(x)).ToList();
}
else
{
this.valueTextBlock.Text = string.Empty;
}
}
private bool HasAChildWithMouse(UIElement element)
{
if (element.IsMouseOver || element.IsMouseDirectlyOver)
return true;
var childCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(element, i);
if (child is UIElement)
if (HasAChildWithMouse((UIElement)child))
return true;
}
return false;
}
private IEnumerable<T> GetAllNestedChildren<T>(DependencyObject obj) where T : UIElement
{
if (obj is T)
yield return obj as T;
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
foreach (var nested in GetAllNestedChildren<T>(child))
yield return nested;
}
}
private T GetObjectByTypeParentHasMouse<T>(DependencyObject obj) where T : UIElement
{
if (obj is T)
{
if ((VisualTreeHelper.GetParent(obj) as UIElement).IsMouseOver )
{
return obj as T;
}
}
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
var correctType = GetObjectByTypeParentHasMouse<T>(child);
if (correctType != null)
return correctType;
}
return null;
}
private T GetContainedType<T>(DependencyObject obj, bool checkForMouseOver) where T : UIElement
{
if (obj is T && ((T)obj).IsMouseOver)
return obj as T;
var childCount = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < childCount; ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
var correctType = GetContainedType<T>(child, checkForMouseOver);
if (correctType != null)
return correctType;
}
return null;
}
The other approach I took was to start with the TextBlock itself, find its containing parent and find out how i can navigate to the answer. I find the templateparent is the ContentPresenter (named ="content") I find the grid, and then the border. The parent of the border is a content presenter whos content is the data view model for the entire row. The parent of this contentpresenter is the grid column's presenter. This is the same one that i was navigating up to in the other one.
It would appear that the first approach objects while are contain the cell do not actually contain the textblock or the entire cell templated items. It would appear to me there is no way to go from the Border or Rectangle that is clicked, back to the actual text field.
"Long story short" is there ANY way to make this connection?
(Btw I am not willing to give up this ListView/GridView because its payoffs far outweigh this negative and I'd gladly give up on this idea to keep the rest).
I think you sjould be able to either
1) Add some kind of (toggle)button to the root of your data template, and either bind to Command and handle it on your viewmodel or bind to IsChecked/IsPressed and handle changes via data triggers or w/e on the view side.
2) Add EventTrigger to your datatemplate at some point, and handle PreviewNouseUp/Down events there via simple animations.
I tried to find the answer by googling but no luck.
I designed form with panel containing textboxes so I can iterate through its controls
and save every textbox in an array (so I can iterate the array when I want to),
the thing is I couldn't find which property the panel knows how to arrange the order to iterate the controls inside it, which is the first, second,etc..
I thought maybe it's by tag, but when I changed them to my likings it didn't change anything.
so I wonder - how can you tell the panel iteration to go through the controls as you prefer?
which property do you need change?
private void CreateTxtArr()
{
txts = new TextBox[8];
for (int i = 0; i < pnlTxt.Controls.Count; i++)
txts[i] = (TextBox)pnlTxt.Controls[i];
}
You can use is operator to check if the child control of panel is TextBox
private void CreateTxtArr()
{
txts = new TextBox[8];
for (int i = 0; i < pnlTxt.Controls.Count; i++)
if( pnlTxt.Controls[i] is TextBox)
txts[i] = (TextBox)pnlTxt.Controls[i];
}
If you can use .Net 3.5 or higher, you can do it pretty easly with Linq:
private void CreateTxtArray()
{
// txts is an Array of TextBox
var txts = (from Control ctrl in pnlTxt.Controls
where ctrl is TextBox
select ctrl as TextBox).ToArray();
}
Is a simpler way to iterate, in all cases
EDIT: this is an old answer I had forgotten about, below there is an even simpler way to do that
private void CreateTxtArray()
{
// txts is an Array of TextBox
var txts = pnlTxt.Controls.OfType<TextBox>().ToArray();
}