Giving SelectedIndex -1 in Listbox - wpf

I'm having listbox and getting selected item. The code is
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int index = listBox1.SelectedIndex;
string str ="";
if (index == 0)
{
str = (string)Text1.Text;
}
else if (index == 1)
{
str = (string)Text2.Text;
}
else if (index == 2)
{
str = (string)Text3.Text;
}
MessageBox.Show(str);
// listBox1.SelectedIndex = -1;
}
By this above code, after I selected one item,blue color bar is displaying in selected item, to avoid that I gave listBox1.SelectedIndex = -1;.
If I gave this this function is get executed twice.and the message box shows empty string.
Why is it happening? How can I avoid that?

try construction like this:
listBox1.SelectionChanged -= listBox1_SelectionChanged;
listBox1.SelectedIndex = -1;
listBox1.SelectionChanged += listBox1_SelectionChanged;
listBox1.SelectedIndex is calling SelectionChanged event so this function is called twice.

Related

WPF list modification during iteration?

I'm trying to make a very simple game where the yellow ball bouncing back and fourth. If it collides with one of the moving blue squares, the square is supposed to disappear and a new one should appear (always 3 in the window) elsewhere. When my code reaches this part, all 3 squares disappear (then reappears as intended it is not a problem) and I just cant figure out why. It would be a huge help if somebody could run over my methods responsible for the problem. Thank you in advance.
So my timer_Tick method, responsible for every frame:
void timer_Tick(object sender, EventArgs e)
{
logic.MoveBall();
if (model.Enemy.Count<3)
{
logic.AddEnemy();
}
int iii = 0;
foreach (MyShape enemy in model.Enemy) //the whole thing from here is me trying to solve list modification during iteration
{
if (logic.MoveEnemy(enemy) == -1)
{
logic.MoveEnemy(enemy);
}
else iii = logic.MoveEnemy(enemy);
}
if (iii > -1)
{
for (int j = model.Enemy.Count - 1; j >= 0; j--)
{
if (j == model.Enemy.Count - iii)
{
model.Enemy.RemoveAt(j);
}
}
}
}
MoveEnemy: I try to decide whether there is collusion and if yes, then try to remove the given shape object (blue square). Because This whole method is in a foreach, I just save the removable element and forward it to timer_Tick
public int MoveEnemy(MyShape shape)
{
int i = 0;
int ii = -1;
if ((shape.Area.IntersectsWith(model.Ball.Area)))
{
i = 0;
foreach (var e in model.Enemy)
{
i++;
if (shape == e)
{
ii = i;
}
}
}
shape.ChangeX(shape.Dx);
shape.ChangeY(shape.Dy);
bool coll = false;
foreach (var e in model.Enemy)
{
if ((e.Area.IntersectsWith(shape.Area)) && (shape != e))
{
coll = true;
}
}
if (shape.Area.Left < 0 || shape.Area.Right > Config.Width-40 || coll)
{
shape.Dx = -shape.Dx;
}
if (shape.Area.Top < 0)
{
shape.Dy = -shape.Dy;
}
if (shape.Area.Bottom > Config.Height/2)
{
shape.Dy = -shape.Dy;
}
RefreshScreen?.Invoke(this, EventArgs.Empty);
return ii;
}
And finally AddEnemy:
public void AddEnemy()
{
rnd = new Random();
int r = rnd.Next(-300, 300);
model.Enemy.Add(new MyShape(Config.Width / 2+r, 0, 40, 40));
RefreshScreen?.Invoke(this, EventArgs.Empty);
}
List<T> (or IList and Enumerable) exposes some useful methods to compact code:
int itemIndex = list.IndexOf(item); // Gets the index of the item if found, otherwise returns -1
list.Remove(item); // Remove item if contained in collection
list.RemoveAll(item => item > 5); // Removes all items that satisfy a condition (replaces explicit iteration)
bool hasAnyMatch = list.Any(item => item > 5); // Returns true as soon as the first item satisfies the condition (replaces explicit iteration)
A simplified version, which should eliminate the flaw:
void timer_Tick(object sender, EventArgs e)
{
if (model.Enemy.Count < 3)
{
logic.AddEnemy();
}
logic.MoveBall();
model.Enemy.ForeEach(logic.MoveEnemy);
model.Enemy.RemoveAll(logic.IsCollidingWithBall);
}
public void AddEnemy()
{
rnd = new Random();
int r = rnd.Next(-300, 300);
model.Enemy.Add(new MyShape(Config.Width / 2 + r, 0, 40, 40));
RefreshScreen?.Invoke(this, EventArgs.Empty);
}
public bool IsCollidingWithBall(MyShape shape)
{
return shape.Area.IntersectsWith(model.Ball.Area);
}
public int MoveEnemy(MyShape shape)
{
shape.ChangeX(shape.Dx);
shape.ChangeY(shape.Dy);
bool hasCollision = model.Enemy.Any(enemy => enemy.Area.IntersectsWith(shape.Area)
&& enemy != shape);
if (hasCollision || shape.Area.Left < 0 || shape.Area.Right > Config.Width - 40)
{
shape.Dx = -shape.Dx;
}
if (shape.Area.Top < 0)
{
shape.Dy = -shape.Dy;
}
if (shape.Area.Bottom > Config.Height / 2)
{
shape.Dy = -shape.Dy;
}
RefreshScreen?.Invoke(this, EventArgs.Empty);
}

