Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Access.Administration/3.3/Views/UserGroupView.cs	(revision 15422)
@@ -40,10 +40,10 @@
     protected override void RegisterContentEvents() {
       base.RegisterContentEvents();
-      Access.AccessClient.Instance.Refreshing += new EventHandler(Content_Refreshing);
+      AccessClient.Instance.Refreshing += new EventHandler(Content_Refreshing);
       refreshableLightweightUserView.StorableStateChanged += new EventHandler(refreshableLightweightUserView_StorableStateChanged);
     }
 
     protected override void DeregisterContentEvents() {
-      Access.AccessClient.Instance.Refreshing -= new EventHandler(Content_Refreshing);
+      AccessClient.Instance.Refreshing -= new EventHandler(Content_Refreshing);
       refreshableLightweightUserView.StorableStateChanged -= new EventHandler(refreshableLightweightUserView_StorableStateChanged);
       base.DeregisterContentEvents();
@@ -63,12 +63,12 @@
         idTextBox.Text = Content.Id.ToString();
 
-        refreshableLightweightUserView.Content = Content.Id != Guid.Empty ? Access.AccessClient.Instance : null;
-        refreshableLightweightUserView.FetchSelectedUsers = Content.Id != Guid.Empty ? new Func<List<Guid>>(delegate { return AccessAdministrationClient.CallAccessService<List<Guid>>(s => s.GetUserGroupIdsOfGroup(Content.Id)); }) : null;
+        refreshableLightweightUserView.Content = Content.Id != Guid.Empty ? AccessClient.Instance : null;
+        refreshableLightweightUserView.FetchSelectedUsers = Content.Id != Guid.Empty ? new Func<List<Guid>>(delegate { return AccessAdministrationClient.CallAccessService(s => s.GetUserGroupIdsOfGroup(Content.Id)); }) : null;
       }
     }
 
-    private void groupNameTextBox_TextChanged(object sender, System.EventArgs e) {
-      if (Content.Name != this.groupNameTextBox.Text)
-        Content.Name = this.groupNameTextBox.Text;
+    private void groupNameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != groupNameTextBox.Text)
+        Content.Name = groupNameTextBox.Text;
     }
 
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Access/3.3/ServiceClients/User.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Access/3.3/ServiceClients/User.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Access/3.3/ServiceClients/User.cs	(revision 15422)
@@ -51,5 +51,5 @@
 
     public override string ToString() {
-      return UserName + "(" + FullName + ")";
+      return UserName + " (" + FullName + ")";
     }
   }
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/HeuristicLab.Clients.Hive.Administrator-3.3.csproj	(revision 15422)
@@ -132,4 +132,16 @@
     </Compile>
     <Compile Include="MenuItems\AdministratorMenuItem.cs" />
+    <Compile Include="Views\ProjectPermissionsView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectPermissionsView.Designer.cs">
+      <DependentUpon>ProjectPermissionsView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ProjectResourcesView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ProjectResourcesView.Designer.cs">
+      <DependentUpon>ProjectResourcesView.cs</DependentUpon>
+    </Compile>
     <Compile Include="Views\ProjectsView.cs">
       <SubType>UserControl</SubType>
@@ -162,4 +174,10 @@
     <Compile Include="Views\ScheduleView.Designer.cs">
       <DependentUpon>ScheduleView.cs</DependentUpon>
+    </Compile>
+    <Compile Include="Views\ResourceView.cs">
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="Views\ResourceView.Designer.cs">
+      <DependentUpon>ResourceView.cs</DependentUpon>
     </Compile>
     <Compile Include="Views\SlaveView.cs">
@@ -178,4 +196,8 @@
   </ItemGroup>
   <ItemGroup>
