我想我回答了一個關於這個問題的前一個問題,看起來這是什麼讓你開始沿着這條路線。第一點是我沒有特別推薦這種模式,只是想教你更多關於軟件開發人員如何管理範圍。
這就是說,你所面臨的問題並非無法克服。例如,您可以在運行時拋出一個異常而不是在設計時拋出一個公共構造函數,並修改Program.cs以使用靜態實例,而不是手動構建表單。
但是。
正如我在其他問題中所說的,更好的選擇是更改體系結構,以便您不需要庫代碼即可直接操作GUI。
您可以通過僅在GUI認爲需要新數據(簡單函數)時詢問庫問題,或者在需要更改某些內容時通知GUI來做到這一點。任何一種方法都會比直接使用庫提供標籤更好。
開始的好地方就像MVC(模型 - 視圖 - 控制器)體系結構,我在之前的回答中提到了這一點。不過,最好給我們一個關於你的高級程序結構現在看起來更詳細一點的想法。你在系統中使用的主要類是什麼(不僅僅是你迄今爲止提到的那些)?每個人的主要責任是什麼,每個人的生活在哪裏?那麼我們的建議可能會更具體。
編輯
所以,我嘲笑了一個可能的替代架構的快速演示,根據您的評論。
我在我的項目如下:
FormMain (Form)
TitleScreen (UserControl)
InGameMenu (UserControl)
MainScreen (UserControl)
GameController (Class)
GameModel (Class)
現在我沒有使用Date
和LoadSave
。
FormMain
只是簡單地在每個UserControl
上放置一個實例。沒有特殊的代碼。
GameController
是單(因爲你嘗試已經使用這種模式,我認爲這將是有益的,您可以嘗試使用它的一個工作版本),其通過操縱模型響應用戶輸入。請注意:您不直接從GUI(這是模型視圖控制器的視圖部分)操縱模型。它暴露的GameModel
一個實例,並有一堆的,讓你進行遊戲操作,如加載/保存方法,結束了轉彎等
GameModel
是你的所有遊戲狀態下進行保存。在這種情況下,這只是一個日期和一個回合計數器(就好像這將是一個回合制遊戲)。日期是一個字符串(在我的遊戲世界中,日期以「Eschaton 23,3834.4」格式顯示),每一輪都是一天。
TitleScreen和InGameMenu每個只有一個按鈕,爲清晰起見。理論上(不是實現),TitleScreen讓你開始一個新的遊戲,InGameMenu讓你加載一個現有的遊戲。
所以通過介紹,這裏是代碼。
GameModel:
public class GameModel
{
string displayDate = "Eschaton 23, 3834.4 (default value for illustration, never actually used)";
public GameModel()
{
// Initialize to 0 and then increment immediately. This is a hack to start on turn 1 and to have the game
// date be initialized to day 1.
incrementableDayNumber = 0;
IncrementDate();
}
public void PretendToLoadAGame(string gameDate)
{
DisplayDate = gameDate;
incrementableDayNumber = 1;
}
public string DisplayDate
{
get { return displayDate; }
set
{
// set the internal value
displayDate = value;
// notify the View of the change in Date
if (DateChanged != null)
DateChanged(this, EventArgs.Empty);
}
}
public event EventHandler DateChanged;
// use similar techniques to handle other properties, like
int incrementableDayNumber;
public void IncrementDate()
{
incrementableDayNumber++;
DisplayDate = "Eschaton " + incrementableDayNumber + ", 9994.9 (from turn end)";
}
}
注意事項:你的模型有一個事件(在這種情況下,只需鍵入事件處理程序的一個,你以後可以創建更多的表現類型的事件,但讓我們從簡單的開始)稱爲DateChanged
。只要DisplayDate
發生變化,就會觸發此操作。您可以在查看屬性定義時看到這種情況:set
訪問器(如果有人正在偵聽,您不會從GUI調用該訪問器)引發該事件。還有內部字段來存儲GameController
(不是您的GUI)將根據需要調用的遊戲狀態和方法。
GameController看起來是這樣的:
public class GameController
{
private static GameController instance;
public static GameController Instance
{
get
{
if (instance == null)
instance = new GameController();
return instance;
}
}
private GameController()
{
Model = new GameModel();
}
public void LoadSavedGame(string file)
{
// set all the state as saved from file. Since this could involve initialization
// code that could be shared with LoadNewGame, for instance, you could move this logic
// to a method on the model. Lots of options, as usual in software development.
Model.PretendToLoadAGame("Eschaton 93, 9776.9 (Debug: LoadSavedGame)");
}
public void LoadNewGame()
{
Model.PretendToLoadAGame("Eschaton 12, 9772.3 (Debug: LoadNewGame)");
}
public void SaveGame()
{
// to do
}
// Increment the date
public void EndTurn()
{
Model.IncrementDate();
}
public GameModel Model
{
get;
private set;
}
}
在你看到的單身實現頂部。然後是構造函數,它確保始終有一個模型,以及加載和保存遊戲的方法。 (在這種情況下,即使在加載新遊戲時,我也不會更改GameModel
的實例,原因是GameModel
有事件,我不希望聽衆必須將這些簡單的示例代碼斷開並重新連線。 )請注意,這些方法基本上實現了GUI可能需要在遊戲狀態下執行的所有高級操作:加載或保存遊戲,結束轉彎等。
現在休息很簡單。
TitleScreen:
public partial class TitleScreen : UserControl
{
public TitleScreen()
{
InitializeComponent();
}
private void btnLoadNew(object sender, EventArgs e)
{
GameController.Instance.LoadNewGame();
}
}
InGameMenu:
public partial class InGameMenu : UserControl
{
public InGameMenu()
{
InitializeComponent();
}
private void btnLoadSaved_Click(object sender, EventArgs e)
{
GameController.Instance.LoadSavedGame("test");
}
}
注意這兩個怎麼做什麼,但在控制器上調用方法。簡單。
public partial class MainScreen : UserControl
{
public MainScreen()
{
InitializeComponent();
GameController.Instance.Model.DateChanged += Model_DateChanged;
lblDate.Text = GameController.Instance.Model.DisplayDate;
}
void Model_DateChanged(object sender, EventArgs e)
{
lblDate.Text = GameController.Instance.Model.DisplayDate;
}
void Instance_CurrentGameChanged(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void btnEndTurn_Click(object sender, EventArgs e)
{
GameController.Instance.EndTurn();
}
}
這是多一點涉及,但不是很多。關鍵是,它會連接模型上的DateChanged
事件。這樣可以在日期遞增時通知它。我還在這裏實現了另一個遊戲功能(結束轉彎)。
如果您複製並運行它,您會發現遊戲日期是從很多地方操縱的,並且標籤始終正確更新。最重要的是,你的控制器和模型實際上並不知道關於視圖的任何事情 - 甚至不是基於WinForms。在Windows Phone或Mono上下文中,您可以輕鬆使用這兩個類作爲其他任何內容。
這是否闡明瞭我和其他人試圖解釋的一些架構原則?
爲什麼你需要控制singletone。這完全不合邏輯,並沒有計劃以這種方式使用 – 2013-05-10 16:21:00
正如我在下面的評論中提到的,我真的只是尋找一種方法來訪問我的控件的公共屬性和其他控件的窗體。到目前爲止,我還沒有找到一個好方法。 – 2013-05-10 16:41:02
很好,你不能以不錯的方式做壞事=)。控制是已完成功能的一部分,它不應該依賴(甚至不知道是否存在)任何外部控制。如果您需要在控件之間實現一些通信,請通過事件(實現您的自定義事件)執行並以主窗體的形式訂閱它們。 – 2013-05-10 16:54:05