textrange losing textpointer reference?

I've been using some code I found here, to try and change the case of text in a flow document. It changes the text properly however all the formatting is lost (bold, italics, etc) and when I save the document to a XML file all the text ends up in the first Run of the document and all the other Run's are empty.
private void ChangeCase()
{
TextPointer start = mergedDocument.ContentStart;
TextPointer end = mergedDocument.ContentEnd;
List<TextRange> textToChange = SplitToTextRanges(start, end);
ChangeCaseToAllRanges(textToChange);
}
private List<TextRange> SplitToTextRanges(TextPointer start, TextPointer end)
{
List<TextRange> textToChange = new List<TextRange>();
var previousPointer = start;
for (var pointer = start; (pointer != null && pointer.CompareTo(end) <= 0); pointer = pointer.GetPositionAtOffset(1, LogicalDirection.Forward))
{
var contextAfter = pointer.GetPointerContext(LogicalDirection.Forward);
var contextBefore = pointer.GetPointerContext(LogicalDirection.Backward);
if (contextBefore != TextPointerContext.Text && contextAfter == TextPointerContext.Text)
{
previousPointer = pointer;
}
if (contextBefore == TextPointerContext.Text && contextAfter != TextPointerContext.Text && previousPointer != pointer)
{
textToChange.Add(new TextRange(previousPointer, pointer));
previousPointer = null;
}
}
textToChange.Add(new TextRange(previousPointer ?? end, end));
return textToChange;
}
private void ChangeCaseToAllRanges(List<TextRange> textToChange)
{
Func<string, string> caseChanger;
ComboBoxItem cbi = cb_Case.SelectedItem as ComboBoxItem;
var textInfo = CultureInfo.CurrentUICulture.TextInfo;
if (cbi == null || (string)cbi.Tag == "none")
{
return;
}
else if((string)cbi.Tag == "title")
{
caseChanger = (text) => textInfo.ToTitleCase(text);
}
else if ((string)cbi.Tag == "upper")
{
caseChanger = (text) => textInfo.ToUpper(text);
}
else if ((string)cbi.Tag == "lower")
{
caseChanger = (text) => textInfo.ToLower(text);
}
else
return;
foreach (var range in textToChange)
{
if (!range.IsEmpty && !string.IsNullOrWhiteSpace(range.Text))
{
System.Diagnostics.Debug.WriteLine("Casing: " + range.Text);
System.Diagnostics.Debug.WriteLine("\tat: " +
range.Start.GetOffsetToPosition(mergedDocument.ContentStart) +
" ," +
range.End.GetOffsetToPosition(mergedDocument.ContentStart));
range.Text = caseChanger(range.Text);
}
}
}
I can't see any reason why this code isn't working properly. It looks like the textpointers in the textrange object are being redirected to the beginning of the document.
When setting TextRange.Text, it first deletes the selection by telling the TextContainer (the FlowDocument) to delete that content. If that content happened to be an entire Inline with your styling dependency properties then goodbye! So, not only does it get unstyled text, but it sets it
Since you want to keep your existing inline objects you can iterate through the entire FlowDocument to find them and set their text property.
Here is a helper method that only supports Paragraphs and finds all inlines in the entire selection (this logic is a lot simpler if you are always doing Document.ContentStart and Document.ContentEnd). You can expand this to include the inlines inside of Lists, ListItems, and Hyperlinks if you need to (by following a similar pattern).
You should then be able to set the Text property on each of these inlines.
List<Inline> GetInlines(TextRange selection)
{
var inlines = new List<Inline>();
foreach (var block in Document.Blocks.Where(x => selection.Start.CompareTo(x.ContentEnd) < 0 && selection.End.CompareTo(x.ContentStart) > 0))
{
var paragraph = block as Paragraph;
if (paragraph != null)
{
inlines.AddRange(paragraph.Inlines.Where(x => selection.Start.CompareTo(x.ContentEnd) < 0 && selection.End.CompareTo(x.ContentStart) > 0));
}
}
return inlines;
Edit: You will want to cast these to Run or Span to access the text property. You can even just remove Inline and get these types (probably just Run).

Error in convert inventory from JS to C# - Unity

Hello :) I'm converting now Inventory system but i'm stuck on List / Arrays (JS).
Here's my script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[AddComponentMenu ("Inventory/Character Sheet")]
[RequireComponent(typeof (Inventory))]
public class Character : MonoBehaviour {
//The Character window (CSheet).
public Transform WeaponSlot; //This is where the Weapons are going to go (be parented too). In my case it's the "Melee" gameobject.
//private Item[] ArmorSlot;
private List<Item> ArmorSlot; //This is the built in Array that stores the Items equipped. You can change this to static if you want to access it from another script.
//public string[] ArmorSlotName;
public List<string> ArmorSlotName; //This determines how many slots the character has (Head, Legs, Weapon and so on) and the text on each slot.
//public Rect[] buttonPositions;
public List<Rect> buttonPositions; //This list will contain where all buttons, equipped or not will be and SHOULD HAVE THE SAME NUMBER OF cells as the ArmorSlot array.
Vector2 windowSize = new Vector2(375,300); //The size of the character window.
public bool useCustomPosition = false; //Do we want to use the customPosition variable to define where on the screen the Character window will appear.
Vector2 customPosition = new Vector2 (70, 70); //The custom position of the Character window.
public GUISkin cSheetSkin; //This is where you can add a custom GUI skin or use the one included (CSheetSkin) under the Resources folder.
public bool canBeDragged = true; //Can the Character window be dragged?
public KeyCode onOffButton = KeyCode.I; //The key to toggle the Character window on and of.
bool DebugMode = false; //If this is enabled, debug.logs will print out information when something happens (equipping items etc.).
static bool csheet = false; //Helps with turning the CharacterSheet on and off.
private Rect windowRect = new Rect(100,100,200,300); //Keeping track of our character window.
//These are keeping track of components such as equipmentEffects and Audio.
private Inventory playersinv; //Refers to the Inventory script.
private bool equipmentEffectIs = false;
private InvAudio invAudio;
private bool invDispKeyIsSame = false;
//Assign the differnet components to variables and other "behind the scenes" stuff.
void Awake ()
{
playersinv = GetComponent<Inventory>();
if (useCustomPosition == false)
{
windowRect = new Rect(Screen.width-windowSize.x-70,Screen.height-windowSize.y-(162.5f+70*2),windowSize.x,windowSize.y);
}
else
{
windowRect = new Rect(customPosition.x,customPosition.y,windowSize.x,windowSize.y);
}
invAudio = GetComponent<InvAudio>();
if (GetComponent<InventoryDisplay>().onOffButton == onOffButton)
{
invDispKeyIsSame = true;
}
}
//Take care of the array lengths.
void Start ()
{
ArmorSlot = new Item[ArmorSlotName.Count];
if (buttonPositions.Count != ArmorSlotName.Count)
{
Debug.LogError("The variables on the Character script attached to " + transform.name + " are not set up correctly. There needs to be an equal amount of slots on 'ArmorSlotName' and 'buttonPositions'.");
}
}
//Checking if we already have somthing equipped
bool CheckSlot(int tocheck)
{
bool toreturn = false;
if(ArmorSlot[tocheck]!=null){
toreturn=true;
}
return toreturn;
}
//Using the item. If we assign a slot, we already know where to equip it.
public void UseItem(Item i, int slot, bool autoequip)
{
if(i.isEquipment){
//This is in case we dbl click the item, it will auto equip it. REMEMBER TO MAKE THE ITEM TYPE AND THE SLOT YOU WANT IT TO BE EQUIPPED TO HAVE THE SAME NAME.
if(autoequip)
{
int index = 0; //Keeping track of where we are in the list.
int equipto = 0; //Keeping track of where we want to be.
foreach(string a in ArmorSlotName) //Loop through all the named slots on the armorslots list
{
if(a == i.itemType) //if the name is the same as the armor type.
{
equipto=index; //We aim for that slot.
}
index++; //We move on to the next slot.
}
EquipItem(i,equipto);
}
else //If we dont auto equip it then it means we must of tried to equip it to a slot so we make sure the item can be equipped to that slot.
{
if(i.itemType==ArmorSlotName[slot]) //If types match.
{
EquipItem(i,slot); //Equip the item to the slot.
}
}
}
if (DebugMode)
{
Debug.Log(i.name + " has been used");
}
}
//Equip an item to a slot.
void EquipItem(Item i, int slot)
{
if(i.itemType == ArmorSlotName[slot]) //If the item can be equipped there:
{
if(CheckSlot(slot)) //If theres an item equipped to that slot we unequip it first:
{
UnequipItem(ArmorSlot[slot]);
ArmorSlot[slot]=null;
}
ArmorSlot[slot]=i; //When we find the slot we set it to the item.
gameObject.SendMessage ("PlayEquipSound", SendMessageOptions.DontRequireReceiver); //Play sound
//We tell the Item to handle EquipmentEffects (if any).
if (i.GetComponent<EquipmentEffect>() != null)
{
equipmentEffectIs = true;
i.GetComponent<EquipmentEffect>().EquipmentEffectToggle(equipmentEffectIs);
}
//If the item is also a weapon we call the PlaceWeapon function.
if (i.isAlsoWeapon == true)
{
if (i.equippedWeaponVersion != null)
{
PlaceWeapon(i);
}
else
{
Debug.LogError("Remember to assign the equip weapon variable!");
}
}
if (DebugMode)
{
Debug.Log(i.name + " has been equipped");
}
playersinv.RemoveItem(i.transform); //We remove the item from the inventory
}
}
//Unequip an item.
void UnequipItem(Item i)
{
gameObject.SendMessage ("PlayPickUpSound", SendMessageOptions.DontRequireReceiver); //Play sound
//We tell the Item to disable EquipmentEffects (if any).
if (i.equipmentEffect != null)
{
equipmentEffectIs = false;
i.GetComponent<EquipmentEffect>().EquipmentEffectToggle(equipmentEffectIs);
}
//If it's a weapon we call the RemoveWeapon function.
if (i.itemType == "Weapon")
{
RemoveWeapon(i);
}
if (DebugMode)
{
Debug.Log(i.name + " has been unequipped");
}
playersinv.AddItem(i.transform);
}
//Places the weapon in the hand of the Player.
void PlaceWeapon (Item item)
{
GameObject Clone = GameObject.Instantiate(item.equippedWeaponVersion, WeaponSlot.position, WeaponSlot.rotation) as GameObject;
Clone.name = item.equippedWeaponVersion.name;
Clone.transform.parent = WeaponSlot;
if (DebugMode)
{
Debug.Log(item.name + " has been placed as weapon");
}
}
//Removes the weapon from the hand of the Player.
void RemoveWeapon (Item item)
{ if (item.equippedWeaponVersion != null)
{
Destroy(WeaponSlot.FindChild("" + item.equippedWeaponVersion.name).gameObject);
if (DebugMode)
{
Debug.Log(item.name + " has been removed as weapon");
}
}
}
void Update ()
{
//This will turn the character sheet on and off.
if (Input.GetKeyDown(onOffButton))
{
if (csheet)
{
csheet = false;
if (invDispKeyIsSame != true)
{
gameObject.SendMessage ("ChangedState", false, SendMessageOptions.DontRequireReceiver); //Play sound
gameObject.SendMessage("PauseGame", false, SendMessageOptions.DontRequireReceiver); //StopPauseGame/EnableMouse/ShowMouse
}
}
else
{
csheet = true;
if (invDispKeyIsSame != true)
{
gameObject.SendMessage ("ChangedState", true, SendMessageOptions.DontRequireReceiver); //Play sound
gameObject.SendMessage("PauseGame", true, SendMessageOptions.DontRequireReceiver); //PauseGame/DisableMouse/HideMouse
}
}
}
}
//Draw the Character Window
void OnGUI()
{
GUI.skin = cSheetSkin; //Use the cSheetSkin variable.
if(csheet) //If the csheet is opened up.
{
//Make a window that shows what's in the csheet called "Character" and update the position and size variables from the window variables.
windowRect = GUI.Window (1, windowRect, DisplayCSheetWindow, "Character");
}
}
//This will display the character sheet and handle the buttons.
void DisplayCSheetWindow(int windowID)
{
if (canBeDragged == true)
{
GUI.DragWindow (new Rect (0,0, 10000, 30)); //The window is dragable.
}
int index = 0;
foreach(Item a in ArmorSlot) //Loop through the ArmorSlot array.
{
if(a == null)
{
if(GUI.Button(buttonPositions[index], ArmorSlotName[index])) //If we click this button (that has no item equipped):
{
InventoryDisplay id = GetComponent<InventoryDisplay>();
if(id.itemBeingDragged != null) //If we are dragging an item:
{
EquipItem(id.itemBeingDragged,index); //Equip the Item.
id.ClearDraggedItem();//Stop dragging the item.
}
}
}
else
{
if(GUI.Button(buttonPositions[index],ArmorSlot[index].itemIcon)) //If we click this button (that has an item equipped):
{
InventoryDisplay id2 = GetComponent<InventoryDisplay>();
if(id2.itemBeingDragged != null) //If we are dragging an item:
{
EquipItem(id2.itemBeingDragged,index); //Equip the Item.
id2.ClearDraggedItem(); //Stop dragging the item.
}
else if (playersinv.Contents.Count < playersinv.MaxContent) //If there is room in the inventory:
{
UnequipItem(ArmorSlot[index]); //Unequip the Item.
ArmorSlot[index] = null; //Clear the slot.
id2.ClearDraggedItem(); //Stop dragging the Item.
}
else if (DebugMode)
{
Debug.Log("Could not unequip " + ArmorSlot[index].name + " since the inventory is full");
}
}
}
index++;
}
}
}
And I have this error:
Assets/Inventory/Scripts/Character.cs(62,17): error CS0029: Cannot implicitly convert type Item[]' toSystem.Collections.Generic.List'
this lane:
ArmorSlot = new Item[ArmorSlotName.Count];
in JS version there were arrays but in c# i convert it to Lists and i have couple of errors ;D Can someone help me with this ? ;/
Error happens over here.
ArmorSlot = new Item[ArmorSlotName.Count];
What is ArmorSlot?
Private List<Item> ArmorSlot; //This is the built in Array that stores the Items equipped. You can change this to static if you want to access it from another script.
you don't make List like normal array.
You don't want List you want Item array.
Fix is like this
private Item[] ArmorSlot;
But maybe you want List because list is easier to control then a native C# array if you wan't to use List you must do something like this
ArmorSlot = new ArrayList<Item>(ArmorSlotName.Count);
But maybe you don't need to put in ArmorSlotName.Count because List is the size of how much items you put inside it.. it gives default size and keeps increasing.
So it's better to just do this
ArmorSlot = new ArrayList<Item>();