+    <ProjectReference Include="..\..\HeuristicLab.Clients.Access.Administration\3.3\HeuristicLab.Clients.Access.Administration-3.3.csproj">
+      <Project>{C9CF6AE8-4637-4A75-9141-042F479B5D50}</Project>
+      <Name>HeuristicLab.Clients.Access.Administration-3.3</Name>
+    </ProjectReference>
     <ProjectReference Include="..\..\HeuristicLab.Clients.Access.Views\3.3\HeuristicLab.Clients.Access.Views-3.3.csproj">
       <Project>{FE856595-64CD-46DA-9CD2-FFF3E6B0D4F2}</Project>
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs	(revision 15422)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.Designer.cs	(revision 15422)
@@ -0,0 +1,190 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectPermissionsView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.inheritButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.permissionsGroupBox = new System.Windows.Forms.GroupBox();
+      this.treeView = new System.Windows.Forms.TreeView();
+      this.detailsGroupBox = new System.Windows.Forms.GroupBox();
+      this.detailsViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.permissionsGroupBox.SuspendLayout();
+      this.detailsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 0;
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // inheritButton
+      // 
+      this.inheritButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.OrgChart;
+      this.inheritButton.Location = new System.Drawing.Point(33, 3);
+      this.inheritButton.Name = "inheritButton";
+      this.inheritButton.Size = new System.Drawing.Size(24, 24);
+      this.inheritButton.TabIndex = 3;
+      this.inheritButton.UseVisualStyleBackColor = true;
+      this.inheritButton.Click += new System.EventHandler(this.inheritButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveButton.Location = new System.Drawing.Point(63, 3);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(24, 24);
+      this.saveButton.TabIndex = 4;
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 33);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.permissionsGroupBox);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.detailsGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(824, 458);
+      this.splitContainer.SplitterDistance = 274;
+      this.splitContainer.TabIndex = 6;
+      // 
+      // permissionsGroupBox
+      // 
+      this.permissionsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.permissionsGroupBox.Controls.Add(this.treeView);
+      this.permissionsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.permissionsGroupBox.Name = "permissionsGroupBox";
+      this.permissionsGroupBox.Size = new System.Drawing.Size(268, 452);
+      this.permissionsGroupBox.TabIndex = 0;
+      this.permissionsGroupBox.TabStop = false;
+      this.permissionsGroupBox.Text = "Permissions (Assigned + Inherited)";
+      // 
+      // treeView
+      // 
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageList;
+      this.treeView.Location = new System.Drawing.Point(6, 19);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(256, 427);
+      this.treeView.TabIndex = 0;
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterCheck);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
+      // 
+      // detailsGroupBox
+      // 
+      this.detailsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsGroupBox.Controls.Add(this.detailsViewHost);
+      this.detailsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.detailsGroupBox.Name = "detailsGroupBox";
+      this.detailsGroupBox.Size = new System.Drawing.Size(540, 452);
+      this.detailsGroupBox.TabIndex = 0;
+      this.detailsGroupBox.TabStop = false;
+      this.detailsGroupBox.Text = "Details";
+      // 
+      // detailsViewHost
+      // 
+      this.detailsViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsViewHost.Caption = "View";
+      this.detailsViewHost.Content = null;
+      this.detailsViewHost.Enabled = false;
+      this.detailsViewHost.Location = new System.Drawing.Point(6, 19);
+      this.detailsViewHost.Name = "detailsViewHost";
+      this.detailsViewHost.ReadOnly = true;
+      this.detailsViewHost.Size = new System.Drawing.Size(528, 427);
+      this.detailsViewHost.TabIndex = 0;
+      this.detailsViewHost.ViewsLabelVisible = true;
+      this.detailsViewHost.ViewType = null;
+      // 
+      // ProjectPermissionsView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.saveButton);
+      this.Controls.Add(this.inheritButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProjectPermissionsView";
+      this.Size = new System.Drawing.Size(830, 494);
+      this.Load += new System.EventHandler(this.ProjectPermissionsView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.permissionsGroupBox.ResumeLayout(false);
+      this.detailsGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button inheritButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.ImageList imageList;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox permissionsGroupBox;
+    private System.Windows.Forms.TreeView treeView;
+    private System.Windows.Forms.GroupBox detailsGroupBox;
+    private MainForm.WindowsForms.ViewHost detailsViewHost;
+  }
+}
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs	(revision 15422)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectPermissionsView.cs	(revision 15422)
@@ -0,0 +1,205 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectPermissionsView : ItemView {
+    private const int userImageIndex = 0;
+    private const int userGroupImageIndex = 1;
+
+    private readonly HashSet<UserGroupBase> assignedPermissions = new HashSet<UserGroupBase>();
+    private readonly HashSet<UserGroupBase> inheritedPermissions = new HashSet<UserGroupBase>();
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectPermissionsView() {
+      InitializeComponent();
+
+      treeView.ImageList.Images.Add(VSImageLibrary.User);
+      treeView.ImageList.Images.Add(VSImageLibrary.UserAccounts);
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        assignedPermissions.Clear();
+        inheritedPermissions.Clear();
+        treeView.Nodes.Clear();
+        detailsViewHost.Content = null;
+      } else {
+        UpdateAssignedPermissions();
+        UpdateInheritedPermissions();
+        var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+        detailsViewHost.Content = top;
+      }
+    }
+    #endregion
+
+    #region Event Handlers
+    private void ProjectPermissionsView_Load(object sender, EventArgs e) {
+
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      UpdateAssignedPermissions();
+      UpdateInheritedPermissions();
+      var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+      detailsViewHost.Content = top;
+    }
+
+    private void inheritButton_Click(object sender, EventArgs e) {
+      SetGrantedProjectPermissions(Content.Id, Enumerable.Empty<Guid>());
+      UpdateAssignedPermissions();
+      UpdateInheritedPermissions();
+      var top = BuildPermissionsList(AccessClient.Instance.UsersAndGroups);
+      detailsViewHost.Content = top;
+    }
+
+    private async void saveButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => SetGrantedProjectPermissions(Content.Id, assignedPermissions.Select(x => x.Id)));
+    }
+
+    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var selectedPermission = (UserGroupBase)e.Node.Tag;
+      detailsViewHost.Content = selectedPermission;
+    }
+
+    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      var checkedPermission = (UserGroupBase)e.Node.Tag;
+      if (e.Node.Parent == null || inheritedPermissions.Contains(checkedPermission) || checkedPermission.Id == Guid.Empty) e.Cancel = true;
+    }
+
+    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedPermission = (UserGroupBase)e.Node.Tag;
+      if (e.Node.Checked)
+        assignedPermissions.Add(checkedPermission);
+      else
+        assignedPermissions.Remove(checkedPermission);
+    }
+    #endregion
+
+    #region Helpers
+    private static IEnumerable<UserGroupBase> GetGrantedPermissionsForProject(Guid projectId) {
+      var projectPermissions = HiveServiceLocator.Instance.CallHiveService(s => s.GetProjectPermissions(projectId));
+      var userIds = new HashSet<Guid>(projectPermissions.Select(x => x.GrantedUserId));
+      return AccessClient.Instance.UsersAndGroups.Where(x => userIds.Contains(x.Id));
+    }
+
+    private static void SetGrantedProjectPermissions(Guid projectId, IEnumerable<Guid> userIds) {
+      HiveServiceLocator.Instance.CallHiveService(s => {
+        var currentlyAssignedPermissions = s.GetProjectPermissions(projectId);
+        s.RevokeProjectPermissions(projectId, currentlyAssignedPermissions.Select(x => x.GrantedUserId).ToList());
+        s.GrantProjectPermissions(projectId, userIds.ToList());
+      });
+    }
+
+    private void UpdateAssignedPermissions() {
+      assignedPermissions.Clear();
+      foreach (var r in GetGrantedPermissionsForProject(Content.Id))
+        assignedPermissions.Add(r);
+    }
+
+    private void UpdateInheritedPermissions() {
+      inheritedPermissions.Clear();
+      var parentProject = HiveAdminClient.Instance.Projects.SingleOrDefault(x => x.Id == Content.ParentProjectId);
+      while (parentProject != null) {
+        foreach (var r in GetGrantedPermissionsForProject(parentProject.Id))
+          inheritedPermissions.Add(r);
+        parentProject = HiveAdminClient.Instance.Projects.SingleOrDefault(x => x.Id == parentProject.ParentProjectId);
+      }
+    }
+
+    private UserGroupBase BuildPermissionsList(IEnumerable<UserGroupBase> usersAndGroups) {
+      treeView.Nodes.Clear();
+      if (!usersAndGroups.Any()) return null;
+
+      treeView.BeforeCheck -= treeView_BeforeCheck;
+      treeView.AfterCheck -= treeView_AfterCheck;
+
+      var userGroups = new HashSet<UserGroup>(usersAndGroups.OfType<UserGroup>());
+      var users = new HashSet<LightweightUser>(usersAndGroups.OfType<LightweightUser>());
+      UserGroupBase first = null;
+
+      var groupsNode = new TreeNode("Groups") { ForeColor = SystemColors.GrayText };
+      groupsNode.ImageIndex = groupsNode.SelectedImageIndex = userGroupImageIndex;
+
+      foreach (var group in userGroups.OrderBy(x => x.Name)) {
+        var node = new TreeNode(group.Name) { Tag = group };
+        node.ImageIndex = userGroupImageIndex;
+        node.SelectedImageIndex = node.ImageIndex;
+
+        if (inheritedPermissions.Contains(group)) {
+          node.Checked = true;
+          node.Text += " (Inherited)";
+          node.ForeColor = SystemColors.GrayText;
+        } else if (assignedPermissions.Contains(group))
+          node.Checked = true;
+
+        groupsNode.Nodes.Add(node);
+        if (first == null) first = group;
+      }
+
+      var usersNode = new TreeNode("Users") { ForeColor = SystemColors.GrayText };
+      usersNode.ImageIndex = usersNode.SelectedImageIndex = userImageIndex;
+
+      foreach (var user in users.OrderBy(x => x.ToString())) {
+        var node = new TreeNode(user.ToString()) { Tag = user };
+        node.ImageIndex = userImageIndex;
+        node.SelectedImageIndex = node.ImageIndex;
+
+        if (inheritedPermissions.Contains(user)) {
+          node.Checked = true;
+          node.Text += " (Inherited)";
+          node.ForeColor = SystemColors.GrayText;
+        } else if (assignedPermissions.Contains(user))
+          node.Checked = true;
+
+        usersNode.Nodes.Add(node);
+        if (first == null) first = user;
+      }
+
+      treeView.Nodes.Add(groupsNode);
+      treeView.Nodes.Add(usersNode);
+      treeView.BeforeCheck += treeView_BeforeCheck;
+      treeView.AfterCheck += treeView_AfterCheck;
+      treeView.ExpandAll();
+
+      return first;
+    }
+    #endregion
+  }
+}
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs	(revision 15422)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.Designer.cs	(revision 15422)
@@ -0,0 +1,191 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ProjectResourcesView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.components = new System.ComponentModel.Container();
+      this.refreshButton = new System.Windows.Forms.Button();
+      this.inheritButton = new System.Windows.Forms.Button();
+      this.saveButton = new System.Windows.Forms.Button();
+      this.splitContainer = new System.Windows.Forms.SplitContainer();
+      this.resourcesGroupBox = new System.Windows.Forms.GroupBox();
+      this.treeView = new System.Windows.Forms.TreeView();
+      this.imageList = new System.Windows.Forms.ImageList(this.components);
+      this.detailsGroupBox = new System.Windows.Forms.GroupBox();
+      this.detailsViewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+      this.splitContainer.Panel1.SuspendLayout();
+      this.splitContainer.Panel2.SuspendLayout();
+      this.splitContainer.SuspendLayout();
+      this.resourcesGroupBox.SuspendLayout();
+      this.detailsGroupBox.SuspendLayout();
+      this.SuspendLayout();
+      // 
+      // refreshButton
+      // 
+      this.refreshButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Refresh;
+      this.refreshButton.Location = new System.Drawing.Point(3, 3);
+      this.refreshButton.Name = "refreshButton";
+      this.refreshButton.Size = new System.Drawing.Size(24, 24);
+      this.refreshButton.TabIndex = 2;
+      this.refreshButton.UseVisualStyleBackColor = true;
+      this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
+      // 
+      // inheritButton
+      // 
+      this.inheritButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.OrgChart;
+      this.inheritButton.Location = new System.Drawing.Point(33, 3);
+      this.inheritButton.Name = "inheritButton";
+      this.inheritButton.Size = new System.Drawing.Size(24, 24);
+      this.inheritButton.TabIndex = 3;
+      this.inheritButton.UseVisualStyleBackColor = true;
+      this.inheritButton.Click += new System.EventHandler(this.inheritButton_Click);
+      // 
+      // saveButton
+      // 
+      this.saveButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
+      this.saveButton.Location = new System.Drawing.Point(63, 3);
+      this.saveButton.Name = "saveButton";
+      this.saveButton.Size = new System.Drawing.Size(24, 24);
+      this.saveButton.TabIndex = 4;
+      this.saveButton.UseVisualStyleBackColor = true;
+      this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+      // 
+      // splitContainer
+      // 
+      this.splitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.splitContainer.Location = new System.Drawing.Point(3, 33);
+      this.splitContainer.Name = "splitContainer";
+      // 
+      // splitContainer.Panel1
+      // 
+      this.splitContainer.Panel1.Controls.Add(this.resourcesGroupBox);
+      // 
+      // splitContainer.Panel2
+      // 
+      this.splitContainer.Panel2.Controls.Add(this.detailsGroupBox);
+      this.splitContainer.Size = new System.Drawing.Size(824, 458);
+      this.splitContainer.SplitterDistance = 274;
+      this.splitContainer.TabIndex = 5;
+      // 
+      // resourcesGroupBox
+      // 
+      this.resourcesGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.resourcesGroupBox.Controls.Add(this.treeView);
+      this.resourcesGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.resourcesGroupBox.Name = "resourcesGroupBox";
+      this.resourcesGroupBox.Size = new System.Drawing.Size(268, 452);
+      this.resourcesGroupBox.TabIndex = 0;
+      this.resourcesGroupBox.TabStop = false;
+      this.resourcesGroupBox.Text = "Resources (Assigned + Inherited)";
+      // 
+      // treeView
+      // 
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.CheckBoxes = true;
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageList;
+      this.treeView.Location = new System.Drawing.Point(6, 19);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(256, 427);
+      this.treeView.TabIndex = 0;
+      this.treeView.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.treeView_BeforeCheck);
+      this.treeView.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterCheck);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
+      // 
+      // imageList
+      // 
+      this.imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
+      this.imageList.ImageSize = new System.Drawing.Size(16, 16);
+      this.imageList.TransparentColor = System.Drawing.Color.Transparent;
+      // 
+      // detailsGroupBox
+      // 
+      this.detailsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsGroupBox.Controls.Add(this.detailsViewHost);
+      this.detailsGroupBox.Location = new System.Drawing.Point(3, 3);
+      this.detailsGroupBox.Name = "detailsGroupBox";
+      this.detailsGroupBox.Size = new System.Drawing.Size(540, 452);
+      this.detailsGroupBox.TabIndex = 0;
+      this.detailsGroupBox.TabStop = false;
+      this.detailsGroupBox.Text = "Details";
+      // 
+      // detailsViewHost
+      // 
+      this.detailsViewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.detailsViewHost.Caption = "View";
+      this.detailsViewHost.Content = null;
+      this.detailsViewHost.Enabled = false;
+      this.detailsViewHost.Location = new System.Drawing.Point(6, 19);
+      this.detailsViewHost.Name = "detailsViewHost";
+      this.detailsViewHost.ReadOnly = true;
+      this.detailsViewHost.Size = new System.Drawing.Size(528, 427);
+      this.detailsViewHost.TabIndex = 0;
+      this.detailsViewHost.ViewsLabelVisible = true;
+      this.detailsViewHost.ViewType = null;
+      // 
+      // ProjectResourcesView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.splitContainer);
+      this.Controls.Add(this.saveButton);
+      this.Controls.Add(this.inheritButton);
+      this.Controls.Add(this.refreshButton);
+      this.Name = "ProjectResourcesView";
+      this.Size = new System.Drawing.Size(830, 494);
+      this.Load += new System.EventHandler(this.ProjectResourcesView_Load);
+      this.splitContainer.Panel1.ResumeLayout(false);
+      this.splitContainer.Panel2.ResumeLayout(false);
+      ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+      this.splitContainer.ResumeLayout(false);
+      this.resourcesGroupBox.ResumeLayout(false);
+      this.detailsGroupBox.ResumeLayout(false);
+      this.ResumeLayout(false);
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Button refreshButton;
+    private System.Windows.Forms.Button inheritButton;
+    private System.Windows.Forms.Button saveButton;
+    private System.Windows.Forms.SplitContainer splitContainer;
+    private System.Windows.Forms.GroupBox resourcesGroupBox;
+    private System.Windows.Forms.GroupBox detailsGroupBox;
+    private System.Windows.Forms.TreeView treeView;
+    private System.Windows.Forms.ImageList imageList;
+    private MainForm.WindowsForms.ViewHost detailsViewHost;
+  }
+}
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs	(revision 15422)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectResourcesView.cs	(revision 15422)
@@ -0,0 +1,247 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Common.Resources;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("ProjectView")]
+  [Content(typeof(Project), IsDefaultView = false)]
+  public partial class ProjectResourcesView : ItemView {
+    private const int slaveImageIndex = 0;
+    private const int slaveGroupImageIndex = 1;
+    public const string ungroupedGroupName = "UNGROUPED";
+    public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group.";
+
+    private readonly HashSet<Resource> assignedResources = new HashSet<Resource>();
+    private readonly HashSet<Resource> inheritedResources = new HashSet<Resource>();
+    private readonly Color ownedResourceColor = Color.LightGreen;
+
+    public new Project Content {
+      get { return (Project)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ProjectResourcesView() {
+      InitializeComponent();
+
+      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
+      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        assignedResources.Clear();
+        inheritedResources.Clear();
+        treeView.Nodes.Clear();
+        detailsViewHost.Content = null;
+      } else {
+        UpdateAssignedResources();
+        UpdateInheritedResources();
+        var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
+        detailsViewHost.Content = top;
+      }
+    }
+    #endregion
+
+    #region Event Handlers
+    private void ProjectResourcesView_Load(object sender, EventArgs e) {
+
+    }
+
+    private void refreshButton_Click(object sender, EventArgs e) {
+      UpdateAssignedResources();
+      UpdateInheritedResources();
+      var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
+      detailsViewHost.Content = top;
+    }
+
+    private void inheritButton_Click(object sender, EventArgs e) {
+      SetAssignedProjectResources(Content.Id, Enumerable.Empty<Guid>());
+      UpdateAssignedResources();
+      UpdateInheritedResources();
+      var top = BuildResourceTree(HiveAdminClient.Instance.Resources);
+      detailsViewHost.Content = top;
+    }
+
+    private async void saveButton_Click(object sender, EventArgs e) {
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => SetAssignedProjectResources(Content.Id, assignedResources.Select(x => x.Id)));
+    }
+
+    private void treeView_AfterSelect(object sender, TreeViewEventArgs e) {
+      var selectedResource = (Resource)e.Node.Tag;
+      detailsViewHost.Content = selectedResource;
+    }
+
+    private void treeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) {
+      var checkedResource = (Resource)e.Node.Tag;
+      if (inheritedResources.Contains(checkedResource) || checkedResource.Id == Guid.Empty) e.Cancel = true;
+    }
+
+    private void treeView_AfterCheck(object sender, TreeViewEventArgs e) {
+      var checkedResource = (Resource)e.Node.Tag;
+      if (e.Node.Checked)
+        assignedResources.Add(checkedResource);
+      else
+        assignedResources.Remove(checkedResource);
+    }
+    #endregion
+
+    #region Helpers
+    private static IEnumerable<Resource> GetAssignedResourcesForProject(Guid projectId) {
+      var assignedProjectResources = HiveServiceLocator.Instance.CallHiveService(s => s.GetAssignedResourcesForProject(projectId));
+      var resourceIds = new HashSet<Guid>(assignedProjectResources.Select(x => x.ResourceId));
+      return HiveAdminClient.Instance.Resources.Where(x => resourceIds.Contains(x.Id));
+    }
+
+    private static void SetAssignedProjectResources(Guid projectId, IEnumerable<Guid> resourceIds) {
+      HiveServiceLocator.Instance.CallHiveService(s => {
+        var currentlyAssignedResources = s.GetAssignedResourcesForProject(projectId);
+        s.UnassignProjectResources(projectId, currentlyAssignedResources.Select(x => x.ResourceId).ToList());
+        s.AssignProjectResources(projectId, resourceIds.ToList());
+      });
+    }
+
+    private void UpdateAssignedResources() {
+      assignedResources.Clear();
+      foreach (var r in GetAssignedResourcesForProject(Content.Id))
+        assignedResources.Add(r);
+    }
+
+    private void UpdateInheritedResources() {
+      inheritedResources.Clear();
+      var parentProject = HiveAdminClient.Instance.Projects.SingleOrDefault(x => x.Id == Content.ParentProjectId);
+      while (parentProject != null) {
+        foreach (var r in GetAssignedResourcesForProject(parentProject.Id))
+          inheritedResources.Add(r);
+        parentProject = HiveAdminClient.Instance.Projects.SingleOrDefault(x => x.Id == parentProject.ParentProjectId);
+      }
+    }
+
+    private Resource BuildResourceTree(IEnumerable<Resource> resources) {
+      treeView.Nodes.Clear();
+      if (!resources.Any()) return null;
+
+      treeView.BeforeCheck -= treeView_BeforeCheck;
+      treeView.AfterCheck -= treeView_AfterCheck;
+
+      var slaveGroups = new HashSet<SlaveGroup>(resources.OfType<SlaveGroup>());
+      var slaves = new HashSet<Slave>(resources.OfType<Slave>());
+
+      var stack = new Stack<Resource>(slaveGroups.OrderByDescending(x => x.Name));
+      var top = stack.Peek();
+
+      TreeNode currentNode = null;
+      Resource currentResource = null;
+
+      while (stack.Any()) {
+        var newResource = stack.Pop();
+        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
+
+        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
+          currentNode = currentNode.Parent;
+          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          treeView.Nodes.Add(newNode);
+        } else {
+          currentNode.Nodes.Add(newNode);
+        }
+
+        if (newResource is Slave) {
+          newNode.ImageIndex = slaveImageIndex;
+        } else {
+          newNode.ImageIndex = slaveGroupImageIndex;
+
+          var childSlaves = slaves.Where(x => x.ParentResourceId == newResource.Id);
+          foreach (var slave in childSlaves.OrderBy(x => x.Name)) {
+            slaves.Remove(slave);
+            var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+
+            slaveNode.SelectedImageIndex = slaveNode.ImageIndex = slaveImageIndex;
+            if (slave.OwnerUserId == UserInformation.Instance.User.Id)
+              slaveNode.BackColor = ownedResourceColor;
+            if (inheritedResources.Contains(slave)) {
+              slaveNode.Checked = true;
+              slaveNode.Text += " (Inherited)";
+              slaveNode.ForeColor = SystemColors.GrayText;
+            } else if (assignedResources.Contains(slave))
+              slaveNode.Checked = true;
+
+            newNode.Nodes.Add(slaveNode);
+          }
+
+          var childSlaveGroups = slaveGroups.Where(x => x.ParentResourceId == newResource.Id);
+          if (childSlaveGroups.Any()) {
+            foreach (var slaveGroup in childSlaveGroups.OrderByDescending(x => x.Name)) {
+              slaveGroups.Remove(slaveGroup);
+              stack.Push(slaveGroup);
+            }
+            currentNode = newNode;
+            currentResource = newResource;
+          }
+        }
+
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+        if (newResource.OwnerUserId == UserInformation.Instance.User.Id)
+          newNode.BackColor = ownedResourceColor;
+        if (inheritedResources.Contains(newResource)) {
+          newNode.Checked = true;
+          newNode.Text += " (Inherited)";
+          newNode.ForeColor = SystemColors.GrayText;
+        } else if (assignedResources.Contains(newResource))
+          newNode.Checked = true;
+      }
+
+      var ungroupedNode = new TreeNode(ungroupedGroupName) {
+        ForeColor = SystemColors.GrayText,
+        Tag = new SlaveGroup() {
+          Name = ungroupedGroupName,
+          Description = ungroupedGroupDescription
+        }
+      };
+
+      foreach (var slave in slaves.OrderBy(x => x.Name)) {
+        var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+        ungroupedNode.Nodes.Add(slaveNode);
+      }
+
+      treeView.Nodes.Add(ungroupedNode);
+      treeView.BeforeCheck += treeView_BeforeCheck;
+      treeView.AfterCheck += treeView_AfterCheck;
+      treeView.ExpandAll();
+
+      return top;
+    }
+    #endregion
+  }
+}
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.Designer.cs	(revision 15422)
@@ -57,4 +57,5 @@
       this.nameTextBox.Size = new System.Drawing.Size(464, 20);
       this.nameTextBox.TabIndex = 1;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
       this.nameTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.nameTextBox_Validating);
       // 
