2010-10-20 6 views
10

Tôi muốn biết cách tốt nhất (đọc thanh lịch nhất) là có một cá thể của một cửa sổ cho mỗi ứng dụng trong WPF.C# WPF làm thế nào để thực thi các trường hợp đơn lẻ của cửa sổ

Tôi là người mới đến .NET và WPF và những gì tôi nghĩ ra có vẻ khá lame.

private static readonly Object MUTEX = new Object(); 
private static AboutWindow INSTANCE; 

public static AboutWindow GetOrCreate() { 
    lock (MUTEX) { 
     if (INSTANCE == null) { 
      INSTANCE = new AboutWindow(); 
     } 
     INSTANCE.Show(); 
     return INSTANCE; 
    } 
} 

private AboutWindow() { 
    InitializeComponent(); 
} 

private void AboutWindow_Closed(object sender, EventArgs e) { 
    // the Closed events are handy for me to update values across 
    // different windows. 
    lock (MUTEX) { 
     INSTANCE = null; 
    } 
} 

Thing là ... điều này có vẻ như hoàn toàn crap. Phải có cách nào đó để đạt được cùng một mục tiêu một cách thanh lịch hơn, đúng không?

PS: Tôi thường sử dụng sự kiện Closed để thay đổi giá trị trong các cửa sổ đang mở khác. Ví dụ: tôi có Cài đặtWindow bằng nút "Tài khoản". Khi tôi nhấn nút đó, AccountWindow bật lên. Khi tôi đóng AcountWindow, tôi muốn một cái gì đó trong Cài đặtWindow để thay đổi (nhãn). Do đó việc tạo cửa sổ liên tục.
Bên cạnh đó, Đóng là thứ bạn luôn phải giải quyết vì nút X trên khung cửa sổ ...

Trả lời

5

Nếu bạn thực sự cần phải thực thi một trường hợp duy nhất của một cửa sổ, sau đó một trường hợp tĩnh (một số hương vị của những gì bạn có) với một phương pháp tạo nhà máy chắc chắn là một lựa chọn khả thi, giống như một cá thể DataContext duy nhất khi làm việc với một cơ sở dữ liệu.

Bạn cũng có thể viết lớp WindowManager của riêng mình, mặc dù có vẻ như quá mức cần thiết, và về cơ bản sẽ giống nhau (ngoại trừ phương thức Factory sẽ nằm trong một lớp đơn).

Tuy nhiên, đọc lại bài đăng của bạn, tôi tự hỏi nếu đây là trường hợp thiếu rừng cho cây. Đề cập đến SettingsWindow của bạn, mà lần lượt gọi AccountWindow, làm cho tôi nghĩ rằng bạn chỉ nên sử dụng ShowDialog(). Điều này sẽ mở ra một cửa sổ một cách bình thường, có nghĩa là không thể tương tác với cửa sổ gọi (hoặc bất kỳ cửa sổ nào khác trong ứng dụng của bạn). Bạn chỉ cần đặt thuộc tính trong hộp thoại đó, đặt DialogResult thành true khi nhấn nút OK và đọc thuộc tính đó trong cửa sổ chính.

Về cơ bản, bạn chỉ cần sử dụng ShowDialog như thế này. Tôi bỏ qua rất nhiều chi tiết thực hiện, theo như ràng buộc so với mã hóa cứng để điều khiển. Những chi tiết đó không quan trọng bằng cách chỉ xem cách hoạt động của ShowDialog.

Để đơn giản, giả sử rằng bạn có một lớp được gọi là MyAppOptions, tốt, phản ánh các tùy chọn của ứng dụng của bạn. Tôi sẽ rời khỏi hầu hết các chi tiết thực hiện điều này vì đơn giản, nhưng nó có khả năng sẽ thực hiện INotifyPropertyChanged, có phương pháp và các lĩnh vực và các tài sản vv

public class MyAppOptions 
{ 
    public MyAppOptions() 
    { 
    } 

    public Boolean MyBooleanOption 
    { 
     get; 
     set; 
    } 

    public String MyStringOption 
    { 
     get; 
     set; 
    } 
} 

