我一直在设计一个网页上有一个网格。网格中有多个组合框。这些组合框相互作用。即,当用户更改了一个值时,另一个值会更改或禁用/启用等等。
我发现为了做到这一点,我必须经常使用FindControl。就像在一个组合框的selectedindexchanged事件中一样,我需要找到另一个组合框。
这似乎是一种混乱的做事方式。看起来它似乎让系统开放出错,如果组合框的ID在以后的行中被更改,编译器将无法找到它。
有人能告诉我有没有更好的方法来解决这个问题?
答案 0 :(得分:1)
我有一个Web应用程序,它也广泛使用各种FindControl排列,以实现您描述的内容。虽然它很脆弱(不经测试就不改变控制ID),但通过一些实用功能可以减少它的麻烦。以下是我使用的所有FindControl类型的函数 - 这至少可以帮助你。
/// <summary>
/// Recursively searches for a control within the heirarchy of a given control.
/// </summary>
/// <param name="root">The control to search within.</param>
/// <param name="id">The ID of the control to find.</param>
/// <returns>The Control object of the found control, or null if the control isn't found.</returns>
public static Control FindControlRecursive(Control root, string id)
{
if (root.ID == id) return root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null) return t;
}
return null;
}
/// <summary>
/// Recursively searches for a control within the heirarchy of a given control using the Client ID
/// of the control. Calling this too early in the lifecycle may not behave as expected.
/// </summary>
/// <param name="root">The control to search within.</param>
/// <param name="clientID">The Client ID of the control to find.</param>
/// <returns>The Control object of the found control, or null if the control isn't found.</returns>
public static Control FindControlRecursiveByClientID(Control root, string clientID)
{
if (0 == String.Compare(root.ClientID, clientID, true)) return root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursiveByClientID(c, clientID);
if (t != null) return t;
}
return null;
}
/// <summary>
/// Recursively searches for a group of controls within the heirarchy of a given control tree using the ID
/// of the control.
/// </summary>
/// <param name="root">The control tree to search within.</param>
/// <param name="id">The ID of the control to find.</param>
/// <returns>
/// A collection of the found controls. The collection will be empty if none are found.
/// </returns>
public static List<Control> FindControlsRecursive(Control root, string id)
{
List<Control> collection = new List<Control>();
FindControlRecursive(root, id, collection);
return collection;
}
private static void FindControlRecursive(Control root, string id, List<Control> collection)
{
foreach (Control c in root.Controls)
{
if (0 == String.Compare(c.ID, id, true)) collection.Add(c);
else FindControlRecursive(c, id, collection);
}
}
/// <summary>
/// Recursively searches for a control within the heirarchy of a given control using the type
/// of the control.
/// </summary>
/// <typeparam name="T">The type of the control to find.</typeparam>
/// <param name="root">The control to search within.</param>
/// <returns>
/// The Control object of the found control, or null if the control isn't found.
/// </returns>
public static T FindControlRecursiveByType<T>(Control root)
where T : Control
{
if (root is T) return (T)root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursiveByType<T>(c);
if (t != null) return (T)t;
}
return null;
}
/// <summary>
/// Recursively searches for a set of controls within the heirarchy of a given control using the type
/// of the control.
/// </summary>
/// <typeparam name="T">The type of the control to find.</typeparam>
/// <param name="root">The control to search within.</param>
/// <returns>
/// A generic List object containing the controls found, or an empty List of none were found.
/// </returns>
public static List<T> FindControlsRecursiveByType<T>(Control root)
where T : Control
{
List<T> collection = new List<T>();
FindControlRecursiveByType<T>(root, collection);
return collection;
}
private static void FindControlRecursiveByType<T>(Control root, List<T> collection)
where T : Control
{
foreach (Control c in root.Controls)
{
if (c is T) collection.Add((T)c);
else FindControlRecursiveByType<T>(c, collection);
}
}
答案 1 :(得分:0)
正是出于这个原因,我从ASP.NET切换到在Silverlight中开发并利用其MVVM模式。即使使用ASP.NET的GridView项目模板,每个项目也不能直接绑定,可以识别或引用同一模板中的其他项目。您的代码必须在某种程度上(通常是最充分的)知道视图控件的组合层次结构。
这是你可以做些什么来接近“更好的约束世界”。您仍然会将组合框绑定到相同的列表数据源,但是当创建每行项目/控件时,您将每个项目与一个对象(即标记项目)相关联。然后在控件的事件处理中,您将检索与引发事件的控件关联的标记项关联的其他控件,并按照它们执行的操作。
我知道,不是最好的主意,而是我的头脑。也许当我有时间考虑更多时,我可以更新这篇文章。
答案 2 :(得分:0)
如何使用事件进行通知?
答案 3 :(得分:0)
这些非常混乱。
这是一个非常简单而优雅的解决方案。假设您的网格需要在包含3列的表中显示数据。数据来自具有以下结构的对象:
[Serializable]
public class Foo
{
public string Bar1 { get; set; }
public string Bar2 { get; set; }
public string Bar3 { get; set; }
}
然后您的用户控件将具有以下标记:
标记(GridDisplayRow.ascx):
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="GridDisplayRow.ascx.cs" Inherits="GridDisplayRow" %>
<div>
<div class="cell">
<asp:TextBox id="TxtProperty1" runat="server"/>
</div>
<div class="cell">
<asp:DropDownList ID="DDLProperty2" runat="server" OnSelectedIndexChanged="DDLProperty2_OnSelectedIndexChanged" AutoPostBack="true">
</asp:DropDownList>
</div>
<div class="cell">
<input type="radio" id="RadProperty3" runat="server">
</div>
</div>
注意所有div标签。儿童div是浮动的......所以它们并排显示。
Codebehind(GridDisplayRow.ascx.cs):
class GridDisplayRow:UserControl
{
public event EventHandler<System.EventArgs> SomethingHappened;
protected void Page_Load(object sender, EventArgs e)
{
//Initialize the drop down with something;
}
//this is where we handle internal events generated by children controls.
//Eg: the drop down's index changed.
protected void DDLProperty2_OnSelectedIndexChanged(object sender, EventArgs e)
{
//we update some other child control in this...
this.TxtProperty1.Text = this.DDLProperty2.Value;
//and maybe we want to signal the page that some event happened
if(SomethingHappened != null)
{
//Notify the page that SomethingHappened event occured
SomethingHappened(this, EventArgs.Empty);
}
}
//This is where the binding happens
public object BindingObject
{
set
{
Foo temp = (Foo)value;
this.TxtProperty1.Text = temp.Bar1;
this.DDLProperty2.Value = temp.Bar2;
this.RadProperty3.Value = temp.Bar3;
this.ViewState["Foo"] = temp;
}
}
}
所以在上面的代码中我们处理绑定到Foo,换句话说,我们在控件的每个单元格(div)中显示Foo的属性。需要强制转换是因为上面的属性是object类型,并且在Grid / Repeater的ItemTemplate中/你有什么,你将GridDisplayRow的一个实例绑定到Container.DataItem对象,如下所示。请记住,如果您的数据源是DataSet,则必须转换为DataRow,或者需要任何适当的数据类型:
页面标记:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>
<%@ Register src="GridDisplayRow.ascx" tagname="GridRow" tagprefix="ctrl" %>
<asp:GridView ID="GridView1" runat="server">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
<div>
<div class="header cell">Header 1</div>
<div class="header cell">Header 2</div>
<div class="header cell">Header 3</div>
</div>
</HeaderTemplate>
<ItemTemplate>
<ctrl:GridRow ID="Row" runat="server" BindingObject='<%# Container.DataItem %>' OnSomethingHappened="Row_SomethingHappened" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
我不喜欢使用网格,因为它们很麻烦且很麻烦。在这个例子中,我们欺骗网格创建一个每行有一个单元格的表格,并通过样式表处理该单元格中的布局。
请注意,使用中继器可以更好地工作,因为它非常高效且开销更少!
现在绑定网格: 任何实现IEnumerable的源都可以用于绑定到网格。因此像List这样的集合就足够了。
当GridRow控件触发其事件时,通过对象发送方发送对控件本身的引用,因此如果您将发送方转换为正确的类型,则可以获取控件的内部属性....可能性是无穷无尽的。
这种方法的优点是抽象。每个控件都可以处理自己的事件,或者选择通知页面内部发生的事件.... bla bla bla。你明白了。
答案 4 :(得分:0)
尝试使用ListView
更容易管理这类要求。如果您将ListView
放在Update Panel
内,则可以获得更好的用户体验