@@ -77,5 +78,5 @@
       this.descriptionTextBox.Size = new System.Drawing.Size(464, 98);
       this.descriptionTextBox.TabIndex = 3;
-      this.descriptionTextBox.Validating += new System.ComponentModel.CancelEventHandler(this.descriptionTextBox_Validating);
+      this.descriptionTextBox.TextChanged += new System.EventHandler(this.descriptionTextBox_TextChanged);
       // 
       // ownerComboBox
@@ -118,5 +119,5 @@
       this.startDateTimePicker.Size = new System.Drawing.Size(200, 20);
       this.startDateTimePicker.TabIndex = 9;
-      this.startDateTimePicker.Validating += new System.ComponentModel.CancelEventHandler(this.startDateTimePicker_Validating);
+      this.startDateTimePicker.ValueChanged += new System.EventHandler(this.startDateTimePicker_ValueChanged);
       // 
       // startLabel
@@ -138,5 +139,5 @@
       this.endDateTimePicker.Size = new System.Drawing.Size(200, 20);
       this.endDateTimePicker.TabIndex = 11;
-      this.endDateTimePicker.Validating += new System.ComponentModel.CancelEventHandler(this.endDateTimePicker_Validating);
+      this.endDateTimePicker.ValueChanged += new System.EventHandler(this.endDateTimePicker_ValueChanged);
       // 
       // endLabel
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectView.cs	(revision 15422)
@@ -34,5 +34,4 @@
   public partial class ProjectView : ItemView {
     private readonly object locker = new object();
-    private bool updatingUsers = false;
 
     public new Project Content {
@@ -91,5 +90,5 @@
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-      bool enabled = Content != null;
+      bool enabled = Content != null && !ReadOnly;
       nameTextBox.Enabled = enabled;
       descriptionTextBox.Enabled = enabled;
@@ -138,6 +137,6 @@
     private async void refreshButton_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (updatingUsers) return;
-        updatingUsers = true;
+        if (!refreshButton.Enabled) return;
+        refreshButton.Enabled = false;
       }
 
@@ -149,5 +148,5 @@
           ownerComboBox.SelectedItem = AccessClient.Instance.UsersAndGroups.FirstOrDefault(x => x.Id == Content.OwnerUserId);
           ownerComboBox.SelectedIndexChanged += ownerComboBox_SelectedIndexChanged;
-          updatingUsers = false;
+          refreshButton.Enabled = true;
         });
     }
@@ -161,30 +160,45 @@
           MessageBoxIcon.Error);
         e.Cancel = true;
-      } else Content.Name = nameTextBox.Text;
-    }
-
-    private void descriptionTextBox_Validating(object sender, CancelEventArgs e) {
-      Content.Description = descriptionTextBox.Text;
+      }
+    }
+
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != nameTextBox.Text)
+        Content.Name = nameTextBox.Text;
+    }
+
+    private void descriptionTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Description != descriptionTextBox.Text)
+        Content.Description = descriptionTextBox.Text;
     }
 
     private void ownerComboBox_SelectedIndexChanged(object sender, EventArgs e) {
       var selectedItem = (LightweightUser)ownerComboBox.SelectedItem;
-      Content.OwnerUserId = selectedItem != null ? selectedItem.Id : Guid.Empty;
-    }
-
-    private void startDateTimePicker_Validating(object sender, CancelEventArgs e) {
+      var selectedOwnerUserId = selectedItem != null ? selectedItem.Id : Guid.Empty;
+      if (Content != null && Content.OwnerUserId != selectedOwnerUserId)
+        Content.OwnerUserId = selectedOwnerUserId;
+    }
+
+    private void startDateTimePicker_ValueChanged(object sender, EventArgs e) {
+      if (Content == null) return;
       if (!Content.EndDate.HasValue || startDateTimePicker.Value > Content.EndDate)
         endDateTimePicker.Value = startDateTimePicker.Value;
-      Content.StartDate = startDateTimePicker.Value;
-    }
-
-    private void endDateTimePicker_Validating(object sender, CancelEventArgs e) {
+      if (Content.StartDate != startDateTimePicker.Value)
+        Content.StartDate = startDateTimePicker.Value;
+    }
+
+    private void endDateTimePicker_ValueChanged(object sender, EventArgs e) {
+      if (Content == null) return;
       if (endDateTimePicker.Value < startDateTimePicker.Value)
         endDateTimePicker.Value = startDateTimePicker.Value;
-      Content.EndDate = endDateTimePicker.Value;
+      if (Content.EndDate != endDateTimePicker.Value)
+        Content.EndDate = endDateTimePicker.Value;
     }
 
     private void indefiniteCheckBox_CheckedChanged(object sender, EventArgs e) {
-      Content.EndDate = indefiniteCheckBox.Checked ? (DateTime?)null : Content.StartDate;
+      if (Content == null) return;
+      var newEndDate = indefiniteCheckBox.Checked ? (DateTime?)null : Content.StartDate;
+      if (Content.EndDate != newEndDate)
+        Content.EndDate = newEndDate;
     }
     #endregion
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.Designer.cs	(revision 15422)
@@ -36,6 +36,7 @@
       this.projectView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectView();
       this.permissionsTabPage = new System.Windows.Forms.TabPage();
-      this.savePermissionsButton = new System.Windows.Forms.Button();
-      this.permissionsView = new HeuristicLab.Clients.Access.Views.RefreshableLightweightUserView();
+      this.projectPermissionsView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectPermissionsView();
+      this.resourcesTabPage = new System.Windows.Forms.TabPage();
+      this.projectResourcesView = new HeuristicLab.Clients.Hive.Administrator.Views.ProjectResourcesView();
       ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
       this.splitContainer.Panel1.SuspendLayout();
@@ -45,4 +46,5 @@
       this.detailsTabPage.SuspendLayout();
       this.permissionsTabPage.SuspendLayout();
+      this.resourcesTabPage.SuspendLayout();
       this.SuspendLayout();
       // 
@@ -82,5 +84,5 @@
       // saveProjectButton
       // 
-      this.saveProjectButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.saveProjectButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.saveProjectButton.Location = new System.Drawing.Point(93, 3);
       this.saveProjectButton.Name = "saveProjectButton";
@@ -143,4 +145,5 @@
       this.tabControl.Controls.Add(this.detailsTabPage);
       this.tabControl.Controls.Add(this.permissionsTabPage);
+      this.tabControl.Controls.Add(this.resourcesTabPage);
       this.tabControl.Location = new System.Drawing.Point(3, 3);
       this.tabControl.Name = "tabControl";
@@ -148,5 +151,4 @@
       this.tabControl.Size = new System.Drawing.Size(585, 541);
       this.tabControl.TabIndex = 0;
-      this.tabControl.SelectedIndexChanged += new System.EventHandler(this.tabControl_SelectedIndexChanged);
       // 
       // detailsTabPage
@@ -176,6 +178,5 @@
       // permissionsTabPage
       // 
-      this.permissionsTabPage.Controls.Add(this.savePermissionsButton);
-      this.permissionsTabPage.Controls.Add(this.permissionsView);
+      this.permissionsTabPage.Controls.Add(this.projectPermissionsView);
       this.permissionsTabPage.Location = new System.Drawing.Point(4, 22);
       this.permissionsTabPage.Name = "permissionsTabPage";
@@ -186,28 +187,36 @@
       this.permissionsTabPage.UseVisualStyleBackColor = true;
       // 
-      // savePermissionsButton
-      // 
-      this.savePermissionsButton.Enabled = false;
-      this.savePermissionsButton.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
-      this.savePermissionsButton.Location = new System.Drawing.Point(39, 9);
-      this.savePermissionsButton.Name = "savePermissionsButton";
-      this.savePermissionsButton.Size = new System.Drawing.Size(24, 24);
-      this.savePermissionsButton.TabIndex = 2;
-      this.savePermissionsButton.UseVisualStyleBackColor = true;
-      this.savePermissionsButton.Click += new System.EventHandler(this.savePermissionsButton_Click);
-      // 
-      // permissionsView
-      // 
-      this.permissionsView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.permissionsView.Caption = "RefreshableLightweightUser View";
-      this.permissionsView.Content = null;
-      this.permissionsView.FetchSelectedUsers = null;
-      this.permissionsView.Location = new System.Drawing.Point(6, 6);
-      this.permissionsView.Name = "permissionsView";
-      this.permissionsView.ReadOnly = false;
-      this.permissionsView.Size = new System.Drawing.Size(565, 503);
-      this.permissionsView.TabIndex = 0;
+      // projectPermissionsView
+      // 
+      this.projectPermissionsView.Caption = "ProjectView";
+      this.projectPermissionsView.Content = null;
+      this.projectPermissionsView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectPermissionsView.Location = new System.Drawing.Point(3, 3);
+      this.projectPermissionsView.Name = "projectPermissionsView";
+      this.projectPermissionsView.ReadOnly = false;
+      this.projectPermissionsView.Size = new System.Drawing.Size(571, 509);
+      this.projectPermissionsView.TabIndex = 0;
+      // 
+      // resourcesTabPage
+      // 
+      this.resourcesTabPage.Controls.Add(this.projectResourcesView);
+      this.resourcesTabPage.Location = new System.Drawing.Point(4, 22);
+      this.resourcesTabPage.Name = "resourcesTabPage";
+      this.resourcesTabPage.Padding = new System.Windows.Forms.Padding(3);
+      this.resourcesTabPage.Size = new System.Drawing.Size(577, 515);
+      this.resourcesTabPage.TabIndex = 2;
+      this.resourcesTabPage.Text = "Resources";
+      this.resourcesTabPage.UseVisualStyleBackColor = true;
+      // 
+      // projectResourcesView
+      // 
+      this.projectResourcesView.Caption = "ProjectView";
+      this.projectResourcesView.Content = null;
+      this.projectResourcesView.Dock = System.Windows.Forms.DockStyle.Fill;
+      this.projectResourcesView.Location = new System.Drawing.Point(3, 3);
+      this.projectResourcesView.Name = "projectResourcesView";
+      this.projectResourcesView.ReadOnly = false;
+      this.projectResourcesView.Size = new System.Drawing.Size(571, 509);
+      this.projectResourcesView.TabIndex = 0;
       // 
       // ProjectsView
@@ -226,4 +235,5 @@
       this.detailsTabPage.ResumeLayout(false);
       this.permissionsTabPage.ResumeLayout(false);
+      this.resourcesTabPage.ResumeLayout(false);
       this.ResumeLayout(false);
 
@@ -243,6 +253,7 @@
     private ProjectView projectView;
     private System.Windows.Forms.ImageList imageList;
-    private Access.Views.RefreshableLightweightUserView permissionsView;
-    private System.Windows.Forms.Button savePermissionsButton;
+    private System.Windows.Forms.TabPage resourcesTabPage;
+    private ProjectResourcesView projectResourcesView;
+    private ProjectPermissionsView projectPermissionsView;
   }
 }
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ProjectsView.cs	(revision 15422)
@@ -42,7 +42,4 @@
 
     private readonly object locker = new object();
-    private bool updatingProjects = false;
-    private bool removingProjects = false;
-    private bool savingProjects = false;
 
     public new IItemList<Project> Content {
@@ -89,10 +86,11 @@
         projectsTreeView.Nodes.Clear();
         projectView.Content = null;
-        permissionsView.Content = null;
-        permissionsView.FetchSelectedUsers = null;
+        projectPermissionsView.Content = null;
+        projectResourcesView.Content = null;
       } else {
         var top = BuildProjectTree(Content);
         projectView.Content = top;
-        permissionsView.Content = AccessClient.Instance;
+        projectPermissionsView.Content = top;
+        projectResourcesView.Content = top;
       }
     }
@@ -100,10 +98,12 @@
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-      bool enabled = Content != null;
+      bool enabled = Content != null && !ReadOnly;
+      refreshButton.Enabled = enabled;
       addButton.Enabled = enabled;
       removeButton.Enabled = enabled;
       saveProjectButton.Enabled = enabled;
-      savePermissionsButton.Enabled = enabled && permissionsView.Content != null && permissionsView.FetchSelectedUsers != null;
-      permissionsView.Enabled = enabled && permissionsView.Content != null && permissionsView.FetchSelectedUsers != null;
+      projectView.Enabled = enabled;
+      projectPermissionsView.Enabled = enabled;
+      projectResourcesView.Enabled = enabled;
     }
     #endregion
