Getting graphical Point of Listview items - c#

I have a ListView control on my form and I am trying to get either a Point or even better, a Rectangle of each visible item in my ListView. Anyone know of a trick to do this?

foreach (ListViewItem item in myListView.Items)
{
Rectangle result = item.Bounds;
if(result.IntersectsWith(myListView.ClientRectangle))
{
//there you go
}
}
More about the Bounds you can find here.

What I have done is handle the ListView.DrawSubItem event, which gives me access to a DrawListViewSubItemEventArgs e instance with a e.Bounds property giving me the rectangle you are asking for.
If you don't want to do any drawing on your own just set e.DrawDefault = true;
Here is an example of the results:

Related

Draw Bézier curve between two related TreeViewItems

I want to draw some Bézier curve inside a canvas on the left side of a treeview to display refrerences between items. I did implement a TreeView with lazy loading and mvvm based on a tutorial from Josh Smith.
This is Minimum example how it should look.
To draw the line I want use a canvas element. So I need the correct coordinate of a TreeViewItem relative to TreeView. I didt get the screenPosition using the Initialized Event.
private static void OnTreeViewItemIsInitialized(object sender, RoutedEventArgs e)
{
// Only react to the Selected event raised by the TreeViewItem
// whose IsSelected property was modified. Ignore all ancestors
// who are merely reporting that a descendant's Selected fired.
if (!Object.ReferenceEquals(sender, e.OriginalSource))
return;
TreeViewItem item = e.OriginalSource as TreeViewItem;
var parent = item.Parent;
var point = item.PointToScreen(new Point(0, 0));
var treeViewItemViewModel = item.DataContext as TreeViewItemViewModel;
if (treeViewItemViewModel != null)
treeViewItemViewModel.Coordinate = point;
}
Two challanges Iam running into using this Code: first i only get the screenposition, second I dont know how to get the parent of TreeViewItem to get calculate the correct position.
Maybe Iam on the wrong way to solve my problem? I did found some example to do something similar, but no example for a spline between two TreeViewItems of the same TreeView.

RenderDelegate not being called

I have set up a RenderDelegate to draw the cell text in a different color. I have also set the OwnerDraw property to true.
This is the render code:
this.olvInvoiceType.RendererDelegate = delegate (EventArgs re, Graphics g, Rectangle r, object rowObject)
{
var item= rowObject as MyItem;
var brush = Brushes.Red;
g.DrawString(item.Name, olvItems.Font, brush, r);
return false;
};
However, nothing is being displayed.
What am I missing?
Despite what the example on the ObjectListView website says it seems that you actually have to return true, and not false.
Other examples I found also return true.
Also, don't forget to check that your object is null before rendering, else it will throw an exception!
this.olvInvoiceType.RendererDelegate = delegate (EventArgs re, Graphics g, Rectangle r, object rowObject)
{
var item = rowObject as MyItem;
if (item != null)
{
//g.FillRectangle(new SolidBrush(this.olvItems.BackColor), r);
g.DrawString(item.Name, olvItems.Font, brush, r);
}
return true;
};
When I tried this I seemed to have the strange issue that the text was rendered twice, once originally by the control in black and then another time by me in red!
This might be a bug, or I might have missed something.
To get around this then I had to paint the background will fillrectangele, but then you have the issue if the row in selected so you would need to handle this separately.
I also found that OwnerDraw made no difference!
I know it's an old question, but I had the same problem and couldn't find any correct answers. I'll leave my answer, maybe it helps somebody.
nameColumn.RendererDelegate is not called no matter what I do or which OwnerRender mode I'm in.
nameColumn.Renderer = SomeCustomIRenderer is also not called completely no matter what I do.
I managed to fix the issue by adding:
treeListView.CellRendererGetter = (object rowObject, OLVColumn column) => new MyCustomIRenderer(); And only after that, my custom renderer was actually called.
Easiest way is to override your own BaseRenderer or just copy one from here http://objectlistview.sourceforge.net/cs/ownerDraw.html#owner-draw-label

C# - Add all Input Controls to List<Controls> in TabIndex order