Sau đó, chúng ta hãy làm đơn giản này, và cho rằng bạn muốn hiển thị hộp thoại Tùy chọn khi bạn nhấn một nút trên một số cửa sổ. Hơn nữa, tôi sẽ giả định rằng có các biến đã được thiết lập với các tùy chọn của bạn, được nạp khi khởi động.

void btnOptions_Click(object sender, RoutedEventArgs e) 
{ 
    MyAppOptions options = new MyAppOptions(); 
    options.MyBooleanOption = mSomeBoolean; 
    options.MyStringOption = mSomeString; 

    OptionsDialog optionsDialog = new optionsDialog(options); 
    if (optionsDialog.ShowDialog() == true) 
    { 
     // Assume this function saves the options to storage 
     // and updates the application (binding) appropriately 
     SetAndSaveOptions(optionsDialog.AppOptions); 
    } 
} 

Bây giờ giả sử rằng OptionsDialog là một cửa sổ bạn đã tạo trong dự án của bạn, và nó có một CheckBox trên nó liên quan đến MyBooleanOption và một TextBox cho MyStringOption. Nó cũng có một nút Ok và một nút Cancel. Mã phía sau có khả năng sẽ sử dụng Binding, nhưng bây giờ chúng tôi sẽ mã hóa các giá trị.

public class OptionsDialog : Window 
{ 
    public OptionsDialog(MyAppOptions options) 
    { 
     chkBooleanOption.IsChecked = options.SomeBooleanOption; 
     txtStringOption.Text = options.SomeStringOption; 
     btnOK.Click += new RoutedEventHandler(btnOK_Click); 
     btnCancel.Click += new RoutedEventHandler(btnCancel_Click); 
    } 

    public MyAppOptions AppOptions 
    { 
     get; 
     set; 
    } 

    void btnOK_Click(object sender, RoutedEventArgs e) 
    { 
     this.AppOptions.SomeBooleanOption = (Boolean) chkBooleanOption.IsChecked; 
     this.AppOptions.SomeStringOption = txtStringOption.Text; 

     // this is the key step - it will close the dialog and return 
     // true to ShowDialog 
     this.DialogResult = true; 
    } 

    void btnClose_Click(object sender, RoutedEventArgs e) 
    { 
     // this will close the dialog and return false to ShowDialog 
     // Note that pressing the X button will also return false to ShowDialog 
     this.DialogResult = false; 
    } 
} 

Đây là ví dụ khá cơ bản về chi tiết triển khai. Tìm kiếm trực tuyến cho ShowDialog để biết thêm chi tiết. Các phím quan trọng cần nhớ là:

  • ShowDialog sẽ mở ra một cửa sổ modally, có nghĩa là nó là cửa sổ duy nhất trong ứng dụng của bạn có thể được tương tác với .
  • Đặt DialogResult thành true sẽ đóng hộp thoại, có thể được kiểm tra từ cha mẹ gọi.
  • Hộp thoại thiết lậpĐối với sai số cũng đóng hộp thoại, trong trường hợp này là , bạn bỏ qua cập nhật giá trị trong cửa sổ gọi .
  • Nhấn nút X trên cửa sổ tự động cài đặt DialogResult false
  • Bạn có thể có tài sản công cộng trong cửa sổ hộp thoại có thể được thiết lập trước khi thực hiện ShowDialog, và có thể nhận được giá trị từ sau khi hộp thoại biến mất. Nó sẽ có sẵn trong khi hộp thoại vẫn còn trong phạm vi.
+0

Thú vị! Trong trường hợp này, tôi không bỏ lỡ rừng vì tôi hoàn toàn mới.Và hơn nữa, tôi chủ yếu là chương trình công cụ phía máy chủ! Đây là một sân cỏ hoàn toàn mới cho tôi :) Tôi đánh giá cao nếu bạn có thể cập nhật câu trả lời của bạn với một ví dụ! – biasedbit

+0

Cảm ơn ví dụ! : D – biasedbit

13

có thể có cách tốt hơn để thực hiện việc này, nhưng đây là một cách khá đơn giản .... đặt một bool tĩnh trên lớp cửa sổ của bạn để gắn cờ nếu nó mở hay không. sau đó, trong sự kiện load() đặt nó thành true, và trên sự kiện đóng đặt nó thành false. Sau đó, trong mã mở cửa sổ, hãy kiểm tra cờ.