@@ -175,6 +175,6 @@
     private async void refreshButton_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (updatingProjects) return;
-        updatingProjects = true;
+        if (!refreshButton.Enabled) return;
+        refreshButton.Enabled = false;
       }
 
@@ -183,5 +183,5 @@
         finallyCallback: () => {
           Content = HiveAdminClient.Instance.Projects;
-          updatingProjects = false;
+          refreshButton.Enabled = true;
         });
     }
@@ -197,6 +197,6 @@
     private async void removeButton_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (removingProjects) return;
-        removingProjects = true;
+        if (!removeButton.Enabled) return;
+        removeButton.Enabled = false;
       }
 
@@ -223,5 +223,5 @@
             finallyCallback: () => {
               Content.Remove(project);
-              removingProjects = false;
+              removeButton.Enabled = true;
             });
         }
@@ -231,6 +231,6 @@
     private async void saveProjectButton_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (savingProjects) return;
-        savingProjects = true;
+        if (!saveProjectButton.Enabled) return;
+        saveProjectButton.Enabled = false;
       }
 
@@ -241,8 +241,8 @@
             project.Store();
         },
-        finallyCallback: () => savingProjects = false);
-    }
-
-    private async void projectsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
+        finallyCallback: () => saveProjectButton.Enabled = true);
+    }
+
+    private void projectsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
       var selectedProject = (Project)e.Node.Tag;
 
@@ -251,4 +251,6 @@
 
       projectView.Content = selectedProject;
+      projectPermissionsView.Content = selectedProject;
+      projectResourcesView.Content = selectedProject;
 
       if (selectedProject != null)
@@ -258,17 +260,11 @@
         if (!tabControl.TabPages.Contains(permissionsTabPage))
           tabControl.TabPages.Add(permissionsTabPage);
-
-        permissionsView.FetchSelectedUsers = () =>
-          HiveServiceLocator.Instance.CallHiveService(s => s.GetProjectPermissions(selectedProject.Id))
-                                     .Select(x => x.GrantedUserId)
-                                     .ToList();
-
-        if (tabControl.SelectedIndex == 1 && permissionsView.Content != null && permissionsView.FetchSelectedUsers != null)
-          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
-            action: () => UpdatePermissions());
+        if (!tabControl.TabPages.Contains(resourcesTabPage))
+          tabControl.TabPages.Add(resourcesTabPage);
       } else {
         if (tabControl.TabPages.Contains(permissionsTabPage))
           tabControl.TabPages.Remove(permissionsTabPage);
-        permissionsView.FetchSelectedUsers = null;
+        if (tabControl.TabPages.Contains(resourcesTabPage))
+          tabControl.TabPages.Remove(resourcesTabPage);
       }
     }
@@ -317,20 +313,4 @@
     private void projectsTreeView_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) {
       e.Action = DragAction.Continue;
-    }
-
-    private async void tabControl_SelectedIndexChanged(object sender, EventArgs e) {
-      if (tabControl.SelectedIndex == 1 && permissionsView.Content != null && permissionsView.FetchSelectedUsers != null)
-        await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
-          action: () => UpdatePermissions());
-    }
-
-    private async void savePermissionsButton_Click(object sender, EventArgs e) {
-      var project = (Project)projectsTreeView.SelectedNode.Tag;
-
-      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
-        action: () => HiveServiceLocator.Instance.CallHiveService(s => {
-          s.GrantProjectPermissions(project.Id, permissionsView.GetAddedUsers().Select(x => x.Id).ToList());
-          s.RevokeProjectPermissions(project.Id, permissionsView.GetDeletedUsers().Select(x => x.Id).ToList());
-        }));
     }
     #endregion
@@ -401,12 +381,4 @@
     }
 
-    private void UpdatePermissions() {
-      try {
-        permissionsView.ManualRefresh();
-      } catch (AnonymousUserException) {
-        ShowHiveInformationDialog();
-      }
-    }
-
     private bool IsAuthorized(Project project) {
       return project != null
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs	(revision 15422)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.Designer.cs	(revision 15422)
@@ -0,0 +1,169 @@
+﻿namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  partial class ResourceView {
+    /// <summary> 
+    /// Required designer variable.
+    /// </summary>
+    private System.ComponentModel.IContainer components = null;
+
+    /// <summary> 
+    /// Clean up any resources being used.
+    /// </summary>
+    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+    protected override void Dispose(bool disposing) {
+      if (disposing && (components != null)) {
+        components.Dispose();
+      }
+      base.Dispose(disposing);
+    }
+
+    #region Component Designer generated code
+
+    /// <summary> 
+    /// Required method for Designer support - do not modify 
+    /// the contents of this method with the code editor.
+    /// </summary>
+    private void InitializeComponent() {
+      this.idLabel = new System.Windows.Forms.Label();
+      this.idTextBox = new System.Windows.Forms.TextBox();
+      this.nameLabel = new System.Windows.Forms.Label();
+      this.nameTextBox = new System.Windows.Forms.TextBox();
+      this.descriptionLabel = new System.Windows.Forms.Label();
+      this.descriptionTextBox = new System.Windows.Forms.TextBox();
+      this.heartbeatIntervalLabel = new System.Windows.Forms.Label();
+      this.heartbeatIntervalNumericUpDown = new System.Windows.Forms.NumericUpDown();
+      this.publicLabel = new System.Windows.Forms.Label();
+      this.publicCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).BeginInit();
+      this.SuspendLayout();
+      // 
+      // idLabel
+      // 
+      this.idLabel.AutoSize = true;
+      this.idLabel.Location = new System.Drawing.Point(3, 11);
+      this.idLabel.Name = "idLabel";
+      this.idLabel.Size = new System.Drawing.Size(19, 13);
+      this.idLabel.TabIndex = 0;
+      this.idLabel.Text = "Id:";
+      // 
+      // idTextBox
+      // 
+      this.idTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.idTextBox.Location = new System.Drawing.Point(130, 8);
+      this.idTextBox.Name = "idTextBox";
+      this.idTextBox.ReadOnly = true;
+      this.idTextBox.Size = new System.Drawing.Size(397, 20);
+      this.idTextBox.TabIndex = 1;
+      // 
+      // nameLabel
+      // 
+      this.nameLabel.AutoSize = true;
+      this.nameLabel.Location = new System.Drawing.Point(3, 37);
+      this.nameLabel.Name = "nameLabel";
+      this.nameLabel.Size = new System.Drawing.Size(38, 13);
+      this.nameLabel.TabIndex = 2;
+      this.nameLabel.Text = "Name:";
+      // 
+      // nameTextBox
+      // 
+      this.nameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.nameTextBox.Location = new System.Drawing.Point(130, 34);
+      this.nameTextBox.Name = "nameTextBox";
+      this.nameTextBox.Size = new System.Drawing.Size(397, 20);
+      this.nameTextBox.TabIndex = 3;
+      this.nameTextBox.TextChanged += new System.EventHandler(this.nameTextBox_TextChanged);
+      // 
+      // descriptionLabel
+      // 
+      this.descriptionLabel.AutoSize = true;
+      this.descriptionLabel.Location = new System.Drawing.Point(3, 63);
+      this.descriptionLabel.Name = "descriptionLabel";
+      this.descriptionLabel.Size = new System.Drawing.Size(63, 13);
+      this.descriptionLabel.TabIndex = 4;
+      this.descriptionLabel.Text = "Description:";
+      // 
+      // descriptionTextBox
+      // 
+      this.descriptionTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.descriptionTextBox.Location = new System.Drawing.Point(130, 60);
+      this.descriptionTextBox.Name = "descriptionTextBox";
+      this.descriptionTextBox.Size = new System.Drawing.Size(397, 20);
+      this.descriptionTextBox.TabIndex = 5;
+      this.descriptionTextBox.TextChanged += new System.EventHandler(this.descriptionTextBox_TextChanged);
+      // 
+      // heartbeatIntervalLabel
+      // 
+      this.heartbeatIntervalLabel.AutoSize = true;
+      this.heartbeatIntervalLabel.Location = new System.Drawing.Point(3, 89);
+      this.heartbeatIntervalLabel.Name = "heartbeatIntervalLabel";
+      this.heartbeatIntervalLabel.Size = new System.Drawing.Size(121, 13);
+      this.heartbeatIntervalLabel.TabIndex = 8;
+      this.heartbeatIntervalLabel.Text = "Heartbeat Interval [sec]:";
+      // 
+      // heartbeatIntervalNumericUpDown
+      // 
+      this.heartbeatIntervalNumericUpDown.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.heartbeatIntervalNumericUpDown.Location = new System.Drawing.Point(130, 86);
+      this.heartbeatIntervalNumericUpDown.Name = "heartbeatIntervalNumericUpDown";
+      this.heartbeatIntervalNumericUpDown.Size = new System.Drawing.Size(397, 20);
+      this.heartbeatIntervalNumericUpDown.TabIndex = 9;
+      this.heartbeatIntervalNumericUpDown.ValueChanged += new System.EventHandler(this.heartbeatIntervalNumericUpDown_ValueChanged);
+      // 
+      // publicLabel
+      // 
+      this.publicLabel.AutoSize = true;
+      this.publicLabel.Location = new System.Drawing.Point(3, 115);
+      this.publicLabel.Name = "publicLabel";
+      this.publicLabel.Size = new System.Drawing.Size(39, 13);
+      this.publicLabel.TabIndex = 12;
+      this.publicLabel.Text = "Public:";
+      // 
+      // publicCheckBox
+      // 
+      this.publicCheckBox.AutoSize = true;
+      this.publicCheckBox.Location = new System.Drawing.Point(130, 114);
+      this.publicCheckBox.Name = "publicCheckBox";
+      this.publicCheckBox.Size = new System.Drawing.Size(15, 14);
+      this.publicCheckBox.TabIndex = 13;
+      this.publicCheckBox.UseVisualStyleBackColor = true;
+      this.publicCheckBox.CheckedChanged += new System.EventHandler(this.publicCheckBox_CheckedChanged);
+      // 
+      // SlaveGroupView
+      // 
+      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+      this.Controls.Add(this.publicCheckBox);
+      this.Controls.Add(this.publicLabel);
+      this.Controls.Add(this.heartbeatIntervalNumericUpDown);
+      this.Controls.Add(this.heartbeatIntervalLabel);
+      this.Controls.Add(this.descriptionTextBox);
+      this.Controls.Add(this.descriptionLabel);
+      this.Controls.Add(this.nameTextBox);
+      this.Controls.Add(this.nameLabel);
+      this.Controls.Add(this.idTextBox);
+      this.Controls.Add(this.idLabel);
+      this.Name = "SlaveGroupView";
+      this.Size = new System.Drawing.Size(530, 350);
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).EndInit();
+      this.ResumeLayout(false);
+      this.PerformLayout();
+
+    }
+
+    #endregion
+
+    private System.Windows.Forms.Label idLabel;
+    private System.Windows.Forms.Label nameLabel;
+    private System.Windows.Forms.Label descriptionLabel;
+    private System.Windows.Forms.Label heartbeatIntervalLabel;
+    private System.Windows.Forms.Label publicLabel;
+    protected System.Windows.Forms.TextBox nameTextBox;
+    protected System.Windows.Forms.TextBox idTextBox;
+    protected System.Windows.Forms.TextBox descriptionTextBox;
+    protected System.Windows.Forms.NumericUpDown heartbeatIntervalNumericUpDown;
+    protected System.Windows.Forms.CheckBox publicCheckBox;
+  }
+}
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs	(revision 15422)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourceView.cs	(revision 15422)
@@ -0,0 +1,91 @@
+﻿#region License Information
+/* HeuristicLab
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ *
+ * This file is part of HeuristicLab.
+ *
+ * HeuristicLab is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * HeuristicLab is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
+ */
+#endregion
+
+using System;
+using HeuristicLab.Clients.Access;
+using HeuristicLab.Core.Views;
+using HeuristicLab.MainForm;
+
+namespace HeuristicLab.Clients.Hive.Administrator.Views {
+  [View("Resource View")]
+  [Content(typeof(Resource), IsDefaultView = true)]
+  public partial class ResourceView : ItemView {
+    public new Resource Content {
+      get { return (Resource)base.Content; }
+      set { base.Content = value; }
+    }
+
+    public ResourceView() {
+      InitializeComponent();
+    }
+
+    #region Overrides
+    protected override void OnContentChanged() {
+      base.OnContentChanged();
+      if (Content == null) {
+        idTextBox.Clear();
+        nameTextBox.Clear();
+        descriptionTextBox.Clear();
+        heartbeatIntervalNumericUpDown.Value = 0;
+        publicCheckBox.Checked = false;
+      } else {
+        idTextBox.Text = Content.Id.ToString();
+        nameTextBox.Text = Content.Name;
+        descriptionTextBox.Text = Content.Description;
+        heartbeatIntervalNumericUpDown.Value = Content.HbInterval;
+        publicCheckBox.Checked = Content.OwnerUserId == null;
+      }
+    }
+
+    protected override void SetEnabledStateOfControls() {
+      base.SetEnabledStateOfControls();
+      bool enabled = Content != null;
+      nameTextBox.Enabled = enabled;
+      descriptionTextBox.Enabled = enabled;
+      heartbeatIntervalNumericUpDown.Enabled = enabled;
+      publicCheckBox.Enabled = enabled;
+    }
+    #endregion
+
+    #region Event Handlers
+    private void nameTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Name != nameTextBox.Text)
+        Content.Name = nameTextBox.Text;
+    }
+
+    private void descriptionTextBox_TextChanged(object sender, EventArgs e) {
+      if (Content != null && Content.Description != descriptionTextBox.Text)
+        Content.Description = descriptionTextBox.Text;
+    }
+
+    private void heartbeatIntervalNumericUpDown_ValueChanged(object sender, EventArgs e) {
+      if (Content != null && Content.HbInterval != (int)heartbeatIntervalNumericUpDown.Value)
+        Content.HbInterval = (int)heartbeatIntervalNumericUpDown.Value;
+    }
+
+    private void publicCheckBox_CheckedChanged(object sender, EventArgs e) {
+      var newOwnerUserId = publicCheckBox.Checked ? null : (Guid?)UserInformation.Instance.User.Id;
+      if (Content != null && Content.OwnerUserId != newOwnerUserId)
+        Content.OwnerUserId = newOwnerUserId;
+    }
+    #endregion
+  }
+}
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.Designer.cs	(revision 15422)
@@ -37,9 +37,4 @@
         components.Dispose();
       }