I have a windows form that just exists to take input from user, for all intents and purposes it is just a label and a corresponding input box (textbox, checkbox, masket textbox etc).
I have programatically placed all the input fields in a TabIndex order that is optimal for cycling through them in a way that fits with their locations (tab down each column of inputs, then to the top of the next column).
The person that I am building this project for has stipulated that only like each textbox to come available one at a time, as the previous one has been filled out. This is a crude way of doing it with no validation, but essentially something like this...
if (String.IsNullOrEmpty(textbox1.Text))
{
textbox2.Enabled = true
}
So this is fine to do with two textboxes in this example, but the form has 28 different inputs on it, so an absurd series of if statements will only be a last resort.
My thoughts has been to put all the inputs in a List, ideally in the same order as is their TabIndexes. I tried to do this using a foreach loop...
List<Control> inputsList = new List<Control>();
public void initialiseControls()
{
//control position to insert control into list at specified index
int cntrlpos = 0;
//for every control in form
foreach (Control cntrl in this.Controls)
{
//not counting labels (not input fields)
if (!(cntrl is Label))
{
//set list position to equal control's TabIndex
cntrlpos = cntrl.TabIndex;
//insert the control into the list at the position reflecting TabIndex
inputsList.Insert(cntrlpos, cntrl); //<---- Error Occurs
//ASK TEXTBOX TO OUTPUT LIST POSITION AS TEST
//foreach (var txtbx in this.Controls.OfType<TextBox>())
//{
// txtbx.Text = Convert.ToString(cntrlpos);
//}
}
}
As soon as the function is called, an exception is thrown stating that "Index must be within the bounds of the list".
When I put a breakpoint into the code, it showed cntrlpos to equal 29, which is more than the 28 total input controls there are on the form.
I don't know where to go from here, if anyone can offer some advice on the code above to place the Controls into the list in the correct order (or point me in the direction of another method to do something like this), then I would really appreciate it.
Thanks,
Mark
To make your list, try this:
List<Control> inputList =
(from Control c in getAllControls(this)
where c.TabStop
orderby c.TabIndex
select c).ToList();
Define the method getAllControls elsewhere in your form class:
IEnumerable<Control> getAllControls(Control parent)
{
foreach (Control control in parent.Controls)
{
yield return control;
foreach (Control descendant in getAllControls(control))
yield return descendant;
}
}
(Taken and modified slightly from Recursive control search with Linq)
This will make it so that you get even nested controls (such as those in panels or groupboxes).
You can't just use the TabIndex as an index into your list, because even stuff like labels have tab indices, which will mess up your indices.
I think you've over complicated it...
Just use Control.GetNextControl:
Retrieves the next control forward or back in the tab order of child
controls.
For example, with just TextBoxes:
private void textBoxes_TextChanged(object sender, EventArgs e)
{
Control ctl = (Control)sender;
if (!String.IsNullOrEmpty(ctl.Text))
{
Control next = this.GetNextControl(ctl, true);
if (next != null)
{
next.Enabled = true;
}
}
}
Obviously you might need a slightly more complicated check for some other types of controls in a different handler, but you'd still just grab the next control to enable using GetNextControl().

C# ComboBox disable highlighting

I have custom ComboBox, where DropDownStyle = ComboBoxStyle.DropDown;.DropDown style is set because I want to set the Text property of the ComboBox to something outside the list of values. Everything works good, except that ComboBox is highlighting the text when it's left and when I click on the combobox editing is avaible. How can I cope with this?
To illustrate:
First Picture is where everything looks good, second is the highlight situation, third editing is on.
Try un-selecting the text after the DropDown closes:
void comboBox1_DropDownClosed(object sender, EventArgs e) {
this.BeginInvoke(new Action(() => { comboBox1.Select(0, 0); }));
}
If you are referring to disabling the highlighting and editing, then you might want to consider setting the DropdownStyle property to DropdownList.
yourComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
Tricky problem to solve. It seems to be from the Resize event. There are a lot of solutions that do something similar to this, but none that I've seen worked for me until I tried this. (This is a solution that does not require inheritance from ComboBox; inheriting is probably a much more straight forward solution, but requires you to always use your inherited class and never the actual ComboBox class.)
comboBox.Resize += (s, e) => {
if (!comboBox.IsHandleCreated)
return; // avoid possible exception
comboBox.BeginInvoke(new Action(() => comboBox.SelectionLength = 0));
};
Set the selection length to zero to get rid of the highlight, but when? Other examples do it in other places, but the problem seems to be specifically caused by Resize, so doing it after Resize fixes it consistently, at least for me. (Can still see it flicker when you resize the window though, but it always ends up ok.)
BeginInvoke ensures that it happens sufficiently after Resize to work, and the check for IsHandleCreated prevents it from being called before the handle is created, in which case BeginInvoke would throw an exception.
This slightly more complex version includes some checks to prevent a focused control from losing highlight, since it actually should have it. It also doesn't fire if the parent doesn't exist yet, or if the parent does not have an active control yet, both signs that things are too early.
comboBox.Resize += (s, e) => {
if (!comboBox.IsHandleCreated)
return;
comboBox.BeginInvoke(new Action(() => {
var parent = comboBox.FindForm();
if (parent == null)
return;
if (parent.ActiveControl == null)
return;
if (parent.ActiveControl == comboBox)
return;
comboBox.SelectionLength = 0;
}));
};
I tried to make a version that would 'preserve' the selection length rather than always set it to zero, but I couldn't get it to synchronize properly. Many Resize events can fire before the BeginInvoke delegates start to fire, so the preserved value will always be overwritten by the broken one. I tried saving them all in a Queue or Stack, but in both cases, I was unable to reverse the ordering (not really sure why, since that makes no sense).
To solve the same I have tried almost EVERYTHING:
setting the DropdownStyle property to DropdownList
this.BeginInvoke(new Action(() => { comboBox1.Select(0, 0); }));
combobox1.SelectionLength = 0;
changing comboBox.TabIndex
Not tried SendKeys.Send("{ESC}"); because it is not a reliable solution
Nothing helped.
The only stable and working solution was to move a focus on another Label control:
label.Focus();
You could also hide that label.
I know this post is old but recently I have the same problem with combobox.
Situation : I have an editable combobox which propose complete words when user write some letters.
But when I want to type a letter, combobox auto highlight the text and the next letter auto replace the previous.
Solution : I use a textbox to avoid any highlight like that:
<ComboBox IsTextSearchEnabled="False" IsEditable="True" x:Name="CMB_ClientName"/>
<TextBox Text="{Binding ElementName=CMB_ClientName, Path=Text}" TextChanged="ComboBoxChange" x:Name="TXT_ClientName"/>
And I generate the textbox TextChanged event :
private void ComboBoxChange(object sender, TextChangedEventArgs e)
{
//Clear ComboBox items
CMB_ClientName.Items.Clear();
//Auto Open DropDownList
CMB_ClientName.IsDropDownOpen = true;
//Get data from database (use entity framework 6.x)
dbEntity.Client.Load();
//Attribute Data to variable
var clients = dbEntity.Client.Local;
foreach (Client client in clients)
{
//If data begin with the texbox text, the data is add to the combobox items list.
if (client.Nom.ToLower().StartsWith(TXT_NomClient.Text.ToLower()))
{
CMB_ClientName.Items.Add(client.Nom);
}
}
}
I know this solution isn't realy beautifull, but it is for me the easiest solution to avoid highlight text and all the solutions in this post don't work for me.
I hope this solution will be helpfull, thanks for reading.
Math.
Ps: My apologies, my English is not very good. I hope you will understand me correctly.
Nothing worked for me ( I want the form to load with no highlighting in any combobox) until I set the combobox property TabStop to false. This meant that one of my buttons took the tab highlight which I didn't like so I set them all to false for start up and adjusted them programatically as needed.
I know this is an old thread, but my solution is similar to that of the others, but relies on the Form.ResizeEnd event. In its event handler, I iterate through the ComboBoxes and set ComboBox.SelectionLength to 0.
private void Form_ResizeEnd(object sender, EventArgs e)
{
foreach(ComboBox comboBox in parentControl.Controls.OfType<ComboBox>
{
comboBox.SelectionLength = 0;
}
}
This is what worked for me:
Set DrawMode to OwnerDrawFixed
Set cbxSubsystems.DrawItem event to the function below
private void cbxSubsystems_DrawItem(object sender, DrawItemEventArgs e)
{
Color BgClr;
Color TxClr;
if( (e.State & DrawItemState.ComboBoxEdit) == DrawItemState.ComboBoxEdit )
{
// Do not highlight main display
BgClr = cbxSubsystems.BackColor;
TxClr = cbxSubsystems.ForeColor;
}
else
{
BgClr = e.BackColor;
TxClr = e.ForeColor;
}
e.Graphics.FillRectangle(new SolidBrush(BgClr), e.Bounds);
TextRenderer.DrawText(e.Graphics, cbxSubsystems.Items[e.Index].ToString(), e.Font, e.Bounds,
TxClr, BgClr, TextFormatFlags.Left | TextFormatFlags.VerticalCenter );
}

Highlighting ListViewItem background color by code

If I click on an item of a list view by mouse, the color becomes that "highlight" color BUT if I do it by code like this:
( MultiSelect should be True and Also I set HideSelection to False)
myListView1.Items[2].Selected = true;
then it will be GRAY...bad! I want it to be the same highlight color when I manually click on them by mouse :(
I also tried adding this code but that didn't work either, Still gray
myListView1.Items[2].BackColor = System.Drawing.Color.Blue;
This is the behaviour of the ListView when it has selected items, but it is not focused.
So, to get the 'blue' colour you are after, just add this;
listView1.Focus();
can you try this please in your listview's SelectedIndexChanged event ?
ListViewItem lv = YourListview.GetItemAt(YourListView.PointToClient(Cursor.Position).X, YourListView.PointToClient(Cursor.Position).Y);
// this kind of Control.GetItemAt() works everywhere you need to find your mouse position ;)
// if you need to find position for screen (i.e. if you want to show a messagebox at the center of screen) you can use PointToScreen instead of PointToClient
if (lv == null)
{
return;
}
else if (yourfirstpossibility == true)
{
lv.Selected = true;
lv.BackColor = Color.FromKnownColor(KnownColor.ButtonHighLight);
// or which color you prefer. FromKnownColor retrieves system colors which you can see in backcolor / forecolor property => "system" named palette
}
a little bit different(more complicated) of this code i use in item_checked event for my listview.. hope it helps you..

Categories

Resources