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.
精彩评论