-      HiveAdminClient.Instance.Refreshing -= new EventHandler(HiveAdminClient_Instance_Refreshing);
-      HiveAdminClient.Instance.Refreshed -= new EventHandler(HiveAdminClient_Instance_Refreshed);
-
-      Access.AccessClient.Instance.Refreshing -= new EventHandler(AccessClient_Instance_Refreshing);
-      Access.AccessClient.Instance.Refreshed -= new EventHandler(AccessClient_Instance_Refreshed);
       base.Dispose(disposing);
     }
@@ -59,11 +54,11 @@
       this.btnRemoveGroup = new System.Windows.Forms.Button();
       this.btnAddGroup = new System.Windows.Forms.Button();
-      this.treeSlaveGroup = new System.Windows.Forms.TreeView();
+      this.treeView = new System.Windows.Forms.TreeView();
       this.tabSlaveGroup = new System.Windows.Forms.TabControl();
       this.tabDetails = new System.Windows.Forms.TabPage();
+      this.viewHost = new HeuristicLab.MainForm.WindowsForms.ViewHost();
       this.tabSchedule = new System.Windows.Forms.TabPage();
+      this.scheduleView = new HeuristicLab.Clients.Hive.Administrator.Views.ScheduleView();
       this.toolTip = new System.Windows.Forms.ToolTip(this.components);
-      this.slaveView = new HeuristicLab.Clients.Hive.Administrator.Views.SlaveView();
-      this.scheduleView = new HeuristicLab.Clients.Hive.Administrator.Views.ScheduleView();
       ((System.ComponentModel.ISupportInitialize)(this.splitSlaves)).BeginInit();
       this.splitSlaves.Panel1.SuspendLayout();
@@ -95,5 +90,5 @@
       this.splitSlaves.Panel1.Controls.Add(this.btnRemoveGroup);
       this.splitSlaves.Panel1.Controls.Add(this.btnAddGroup);
-      this.splitSlaves.Panel1.Controls.Add(this.treeSlaveGroup);
+      this.splitSlaves.Panel1.Controls.Add(this.treeView);
       // 
       // splitSlaves.Panel2
@@ -117,5 +112,5 @@
       // btnSave
       // 
-      this.btnSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.PublishToWeb;
+      this.btnSave.Image = HeuristicLab.Common.Resources.VSImageLibrary.Save;
       this.btnSave.Location = new System.Drawing.Point(93, 3);
       this.btnSave.Name = "btnSave";
@@ -148,23 +143,23 @@
       this.btnAddGroup.Click += new System.EventHandler(this.btnAddGroup_Click);
       // 
-      // treeSlaveGroup
-      // 
-      this.treeSlaveGroup.AllowDrop = true;
-      this.treeSlaveGroup.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.treeSlaveGroup.ImageIndex = 0;
-      this.treeSlaveGroup.ImageList = this.imageListSlaveGroups;
-      this.treeSlaveGroup.Location = new System.Drawing.Point(3, 33);
-      this.treeSlaveGroup.Name = "treeSlaveGroup";
-      this.treeSlaveGroup.SelectedImageIndex = 0;
-      this.treeSlaveGroup.Size = new System.Drawing.Size(243, 511);
-      this.treeSlaveGroup.TabIndex = 0;
-      this.treeSlaveGroup.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeSlaveGroup_ItemDrag);
-      this.treeSlaveGroup.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeSlaveGroup_AfterSelect);
-      this.treeSlaveGroup.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragDrop);
-      this.treeSlaveGroup.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnter);
-      this.treeSlaveGroup.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragOver);
-      this.treeSlaveGroup.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.treeSlaveGroup_QueryContinueDrag);
+      // treeView
+      // 
+      this.treeView.AllowDrop = true;
+      this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.treeView.ImageIndex = 0;
+      this.treeView.ImageList = this.imageListSlaveGroups;
+      this.treeView.Location = new System.Drawing.Point(3, 33);
+      this.treeView.Name = "treeView";
+      this.treeView.SelectedImageIndex = 0;
+      this.treeView.Size = new System.Drawing.Size(243, 511);
+      this.treeView.TabIndex = 0;
+      this.treeView.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.treeSlaveGroup_ItemDrag);
+      this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeSlaveGroup_AfterSelect);
+      this.treeView.DragDrop += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragDrop);
+      this.treeView.DragEnter += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragEnter);
+      this.treeView.DragOver += new System.Windows.Forms.DragEventHandler(this.treeSlaveGroup_DragOver);
+      this.treeView.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.treeSlaveGroup_QueryContinueDrag);
       // 
       // tabSlaveGroup
@@ -180,9 +175,8 @@
       this.tabSlaveGroup.Size = new System.Drawing.Size(585, 541);
       this.tabSlaveGroup.TabIndex = 1;
-      this.tabSlaveGroup.SelectedIndexChanged += new System.EventHandler(this.tabSlaveGroup_SelectedIndexChanged);
       // 
       // tabDetails
       // 
-      this.tabDetails.Controls.Add(this.slaveView);
+      this.tabDetails.Controls.Add(this.viewHost);
       this.tabDetails.Location = new System.Drawing.Point(4, 22);
       this.tabDetails.Name = "tabDetails";
@@ -192,4 +186,20 @@
       this.tabDetails.Text = "Details";
       this.tabDetails.UseVisualStyleBackColor = true;
+      // 
+      // viewHost
+      // 
+      this.viewHost.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.viewHost.Caption = "View";
+      this.viewHost.Content = null;
+      this.viewHost.Enabled = false;
+      this.viewHost.Location = new System.Drawing.Point(6, 6);
+      this.viewHost.Name = "viewHost";
+      this.viewHost.ReadOnly = false;
+      this.viewHost.Size = new System.Drawing.Size(565, 503);
+      this.viewHost.TabIndex = 0;
+      this.viewHost.ViewsLabelVisible = true;
+      this.viewHost.ViewType = null;
       // 
       // tabSchedule
@@ -203,17 +213,4 @@
       this.tabSchedule.Text = "Schedule";
       this.tabSchedule.UseVisualStyleBackColor = true;
-      // 
-      // slaveView
-      // 
-      this.slaveView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
-            | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.slaveView.Caption = "SlaveView";
-      this.slaveView.Content = null;
-      this.slaveView.Location = new System.Drawing.Point(6, 6);
-      this.slaveView.Name = "slaveView";
-      this.slaveView.ReadOnly = false;
-      this.slaveView.Size = new System.Drawing.Size(565, 503);
-      this.slaveView.TabIndex = 0;
       // 
       // scheduleView
@@ -253,9 +250,8 @@
     private System.Windows.Forms.Button btnRemoveGroup;
     private System.Windows.Forms.Button btnAddGroup;
-    private System.Windows.Forms.TreeView treeSlaveGroup;
+    private System.Windows.Forms.TreeView treeView;
     private System.Windows.Forms.TabControl tabSlaveGroup;
     private System.Windows.Forms.TabPage tabDetails;
     private System.Windows.Forms.TabPage tabSchedule;
-    private SlaveView slaveView;
     private System.Windows.Forms.ImageList imageListSlaveGroups;
     private ScheduleView scheduleView;
@@ -263,4 +259,5 @@
     private System.Windows.Forms.Button btnRefresh;
     private System.Windows.Forms.ToolTip toolTip;
+    private MainForm.WindowsForms.ViewHost viewHost;
   }
 }
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/ResourcesView.cs	(revision 15422)
@@ -21,4 +21,5 @@
 
 using System;
+using System.Collections.Generic;
 using System.ComponentModel;
 using System.Drawing;
@@ -37,13 +38,11 @@
   [Content(typeof(IItemList<Resource>), false)]
   public partial class ResourcesView : ItemView, IDisposable {
-    private const string UngroupedGroupName = "UNGROUPED";
     private const int slaveImageIndex = 0;
     private const int slaveGroupImageIndex = 1;
+    public const string ungroupedGroupName = "UNGROUPED";
+    public const string ungroupedGroupDescription = "Contains slaves that are not assigned to any group.";
 
     private readonly Color ownedResourceColor = Color.LightGreen;
     private readonly object locker = new object();
-    private bool updatingResources = false;
-    private bool removingResources = false;
-    private bool savingResources = false;
 
     public new IItemList<Resource> Content {
@@ -55,6 +54,6 @@
       InitializeComponent();
 
-      treeSlaveGroup.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
-      treeSlaveGroup.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
+      treeView.ImageList.Images.Add(VSImageLibrary.MonitorLarge);
+      treeView.ImageList.Images.Add(VSImageLibrary.NetworkCenterLarge);
 
       HiveAdminClient.Instance.Refreshing += HiveAdminClient_Instance_Refreshing;
@@ -88,47 +87,10 @@
       base.OnContentChanged();
       if (Content == null) {
-        slaveView.Content = null;
+        treeView.Nodes.Clear();
+        viewHost.Content = null;
         scheduleView.Content = null;
-        treeSlaveGroup.Nodes.Clear();
       } else {
-        treeSlaveGroup.Nodes.Clear();
-
-        //rebuild
-        TreeNode ungrp = new TreeNode(UngroupedGroupName);
-        ungrp.ImageIndex = slaveGroupImageIndex;
-        ungrp.SelectedImageIndex = ungrp.ImageIndex;
-        var newGroup = new SlaveGroup();
-        newGroup.Name = UngroupedGroupName;
-        newGroup.Id = Guid.NewGuid();
-        newGroup.Description = "Contains slaves which are in no group";
-        ungrp.Tag = newGroup;
-
-        foreach (Resource g in Content.OrderBy(x => x.Name)) {
-          if (g.GetType() == typeof(SlaveGroup)) {
-            //root node
-            if (g.ParentResourceId == null) {
-              TreeNode tn = new TreeNode();
-              tn.ImageIndex = slaveGroupImageIndex;
-              tn.SelectedImageIndex = tn.ImageIndex;
-
-              tn.Tag = g;
-              tn.Text = g.Name;
-              if (g.OwnerUserId == Access.UserInformation.Instance.User.Id) tn.BackColor = ownedResourceColor;
-
-              BuildSlaveGroupTree(g, tn);
-              treeSlaveGroup.Nodes.Add(tn);
-            }
-          } else if (g.GetType() == typeof(Slave)) {
-            if (g.ParentResourceId == null) {
-              var stn = new TreeNode(g.Name);
-              stn.ImageIndex = slaveImageIndex;
-              stn.SelectedImageIndex = stn.ImageIndex;
-              stn.Tag = g;
-              if (g.OwnerUserId == Access.UserInformation.Instance.User.Id) stn.BackColor = ownedResourceColor;
-              ungrp.Nodes.Add(stn);
-            }
-          }
-        }
-        treeSlaveGroup.Nodes.Add(ungrp);
+        var top = BuildResourceTree(Content);
+        viewHost.Content = top;
       }
     }
@@ -140,5 +102,5 @@
       btnRemoveGroup.Enabled = enabled;
       btnSave.Enabled = enabled;
-      scheduleView.SetEnabledStateOfSchedule(enabled && IsAuthorized(slaveView.Content));
+      scheduleView.SetEnabledStateOfSchedule(enabled && IsAuthorized((Resource)viewHost.Content));
     }
     #endregion
@@ -164,5 +126,5 @@
         OnContentChanged();
         if (e.PropertyName == "HbInterval") {
-          UpdateChildHbIntervall(slaveView.Content);
+          UpdateChildHbIntervall((Resource)viewHost.Content);
         }
       }
@@ -212,11 +174,11 @@
     private async void btnRefresh_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (updatingResources) return;
-        updatingResources = true;
+        if (!btnRefresh.Enabled) return;
+        btnRefresh.Enabled = false;
       }
 
       await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
         action: () => UpdateResources(),
-        finallyCallback: () => updatingResources = false);
+        finallyCallback: () => btnRefresh.Enabled = true);
     }
 
@@ -231,17 +193,17 @@
     private async void btnRemoveGroup_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (removingResources) return;
-        removingResources = true;
+        if (!btnRemoveGroup.Enabled) return;
+        btnRemoveGroup.Enabled = false;
       }
 
       await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
         action: () => RemoveResource(),
-        finallyCallback: () => removingResources = false);
+        finallyCallback: () => btnRemoveGroup.Enabled = true);
     }
 
     private async void btnSave_Click(object sender, EventArgs e) {
       lock (locker) {
-        if (savingResources) return;
-        savingResources = true;
+        if (!btnSave.Enabled) return;
+        btnSave.Enabled = false;
       }
 
@@ -252,5 +214,5 @@
             resource.Store();
         },
-        finallyCallback: () => savingResources = false);
+        finallyCallback: () => btnSave.Enabled = true);
     }
 
@@ -258,8 +220,12 @@
       var selectedResource = (Resource)e.Node.Tag;
 
