开发者

C# Two Forms accessing the same class data

I'm using MS Visual C# 2010.

I created 2 WPF forms, MainWindow.xaml and CreateCharacter.xaml. I did Project->Add Class... to add an empty class c开发者_Go百科alled Hero.cs. The Hero class will hold the players name and other vital data.

In the MainWindow.xaml.cs I made an instance of the Hero class: Hero player = new Hero();. I click a button on the MainWindow and it opens the CreateCharacter form. On the CreateCharacter form I want to enter the players name and store this data in player.name, which was instantiated in MainWindow.

How do I make the Hero object I created in the MainWindow class available to the CreateCharacter class?


If the CreateCharacter form is creating a character, then perhaps it should create the Hero instance. It could return it in a public property that the main form could read after the CreateCharacter form successfully returned.


This is probably one of the most common questions ever asked on internet forums.
I suggest you get familiar with object-oriented programming before diving into GUI programming so you understand the basic concepts.

To pass an object to another window, you could add a special constructor that accepts it as a parameter, and a property or field to hold it (depending on whether you want to make it accessible or not):

partial class CreateCharacterWindow : Window {
    private Character character;

    public CreateCharacterWindow ()
        : this (null) { } // designer requires parameterless constructor

    public CreateCharacterWindow (Character character)
    {
        this.character = character;
        InitializeComponent ();
    }
}

var spiderman = new Character ();
var charWindow = new CreateCharacterWindow (spiderman);

I happen to dislike this approach though.

It seems logical that you intend to create the character in the very window you're about to show. What is the point of creating the object before the window is shown? I believe it's CreateCharacterWindow's reponsibility to actually instantiate Character because it knows most about its properties, and anyway there is no sense in having dummy character before user presses Create button or something similar if you don't plan to use databinding.

If you do intend to use databinding, create a public readonly Character property in CreateCharacterWindow so MainWindow could access it, instantiate it in the constructor, assign the instance to DataContext object and wire up UI controls and object properties in XAML markup.

But I wouldn't use databinding in this case either.

What I would do is to show CreateCharacterWindow modally with ShowModal method. In this class I would set this.DialogResult to true if the user chooses to actually create the character (as opposed to pressing Cancel button, for example).

ShowModal will return value we assigned to DialogResult so MainWindow knows if the user really wants to create the character. If this is the case, we finally ask CreateCharacterWindow to create Character instance:

partial class CreateCharacterWindow : Window {

    public CreateCharacterWindow ()
    {
        InitializeComponent ();
        createButton.Click += (sender, e) => {
            this.DialogResult = true;
            this.Close ();
        };
    }

    public Character CreateCharacter ()
    {
        return new Character {
            Name = nameBox.Text
        };
    }
}

var createWindow = new CreateCharacterWindow ();
var doCreate = createWindow.ShowDialog ();

if (doCreate ?? false) { // if DialogResult was not specified, assume it's false 
    var character = createWindow.CreateCharacter ();
    // do whatever you like with it
}

This method lacks some WPF databinding fanciness but I like that Character only gets created when there indeed is a character from business logic point of view, and the object doesn't act like some placeholder that may or may not be used.


add to your CreateCharacter argument or property of Hero class, and pass it to its constructor or any else method.


This is a pretty common pattern. Here's how I'd do it in WPF.

In the view model for the main window, there should be a property called Characters that's an ObservableCollection<CharacterViewModel>. In the view, this is bound to some kind of items control, say a ListBox. There's either a DataTemplate for the CharacterViewModel, or the class implements ToString(), so that the characters are presented usefully. The SelectedItem property of the ListBox is bound to a SelectedCharacter property in the view model, so that whenever the user clicks on an item in the list box, the view model knows what the currently-selected character is.

The main window view model also implements an EditingCharacter event (it's just an ordinary event handler) and an EditCharacterCommand (using Josh Smith's RelayCommand pattern), and its accompanying property and method, e.g.:

public bool CanEditCharacter { get { return SelectedCharacter != null; } }

public void EditCharacter()
{
   EventHandler h = EditingCharacter;
   if (EditingCharacter != null)
   {
      EditingCharacter(this, EventArgs.Empty);
   }
}

The EditCharacterCommand is bound to a clickable control (a button or hyperlink, say) in the view.

The main window view instantiates the main window view model and registers a handler for the EditingCharacter event:

private void ViewModel_EditingCharacter(object sender, EventArgs e)
{
   CharacterViewModel cvm = ((MainWindowViewModel)sender).SelectedCharacter;
   CharacterWindow cw = new CharacterWindow();
   cw.ShowDialog(cvm);
}

(Why use an event? Because using events keeps the implementation details of creating and displaying windows outside of the view model objects. The CharacterViewModel doesn't need to know any details about how characters are edited. It just raises an event saying "hey, it's time to edit the currently-selected character." It's up to the main window to decide what it's going to do when the event is raised.)

The CharacterWindow is what actually lets the user edit a character. Its controls are bound to properties of the CharacterViewModel. It implements an overload of ShowDialog:

public bool? ShowDialog(CharacterViewModel cvm)
{
   DataContext = cvm;
   return ShowDialog();
}

(Making it possible to cancel this dialog without saving changes is an exercise for the reader.)

Finally, you also implement an AddCharacterCommand in the window view model - it creates a new CharacterViewModel, adds it to the Characters collection, sets SelectedCharacter, and raises EditingCharacter. Bind this to a button or hyperlink or menu item in the main window, and you're done.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