đây là một số mã giả để cung cấp cho bạn một ý tưởng ...

public class AboutWindow 
{ 

    public static bool IsOpen {get;private set;} 

    onLoadEvent(....) 
    { 
     IsOpen = true; 
    } 

    onUnloadEvent(...) 
    { 
     IsOpen = false; 
    } 

} 


public void OpenAbout() 
{ 
    if (AboutWindow.IsOpen) return; 
    AboutWindow win = new AboutWindow(); 
    win.Show(); 

} 
+2

Giống như điều này hơn cả singleton với điều mutex. Không để lại một số lợi nhuận cho việc mở nhiều hơn một cửa sổ nhưng tỷ lệ cược xảy ra trong các ứng dụng dựa trên giao diện người dùng là không hề - Tôi là một dude máy chủ, không thể ngừng suy nghĩ về tất cả các trường hợp xấu nhất trong đồng thời :) – biasedbit

1

Sau đây mở rộng giải pháp trên để chỉnh sửa lại cửa sổ nếu cửa sổ đã mở. Trong trường hợp này, nó là một cửa sổ trợ giúp.

///<summary> 
    /// Show help from the resources for a particular control by contextGUID 
    ///</summary> 
    ///<param name="contextGUID"></param> 
    private void ShowApplicationHelp(string contextGUID = "1") 
    { 

     if (HelpWin != null) 
     { 
      if (HelpWin.IsOpen) 
      { 
       HelpWin.BringToFront(); 
       return; 
      } 
     } 

     HelpWin = new MigratorHelpWindow(); 
     HelpWin.Owner = Application.Current.MainWindow; 
     HelpWin.ResizeMode = ResizeMode.CanResizeWithGrip; 
     HelpWin.Icon = new Image() 
          { 
           Source = 
            new BitmapImage(
            new Uri(
             "pack://application:,,,/ResourceLibrary;component/Resources/Images/Menu/Help.png", 
             UriKind.RelativeOrAbsolute)) 
          }; 
     HelpWin.Show(); 
     HelpWin.BringToFront(); 
    } 

Mã này là tất cả trong chế độ xem (MVVM) được liên kết với cửa sổ. Nó được gọi là bởi một ICommand nối với một nút trên cửa sổ (tự nhiên, nó cho thấy một dấu hỏi !!) Các tài sản sau đây có liên quan (trong trường hợp này nó là một Telerik RadWindow nhưng nó có thể là bất kỳ đối tượng cửa sổ, và bạn có thể có lẽ cũng chỉ lưu trữ các cửa sổ xử lý nhưng sử dụng tài sản này cho phép thao tác của đối tượng trơn tru hơn ví dụ HelpWin.BringToFront() như trong ví dụ trên ...

... 
    ... 
    private Telerik.Windows.Controls.RadWindow **HelpWin** 
    { 
     get; 
     set; 
    } 
    ... 
    ... 

trong cửa sổ chính (WPF cửa sổ)

///<summary> 
    /// Flag to indicate the window is open - use to prevent opening this particular help window multiple times... 
    ///</summary> 
    public static bool IsOpen { get; private set; } 
    ... 
    ... 
    ... 

    private void HelpWindowLoaded(object sender, RoutedEventArgs e) 
    { 
     IsOpen = true; 
    } 

    private void HelpWindowUnloaded(object sender, RoutedEventArgs e) 
    { 
     IsOpen = false; 
    } 

và trong chế độ xem Xaml ... ...

DataContext="{Binding Path=OnlineHelpViewModelStatic,Source={StaticResource Locator}}" 
    RestoreMinimizedLocation="True" 
    **Loaded="HelpWindowLoaded" Unloaded="HelpWindowUnloaded"** > 
0

Làm thế nào để sử dụng Singleton?

public class MyWindow : Window { 

    private static MyWindow instance; 

    public static MyWindow Instance 
    { 
     get 
     { 
      if (instance == null) 
      { 
       instance = new MyWindow(); 
      } 
      return instance; 
     } 
    } 
} 

Sau đó, chỉ cần sử dụng

MyWindow.Instance.Show() and MyWindow.Instance.Hide()