-      if (slaveView.Content != null && slaveView.Content is SlaveGroup)
-        slaveView.Content.PropertyChanged -= SlaveViewContent_PropertyChanged;
-
-      slaveView.Content = selectedResource;
+      if (viewHost.Content != null && viewHost.Content is SlaveGroup)
+        ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged;
+
+      viewHost.Content = selectedResource;
+      HiveAdminClient.Instance.DowntimeForResourceId = selectedResource != null ? selectedResource.Id : Guid.Empty;
+      await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
+        action: () => UpdateSchedule(),
+        finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
 
       if (selectedResource != null && selectedResource is SlaveGroup)
@@ -269,79 +235,43 @@
         if (!tabSlaveGroup.TabPages.Contains(tabSchedule))
           tabSlaveGroup.TabPages.Add(tabSchedule);
-
-        HiveAdminClient.Instance.DowntimeForResourceId = selectedResource.Id;
-
-        if (tabSlaveGroup.SelectedIndex == 1) {
-          await SecurityExceptionUtil.TryAsyncAndReportSecurityExceptions(
-            action: () => UpdateSchedule(),
-            finallyCallback: () => scheduleView.Content = HiveAdminClient.Instance.Downtimes);
-        }
       } else {
         if (tabSlaveGroup.TabPages.Contains(tabSchedule))
           tabSlaveGroup.TabPages.Remove(tabSchedule);
-        HiveAdminClient.Instance.DowntimeForResourceId = Guid.Empty;
       }
     }
 
     private void treeSlaveGroup_DragDrop(object sender, DragEventArgs e) {
-      if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false)) {
-        Point pt = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
-        TreeNode destNode = ((TreeView)sender).GetNodeAt(pt);
-        TreeNode newNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
-
-        if (destNode.TreeView == newNode.TreeView) {
-          if (destNode.Text == UngroupedGroupName || (destNode.Parent != null && destNode.Parent.Text == UngroupedGroupName)) {
-            MessageBox.Show(string.Format("You can't drag items to the group \"{0}\".{1}This group only contains slaves which haven't yet been assigned to a real group.",
-              UngroupedGroupName, Environment.NewLine), "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Information);
-            return;
-          }
-
-          SlaveGroup sgrp = null;
-          TreeNode parentNode = null;
-          if (destNode.Tag != null && destNode.Tag is SlaveGroup) {
-            sgrp = (SlaveGroup)destNode.Tag;
-            parentNode = destNode;
-          } else if (destNode.Parent != null && destNode.Parent.Tag is SlaveGroup) {
-            sgrp = (SlaveGroup)destNode.Parent.Tag;
-            parentNode = destNode.Parent;
-          }
-
-          if (newNode.Tag is SlaveGroup && CheckParentsEqualsMovedNode(parentNode, newNode)) {
-            return;
-          }
-
-          SlaveGroup parent = (SlaveGroup)parentNode.Tag;
-
-          if (parent.OwnerUserId != null && !IsAuthorized(parent)) {
-            MessageBox.Show(string.Format("You don't have the permissions to drag items to the group \"{0}\".", ((Resource)parentNode.Tag).Name),
-              "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            return;
-          }
-
-          if (sgrp != null && newNode.Tag != null) {
-            //save parent group to get an id
-            if (sgrp.Id == Guid.Empty) {
-              sgrp.Store();
-            }
-
-            if (newNode.Tag is Slave) {
-              Slave slave = (Slave)newNode.Tag;
-              if (slave.ParentResourceId == null || (slave.ParentResourceId != null && slave.ParentResourceId != sgrp.Id)) {
-                slave.ParentResourceId = sgrp.Id;
-                newNode.Remove();
-                parentNode.Nodes.Clear();
-                BuildSlaveGroupTree(sgrp, parentNode);
-              }
-            } else if (newNode.Tag is SlaveGroup) {
-              SlaveGroup slaveGroup = (SlaveGroup)newNode.Tag;
-              if (slaveGroup.ParentResourceId == null || (slaveGroup.ParentResourceId != null && slaveGroup.ParentResourceId != sgrp.Id)) {
-                slaveGroup.ParentResourceId = sgrp.Id;
-                newNode.Remove();
-                parentNode.Nodes.Clear();
-                BuildSlaveGroupTree(sgrp, parentNode);
-              }
-            }
-          }
-        }
+      var sourceNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
+      if (sourceNode == null) return;
+
+      var treeView = (TreeView)sender;
+      if (sourceNode.TreeView != treeView) return;
+
+      var targetPoint = treeView.PointToClient(new Point(e.X, e.Y));
+      var targetNode = treeView.GetNodeAt(targetPoint);
+
+      if (sourceNode == targetNode) return;
+
+      if (targetNode.Text == ungroupedGroupName || targetNode.Parent != null && targetNode.Parent.Text == ungroupedGroupName) {
+        MessageBox.Show(
+          string.Format(@"You cannot drag resources to group ""{0}"". This group only contains slaves which have not been assigned to a real group yet.", ungroupedGroupName),
+          "HeuristicLab Hive Administrator",
+          MessageBoxButtons.OK,
+          MessageBoxIcon.Information);
+        return;
+      }
+
+      if (sourceNode.Parent == null)
+        treeView.Nodes.Remove(sourceNode);
+      else {
+        sourceNode.Parent.Nodes.Remove(sourceNode);
+        ((Resource)sourceNode.Tag).ParentResourceId = null;
+      }
+
+      if (targetNode == null) {
+        treeView.Nodes.Add(sourceNode);
+      } else {
+        targetNode.Nodes.Add(sourceNode);
+        ((Resource)sourceNode.Tag).ParentResourceId = ((Project)targetNode.Tag).Id;
       }
     }
@@ -364,28 +294,85 @@
       e.Action = DragAction.Continue;
     }
-
-    private void tabSlaveGroup_SelectedIndexChanged(object sender, EventArgs e) {
-      if (tabSlaveGroup.SelectedIndex == 1) {
-        UpdateSchedule();
-      }
-    }
     #endregion
 
     #region Helpers
-    private void BuildSlaveGroupTree(Resource g, TreeNode tn) {
-      foreach (Resource r in Content.Where(s => s.ParentResourceId != null && s.ParentResourceId == g.Id).OrderBy(x => x.Name)) {
-        TreeNode stn = new TreeNode(r.Name);
-        if (r is Slave) {
-          stn.ImageIndex = slaveImageIndex;
-        } else if (r is SlaveGroup) {
-          stn.ImageIndex = slaveGroupImageIndex;
-        }
-        stn.SelectedImageIndex = stn.ImageIndex;
-        stn.Tag = r;
-        if (r.OwnerUserId == Access.UserInformation.Instance.User.Id) stn.BackColor = ownedResourceColor;
-        tn.Nodes.Add(stn);
-
-        BuildSlaveGroupTree(r, stn);
-      }
+    private Resource BuildResourceTree(IEnumerable<Resource> resources) {
+      treeView.Nodes.Clear();
+      if (!resources.Any()) return null;
+
+      var slaveGroups = new HashSet<SlaveGroup>(resources.OfType<SlaveGroup>());
+      var slaves = new HashSet<Slave>(resources.OfType<Slave>());
+
+      var stack = new Stack<Resource>(slaveGroups.OrderByDescending(x => x.Name));
+      var top = stack.Peek();
+
+      TreeNode currentNode = null;
+      Resource currentResource = null;
+
+      while (stack.Any()) {
+        var newResource = stack.Pop();
+        var newNode = new TreeNode(newResource.Name) { Tag = newResource };
+
+        while (currentNode != null && newResource.ParentResourceId != currentResource.Id) {
+          currentNode = currentNode.Parent;
+          currentResource = currentNode == null ? null : (Resource)currentNode.Tag;
+        }
+
+        if (currentNode == null) {
+          treeView.Nodes.Add(newNode);
+        } else {
+          currentNode.Nodes.Add(newNode);
+        }
+
+        if (newResource is Slave) {
+          newNode.ImageIndex = slaveImageIndex;
+        } else {
+          newNode.ImageIndex = slaveGroupImageIndex;
+
+          var childSlaves = slaves.Where(x => x.ParentResourceId == newResource.Id);
+          foreach (var slave in childSlaves.OrderBy(x => x.Name)) {
+            slaves.Remove(slave);
+            var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+
+            slaveNode.SelectedImageIndex = slaveNode.ImageIndex = slaveImageIndex;
+            if (slave.OwnerUserId == UserInformation.Instance.User.Id)
+              slaveNode.BackColor = ownedResourceColor;
+
+            newNode.Nodes.Add(slaveNode);
+          }
+
+          var childSlaveGroups = slaveGroups.Where(x => x.ParentResourceId == newResource.Id);
+          if (childSlaveGroups.Any()) {
+            foreach (var slaveGroup in childSlaveGroups.OrderByDescending(x => x.Name)) {
+              slaveGroups.Remove(slaveGroup);
+              stack.Push(slaveGroup);
+            }
+            currentNode = newNode;
+            currentResource = newResource;
+          }
+        }
+
+        newNode.SelectedImageIndex = newNode.ImageIndex;
+        if (newResource.OwnerUserId == UserInformation.Instance.User.Id)
+          newNode.BackColor = ownedResourceColor;
+      }
+
+      var ungroupedNode = new TreeNode(ungroupedGroupName) {
+        ForeColor = SystemColors.GrayText,
+        Tag = new SlaveGroup() {
+          Name = ungroupedGroupName,
+          Description = ungroupedGroupDescription
+        }
+      };
+
+      foreach (var slave in slaves.OrderBy(x => x.Name)) {
+        var slaveNode = new TreeNode(slave.Name) { Tag = slave };
+        ungroupedNode.Nodes.Add(slaveNode);
+      }
+
+      treeView.Nodes.Add(ungroupedNode);
+      treeView.ExpandAll();
+
+      return top;
     }
 
@@ -414,11 +401,11 @@
       if (InvokeRequired) Invoke((Action)ResetView);
       else {
-        treeSlaveGroup.Nodes.Clear();
-
-        if (slaveView.Content != null && slaveView.Content is SlaveGroup) {
-          slaveView.Content.PropertyChanged -= SlaveViewContent_PropertyChanged;
-        }
-
-        slaveView.Content = null;
+        treeView.Nodes.Clear();
+
+        if (viewHost.Content != null && viewHost.Content is SlaveGroup) {
+          ((SlaveGroup)viewHost.Content).PropertyChanged -= SlaveViewContent_PropertyChanged;
+        }
+
+        viewHost.Content = null;
         if (scheduleView.Content != null) {
           scheduleView.Content.Clear();
@@ -435,5 +422,5 @@
 
     private void RemoveResource() {
-      var selectedNode = treeSlaveGroup.SelectedNode;
+      var selectedNode = treeView.SelectedNode;
       if (selectedNode == null || selectedNode.Tag == null) return;
 
@@ -474,5 +461,5 @@
     private bool IsAuthorized(Resource resource) {
       return resource != null
-          && resource.Name != UngroupedGroupName
+          && resource.Name != ungroupedGroupName
           && resource.Id != Guid.Empty
           && UserInformation.Instance.UserExists
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.Designer.cs	(revision 15422)
@@ -45,290 +45,171 @@
     /// </summary>
     private void InitializeComponent() {
-      this.txtSlaveState = new System.Windows.Forms.TextBox();
-      this.txtOS = new System.Windows.Forms.TextBox();
-      this.label15 = new System.Windows.Forms.Label();
-      this.label14 = new System.Windows.Forms.Label();
-      this.txtDetailsDescription = new System.Windows.Forms.TextBox();
-      this.label13 = new System.Windows.Forms.Label();
-      this.txtName = new System.Windows.Forms.TextBox();
-      this.txtCPU = new System.Windows.Forms.TextBox();
-      this.txtMemory = new System.Windows.Forms.TextBox();
-      this.txtLastHeartbeat = new System.Windows.Forms.TextBox();
-      this.label12 = new System.Windows.Forms.Label();
-      this.label11 = new System.Windows.Forms.Label();
-      this.label10 = new System.Windows.Forms.Label();
-      this.label9 = new System.Windows.Forms.Label();
-      this.label1 = new System.Windows.Forms.Label();
-      this.txtFreeMemory = new System.Windows.Forms.TextBox();
-      this.txtId = new System.Windows.Forms.TextBox();
-      this.label2 = new System.Windows.Forms.Label();
-      this.label3 = new System.Windows.Forms.Label();
-      this.txtHbIntervall = new System.Windows.Forms.TextBox();
-      this.cbxDisposable = new System.Windows.Forms.CheckBox();
-      this.label4 = new System.Windows.Forms.Label();
-      this.cbxPublic = new System.Windows.Forms.CheckBox();
-      this.label5 = new System.Windows.Forms.Label();
+      this.cpuLabel = new System.Windows.Forms.Label();
+      this.cpuTextBox = new System.Windows.Forms.TextBox();
+      this.memoryLabel = new System.Windows.Forms.Label();
+      this.memoryTextBox = new System.Windows.Forms.TextBox();
+      this.operatingSystemLabel = new System.Windows.Forms.Label();
+      this.operatingSystemTextBox = new System.Windows.Forms.TextBox();
+      this.stateLabel = new System.Windows.Forms.Label();
+      this.stateTextBox = new System.Windows.Forms.TextBox();
+      this.lastHeartbeatLabel = new System.Windows.Forms.Label();
+      this.lastHeartbeatTextBox = new System.Windows.Forms.TextBox();
+      this.disposableLabel = new System.Windows.Forms.Label();
+      this.disposableCheckBox = new System.Windows.Forms.CheckBox();
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).BeginInit();
       this.SuspendLayout();
       // 
-      // txtSlaveState
-      // 
-      this.txtSlaveState.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtSlaveState.Enabled = false;
-      this.txtSlaveState.Location = new System.Drawing.Point(146, 242);
-      this.txtSlaveState.Name = "txtSlaveState";
-      this.txtSlaveState.Size = new System.Drawing.Size(390, 20);
-      this.txtSlaveState.TabIndex = 27;
-      // 
-      // txtOS
-      // 
-      this.txtOS.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtOS.Enabled = false;
-      this.txtOS.Location = new System.Drawing.Point(146, 216);
-      this.txtOS.Name = "txtOS";
-      this.txtOS.Size = new System.Drawing.Size(390, 20);
-      this.txtOS.TabIndex = 26;
-      // 
-      // label15
-      // 
-      this.label15.AutoSize = true;
-      this.label15.Location = new System.Drawing.Point(3, 245);
-      this.label15.Name = "label15";
-      this.label15.Size = new System.Drawing.Size(35, 13);
-      this.label15.TabIndex = 25;
-      this.label15.Text = "State:";
-      // 
-      // label14
-      // 
-      this.label14.AutoSize = true;
-      this.label14.Location = new System.Drawing.Point(3, 219);
-      this.label14.Name = "label14";
-      this.label14.Size = new System.Drawing.Size(93, 13);
-      this.label14.TabIndex = 24;
-      this.label14.Text = "Operating System:";
-      // 
-      // txtDetailsDescription
-      // 
-      this.txtDetailsDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtDetailsDescription.Enabled = false;
-      this.txtDetailsDescription.Location = new System.Drawing.Point(146, 112);
-      this.txtDetailsDescription.Name = "txtDetailsDescription";
-      this.txtDetailsDescription.Size = new System.Drawing.Size(390, 20);
-      this.txtDetailsDescription.TabIndex = 23;
-      // 
-      // label13
-      // 
-      this.label13.AutoSize = true;
-      this.label13.Location = new System.Drawing.Point(3, 115);
-      this.label13.Name = "label13";
-      this.label13.Size = new System.Drawing.Size(63, 13);
-      this.label13.TabIndex = 22;
-      this.label13.Text = "Description:";
-      // 
-      // txtName
-      // 
-      this.txtName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtName.Location = new System.Drawing.Point(146, 8);
-      this.txtName.Name = "txtName";
-      this.txtName.Size = new System.Drawing.Size(390, 20);
-      this.txtName.TabIndex = 21;
-      this.txtName.TextChanged += new System.EventHandler(this.txtName_TextChanged);
-      // 
-      // txtCPU
-      // 
-      this.txtCPU.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtCPU.Enabled = false;
-      this.txtCPU.Location = new System.Drawing.Point(146, 138);
-      this.txtCPU.Name = "txtCPU";
-      this.txtCPU.Size = new System.Drawing.Size(390, 20);
-      this.txtCPU.TabIndex = 20;
-      // 
-      // txtMemory
-      // 
-      this.txtMemory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtMemory.Enabled = false;
-      this.txtMemory.Location = new System.Drawing.Point(146, 164);
-      this.txtMemory.Name = "txtMemory";
-      this.txtMemory.Size = new System.Drawing.Size(390, 20);
-      this.txtMemory.TabIndex = 19;
-      // 
-      // txtLastHeartbeat
-      // 
-      this.txtLastHeartbeat.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtLastHeartbeat.Enabled = false;
-      this.txtLastHeartbeat.Location = new System.Drawing.Point(146, 268);
-      this.txtLastHeartbeat.Name = "txtLastHeartbeat";
-      this.txtLastHeartbeat.Size = new System.Drawing.Size(390, 20);
-      this.txtLastHeartbeat.TabIndex = 18;
-      // 
-      // label12
-      // 
-      this.label12.AutoSize = true;
-      this.label12.Location = new System.Drawing.Point(3, 271);
-      this.label12.Name = "label12";
-      this.label12.Size = new System.Drawing.Size(80, 13);
-      this.label12.TabIndex = 17;
-      this.label12.Text = "Last Heartbeat:";
-      // 
-      // label11
-      // 
-      this.label11.AutoSize = true;
-      this.label11.Location = new System.Drawing.Point(3, 167);
-      this.label11.Name = "label11";
-      this.label11.Size = new System.Drawing.Size(47, 13);
-      this.label11.TabIndex = 16;
-      this.label11.Text = "Memory:";
-      // 
-      // label10
-      // 
-      this.label10.AutoSize = true;
-      this.label10.Location = new System.Drawing.Point(3, 141);
-      this.label10.Name = "label10";
-      this.label10.Size = new System.Drawing.Size(32, 13);
-      this.label10.TabIndex = 15;
-      this.label10.Text = "CPU:";
-      // 
-      // label9
-      // 
-      this.label9.AutoSize = true;
-      this.label9.Location = new System.Drawing.Point(3, 11);
-      this.label9.Name = "label9";
-      this.label9.Size = new System.Drawing.Size(38, 13);
-      this.label9.TabIndex = 14;
-      this.label9.Text = "Name:";
-      // 
-      // label1
-      // 
-      this.label1.AutoSize = true;
-      this.label1.Location = new System.Drawing.Point(3, 193);
-      this.label1.Name = "label1";
-      this.label1.Size = new System.Drawing.Size(70, 13);
-      this.label1.TabIndex = 28;
-      this.label1.Text = "Free memory:";
-      // 
-      // txtFreeMemory
-      // 
-      this.txtFreeMemory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtFreeMemory.Enabled = false;
-      this.txtFreeMemory.Location = new System.Drawing.Point(146, 190);
-      this.txtFreeMemory.Name = "txtFreeMemory";
-      this.txtFreeMemory.Size = new System.Drawing.Size(390, 20);
-      this.txtFreeMemory.TabIndex = 29;
-      // 
-      // txtId
-      // 
-      this.txtId.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtId.Enabled = false;
-      this.txtId.Location = new System.Drawing.Point(146, 86);
-      this.txtId.Name = "txtId";
-      this.txtId.Size = new System.Drawing.Size(390, 20);
-      this.txtId.TabIndex = 30;
-      // 
-      // label2
-      // 
-      this.label2.AutoSize = true;
-      this.label2.Location = new System.Drawing.Point(3, 89);
-      this.label2.Name = "label2";
-      this.label2.Size = new System.Drawing.Size(19, 13);
-      this.label2.TabIndex = 31;
-      this.label2.Text = "Id:";
-      // 
-      // label3
-      // 
-      this.label3.AutoSize = true;
-      this.label3.Location = new System.Drawing.Point(3, 37);
-      this.label3.Name = "label3";
-      this.label3.Size = new System.Drawing.Size(137, 13);
-      this.label3.TabIndex = 32;
-      this.label3.Text = "Heartbeat Intervall (in sec) :";
-      // 
-      // txtHbIntervall
-      // 
-      this.txtHbIntervall.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
-            | System.Windows.Forms.AnchorStyles.Right)));
-      this.txtHbIntervall.Location = new System.Drawing.Point(146, 34);
-      this.txtHbIntervall.Name = "txtHbIntervall";
-      this.txtHbIntervall.Size = new System.Drawing.Size(390, 20);
-      this.txtHbIntervall.TabIndex = 33;
-      this.txtHbIntervall.TextChanged += new System.EventHandler(this.txtHbIntervall_TextChanged);
-      // 
-      // cbxDisposable
-      // 
-      this.cbxDisposable.AutoSize = true;
-      this.cbxDisposable.Enabled = false;
-      this.cbxDisposable.Location = new System.Drawing.Point(146, 297);
-      this.cbxDisposable.Name = "cbxDisposable";
-      this.cbxDisposable.Size = new System.Drawing.Size(15, 14);
-      this.cbxDisposable.TabIndex = 34;
-      this.cbxDisposable.UseVisualStyleBackColor = true;
-      this.cbxDisposable.CheckedChanged += new System.EventHandler(this.cbxDisposable_CheckedChanged);
-      // 
-      // label4
-      // 
-      this.label4.AutoSize = true;
-      this.label4.Location = new System.Drawing.Point(3, 297);
-      this.label4.Name = "label4";
-      this.label4.Size = new System.Drawing.Size(62, 13);
-      this.label4.TabIndex = 35;
-      this.label4.Text = "Disposable:";
-      // 
-      // cbxPublic
-      // 
-      this.cbxPublic.AutoSize = true;
-      this.cbxPublic.Enabled = false;
-      this.cbxPublic.Location = new System.Drawing.Point(146, 63);
-      this.cbxPublic.Name = "cbxPublic";
-      this.cbxPublic.Size = new System.Drawing.Size(15, 14);
-      this.cbxPublic.TabIndex = 36;
-      this.cbxPublic.UseVisualStyleBackColor = true;
-      this.cbxPublic.CheckedChanged += new System.EventHandler(this.cbxPublic_CheckedChanged);
-      // 
-      // label5
-      // 
-      this.label5.AutoSize = true;
-      this.label5.Location = new System.Drawing.Point(3, 63);
-      this.label5.Name = "label5";
-      this.label5.Size = new System.Drawing.Size(39, 13);
-      this.label5.TabIndex = 37;
-      this.label5.Text = "Public:";
+      // nameTextBox
+      // 
+      this.nameTextBox.ReadOnly = true;
+      // 
+      // cpuLabel
+      // 
+      this.cpuLabel.AutoSize = true;
+      this.cpuLabel.Location = new System.Drawing.Point(3, 141);
+      this.cpuLabel.Name = "cpuLabel";
+      this.cpuLabel.Size = new System.Drawing.Size(32, 13);
+      this.cpuLabel.TabIndex = 16;
+      this.cpuLabel.Text = "CPU:";
+      // 
+      // cpuTextBox
+      // 
+      this.cpuTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.cpuTextBox.Location = new System.Drawing.Point(130, 138);
+      this.cpuTextBox.Name = "cpuTextBox";
+      this.cpuTextBox.ReadOnly = true;
+      this.cpuTextBox.Size = new System.Drawing.Size(397, 20);
+      this.cpuTextBox.TabIndex = 17;
+      // 
+      // memoryLabel
+      // 
+      this.memoryLabel.AutoSize = true;
+      this.memoryLabel.Location = new System.Drawing.Point(3, 167);
+      this.memoryLabel.Name = "memoryLabel";
+      this.memoryLabel.Size = new System.Drawing.Size(47, 13);
+      this.memoryLabel.TabIndex = 18;
+      this.memoryLabel.Text = "Memory:";
+      // 
+      // memoryTextBox
+      // 
+      this.memoryTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.memoryTextBox.Location = new System.Drawing.Point(130, 164);
+      this.memoryTextBox.Name = "memoryTextBox";
+      this.memoryTextBox.ReadOnly = true;
+      this.memoryTextBox.Size = new System.Drawing.Size(397, 20);
+      this.memoryTextBox.TabIndex = 19;
+      // 
+      // operatingSystemLabel
+      // 
+      this.operatingSystemLabel.AutoSize = true;
+      this.operatingSystemLabel.Location = new System.Drawing.Point(3, 193);
+      this.operatingSystemLabel.Name = "operatingSystemLabel";
+      this.operatingSystemLabel.Size = new System.Drawing.Size(93, 13);
+      this.operatingSystemLabel.TabIndex = 20;
+      this.operatingSystemLabel.Text = "Operating System:";
+      // 
+      // operatingSystemTextBox
+      // 
+      this.operatingSystemTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.operatingSystemTextBox.Location = new System.Drawing.Point(130, 190);
+      this.operatingSystemTextBox.Name = "operatingSystemTextBox";
+      this.operatingSystemTextBox.ReadOnly = true;
+      this.operatingSystemTextBox.Size = new System.Drawing.Size(397, 20);
+      this.operatingSystemTextBox.TabIndex = 21;
+      // 
+      // stateLabel
+      // 
+      this.stateLabel.AutoSize = true;
+      this.stateLabel.Location = new System.Drawing.Point(3, 219);
+      this.stateLabel.Name = "stateLabel";
+      this.stateLabel.Size = new System.Drawing.Size(35, 13);
+      this.stateLabel.TabIndex = 22;
+      this.stateLabel.Text = "State:";
+      // 
+      // stateTextBox
+      // 
+      this.stateTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.stateTextBox.Location = new System.Drawing.Point(130, 216);
+      this.stateTextBox.Name = "stateTextBox";
+      this.stateTextBox.ReadOnly = true;
+      this.stateTextBox.Size = new System.Drawing.Size(397, 20);
+      this.stateTextBox.TabIndex = 23;
+      // 
+      // lastHeartbeatLabel
+      // 
+      this.lastHeartbeatLabel.AutoSize = true;
+      this.lastHeartbeatLabel.Location = new System.Drawing.Point(3, 245);
+      this.lastHeartbeatLabel.Name = "lastHeartbeatLabel";
+      this.lastHeartbeatLabel.Size = new System.Drawing.Size(80, 13);
+      this.lastHeartbeatLabel.TabIndex = 24;
+      this.lastHeartbeatLabel.Text = "Last Heartbeat:";
+      // 
+      // lastHeartbeatTextBox
+      // 
+      this.lastHeartbeatTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+      this.lastHeartbeatTextBox.Location = new System.Drawing.Point(130, 242);
+      this.lastHeartbeatTextBox.Name = "lastHeartbeatTextBox";
+      this.lastHeartbeatTextBox.ReadOnly = true;
+      this.lastHeartbeatTextBox.Size = new System.Drawing.Size(397, 20);
+      this.lastHeartbeatTextBox.TabIndex = 25;
+      // 
+      // disposableLabel
+      // 
+      this.disposableLabel.AutoSize = true;
+      this.disposableLabel.Location = new System.Drawing.Point(3, 271);
+      this.disposableLabel.Name = "disposableLabel";
+      this.disposableLabel.Size = new System.Drawing.Size(62, 13);
+      this.disposableLabel.TabIndex = 28;
+      this.disposableLabel.Text = "Disposable:";
+      // 
+      // disposableCheckBox
+      // 
+      this.disposableCheckBox.AutoSize = true;
+      this.disposableCheckBox.Location = new System.Drawing.Point(130, 270);
+      this.disposableCheckBox.Name = "disposableCheckBox";
+      this.disposableCheckBox.Size = new System.Drawing.Size(15, 14);
+      this.disposableCheckBox.TabIndex = 29;
+      this.disposableCheckBox.UseVisualStyleBackColor = true;
+      this.disposableCheckBox.CheckedChanged += new System.EventHandler(this.disposableCheckBox_CheckedChanged);
       // 
       // SlaveView
       // 
-      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
       this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
-      this.Controls.Add(this.label5);
-      this.Controls.Add(this.cbxPublic);
-      this.Controls.Add(this.label4);
-      this.Controls.Add(this.cbxDisposable);
-      this.Controls.Add(this.txtHbIntervall);
-      this.Controls.Add(this.label3);
-      this.Controls.Add(this.label2);
-      this.Controls.Add(this.txtId);
-      this.Controls.Add(this.txtFreeMemory);
-      this.Controls.Add(this.label1);
-      this.Controls.Add(this.txtSlaveState);
-      this.Controls.Add(this.txtOS);
-      this.Controls.Add(this.label15);
-      this.Controls.Add(this.label14);
-      this.Controls.Add(this.txtDetailsDescription);
-      this.Controls.Add(this.label13);
-      this.Controls.Add(this.txtName);
-      this.Controls.Add(this.txtCPU);
-      this.Controls.Add(this.txtMemory);
-      this.Controls.Add(this.txtLastHeartbeat);
-      this.Controls.Add(this.label12);
-      this.Controls.Add(this.label11);
-      this.Controls.Add(this.label10);
-      this.Controls.Add(this.label9);
+      this.Controls.Add(this.disposableCheckBox);
+      this.Controls.Add(this.disposableLabel);
+      this.Controls.Add(this.lastHeartbeatTextBox);
+      this.Controls.Add(this.lastHeartbeatLabel);
+      this.Controls.Add(this.stateTextBox);
+      this.Controls.Add(this.stateLabel);
+      this.Controls.Add(this.operatingSystemTextBox);
+      this.Controls.Add(this.operatingSystemLabel);
+      this.Controls.Add(this.memoryTextBox);
+      this.Controls.Add(this.memoryLabel);
+      this.Controls.Add(this.cpuTextBox);
+      this.Controls.Add(this.cpuLabel);
       this.Name = "SlaveView";
-      this.Size = new System.Drawing.Size(539, 407);
+      this.Controls.SetChildIndex(this.idTextBox, 0);
+      this.Controls.SetChildIndex(this.descriptionTextBox, 0);
+      this.Controls.SetChildIndex(this.heartbeatIntervalNumericUpDown, 0);
+      this.Controls.SetChildIndex(this.publicCheckBox, 0);
+      this.Controls.SetChildIndex(this.nameTextBox, 0);
+      this.Controls.SetChildIndex(this.cpuLabel, 0);
+      this.Controls.SetChildIndex(this.cpuTextBox, 0);
+      this.Controls.SetChildIndex(this.memoryLabel, 0);
+      this.Controls.SetChildIndex(this.memoryTextBox, 0);
+      this.Controls.SetChildIndex(this.operatingSystemLabel, 0);
+      this.Controls.SetChildIndex(this.operatingSystemTextBox, 0);
+      this.Controls.SetChildIndex(this.stateLabel, 0);
+      this.Controls.SetChildIndex(this.stateTextBox, 0);
+      this.Controls.SetChildIndex(this.lastHeartbeatLabel, 0);
+      this.Controls.SetChildIndex(this.lastHeartbeatTextBox, 0);
+      this.Controls.SetChildIndex(this.disposableLabel, 0);
+      this.Controls.SetChildIndex(this.disposableCheckBox, 0);
+      ((System.ComponentModel.ISupportInitialize)(this.heartbeatIntervalNumericUpDown)).EndInit();
       this.ResumeLayout(false);
       this.PerformLayout();
@@ -338,28 +219,16 @@
     #endregion
 
-    private System.Windows.Forms.TextBox txtSlaveState;
-    private System.Windows.Forms.TextBox txtOS;
-    private System.Windows.Forms.Label label15;
-    private System.Windows.Forms.Label label14;
-    private System.Windows.Forms.TextBox txtDetailsDescription;
-    private System.Windows.Forms.Label label13;
-    private System.Windows.Forms.TextBox txtName;
-    private System.Windows.Forms.TextBox txtCPU;
-    private System.Windows.Forms.TextBox txtMemory;
-    private System.Windows.Forms.TextBox txtLastHeartbeat;
-    private System.Windows.Forms.Label label12;
-    private System.Windows.Forms.Label label11;
-    private System.Windows.Forms.Label label10;
-    private System.Windows.Forms.Label label9;
-    private System.Windows.Forms.Label label1;
-    private System.Windows.Forms.TextBox txtFreeMemory;
-    private System.Windows.Forms.TextBox txtId;
-    private System.Windows.Forms.Label label2;
-    private System.Windows.Forms.Label label3;
-    private System.Windows.Forms.TextBox txtHbIntervall;
-    private System.Windows.Forms.CheckBox cbxDisposable;
-    private System.Windows.Forms.Label label4;
-    private System.Windows.Forms.CheckBox cbxPublic;
-    private System.Windows.Forms.Label label5;
+    private System.Windows.Forms.Label cpuLabel;
+    private System.Windows.Forms.TextBox cpuTextBox;
+    private System.Windows.Forms.Label memoryLabel;
+    private System.Windows.Forms.TextBox memoryTextBox;
+    private System.Windows.Forms.Label operatingSystemLabel;
+    private System.Windows.Forms.TextBox operatingSystemTextBox;
+    private System.Windows.Forms.Label stateLabel;
+    private System.Windows.Forms.TextBox stateTextBox;
+    private System.Windows.Forms.Label lastHeartbeatLabel;
+    private System.Windows.Forms.TextBox lastHeartbeatTextBox;
+    private System.Windows.Forms.Label disposableLabel;
+    private System.Windows.Forms.CheckBox disposableCheckBox;
   }
 }
Index: /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs
===================================================================
--- /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs	(revision 15421)
+++ /branches/HiveProjectManagement/HeuristicLab.Clients.Hive.Administrator/3.3/Views/SlaveView.cs	(revision 15422)
@@ -1,5 +1,5 @@
 ﻿#region License Information
 /* HeuristicLab
- * Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
+ * Copyright (C) 2002-2017 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
  *
  * This file is part of HeuristicLab.
@@ -22,15 +22,13 @@
 using System;
 using System.Windows.Forms;
-using HeuristicLab.Clients.Access;
-using HeuristicLab.Core.Views;
 using HeuristicLab.MainForm;
 using HeuristicLab.MainForm.WindowsForms;
 
 namespace HeuristicLab.Clients.Hive.Administrator.Views {
-  [View("SlaveView")]
-  [Content(typeof(Resource), IsDefaultView = true)]
-  public partial class SlaveView : ItemView {
-    public new Resource Content {
-      get { return (Resource)base.Content; }
+  [View("Slave View")]
+  [Content(typeof(Slave), IsDefaultView = true)]
+  public sealed partial class SlaveView : ResourceView {
+    public new Slave Content {
+      get { return (Slave)base.Content; }
       set { base.Content = value; }
     }
@@ -40,126 +38,36 @@
     }
 
-    #region Register Content Events
-    protected override void DeregisterContentEvents() {
-      base.DeregisterContentEvents();
-    }
-    protected override void RegisterContentEvents() {
-      base.RegisterContentEvents();
-    }
-    #endregion
-
+    #region Overrides
     protected override void OnContentChanged() {
       base.OnContentChanged();
       if (Content == null) {
-        ShowSlaveUI(true);
-        txtName.Clear();
-        txtCPU.Clear();
-        txtDetailsDescription.Clear();
-        txtMemory.Clear();
-        txtOS.Clear();
-        txtSlaveState.Clear();
-        txtLastHeartbeat.Clear();
-        txtFreeMemory.Clear();
-        txtId.Clear();
-        txtHbIntervall.Clear();
-        cbxDisposable.Checked = false;
-        cbxPublic.Checked = false;
+        cpuTextBox.Clear();
+        memoryTextBox.Clear();
+        operatingSystemTextBox.Clear();
+        stateTextBox.Clear();
+        lastHeartbeatTextBox.Clear();
+        disposableCheckBox.Checked = false;
       } else {
-        if (Content.GetType() == typeof(Slave)) {
-          ShowSlaveUI(true);
-          Slave ct = (Slave)Content;
-          bool authorized = UserInformation.Instance.UserExists && (ct.OwnerUserId == UserInformation.Instance.User.Id || HiveRoles.CheckAdminUserPermissions());
-          txtName.Text = ct.Name;
-          txtHbIntervall.Text = ct.HbInterval.ToString();
-          cbxPublic.Enabled = authorized;
-          cbxPublic.CheckedChanged -= new EventHandler(cbxPublic_CheckedChanged);
-          cbxPublic.Checked = ct.OwnerUserId == null;
-          cbxPublic.CheckedChanged += new EventHandler(cbxPublic_CheckedChanged);
-          txtCPU.Text = string.Format("{0} Cores @ {1} Mhz, Arch.: {2}", ct.Cores.ToString(), ct.CpuSpeed.ToString(), ct.CpuArchitecture.ToString());
-          txtDetailsDescription.Text = ct.Description;
-          txtMemory.Text = ct.Memory.ToString();
-          txtOS.Text = ct.OperatingSystem;
-          txtSlaveState.Text = ct.SlaveState.ToString();
-          txtLastHeartbeat.Text = ct.LastHeartbeat.ToString();
-          txtFreeMemory.Text = ct.FreeMemory.ToString();
-          txtId.Text = ct.Id.ToString();
-          cbxDisposable.Enabled = authorized;
-          cbxDisposable.Checked = ct.IsDisposable.GetValueOrDefault();
-        } else if (Content.GetType() == typeof(SlaveGroup)) {
-          SlaveGroup ct = (SlaveGroup)Content;
-          txtName.Text = ct.Name;
-          txtHbIntervall.Text = ct.HbInterval.ToString();
-          cbxPublic.Enabled = ct.Name != "UNGROUPED" && HiveRoles.CheckAdminUserPermissions();
-          cbxPublic.CheckedChanged -= new EventHandler(cbxPublic_CheckedChanged);
-          cbxPublic.Checked = ct.OwnerUserId == null;
-          cbxPublic.CheckedChanged += new EventHandler(cbxPublic_CheckedChanged);
-          ShowSlaveUI(false);
-        } else {
-          throw new Exception("Unknown Resource in SlaveView");
-        }
+        cpuTextBox.Text = string.Format("{0} Cores @ {1} MHz, Arch.: {2}", Content.Cores, Content.CpuSpeed, Content.CpuArchitecture);
+        memoryTextBox.Text = string.Format("{0} ({1} Free)", Content.Memory, Content.FreeMemory);
+        operatingSystemTextBox.Text = Content.OperatingSystem;
+        stateTextBox.Text = Content.SlaveState.ToString();
+        lastHeartbeatTextBox.Text = Content.LastHeartbeat.ToString();
+        disposableCheckBox.Checked = Content.IsDisposable.GetValueOrDefault();
       }
-    }
-
-    private void ShowSlaveUI(bool show) {
-      label1.Visible = show;
-      label2.Visible = show;
-      label4.Visible = show;
-      label10.Visible = show;
-      label11.Visible = show;
-      label12.Visible = show;
-      label13.Visible = show;
-      label14.Visible = show;
-      label15.Visible = show;
-      txtCPU.Visible = show;
-      txtDetailsDescription.Visible = show;
-      txtMemory.Visible = show;
-      txtOS.Visible = show;
-      txtSlaveState.Visible = show;
-      txtLastHeartbeat.Visible = show;
-      txtFreeMemory.Visible = show;
-      txtId.Visible = show;
-      txtName.Enabled = !show;
-      cbxDisposable.Visible = show;
     }
 
     protected override void SetEnabledStateOfControls() {
       base.SetEnabledStateOfControls();
-      bool enabled = Content != null;
-      txtName.Enabled = enabled;
-      txtHbIntervall.Enabled = enabled;
-      cbxPublic.Enabled = enabled;
+      disposableCheckBox.Enabled = Content != null;
     }
+    #endregion
 
-    private void txtName_TextChanged(object sender, EventArgs e) {
-      if (Content != null && Content is SlaveGroup) {
-        Content.Name = txtName.Text;
-      }
+    #region Event Handlers
+    private void disposableCheckBox_CheckedChanged(object sender, EventArgs e) {
+      if (Content != null)
+        Content.IsDisposable = disposableCheckBox.Checked;
     }
-
-    private void txtHbIntervall_TextChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        if (txtHbIntervall.Text.Length > 0) {
-          try {
-            int interval = int.Parse(txtHbIntervall.Text);
-            Content.HbInterval = interval;
-          } catch (Exception) {
-            MessageBox.Show("Please enter a numeric value for the Heartbeat Interval.", "HeuristicLab Hive Administrator", MessageBoxButtons.OK, MessageBoxIcon.Error);
-            txtHbIntervall.Text = "10";
-          }
-        }
-      }
-    }
-
-    private void cbxDisposable_CheckedChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        ((Slave)Content).IsDisposable = cbxDisposable.Checked;
-      }
-    }
-
-    private void cbxPublic_CheckedChanged(object sender, EventArgs e) {
-      if (Content != null) {
-        Content.OwnerUserId = cbxPublic.Checked ? null : new Guid?(UserInformation.Instance.User.Id);
-      }
-    }
+    #endregion
   }
 }
