在C#中访问多个表单中的数据

时间:2013-01-16 14:09:20

标签: c# wpf multiple-forms

我试着搜索,但找不到任何能让我满意的东西。我正在创建一个WPF桌面应用程序,最终将有四个或五个表单。每个表单将收集要通过电子邮件发送的数据。我还创建了一个单独的类(SendMail),它具有发送电子邮件的代码。我希望能够从各种表单的文本框中访问文本,并通过SendMail类中的方法发送它们。

目前,我只有两个基本表单设置了几个字段和下一页,提交和退出按钮。如果以下页面都不需要填写,我希望能够从任何页面提交数据。我目前能够通过内部属性从SendMail类访问每个表单,当我点击第一个表单上的“提交”按钮时,电子邮件将正确发送。但是,如果我转到下一个表单并单击“提交”按钮,则会在引用第一个表单上的文本框文本的属性中收到“未设置为对象实例的对象引用”错误。我假设通过转到第二种形式,第一种形式的实例不再存在。

几年前我在大学读了几门编程课,但我现在决定自己更认真地学习它。我读过几本书,但只学了几个月,所以我可能接近这个错误的方法了。任何帮助表示赞赏。

编辑 - 以下是代码的一些示例,如请求的那样。我从SendMail类中删除了电子邮件地址/密码。

第一个窗口

public partial class MainWindow : Window
{
    SendMail page1;

    // Properties to allow access to SendMail.
    internal string CmbEmail
    {
        get { return this.cmbEmail.Text; }
    }

    internal string DateWritten
    {
        get { return this.dateWritten.Text; }
    }

    public MainWindow()
    {
        InitializeComponent();
        page1 = new SendMail(this);
    }

    private void btnSubmit_Click_1(object sender, RoutedEventArgs e)
    {
        page1.Email();
    }

    private void btnNextPage_Click(object sender, RoutedEventArgs e)
    {
        Window1 nextPage = new Window1(this);
        nextPage.Show();
        this.Close();
    }
}

第二个窗口

public partial class Window1 : Window
{
    SendMail page2;

    public Window1(MainWindow parent)
    {
        InitializeComponent();
        page2 = new SendMail(this);
    }

    private void btnExit_Click(object sender, RoutedEventArgs e)
    {
        this.Close();
    }

    private void btnSubmit_Click(object sender, RoutedEventArgs e)
    {
        page2.Email();
    }
}

SendMail类

class SendMail
{
    MainWindow page1;
    Window1 page2;

    public SendMail(MainWindow form)
    {
        page1 = form;
    }

    public SendMail(Window1 form)
    {
        page2 = form;
    }

    public void Email()
    {
        NetworkCredential cred = new NetworkCredential("", "");
        MailMessage msg = new MailMessage();
        msg.To.Add("");

        // Send an email to address in the Email field, if not empty.
        if (page1.CmbEmail != "") // This is the line throwing the error, but only when submitting from the second window.
        {
            msg.To.Add(page1.CmbEmail);
        }

        msg.From = new MailAddress("");
        msg.Subject = "Garment Order " + page1.DateWritten.ToString();
        msg.Body = "Test email";

        SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
        client.Credentials = cred;
        client.EnableSsl = true;
        client.Send(msg);
    }
}

2 个答案:

答案 0 :(得分:0)

我认为你的第一个表单上的TextBox不再存在是正确的,这就是你收到错误的原因。这是因为WPF卸载了不再可见的控件,并且忘记了它们的所有数据,除非它们绑定到DataContext中的某些内容。

如果你正在使用WPF,我强烈建议使用MVVM设计模式。它非常适合这项技术,我发现它可以保持代码清洁,易于维护。

在您的情况下,我会为您的整个应用程序设置一个ViewModel,并使其包含SubmitCommand和5个数据对象,每个数据对象代表一个“表单”中的数据

例如,您的MainViewModel可能如下所示:

public MainViewModel : INotifyPropertyChanged
{
    // Should be full properties that implement INotifyPropertyChanged, 
    // but leaving that out for simplicity right now
    public ObservableCollection<object> Forms { get; set; }
    public object CurrentForm { get; set; }

    public ICommand SubmitCommand { get; set; }
    // Could also add ICommands for Back and Next buttons as well

    public MainViewModel()
    {
        Forms = new ObservableCollection()
        {
            new Form1Data(),
            new Form2Data(),
            new Form3Data(),
            new Form4Data(),
            new Form5Data()
        };

        CurrentForm = Forms.FirstOrDefault();
    }
}

你的XAML看起来像这样:

<Window>
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:Form1Data}">
            <local:Form1UserControl /> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Form2Data}">
            <local:Form2UserControl /> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Form3Data}">
            <local:Form3UserControl /> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Form4Data}">
            <local:Form4UserControl /> 
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Form5Data}">
            <local:Form5UserControl /> 
        </DataTemplate>
    </Window.Resources>

    <DockPanel>
        <Button Command="{Binding SubmitCommand}" 
                Content="Submit" DockPanel.Dock="Bottom" />

        <ContentControl Content="{Binding CurrentForm}" />
    </DockPanel>
</Window>

(您也可以在DataTemplates中写出XAML中的表单控件,而不是使用UserControl)

如果您是WPF和MVVM设计模式的新手,我有一个非常simple example on my blog您可能有兴趣阅读以开始使用。

修改

我刚刚看到您的更新问题与代码,这里的问题是您有SendMail类的两个副本,一个有您的第一个窗口,另一个有您的第二个窗口。您需要拥有SendMail类的单个副本,并让它引用两个窗口。

虽然就像我在回答的开头所说的那样,我认为这不会起作用,因为WPF会卸载不可见的UI对象,所以当{{1}时,窗口中的任何数据都可能会丢失它。

答案 1 :(得分:0)

看看构造函数。

这将使用第二个ctor(而不是分配page1)

page2 = new SendMail(this);

以上是Window1

public SendMail(MainWindow form)
{
    page1 = form;
}

public SendMail(Window1 form)
{
    page2 = form;
}

Window1 ctor不指定page1 因此page1.CmbEmail将抛出“未将对象引用设置为对象的实例”。

我认为你可以用

解决这个问题
   public SendMail(Window1 form, MainWindow mainWindow)
    {
        page2 = form;
        page1 = mainWindow;
    }

然后当你打电话时

page2 = new SendMail(this,parent);

但如果父窗口关闭,则会中断 但是,由于父Windows是MainWindow,无论如何都会关闭所有内容 所以不会有一个失败的Page1提交按钮。