Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OKB/HeuristicLab.MainForm.WPF/WPFMainFormBase.cs @ 6062

Last change on this file since 6062 was 4311, checked in by swagner, 14 years ago

Integrated OKB clients for HL 3.3 (#1166)

File size: 17.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Windows;
6using HeuristicLab.PluginInfrastructure;
7using System.Windows.Controls;
8using AvalonDock;
9using System.Windows.Media.Imaging;
10using System.Reflection;
11using System.Windows.Media;
12using HeuristicLab.BackgroundProcessing;
13using System.Threading;
14using System.Windows.Threading;
15using System.ComponentModel;
16using System.Windows.Input;
17using System.Windows.Controls.Primitives;
18using HeuristicLab.Core;
19using System.Windows.Forms.Integration;
20using HeuristicLab.Common;
21
22namespace HeuristicLab.MainForm.WPF { 
23
24  public abstract class WPFMainFormBase : Window, IMainForm, IControlManager {
25
26    public static readonly Size ToolBarImageSize = new Size(20, 20);
27    public abstract Type UserInterfaceItemType { get; }
28
29    public WPFMainFormBase() {
30      InitializeComponent();
31      InstallExceptionHandlers();
32      InitializeBackgroundWorkerMonitoring();
33      BuildRootMenu();
34      BuildViewsMenu();
35      BuildToolBar();
36      Messages.Items.Clear();
37      DockingManager.PropertyChanged += (sender, args) => {
38        if (args.PropertyName == "ActiveDocument")
39          OnActiveDocumentChanged();
40      };
41      MainFormManager.RegisterMainForm(this);
42      ControlManager.RegisterManager(this);
43    }
44
45    public void ShowControl(object control) {
46      object content = null;
47      string name = "Control";
48      if (control is HeuristicLab.MainForm.IView) {
49        content = control;
50        name = ((HeuristicLab.MainForm.IView)control).Caption;
51      }
52      ShowObjectAsView(content, name);
53    }
54
55    public IContentView ShowContent(IContent content) {
56      throw new NotImplementedException();
57    }
58
59    public IContentView ShowContent(IContent content, Type viewType) {
60      throw new NotImplementedException();
61    }
62
63    #region GUI
64
65    private DockPanel Panel = new DockPanel();
66    private Menu Menu = new Menu();
67    private ToolBar ToolBar = new ToolBar();
68    private StatusBar StatusBar = new StatusBar();
69    private StatusBar BackgroundWorkers = new OperationBarPanel();
70    private StatusBar Messages = new StatusBar();
71    private DocumentPane DocumentPane = new DocumentPane();
72    private DockingManager DockingManager = new DockingManager() {
73      MinHeight = 50,
74    };
75
76    protected void InitializeComponent() {
77      StatusBar.Items.Add("OKB Cockpit");
78      StatusBar.Items.Add(new Separator());
79      StatusBar.Items.Add("Operations:");
80      StatusBar.Items.Add(BackgroundWorkers);
81      StatusBar.Items.Add(new Separator());
82      StatusBar.Items.Add(Messages);
83      StatusBar.AddHandler(Button.ClickEvent, new RoutedEventHandler(OperationAborted));
84      Menu.SetValue(DockPanel.DockProperty, Dock.Top);
85      ToolBar.SetValue(DockPanel.DockProperty, Dock.Top);
86      StatusBar.SetValue(DockPanel.DockProperty, Dock.Bottom);
87      DockingManager.Content = DocumentPane;
88      Panel.Children.Add(Menu);
89      Panel.Children.Add(ToolBar);
90      Panel.Children.Add(StatusBar);
91      Panel.Children.Add(DockingManager);
92      Content = Panel;
93      Title = "WPF Main Window";
94    }
95
96    #endregion
97
98    #region fallback exception handling
99
100    private void InstallExceptionHandlers() {
101      AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
102      Dispatcher.UnhandledException += Dispatcher_UnhandledException;
103      WorkerMonitor.Default.BackgroundWorkerException += WorkerMonitor_BackgroundWorkerException;
104    }
105
106    private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
107      Exception x = e.ExceptionObject as Exception;
108      if (x != null)
109        ShowException("Unhandled application domain exception", x);
110    }
111
112    private void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) {
113      ShowException("Unhandled dispatcher exception", e.Exception);
114    }
115
116    private void WorkerMonitor_BackgroundWorkerException(object sender, ThreadExceptionEventArgs e) {
117      ShowException(String.Format("'{0}' raised async exception", e.Exception.Message), e.Exception.InnerException);
118    }
119
120    protected void ShowException(string message, Exception e) {
121      Dispatcher.BeginInvoke(new Action(() => {
122        MessageBox.Show(this,
123          String.Format("{0}:\n{1}", message, e.ToString()),
124          Title, MessageBoxButton.OK, MessageBoxImage.Error);
125      }), DispatcherPriority.SystemIdle);
126    }
127
128    #endregion
129
130    #region view discovery
131
132    private void BuildViewsMenu() {
133      MenuItem viewsMenu = new MenuItem() { Header = "Views" };
134      Menu.Items.Add(viewsMenu);
135      foreach (Type viewType in ApplicationManager.Manager
136          .GetTypes(UserInterfaceItemType)
137          .Where(t => t.GetInterface(typeof(IAutoView).FullName) != null)) {
138        viewsMenu.Items.Add(CreateViewsMenuEntry(viewType));
139      }
140    }
141
142    private MenuItem CreateViewsMenuEntry(Type viewType) {
143      MenuItem item = new MenuItem() { Header = viewType.Name };
144      item.Click += (sender, args) => {
145        ShowView((IView)Activator.CreateInstance(viewType, true));
146      };
147      return item;
148    }
149
150    #endregion
151
152    #region menu discovery
153
154    private void BuildRootMenu() {
155      foreach (Type menuItemType in
156          ApplicationManager.Manager
157          .GetTypes(UserInterfaceItemType)
158          .Where(t => t.GetInterface(typeof(IMenuItem).FullName) != null)) {
159        IMenuItem item = (IMenuItem)Activator.CreateInstance(menuItemType, true);
160        AddMenuItem(Menu.Items, item, item.Structure.GetEnumerator());
161      }
162    }
163
164    private void AddMenuItem(ItemCollection items, IMenuItem newItem, IEnumerator<string> structure) {
165      if (structure.MoveNext()) {
166        string name = structure.Current;
167        MenuItem parent = items.Cast<MenuItem>().SingleOrDefault(i => i.Header as string == name);
168        if (parent == null) {
169          parent = new MenuItem() { Header = name, Tag = newItem };
170          InsertSortedMenuItem(items, parent);
171        }
172        AddMenuItem(parent.Items, newItem, structure);
173      } else {
174        MenuItem item = new MenuItem() { Header = newItem.Name, Tag = newItem };
175        item.Click += (s, a) => newItem.Execute();
176        if (newItem.Image != null)
177          item.Icon = newItem.Image;
178        WPFMenuItem wpfItem = newItem as WPFMenuItem;
179        if (wpfItem != null) {
180          wpfItem.MenuItem = item;
181          item.IsEnabled = wpfItem.IsEnabled;
182          wpfItem.IsEnabledChanged += (s, a) => item.IsEnabled = wpfItem.IsEnabled;
183        }
184        InsertSortedMenuItem(items, item);
185      }
186    }
187
188    private static void InsertSortedMenuItem(ItemCollection items, MenuItem menuItem) {
189      IMenuItem newItem = menuItem.Tag as IMenuItem;
190      if (newItem == null) {
191        items.Add(menuItem);
192      } else {
193        int index = 0;
194        while (index < items.Count) {
195          MenuItem it = items[index] as MenuItem;
196          if (it == null) break;
197          IMenuItem item = it.Tag as IMenuItem;
198          if (item == null) break;
199          if (newItem.Position < item.Position) break;
200          index++;
201        }
202        items.Insert(index, menuItem);
203      }
204    }
205
206    #endregion
207
208    #region tool bar discovery
209
210    private void BuildToolBar() {
211      foreach (Type itemType in
212          ApplicationManager.Manager
213          .GetTypes(UserInterfaceItemType)
214          .Where(t => t.GetInterface(typeof(IToolBarItem).FullName) != null)) {
215        IToolBarItem item = (IToolBarItem)Activator.CreateInstance(itemType, true);
216        StackPanel visualItem = new StackPanel() { Orientation = Orientation.Vertical };
217        Image image =
218          ImageConverter.Win32Image2WPFImage(item.Image) ??
219          new Image() {
220            Source = new BitmapImage(new Uri(
221              "pack://application:,,,/" +
222              Assembly.GetExecutingAssembly().GetName().Name +
223              ";component/Images/Lambda.png"))
224          };
225        image.Width = ToolBarImageSize.Width;
226        image.Height = ToolBarImageSize.Height;
227        image.Stretch = Stretch.UniformToFill;
228        visualItem.Children.Add(image);
229        visualItem.Children.Add(new TextBlock() { Text = item.Name });
230        Button b = new Button() { Content = visualItem };
231        b.Click += (s, a) => item.Execute();
232        WPFToolBarItem wpfItem = item as WPFToolBarItem;
233        if (wpfItem != null) {
234          b.IsEnabled = wpfItem.IsEnabled;
235          if (wpfItem.IsVisible)
236            b.Visibility = Visibility.Visible;
237          else
238            b.Visibility = Visibility.Collapsed;
239          wpfItem.IsEnabledChanged += (s, a) => b.IsEnabled = wpfItem.IsEnabled;
240          wpfItem.IsVisibleChanged += (s, a) => {
241            if (wpfItem.IsVisible)
242              b.Visibility = Visibility.Visible;
243            else
244              b.Visibility = Visibility.Collapsed;
245          };
246        }
247        ToolBar.Items.Add(b);
248      }
249    }
250
251    #endregion
252
253    #region Background Worker Monitoring
254
255    private void InitializeBackgroundWorkerMonitoring() {
256      var BackgroundWorkerView = new DispatchedView<ObservableBackgroundWorker>(
257        WorkerMonitor.Default, Dispatcher, DispatcherPriority.SystemIdle);
258      BackgroundWorkers.ItemsSource = BackgroundWorkerView;
259      BackgroundWorkerView.CollectionChanged += (sender, args) => {
260        if (args.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add) {
261          if (Cursor != Cursors.AppStarting)
262            Cursor = Cursors.AppStarting;
263        } else {
264          bool active = BackgroundWorkerView.Count() > 0;
265          if (active && Cursor == Cursors.Arrow) {
266            Cursor = Cursors.AppStarting;
267          } else if (!active && Cursor == Cursors.AppStarting) {
268            Cursor = Cursors.Arrow;
269          }
270        }
271      };
272    }
273
274    protected void OperationAborted(object sender, RoutedEventArgs args) {
275      Button button = args.OriginalSource as Button;
276      if (button == null) return;
277      ObservableBackgroundWorker worker = button.Tag as ObservableBackgroundWorker;
278      if (worker == null) return;
279      if (worker.WorkerSupportsCancellation &&
280        MessageBox.Show(this,
281          String.Format("Do you want to cancel the background execution of '{0}'?", worker.Name), Title,
282          MessageBoxButton.YesNo, MessageBoxImage.Question) ==
283          MessageBoxResult.Yes)
284        worker.CancelAsync();
285      args.Handled = true;
286    }
287
288    protected override void OnClosing(CancelEventArgs e) {
289      base.OnClosing(e);
290      if (WorkerMonitor.Default.Count() > 0) {
291        var result = MessageBox.Show(
292          this,
293          "You have tasks running in the background. Do you want to signal and wait for their termination?",
294          Title,
295          MessageBoxButton.YesNoCancel,
296          MessageBoxImage.Warning);
297        if (result == MessageBoxResult.Cancel || result == MessageBoxResult.None)
298          e.Cancel = true;
299        else if (result == MessageBoxResult.Yes)
300          WorkerMonitor.Default.CancelAllAsync();
301        Thread.Sleep(0);
302      }
303    }
304
305    protected override void OnClosed(EventArgs e) {
306      AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
307      Dispatcher.UnhandledException -= Dispatcher_UnhandledException;
308      WorkerMonitor.Default.BackgroundWorkerException += WorkerMonitor_BackgroundWorkerException;
309      base.OnClosed(e);
310    }
311
312    #endregion
313
314    #region Status Messages
315
316    class Message : IStatusMessage {
317
318      public string Text { get; set; }
319      public Action HideAction { get; set; }
320
321      public override string ToString() {
322        return Text;
323      }
324
325      public void Hide() {
326        HideAction();
327      }
328    }
329
330    public interface IStatusMessage {
331      string Text { get; }
332      void Hide();
333    }
334
335    public IStatusMessage ShowMessage(string message, TimeSpan duration) {
336      Message m = new Message() { Text = message };
337      DispatcherTimer timer = new DispatcherTimer() { Interval = duration };
338      Action hideAction = () => {
339        Messages.Items.Remove(m);
340        timer.Stop();
341      };
342      m.HideAction = hideAction;
343      Messages.Items.Add(m);
344      if (duration != TimeSpan.Zero) {
345        timer.Tick += (sender, args) => {
346          hideAction();
347        };
348        timer.Start();
349      }
350      return m;
351    }
352
353    #endregion
354
355    #region View management
356
357    public event EventHandler<ViewEventArgs> ViewClosed;
358    public event EventHandler<ViewShownEventArgs> ViewShown;
359    public event EventHandler<ViewEventArgs> ViewHidden;
360
361    protected void OnViewClosed(IView view) {
362      EventHandler<ViewEventArgs> handler = ViewClosed;
363      if (handler != null)
364        handler(this, new ViewEventArgs(view));
365    }
366
367    protected void OnViewShown(IView view, bool firstTimeShown) {
368      EventHandler<ViewShownEventArgs> handler = ViewShown;
369      if (handler != null)
370        handler(this, new ViewShownEventArgs(view, firstTimeShown));
371    }
372
373    protected void OnViewHidden(IView view) {
374      EventHandler<ViewEventArgs> handler = ViewHidden;
375      if (handler != null)
376        handler(this, new ViewEventArgs(view));
377    }
378
379    public IEnumerable<IView> Views {
380      get {
381        return DockingManager.DockableContents
382          .Select(c => c.Content)
383          .Where(c => c is IView)
384          .Cast<IView>().AsEnumerable();
385      }
386    }
387
388    public IView ActiveView {
389      get {
390        return DockingManager.ActiveContent as IView;
391      }
392    }
393
394    protected void OnActiveDocumentChanged() {
395      EventHandler handler = ActiveViewChanged;
396      if (handler != null)
397        handler(null, EventArgs.Empty);
398    }
399
400    public event EventHandler ActiveViewChanged;
401
402    private void ShowObjectAsView(object o, string name) {
403      if (o as System.Windows.Forms.Control != null) {
404        o = new WindowsFormsHost() { Child = (System.Windows.Forms.Control)o };
405      }
406      var content = new DockableContent() {
407        Content = o,
408        Title = name,
409        HideOnClose = false,
410      };
411      DocumentPane.Items.Add(content);
412      DocumentPane.SelectedItem = content;
413    }
414
415    public DockableContent AddView(IView view) {
416      try {
417        DockableContent content = new DockableContent() {
418          Content = view,
419          Title = view.Caption,
420          HideOnClose = true, // to enable capturing of state change events
421        };
422        content.StateChanged += (s, a) => {
423          if (a == DockableContentState.Hidden) {
424            OnViewClosed(view);
425            content.HideOnClose = false;
426            // HideOnClose moves content to _hiddenContents private member.
427            // To remove it from there, we have to show the content again
428            // to move it back into an accessible container and remove
429            // it from there.
430            Dispatcher.BeginInvoke(new Action(() => {
431              DockingManager.Show(content);
432              content.ContainerPane.Items.Remove(content);
433            }));
434          } else {
435            OnViewShown(view, false);
436          }
437        };
438        DocumentPane.Items.Add(content);
439        DocumentPane.SelectedItem = content;
440        OnViewShown(view, true);
441        return content;
442      } catch (Exception x) {
443        MessageBox.Show(this,
444          String.Format("Could not add view '{0}':\n{1}", view.Caption, x.ToString()),
445          Title,
446          MessageBoxButton.OK, MessageBoxImage.Exclamation);
447        return null;
448      }
449    }
450
451    /// <summary>
452    /// Show existing view or create new one. Return true if this is the first time shown.
453    /// </summary>   
454    public bool ShowView(IView view) {
455      DockableContent content = GetContent(view);
456      if (content == null) {
457        content = AddView(view);
458        return true;
459      } else {
460        DockingManager.Show(content);
461        content.ContainerPane.SelectedItem = content;
462        OnViewShown(view, false);
463        return false;
464      }
465    }
466
467    public void HideView(IView view) {
468      DockableContent content = GetContent(view);
469      if (content != null)
470        DockingManager.Hide(content);
471      OnViewHidden(view);
472    }
473
474    public void CloseView(IView view) {
475      DockableContent content = GetContent(view);
476      content.ContainerPane.Items.Remove(content);
477      OnViewClosed(view);
478    }
479
480    public void CloseAllViews() {
481      foreach (var viewContainer in DockingManager.DockableContents.Where(c => c.Content is IView)) {
482        viewContainer.ContainerPane.Items.Remove(viewContainer);
483        OnViewClosed(viewContainer.Content as IView);
484      }
485    }
486
487    private DockableContent GetContent(IView view) {
488      DockableContent content =
489        DockingManager
490        .DockableContents
491        .SingleOrDefault(c => c.Content as IView == view);
492      return content;
493    }
494
495    #endregion
496
497    #region IMainForm
498
499    public event EventHandler Changed;
500
501    protected void OnChanged() {
502      EventHandler handler = Changed;
503      if (handler != null)
504        handler(this, EventArgs.Empty);
505    }
506
507    #endregion
508  }
509}
Note: See TracBrowser for help on using the repository browser.