C# 在WinForm应用程序中检查输入的文本

C# 在WinForm应用程序中检查输入的文本,c#,.net,winforms,user-interface,.net-3.5,C#,.net,Winforms,User Interface,.net 3.5,我有一个WinForm应用程序(.Net 3.5),我们在内部使用它来输入数据……就像这样。成员计划、人口统计等。该应用程序有一个包含所有导航的Shell表单,然后根据这些导航选项嵌入用户控件 我需要能够告诉他们是否对任何控件进行了任何更改,以便在他们尝试导航离开时,我可以弹出一个保存提醒。最好的办法是什么?欢迎提出任何建议 我认为将每个用户控件上的每个控件的事件挂钩以检查是否输入了任何数据是荒谬的,对吗?如果使用数据绑定,您可以在类中实现(手动或生成运行时代理),订阅事件并在那里执行所有跟踪

我有一个WinForm应用程序(.Net 3.5),我们在内部使用它来输入数据……就像这样。成员计划、人口统计等。该应用程序有一个包含所有导航的Shell表单,然后根据这些导航选项嵌入用户控件

我需要能够告诉他们是否对任何控件进行了任何更改,以便在他们尝试导航离开时,我可以弹出一个保存提醒。最好的办法是什么?欢迎提出任何建议


我认为将每个用户控件上的每个控件的事件挂钩以检查是否输入了任何数据是荒谬的,对吗?

如果使用数据绑定,您可以在类中实现(手动或生成运行时代理),订阅事件并在那里执行所有跟踪


如果没有,您可以递归地迭代表单/用户控件上的控件,为每个控件订阅适当的事件(例如
TextChanged
用于
TextBox
SelectedItemChanged
用于
组合框
控件等)并将调用转发到您的跟踪例程。

我不会说在这里使用事件是荒谬的,但您可能会在事件注册方面有所创新。在实例化用户控件时,可以循环遍历其中的所有UI控件,并根据UI元素的类型(文本框与标签等),决定将该UI元素的更改事件连接到处理程序


每个UI元素类型可能只有一个处理程序(一个用于文本框,一个用于复选框等),因为我相信这些事件的处理程序需要有不同的参数。这些处理程序的逻辑也可以拉入它自己的类中,这样就不必在每个用户控件中复制相同的处理逻辑。

Anton所写的推论是,除非控件绑定到提供更改通知的数据源,否则必须为每个控件挂接事件。不同的控件具有不同的属性集,这些属性集可能会发生变化,有时,其中的多个属性会起作用,具体取决于用户界面的设计方式

我维护一些类似的应用程序,我喜欢使用的模式之一是“事件服务”。您可以使用IoC容器将所有内容连接起来,并且主窗体/子任务实际上不需要直接相互对话

界面如下所示:

public interface IEventService
{
    void RaiseChanged(object sender, EventArgs e);
    event EventHandler Changed;
}
public class MainForm : Form
{
    private bool currentTaskChanged;

    public MainForm()
    {
        InitializeComponent();
        InitializeChangeEvents();
    }

    public void LoadTask(ITask task)
    {
        if (currentTaskChanged)
        {
            // Confirm whether or not to save changes
        }

        // Code to change the current task view here ...
        currentTaskChanged = false;
    }

    private void InitializedChangedEvents()
    {
        IEventService service = IoC.Resolve<IEventService>();
        service.Changed += TaskChanged;
    }

    private void TaskChanged(object sender, EventArgs e)
    {
        currentTaskChanged = true;
    }
}
基本实现非常简单,但我还是要包括它:

public class EventService : IEventService
{
    public void RaiseChanged(object sender, EventArgs e)
    {
        EventHandler handler = Changed;
        if (handler != null)
            handler(sender, e);
    }

    public event EventHandler Changed;
}
在您选择的IoC容器中定义它,并将其设置为单例,或者如果IoC需要做太多的工作,就将其作为单例实现;我想你会选择第一种。然后,您的主表单代码如下所示:

public interface IEventService
{
    void RaiseChanged(object sender, EventArgs e);
    event EventHandler Changed;
}
public class MainForm : Form
{
    private bool currentTaskChanged;

    public MainForm()
    {
        InitializeComponent();
        InitializeChangeEvents();
    }

    public void LoadTask(ITask task)
    {
        if (currentTaskChanged)
        {
            // Confirm whether or not to save changes
        }

        // Code to change the current task view here ...
        currentTaskChanged = false;
    }

    private void InitializedChangedEvents()
    {
        IEventService service = IoC.Resolve<IEventService>();
        service.Changed += TaskChanged;
    }

    private void TaskChanged(object sender, EventArgs e)
    {
        currentTaskChanged = true;
    }
}
公共类主窗体:窗体
{
私有布尔值已更改;
公共表格(
{
初始化组件();
InitializeChangeEvents();
}
公共无效加载任务(ITask任务)
{
如果(当前任务已更改)
{
//确认是否保存更改
}
//在此处更改当前任务视图的代码。。。
currentTaskChanged=false;
}
private void InitializedChangedEvents()
{
IEventService service=IoC.Resolve();
service.Changed+=TaskChanged;
}
私有void任务已更改(对象发送方,事件参数e)
{
currentTaskChanged=true;
}
}
然后,登记新的子任务/控件变得非常简单:

public class CustomerSetupControl : UserControl
{
    public CustomerSetupControl()
    {
        InitializeComponent();
        InitializeChangeTriggers();
    }

    private void InitializeChangeTriggers()
    {
        IEventService service = IoC.Resolve<IEventService>();
        txtAccountNumber.TextChanged += service.RaiseChanged;
        txtName.TextChanged += service.RaiseChanged;
        chkIsVip.CheckedChanged += service.RaiseChanged;
        // etc.
    }
}
公共类CustomerSetupControl:UserControl { 公共客户设置控制() { 初始化组件(); InitializeChangeTriggers(); } 私有void InitializeChangeTriggers() { IEventService service=IoC.Resolve(); txtAccountNumber.TextChanged+=service.RaiseChanged; txtName.TextChanged+=service.RaiseChanged; chkIsVip.CheckedChanged+=service.RaiseChanged; //等等。 } } 理论上,您可以通过迭代子任务的整个控制树来自动化其中的一部分,但这有点困难。例如,如果其中一个控件是
复选框
,那么挂钩
TextChanged
事件实际上对您没有帮助。因此,您需要一个接一个地执行此操作,以确保实际检测到正确控件的正确更改事件-在某些情况下,您甚至可能希望忽略特定控件上的更改(例如,可能有一个组合框,它只在某处更改过滤器,而不修改任何数据)

这与我使用的代码不完全相同,但非常相似。我建议试试看;我发现维护起来并不太困难,只要记住在添加新控件时“注册”它们就可以了


这种方法特别好的地方在于,任何控件都可以注册到事件服务;您可以轻松地扩展服务,以包括不同类型的“公共”事件,这些事件可以在应用程序中的任何位置使用(只需记住从任何控件中取消注册事件处理程序,这些控件不会像主窗体那样永远存在)。

这可能看起来很可笑,但我该如何处理
PropertyChanged
事件,因为它与此相关?我已经像链接秀一样实现了它,也许,也许,我只是个白痴,但是现在呢?在
UserControl\u离开时
检查。。。什么?对不起,如果我只是在说粗话。我知道,这是一个穷人的服务定位器,但是用它改造现有的应用程序要比用构造函数/属性注入重写每个控件容易得多,而且你可以通过移动当情况需要时,将“服务”分配到构造函数/属性中。