C#用户控件 - 如何告诉包含该控件的对象需要数据

4

我正在创建一个C# WinForms用户控件。有时用户控件需要从包含它的表单中获取数据。我该如何让用户控件告诉包含它的表单它需要一些数据呢?

谢谢!


1
好问题;然而,如果需要从其包含控件获取数据,则我不认为需要使用用户控件。在这种情况下,不应该使用用户控件。您不应该在用户控件内编写此类逻辑。 - Pabuc
2
@Pabuc - 由于您不知道我的用户控件在做什么,所以您的陈述毫无意义。 - Hosea146
我和Pabuc在一起。我看不到你的用户控件,但听起来你可能正在违反关注点分离原则。我想知道是否有一个小的重新设计可以解决这个问题。 - Waylon Flinn
应该使用什么替代品?例如,我有一个分页器,它呈现链接。我有一个对象生成需要由ascx模板渲染的超链接对象。用户控件似乎是一种DRY的方式,在页面上放置分页而不需要进行大量绑定。在MVC框架中,我只需使用某种模板继承,但显然我不能在asp.net webforms中这样做? - Benbob
5个回答

4

您可以订阅UserControl引发的事件。

您的架构决定了何时何地需要订阅数据事件。在不知道您是在运行时还是设计时添加控件的情况下,我们无法回答这个问题。每种情况都需要一点推导。从在运行时添加控件的角度来看,您可以执行类似以下操作的内容:

// Your sample control
public class MyUserControl : Control
{
    public event EventHandler<EventArgs> INeedData;
    public Data Data {get; set;}

    private class DoSomething()
    {
        if(INeedData!=null) INeedData(this,null);
    }
}

...

// Your Form, in the case that the control isn't already added.
var _myUserControl = new MyUserControl();
private void Form1_Load(object sender, EventArgs e)
{
    _myUserControl.INeedData += new EventHandler<EventArgs>(MyUserControl_INeedData);
    this.Controls.Add(myUserControl);
}

void MyUserControl_INeedData(object sender, EventArgs e)
{
    _myUserControl.Data = SomeData;
}

1
在用户控件上创建一个事件,其中事件参数是可编辑的。让表单附加一个处理程序到该事件,以更新这些字段。在OnEvent方法中使用这些字段。
[未经测试] 例如:
public delegate void NeedsUserDataEventHandler(object sender, NeedsUserDataEventArgs args);

public class NeedsUserDataEventArgs : EventArgs
{
    public UserData UserData { get; set; }
}

// In Control
public event NeedsUserDataEventHandler NeedsUserData;

public void OnNeedsUserData(NeedsUserDataEventArgs args)
{
    NeedsUserDataEventHandler handler = NeedsUserData;
    if (handler != null) handler(this, args);
    // store your data somewhere here
}

// In Form
public override void OnLoad(object sender, EventArgs args)
{
    control.NeedsUserData += ControlNeedsUserData;
}

public override void OnClosed(object sender, EventArgs args)
{
    control.NeedsUserData -= ControlNeedsUserData;
}

public void ControlNeedsUserData (object sender, NeedsUserDataEventArgs args)
{
    args.UserData = // set whatever here
}

然后,当另一个控件需要数据时,您需要返回到对象并注册另一个处理程序...哎呀...让关心数据的人来注册它。 - Aaron McIver

1
在用户控件中创建自定义事件,并让表单连接到它。如果需要自定义事件参数,也可以创建它们。

在用户控件中:

//Define your Custom Event argument
public class MyEventArgs : EventArgs
{
    //Define some fields of your custom event argument
    private int m_SomeValue = 0;

    //Define some properties of your custom event argument
    public int SomeValue
    {
        get { return m_SomeValue; }
        set { m_SomeValue = value; }
    }
}

//Define the event handler data type
public delegate void MyEventHandler(object sender, MyEventArgs e);

//Define the object which holds the outside event handlers
public event MyEventHandler SomeEvent;

//Define the function which will generate the event
public virtual void OnSomeEvent(MyEventArgs e)
{
    if (SomeEvent != null)
        SomeEvent(this, e);
}

.
. //Then later in the control
.
{
    //We need new data

    //Create the event arguments
    MyEventArgs newEvent = new MyEventArgs();

    //Set the values
    newEvent.SomeValue = 17;

    //Call the event generating function
    OnSomeEvent(newEvent);
}

在你的表单中,只需使用类似以下的代码:
myControl.SomeEvent += new MyEventHandler(handlerName);

由于您的事件是公共的,因此您应该在控件的属性窗口中看到它。

您可以使用元数据属性来美化事件,但我将让您自己去发现这些属性。


0

这取决于何时需要将数据推送到UserControl。表单上是否有事件会驱动在UserControl内部移动数据的需求?如果是这样...只需在该点获取您的实例,并通过公共属性将数据向下推送到UserControl。

如果这种情况不使用事件或表单以某种方式“接收数据”,则可以在表单上公开event,例如...

public event DataHandler ReceivedData;

...并允许UserControl或任何其他容器注册事件并通过自定义事件参数接收数据。将事件推入UserControl并强制Form依附于UserControl似乎是反向的,因为Form是数据的发起者。


不同意。将事件放在窗体上,并让用户控件附加到它是危险的,因为清理的顺序会令人困惑。你很容易就会遇到循环引用的情况,导致两者都无法被处理。这种内存泄漏非常难以调试。 - pdr
@pdr 因为它令人困惑,所以就是错的吗?嗯...在这种情况下,表单(它可以是任何对象)是数据背后的对象。如果其他对象想要从此表单获取数据,它们也应该有能力订阅事件并接收数据。强制表单附着到每个对象上,当他是引发数据的对象时,没有任何价值。 - Aaron McIver
我并不是说这种方法“错误”,而是说它很危险。然而,你回答中的最后一句话是完全错误的。 - pdr
@pdr 这怎么可能是纯粹的错误呢?为什么数据的发起者要订阅应用程序中各种对象上的事件?你不会在你的对象上使用事件并强制一个按钮订阅你的对象,告诉你他已经被点击了...这有什么不同呢? - Aaron McIver
抱歉,我不明白您的例子。但是我想不出有什么场景会让一个子对象订阅父对象的事件(其中定义了父对象封装子对象)。这就是为什么我们有观察者模式的原因。 - pdr

0

对我来说有点模糊,但是:

将其作为包含 WinForm 中的事件,以便每当准备好一些数据时,所有订阅者都可以在一对多模式下得到通知;或者将其作为订阅控件中的事件,在一对一模式下调用容器的方法来检索这些数据?


OP - 这是观察者模式。谷歌一下,这可能是你正在寻找的东西。 - Darren Young

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接