WinForms有无线电和复选框的TreeView [英] WinForms TreeView with both radios and checkboxes
问题描述
我有一个案例,我希望TreeView能够显示多个根节点上的单选按钮和他们的孩子的复选框。在任何根节点下只有一级子级。
无线电也应该像一个组一样工作,即选择一个根,取消选择其他的无线电。 / p>
我一直试图用图像伪造它,但它看起来不现实。我原来有一个列表框和一个单独的检查列表框,但可用性上帝击中了它。
有没有人实现这个功能或有另一个建议?
这样想:
(o)McDonalds
[] Burger < br>
[] Fries
[]饮料
(o)汉堡王
[] Burger
[] Fries
[]饮料
)Wendy的
[x] Burger
[x] Fries
] Drink
您可以有一个大选项,但在大选项下选择1..n。
我想出了一个基于文章的解决方案http://www.codeproject.com/KB/combobox/RadioListBoxDotNetVersion.aspx 。我的实现继承自CheckedListBox并包括以下方法:
protected override void OnDrawItem(DrawItemEventArgs e)
{
//如果控件没有项目,则擦除所有背景
if(e.Index< 0 || e.Index> this.Items.Count - 1)
{
e。 Graphics.FillRectangle(new SolidBrush(this.BackColor),this.ClientRectangle);
return;
}
//如果最后一个项目绘制到控件的底部,则计算背景的边界
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);
//确定文本颜色/画笔
刷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))//绑定数据表?然后显示写在Displaymember中的列
strText =((System.Data.DataRowView)this.Items [e.Index])[this.DisplayMember] .ToString();
else
strText = this.Items [e.Index] .ToString();
大小sizeGlyph;
Point ptGlyph;
if(this.GetItemType(e.Index)== ItemType.Radio)
{
RadioButtonState stateRadio = bIsChecked? RadioButtonState.UncheckedNormal;
if((e.State& DrawItemState.Disabled)== DrawItemState.Disabled ||(e.State& DrawItemState.Grayed)== DrawItemState.Grayed)
stateRadio = bIsChecked? RadioButtonState.UncheckedDisabled;
//确定文本和单选按钮的边界
sizeGlyph = RadioButtonRenderer.GetGlyphSize(e.Graphics,stateRadio);
ptGlyph = e.Bounds.Location;
ptGlyph.X + = 4; //从边缘的舒适距离
ptGlyph.Y + =(e.Bounds.Height - sizeGlyph.Height)/ 2;
//绘制单选按钮
RadioButtonRenderer.DrawRadioButton(e.Graphics,ptGlyph,stateRadio);
}
else
{
CheckBoxState stateCheck = bIsChecked? CheckBoxState.UncheckedNormal;
if((e.State& DrawItemState.Disabled)== DrawItemState.Disabled ||(e.State& DrawItemState.Grayed)== DrawItemState.Grayed)
stateCheck = bIsChecked? CheckBoxState.UncheckedDisabled;
//确定文本和单选按钮的边界
sizeGlyph = CheckBoxRenderer.GetGlyphSize(e.Graphics,stateCheck);
ptGlyph = e.Bounds.Location;
ptGlyph.X + = 20; //从边缘的舒适距离
ptGlyph.Y + =(e.Bounds.Height - sizeGlyph.Height)/ 2;
//绘制单选按钮
CheckBoxRenderer.DrawCheckBox(e.Graphics,ptGlyph,stateCheck);
}
//绘制文本
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);
//如果ListBox有焦点,在所选项目周围绘制一个焦点矩形。
e.DrawFocusRectangle();
}
protected override void OnItemCheck(ItemCheckEventArgs ice)
{
base.OnItemCheck(ice);
if(ice.NewValue == CheckState.Unchecked)
return;
if(this.GetItemType(ice.Index)== ItemType.Radio)//如果他们选择了一个根,取消选择其他根和他们的孩子
{
for(Int32 i = 0; 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)//他们选择了一个孩子;选择根也取消选择其他根和他们的孩子
{
//查找父
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));
}
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"));
}
这篇关于WinForms有无线电和复选框的TreeView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!