wpf richtextbox check if caret is in the last line or count how many lines it has

I am trying to find out in a richtext box whether the caret is position in the last line. Is this possible?
NOTE: At the end I also added: or count how many lines it has, this is because in the miscrosoft forum there is an example for detecting in which line the caret is.
thanks
Please verify the msdn link
http://social.msdn.microsoft.com/Forums/en/wpf/thread/667b5d2a-84c3-4bc0-a6c0-33f9933db07f
If you really wanted to know the line number of the caret, you could do something like the following (probably needs some tweaking):
TextPointer caretLineStart = rtb.CaretPosition.GetLineStartPosition(0);
TextPointer p = rtb.Document.ContentStart.GetLineStartPosition(0);
int caretLineNumber = 1;
while (true)
{
if (caretLineStart.CompareTo(p) < 0)
{
break;
}
int result;
p = p.GetLineStartPosition(1, out result);
if (result == 0)
{
break;
}
caretLineNumber++;
}
The code to get the number of lines:
Int32 CountDisplayedLines(RichTextBox rtb)
{
Int32 result = -1;
rtb.CaretPosition = rtb.Document.ContentStart;
while (rtb.CaretPosition.GetLineStartPosition(++result) != null)
{
}
return result;
}
I have found a solution. Maybe there is a simpler way, if so please let me know
private void OnHasRtbReachedEnd(System.Windows.Controls.RichTextBox rtb)
{
TextPointer pointer1 = rtb.CaretPosition;
int iCurrentLine = GetLineNumber(rtb);
rtb.CaretPosition = rtb.Document.ContentEnd;
int iLastLine = GetLineNumber(rtb);
if (iCurrentLine == iLastLine)
{
if (!_bIsRtbMovingUpDown)
MoveToNextDataGridRow();
_bIsRtbMovingUpDown= false;
}
else
{
_bIsRtbMovingUpDown= true;
}
rtb.CaretPosition = pointer1;
}
//This code comes from
//http://social.msdn.microsoft.com/Forums/en/wpf/thread/667b5d2a-84c3-4bc0-a6c0-33f9933db07f
private int GetLineNumber(System.Windows.Controls.RichTextBox rtb)
{
TextPointer caretLineStart = rtb.CaretPosition.GetLineStartPosition(0);
TextPointer p = rtb.Document.ContentStart.GetLineStartPosition(0);
int caretLineNumber = 1;
while (true)
{
if (caretLineStart.CompareTo(p) < 0)
{
break;
}
int result;
p = p.GetLineStartPosition(1, out result);
if (result == 0)
{
break;
}
caretLineNumber++;
}
return caretLineNumber;
}
I tried the code and is not giving correct results always.
One smart way to do it is this
int previousCursorPosition;
private void RichTarget_KeyDown_1(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up || e.Key == Key.Down)
{
Xceed.Wpf.Toolkit.RichTextBox rich = (Xceed.Wpf.Toolkit.RichTextBox)sender;
previousCursorPosition = rich.CaretPosition.GetOffsetToPosition(rich.CaretPosition.DocumentStart);
}
}
private void RichTextBox_KeyUp_1(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up)
{
Xceed.Wpf.Toolkit.RichTextBox rich = (Xceed.Wpf.Toolkit.RichTextBox)sender;
if (previousCursorPosition == rich.CaretPosition.GetOffsetToPosition(rich.CaretPosition.DocumentStart))
{
//do your staff
}
}
else if (e.Key == Key.Down)
{
Xceed.Wpf.Toolkit.RichTextBox rich = (Xceed.Wpf.Toolkit.RichTextBox)sender;
if (previousCursorPosition == rich.CaretPosition.GetOffsetToPosition(rich.CaretPosition.DocumentStart))
{
//do your staff
}
}
}

WinForms TreeView with both radios and checkboxes

I have a case where I would like TreeView to be able to show radio buttons on multiple root nodes, and checkboxes on their children. There would only be one level of children beneath any root node.
The radios should also behave like a group, ie one root is selected and the others' radios deselect.
I've been trying to fake it with images, but it doesn't look realistic. I originally had a listbox and a separate checkedlistbox, but the usability gods struck it down.
Has anyone implemented this functionality or have another suggestion?
Think of it this way:
(o) McDonalds
[ ] Burger
[ ] Fries
[ ] Drink
(o) Burger King
[ ] Burger
[ ] Fries
[ ] Drink
(*) Wendy's
[x] Burger
[x] Fries
[ ] Drink
You can have one big option, but select 1..n underneath the big option.
I came up with a solution based on the article http://www.codeproject.com/KB/combobox/RadioListBoxDotNetVersion.aspx. My implementation inherits from CheckedListBox and includes the following methods:
protected override void OnDrawItem (DrawItemEventArgs e)
{
// Erase all background if control has no items
if (e.Index < 0 || e.Index > this.Items.Count - 1)
{
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
return;
}
// Calculate bounds for background, if last item paint up to bottom of control
Rectangle rectBack = e.Bounds;
if (e.Index == this.Items.Count - 1)
rectBack.Height = this.ClientRectangle.Top + this.ClientRectangle.Height - e.Bounds.Top;
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), rectBack);
// Determines text color/brush
Brush brushText = SystemBrushes.FromSystemColor(this.ForeColor);
if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled || (e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
brushText = SystemBrushes.GrayText;
Boolean bIsChecked = this.GetItemChecked(e.Index);
String strText;
if (!string.IsNullOrEmpty(DisplayMember)) // Bound Datatable? Then show the column written in Displaymember
strText = ((System.Data.DataRowView) this.Items[e.Index])[this.DisplayMember].ToString();
else
strText = this.Items[e.Index].ToString();
Size sizeGlyph;
Point ptGlyph;
if (this.GetItemType(e.Index) == ItemType.Radio)
{
RadioButtonState stateRadio = bIsChecked ? RadioButtonState.CheckedNormal : RadioButtonState.UncheckedNormal;
if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled || (e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
stateRadio = bIsChecked ? RadioButtonState.CheckedDisabled : RadioButtonState.UncheckedDisabled;
// Determines bounds for text and radio button
sizeGlyph = RadioButtonRenderer.GetGlyphSize(e.Graphics, stateRadio);
ptGlyph = e.Bounds.Location;
ptGlyph.X += 4; // a comfortable distance from the edge
ptGlyph.Y += (e.Bounds.Height - sizeGlyph.Height) / 2;
// Draws the radio button
RadioButtonRenderer.DrawRadioButton(e.Graphics, ptGlyph, stateRadio);
}
else
{
CheckBoxState stateCheck = bIsChecked ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal;
if ((e.State & DrawItemState.Disabled) == DrawItemState.Disabled || (e.State & DrawItemState.Grayed) == DrawItemState.Grayed)
stateCheck = bIsChecked ? CheckBoxState.CheckedDisabled : CheckBoxState.UncheckedDisabled;
// Determines bounds for text and radio button
sizeGlyph = CheckBoxRenderer.GetGlyphSize(e.Graphics, stateCheck);
ptGlyph = e.Bounds.Location;
ptGlyph.X += 20; // a comfortable distance from the edge
ptGlyph.Y += (e.Bounds.Height - sizeGlyph.Height) / 2;
// Draws the radio button
CheckBoxRenderer.DrawCheckBox(e.Graphics, ptGlyph, stateCheck);
}
// Draws the text
Rectangle rectText = new Rectangle(ptGlyph.X + sizeGlyph.Width + 3, e.Bounds.Y, e.Bounds.Width - sizeGlyph.Width, e.Bounds.Height);
e.Graphics.DrawString(strText.Substring(4), e.Font, brushText, rectText, this.oAlign);
// If the ListBox has focus, draw a focus rectangle around the selected item.
e.DrawFocusRectangle();
}
protected override void OnItemCheck (ItemCheckEventArgs ice)
{
base.OnItemCheck(ice);
if (ice.NewValue == CheckState.Unchecked)
return;
if (this.GetItemType(ice.Index) == ItemType.Radio) // if they selected a root, deselect other roots and their children
{
for (Int32 i = 0; i < this.Items.Count; ++i)
{
if (i == ice.Index)
continue;
if (this.GetItemType(i) == ItemType.Radio)
{
this.SetItemChecked(i, false);
Int32 j = i + 1;
while (j < this.Items.Count && this.GetItemType(j) == ItemType.Checkbox)
{
this.SetItemChecked(j, false);
j++;
}
}
}
}
else if (this.GetItemType(ice.Index) == ItemType.Checkbox) // they selected a child; select the root too and deselect other roots and their children
{
// Find parent
Int32 iParentIdx = ice.Index - 1;
while (iParentIdx >= 0 && this.GetItemType(iParentIdx) == ItemType.Checkbox)
iParentIdx--;
this.SetItemChecked(iParentIdx, true);
}
}
protected ItemType GetItemType (Int32 iIdx)
{
String strText = this.Items[iIdx].ToString();
if (strText.StartsWith("(o)"))
return (ItemType.Radio);
else if (strText.StartsWith("[x]"))
return (ItemType.Checkbox);
throw (new Exception("Invalid item type"));
}

Resources