Implemented tab reordering
Implemented tab undocking / docking
This commit is contained in:
parent
2e6524f3b9
commit
c9636aff04
50
PettingZoo/Images/Dock.svg
Normal file
50
PettingZoo/Images/Dock.svg
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect y="16" style="fill:#ECF0F1;" width="50" height="35"/>
|
||||
<rect y="2" style="fill:#546A79;" width="50" height="14"/>
|
||||
<circle style="fill:#ED7161;" cx="7" cy="9" r="3"/>
|
||||
<circle style="fill:#EFC41A;" cx="16" cy="9" r="3"/>
|
||||
<circle style="fill:#4FBA6E;" cx="25" cy="9" r="3"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="36" y="34" style="fill:#48A0DC;" width="22" height="22"/>
|
||||
<rect x="46" y="40" style="fill:#FFFFFF;" width="2" height="16"/>
|
||||
<polygon style="fill:#FFFFFF;" points="52.293,46.707 47,41.414 41.707,46.707 40.293,45.293 47,38.586 53.707,45.293 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
50
PettingZoo/Images/Undock.svg
Normal file
50
PettingZoo/Images/Undock.svg
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<rect y="16" style="fill:#ECF0F1;" width="50" height="35"/>
|
||||
<rect y="2" style="fill:#546A79;" width="50" height="14"/>
|
||||
<circle style="fill:#ED7161;" cx="7" cy="9" r="3"/>
|
||||
<circle style="fill:#EFC41A;" cx="16" cy="9" r="3"/>
|
||||
<circle style="fill:#4FBA6E;" cx="25" cy="9" r="3"/>
|
||||
</g>
|
||||
<g>
|
||||
<rect x="36" y="34" style="fill:#21AE5E;" width="22" height="22"/>
|
||||
<rect x="46" y="35.586" style="fill:#FFFFFF;" width="2" height="16"/>
|
||||
<polygon style="fill:#FFFFFF;" points="47,53 40,46.293 41.476,44.879 47,50.172 52.524,44.879 54,46.293 "/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -18,13 +18,16 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Images\Dock.svg" />
|
||||
<None Remove="Images\PublishSend.svg" />
|
||||
<None Remove="Images\Undock.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Clear.svg" />
|
||||
<Resource Include="Images\Connect.svg" />
|
||||
<Resource Include="Images\Disconnect.svg" />
|
||||
<Resource Include="Images\Dock.svg" />
|
||||
<Resource Include="Images\Publish.svg" />
|
||||
<Resource Include="Images\PublishSend.svg" />
|
||||
<Resource Include="Images\Subscribe.svg" />
|
||||
@ -42,6 +45,10 @@
|
||||
<ProjectReference Include="..\PettingZoo.Settings.LiteDB\PettingZoo.Settings.LiteDB.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Undock.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="UI\Connection\ConnectionDisplayNameStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
@ -91,6 +98,11 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
<Compile Update="UI\Tab\Undocked\UndockedTabHostStrings.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>UndockedTabHostStrings.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -130,6 +142,10 @@
|
||||
<LastGenOutput>SubscriberViewStrings.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="UI\Tab\Undocked\UndockedTabHostStrings.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>UndockedTabHostStrings.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,11 +1,10 @@
|
||||
Must-have
|
||||
---------
|
||||
- Option to not save password in profiles
|
||||
|
||||
|
||||
Should-have
|
||||
-----------
|
||||
- Support undocking tabs (and redocking afterwards)
|
||||
- Allow tab reordering
|
||||
- Save / load publisher messages (either as templates or to disk)
|
||||
- Export received messages to Tapeti JSON file / Tapeti.Cmd command-line
|
||||
|
||||
|
@ -235,7 +235,6 @@ namespace PettingZoo.UI.Connection
|
||||
|
||||
private bool SaveCanExecute()
|
||||
{
|
||||
// TODO check changes in parameters (excluding password)
|
||||
return SelectedStoredConnection != null &&
|
||||
SelectedStoredConnection.Id != Guid.Empty &&
|
||||
ValidConnection(false) &&
|
||||
|
8
PettingZoo/UI/Main/ITabContainer.cs
Normal file
8
PettingZoo/UI/Main/ITabContainer.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
public interface ITabContainer
|
||||
{
|
||||
public double TabWidth { get; }
|
||||
public double TabHeight { get; }
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
xmlns:main="clr-namespace:PettingZoo.UI.Main"
|
||||
xmlns:tab="clr-namespace:PettingZoo.UI.Tab"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
xmlns:ui="clr-namespace:PettingZoo.UI"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance main:DesignTimeMainWindowViewModel, IsDesignTimeCreatable=True}"
|
||||
Width="800"
|
||||
@ -17,7 +18,11 @@
|
||||
Closed="MainWindow_OnClosed">
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Modifiers="Control" Key="W" Command="{Binding CloseTabCommand}" />
|
||||
<KeyBinding Modifiers="Control" Key="U" Command="{Binding UndockTabCommand}" />
|
||||
</Window.InputBindings>
|
||||
<Window.Resources>
|
||||
<ui:BindingProxy x:Key="ContextMenuProxy" Data="{Binding}" />
|
||||
</Window.Resources>
|
||||
<DockPanel>
|
||||
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
|
||||
<Button Command="{Binding ConnectCommand}">
|
||||
@ -45,6 +50,12 @@
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandPublish}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button Command="{Binding UndockTabCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Undock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static main:MainWindowStrings.CommandUndock}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Visibility="{Binding ToolbarCommandsSeparatorVisibility}" />
|
||||
<ItemsControl ItemsSource="{Binding ToolbarCommands}">
|
||||
@ -70,28 +81,39 @@
|
||||
<TextBlock Text="{Binding ConnectionStatus}"/>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
<TabControl
|
||||
<Grid>
|
||||
<TabControl
|
||||
Name="SubscriberTabs"
|
||||
ItemsSource="{Binding Tabs}"
|
||||
SelectedValue="{Binding ActiveTab}">
|
||||
<TabControl.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TabItem}">
|
||||
<Setter Property="Header" Value="{Binding Title}" />
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static main:MainWindowStrings.ContextMenuCloseTab}" Command="{Binding CloseTabCommand}" InputGestureText="Ctrl+W" />
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</TabControl.ItemContainerStyle>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate DataType="{x:Type tab:ITab}">
|
||||
<ContentControl Content="{Binding Content}" />
|
||||
</DataTemplate>
|
||||
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
<TabControl.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type TabItem}">
|
||||
<!-- ReSharper disable once Xaml.BindingWithContextNotResolved - valid property for ITab, not sure how to specify the DataContext here so just ignore the warning for now -->
|
||||
<Setter Property="Header" Value="{Binding Title}" />
|
||||
<Setter Property="AllowDrop" Value="True" />
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<!-- ReSharper disable Xaml.BindingWithContextNotResolved - binding is correct, just weird because of the required proxy -->
|
||||
<MenuItem Header="{x:Static main:MainWindowStrings.ContextMenuUndockTab}" Command="{Binding Data.UndockTabCommand, Source={StaticResource ContextMenuProxy}}" InputGestureText="Ctrl+U" />
|
||||
<MenuItem Header="{x:Static main:MainWindowStrings.ContextMenuCloseTab}" Command="{Binding Data.CloseTabCommand, Source={StaticResource ContextMenuProxy}}" InputGestureText="Ctrl+W" />
|
||||
<!-- ReSharper restore Xaml.BindingWithContextNotResolved -->
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<EventSetter Event="PreviewMouseRightButtonDown" Handler="TabItem_PreviewRightMouseDown" />
|
||||
<EventSetter Event="PreviewMouseMove" Handler="TabItem_PreviewMouseMove" />
|
||||
<EventSetter Event="Drop" Handler="TabItem_Drop" />
|
||||
</Style>
|
||||
</TabControl.ItemContainerStyle>
|
||||
<TabControl.ContentTemplate>
|
||||
<DataTemplate DataType="{x:Type tab:ITab}">
|
||||
<ContentControl Content="{Binding Content}" />
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
</TabControl>
|
||||
|
||||
<TextBlock Text="{x:Static main:MainWindowStrings.TabsEmptyText}" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding NoTabsVisibility}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
@ -1,15 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using PettingZoo.Core.Connection;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using PettingZoo.UI.Tab;
|
||||
|
||||
// TODO improve readability of the connection status (especially when connecting/disconnected)
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
#pragma warning disable CA1001 // MainWindow can't be IDisposable, handled instead in OnDispatcherShutDownStarted
|
||||
public partial class MainWindow
|
||||
public partial class MainWindow : ITabContainer
|
||||
{
|
||||
private readonly MainWindowViewModel viewModel;
|
||||
|
||||
@ -18,7 +23,7 @@ namespace PettingZoo.UI.Main
|
||||
{
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
|
||||
viewModel = new MainWindowViewModel(connectionFactory, connectionDialog, subscribeDialog);
|
||||
viewModel = new MainWindowViewModel(connectionFactory, connectionDialog, subscribeDialog, this);
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
|
||||
@ -43,6 +48,74 @@ namespace PettingZoo.UI.Main
|
||||
{
|
||||
var _ = Application.Current.Windows;
|
||||
}
|
||||
|
||||
|
||||
private void TabItem_PreviewRightMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var tabItem = GetParent<TabItem>(e.OriginalSource);
|
||||
if (tabItem == null)
|
||||
return;
|
||||
|
||||
var tabControl = GetParent<TabControl>(tabItem);
|
||||
if (tabControl == null)
|
||||
return;
|
||||
|
||||
tabControl.SelectedItem = tabItem.DataContext;
|
||||
}
|
||||
|
||||
|
||||
private void TabItem_PreviewMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Source is not TabItem tabItem)
|
||||
return;
|
||||
|
||||
if (Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed)
|
||||
DragDrop.DoDragDrop(tabItem, tabItem, DragDropEffects.All);
|
||||
}
|
||||
|
||||
|
||||
private void TabItem_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
var targetTab = GetParent<TabItem>(e.OriginalSource);
|
||||
if (targetTab == null)
|
||||
return;
|
||||
|
||||
var sourceTab = (TabItem?)e.Data.GetData(typeof(TabItem));
|
||||
if (sourceTab == null || sourceTab == targetTab)
|
||||
return;
|
||||
|
||||
var tabControl = GetParent<TabControl>(targetTab);
|
||||
if (tabControl?.ItemsSource is not ObservableCollection<ITab> dataCollection)
|
||||
return;
|
||||
|
||||
if (sourceTab.DataContext is not ITab sourceData || targetTab.DataContext is not ITab targetData)
|
||||
return;
|
||||
|
||||
var sourceIndex = dataCollection.IndexOf(sourceData);
|
||||
var targetIndex = dataCollection.IndexOf(targetData);
|
||||
|
||||
dataCollection.Move(sourceIndex, targetIndex);
|
||||
}
|
||||
|
||||
|
||||
private static T? GetParent<T>(object originalSource) where T : DependencyObject
|
||||
{
|
||||
var current = originalSource as DependencyObject;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (current is T targetType)
|
||||
return targetType;
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public double TabWidth => SubscriberTabs.ActualWidth;
|
||||
public double TabHeight => SubscriberTabs.ActualHeight;
|
||||
}
|
||||
#pragma warning restore CA1001
|
||||
}
|
||||
|
29
PettingZoo/UI/Main/MainWindowStrings.Designer.cs
generated
29
PettingZoo/UI/Main/MainWindowStrings.Designer.cs
generated
@ -19,7 +19,7 @@ namespace PettingZoo.UI.Main {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class MainWindowStrings {
|
||||
@ -96,6 +96,15 @@ namespace PettingZoo.UI.Main {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Undock tab.
|
||||
/// </summary>
|
||||
public static string CommandUndock {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandUndock", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Close tab.
|
||||
/// </summary>
|
||||
@ -105,6 +114,15 @@ namespace PettingZoo.UI.Main {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Undock.
|
||||
/// </summary>
|
||||
public static string ContextMenuUndockTab {
|
||||
get {
|
||||
return ResourceManager.GetString("ContextMenuUndockTab", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Non-persistent.
|
||||
/// </summary>
|
||||
@ -159,6 +177,15 @@ namespace PettingZoo.UI.Main {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No open tabs. Click on New Publisher or New Subscriber to open a new tab..
|
||||
/// </summary>
|
||||
public static string TabsEmptyText {
|
||||
get {
|
||||
return ResourceManager.GetString("TabsEmptyText", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Petting Zoo - a RabbitMQ live message viewer.
|
||||
/// </summary>
|
||||
|
@ -129,9 +129,15 @@
|
||||
<data name="CommandSubscribe" xml:space="preserve">
|
||||
<value>New Subscriber...</value>
|
||||
</data>
|
||||
<data name="CommandUndock" xml:space="preserve">
|
||||
<value>Undock tab</value>
|
||||
</data>
|
||||
<data name="ContextMenuCloseTab" xml:space="preserve">
|
||||
<value>Close tab</value>
|
||||
</data>
|
||||
<data name="ContextMenuUndockTab" xml:space="preserve">
|
||||
<value>Undock</value>
|
||||
</data>
|
||||
<data name="DeliveryModeNonPersistent" xml:space="preserve">
|
||||
<value>Non-persistent</value>
|
||||
</data>
|
||||
@ -150,6 +156,9 @@
|
||||
<data name="StatusError" xml:space="preserve">
|
||||
<value>Error: {0}</value>
|
||||
</data>
|
||||
<data name="TabsEmptyText" xml:space="preserve">
|
||||
<value>No open tabs. Click on New Publisher or New Subscriber to open a new tab.</value>
|
||||
</data>
|
||||
<data name="WindowTitle" xml:space="preserve">
|
||||
<value>Petting Zoo - a RabbitMQ live message viewer</value>
|
||||
</data>
|
||||
|
@ -9,6 +9,7 @@ using PettingZoo.Core.Connection;
|
||||
using PettingZoo.UI.Connection;
|
||||
using PettingZoo.UI.Subscribe;
|
||||
using PettingZoo.UI.Tab;
|
||||
using PettingZoo.UI.Tab.Undocked;
|
||||
|
||||
namespace PettingZoo.UI.Main
|
||||
{
|
||||
@ -17,18 +18,21 @@ namespace PettingZoo.UI.Main
|
||||
private readonly IConnectionFactory connectionFactory;
|
||||
private readonly IConnectionDialog connectionDialog;
|
||||
private readonly ISubscribeDialog subscribeDialog;
|
||||
private readonly ITabContainer tabContainer;
|
||||
private readonly ITabFactory tabFactory;
|
||||
|
||||
private SubscribeDialogParams? subscribeDialogParams;
|
||||
private IConnection? connection;
|
||||
private string connectionStatus;
|
||||
private ITab? activeTab;
|
||||
private readonly Dictionary<ITab, Window> undockedTabs = new();
|
||||
|
||||
private readonly DelegateCommand connectCommand;
|
||||
private readonly DelegateCommand disconnectCommand;
|
||||
private readonly DelegateCommand publishCommand;
|
||||
private readonly DelegateCommand subscribeCommand;
|
||||
private readonly DelegateCommand closeTabCommand;
|
||||
private readonly DelegateCommand undockTabCommand;
|
||||
|
||||
|
||||
public string ConnectionStatus
|
||||
@ -60,6 +64,7 @@ namespace PettingZoo.UI.Main
|
||||
public ICommand PublishCommand => publishCommand;
|
||||
public ICommand SubscribeCommand => subscribeCommand;
|
||||
public ICommand CloseTabCommand => closeTabCommand;
|
||||
public ICommand UndockTabCommand => undockTabCommand;
|
||||
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => ActiveTab is ITabToolbarCommands tabToolbarCommands
|
||||
? tabToolbarCommands.ToolbarCommands
|
||||
@ -68,13 +73,17 @@ namespace PettingZoo.UI.Main
|
||||
public Visibility ToolbarCommandsSeparatorVisibility =>
|
||||
ToolbarCommands.Any() ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public Visibility NoTabsVisibility =>
|
||||
Tabs.Count > 0 ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
|
||||
public MainWindowViewModel(IConnectionFactory connectionFactory, IConnectionDialog connectionDialog,
|
||||
ISubscribeDialog subscribeDialog)
|
||||
ISubscribeDialog subscribeDialog, ITabContainer tabContainer)
|
||||
{
|
||||
this.connectionFactory = connectionFactory;
|
||||
this.connectionDialog = connectionDialog;
|
||||
this.subscribeDialog = subscribeDialog;
|
||||
this.tabContainer = tabContainer;
|
||||
|
||||
connectionStatus = GetConnectionStatus(null);
|
||||
|
||||
@ -83,9 +92,10 @@ namespace PettingZoo.UI.Main
|
||||
disconnectCommand = new DelegateCommand(DisconnectExecute, IsConnectedCanExecute);
|
||||
publishCommand = new DelegateCommand(PublishExecute, IsConnectedCanExecute);
|
||||
subscribeCommand = new DelegateCommand(SubscribeExecute, IsConnectedCanExecute);
|
||||
closeTabCommand = new DelegateCommand(CloseTabExecute, CloseTabCanExecute);
|
||||
closeTabCommand = new DelegateCommand(CloseTabExecute, HasActiveTabCanExecute);
|
||||
undockTabCommand = new DelegateCommand(UndockTabExecute, HasActiveTabCanExecute);
|
||||
|
||||
tabFactory = new ViewTabFactory(this, closeTabCommand);
|
||||
tabFactory = new ViewTabFactory(this);
|
||||
}
|
||||
|
||||
|
||||
@ -125,7 +135,16 @@ namespace PettingZoo.UI.Main
|
||||
private async void DisconnectExecute()
|
||||
{
|
||||
Tabs.Clear();
|
||||
|
||||
|
||||
var capturedUndockedTabs = undockedTabs.ToList();
|
||||
undockedTabs.Clear();
|
||||
|
||||
foreach (var undockedTab in capturedUndockedTabs)
|
||||
undockedTab.Value.Close();
|
||||
|
||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
||||
undockTabCommand.RaiseCanExecuteChanged();
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
await connection.DisposeAsync();
|
||||
@ -170,24 +189,48 @@ namespace PettingZoo.UI.Main
|
||||
|
||||
private void CloseTabExecute()
|
||||
{
|
||||
if (ActiveTab == null)
|
||||
RemoveActiveTab();
|
||||
}
|
||||
|
||||
|
||||
private void UndockTabExecute()
|
||||
{
|
||||
var tab = RemoveActiveTab();
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
var tabHostWindow = UndockedTabHostWindow.Create(this, tab, tabContainer.TabWidth, tabContainer.TabHeight);
|
||||
undockedTabs.Add(tab, tabHostWindow);
|
||||
|
||||
tabHostWindow.Show();
|
||||
}
|
||||
|
||||
|
||||
private ITab? RemoveActiveTab()
|
||||
{
|
||||
if (ActiveTab == null)
|
||||
return null;
|
||||
|
||||
var activeTabIndex = Tabs.IndexOf(ActiveTab);
|
||||
if (activeTabIndex == -1)
|
||||
return;
|
||||
return null;
|
||||
|
||||
var tab = Tabs[activeTabIndex];
|
||||
Tabs.RemoveAt(activeTabIndex);
|
||||
|
||||
|
||||
if (activeTabIndex == Tabs.Count)
|
||||
activeTabIndex--;
|
||||
|
||||
ActiveTab = activeTabIndex >= 0 ? Tabs[activeTabIndex] : null;
|
||||
closeTabCommand.RaiseCanExecuteChanged();
|
||||
undockTabCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
|
||||
private bool CloseTabCanExecute()
|
||||
|
||||
private bool HasActiveTabCanExecute()
|
||||
{
|
||||
return ActiveTab != null;
|
||||
}
|
||||
@ -199,9 +242,25 @@ namespace PettingZoo.UI.Main
|
||||
ActiveTab = tab;
|
||||
|
||||
closeTabCommand.RaiseCanExecuteChanged();
|
||||
undockTabCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(NoTabsVisibility));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void DockTab(ITab tab)
|
||||
{
|
||||
if (undockedTabs.Remove(tab, out var tabHostWindow))
|
||||
tabHostWindow.Close();
|
||||
|
||||
AddTab(tab);
|
||||
}
|
||||
|
||||
public void UndockedTabClosed(ITab tab)
|
||||
{
|
||||
undockedTabs.Remove(tab);
|
||||
}
|
||||
|
||||
|
||||
private void ConnectionChanged()
|
||||
{
|
||||
disconnectCommand.RaiseCanExecuteChanged();
|
||||
@ -232,7 +291,7 @@ namespace PettingZoo.UI.Main
|
||||
|
||||
public class DesignTimeMainWindowViewModel : MainWindowViewModel
|
||||
{
|
||||
public DesignTimeMainWindowViewModel() : base(null!, null!, null!)
|
||||
public DesignTimeMainWindowViewModel() : base(null!, null!, null!, null!)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ namespace PettingZoo.UI.Tab
|
||||
{
|
||||
string Title { get; }
|
||||
ContentControl Content { get; }
|
||||
ICommand CloseTabCommand { get; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,5 +3,8 @@
|
||||
public interface ITabHost
|
||||
{
|
||||
void AddTab(ITab tab);
|
||||
|
||||
void DockTab(ITab tab);
|
||||
void UndockedTabClosed(ITab tab);
|
||||
}
|
||||
}
|
||||
|
@ -10,54 +10,57 @@
|
||||
d:DesignWidth="800"
|
||||
d:DataContext="{d:DesignInstance res:DesignTimePublisherViewModel, IsDesignTimeCreatable=True}"
|
||||
Background="White">
|
||||
<ui:GridLayout Style="{StaticResource Form}" Margin="4" Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ui:GridLayout Style="{StaticResource Form}" Margin="4" Grid.IsSharedSizeScope="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="Label" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelSendToExchange}" IsChecked="{Binding SendToExchange}" Style="{StaticResource TypeSelection}" />
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelSendToQueue}" IsChecked="{Binding SendToQueue}" Style="{StaticResource TypeSelection}" />
|
||||
<Label Grid.Row="0" Grid.Column="1">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelSendToExchange}" IsChecked="{Binding SendToExchange}" Style="{StaticResource TypeSelection}" />
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelSendToQueue}" IsChecked="{Binding SendToQueue}" Style="{StaticResource TypeSelection}" />
|
||||
</StackPanel>
|
||||
</Label>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelExchange}" Visibility="{Binding ExchangeVisibility}" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Exchange}" Visibility="{Binding ExchangeVisibility}" />
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelRoutingKey}" Visibility="{Binding ExchangeVisibility}" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding RoutingKey}" Visibility="{Binding ExchangeVisibility}" />
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelQueue}" Visibility="{Binding QueueVisibility}" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Queue}" Visibility="{Binding QueueVisibility}" />
|
||||
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelReplyTo}" />
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="5" Grid.Column="1">
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelReplyToSpecified}" IsChecked="{Binding ReplyToSpecified}" Style="{StaticResource TypeSelection}" />
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelReplyToNewSubscriber}" IsChecked="{Binding ReplyToNewSubscriber}" Style="{StaticResource TypeSelection}" />
|
||||
</StackPanel>
|
||||
</Label>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding ReplyTo}" IsEnabled="{Binding ReplyToSpecified}" />
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelExchange}" Visibility="{Binding ExchangeVisibility}" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Exchange}" Visibility="{Binding ExchangeVisibility}" />
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center">
|
||||
<ToggleButton Content="{x:Static res:PublisherViewStrings.OptionMessageTypeRaw}" Style="{StaticResource TypeSelection}" IsChecked="{Binding MessageTypeRaw}" />
|
||||
<ToggleButton Content="{x:Static res:PublisherViewStrings.OptionMessageTypeTapeti}" Style="{StaticResource TypeSelection}" IsChecked="{Binding MessageTypeTapeti}" />
|
||||
</StackPanel>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelRoutingKey}" Visibility="{Binding ExchangeVisibility}" />
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding RoutingKey}" Visibility="{Binding ExchangeVisibility}" />
|
||||
<ContentControl Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 8 0 0" Content="{Binding MessageTypeControl}" />
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelQueue}" Visibility="{Binding QueueVisibility}" />
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Queue}" Visibility="{Binding QueueVisibility}" />
|
||||
|
||||
<Label Grid.Row="5" Grid.Column="0" Content="{x:Static res:PublisherViewStrings.LabelReplyTo}" />
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="5" Grid.Column="1">
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelReplyToSpecified}" IsChecked="{Binding ReplyToSpecified}" Style="{StaticResource TypeSelection}" />
|
||||
<RadioButton Content="{x:Static res:PublisherViewStrings.LabelReplyToNewSubscriber}" IsChecked="{Binding ReplyToNewSubscriber}" Style="{StaticResource TypeSelection}" />
|
||||
</StackPanel>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding ReplyTo}" IsEnabled="{Binding ReplyToSpecified}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="8" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center">
|
||||
<ToggleButton Content="{x:Static res:PublisherViewStrings.OptionMessageTypeRaw}" Style="{StaticResource TypeSelection}" IsChecked="{Binding MessageTypeRaw}" />
|
||||
<ToggleButton Content="{x:Static res:PublisherViewStrings.OptionMessageTypeTapeti}" Style="{StaticResource TypeSelection}" IsChecked="{Binding MessageTypeTapeti}" />
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Auto">
|
||||
<ContentControl Margin="0 8 0 0" Content="{Binding MessageTypeControl}" />
|
||||
</ScrollViewer>
|
||||
</ui:GridLayout>
|
||||
<Button Grid.Row="10" Grid.Column="1" Command="{Binding PublishCommand}" Content="{x:Static res:PublisherViewStrings.CommandPublish}" HorizontalAlignment="Left" />
|
||||
</ui:GridLayout>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
|
@ -5,8 +5,6 @@ using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using PettingZoo.Core.Connection;
|
||||
|
||||
// TODO publish button in page instead of just in toolbar
|
||||
|
||||
namespace PettingZoo.UI.Tab.Publisher
|
||||
{
|
||||
public enum MessageType
|
||||
|
72
PettingZoo/UI/Tab/Undocked/UndockedTabHostStrings.Designer.cs
generated
Normal file
72
PettingZoo/UI/Tab/Undocked/UndockedTabHostStrings.Designer.cs
generated
Normal file
@ -0,0 +1,72 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace PettingZoo.UI.Tab.Undocked {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class UndockedTabHostStrings {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal UndockedTabHostStrings() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PettingZoo.UI.Tab.Undocked.UndockedTabHostStrings", typeof(UndockedTabHostStrings).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Dock tab.
|
||||
/// </summary>
|
||||
public static string CommandDock {
|
||||
get {
|
||||
return ResourceManager.GetString("CommandDock", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
123
PettingZoo/UI/Tab/Undocked/UndockedTabHostStrings.resx
Normal file
123
PettingZoo/UI/Tab/Undocked/UndockedTabHostStrings.resx
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="CommandDock" xml:space="preserve">
|
||||
<value>Dock tab</value>
|
||||
</data>
|
||||
</root>
|
85
PettingZoo/UI/Tab/Undocked/UndockedTabHostViewModel.cs
Normal file
85
PettingZoo/UI/Tab/Undocked/UndockedTabHostViewModel.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Undocked
|
||||
{
|
||||
public class UndockedTabHostViewModel : BaseViewModel
|
||||
{
|
||||
private readonly ITabHost tabHost;
|
||||
private readonly ITab tab;
|
||||
private readonly DelegateCommand dockCommand;
|
||||
|
||||
|
||||
public string Title => tab.Title;
|
||||
public ContentControl Content => tab.Content;
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => tab.ToolbarCommands;
|
||||
|
||||
public Visibility ToolbarCommandsSeparatorVisibility =>
|
||||
ToolbarCommands.Any() ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public ICommand DockCommand => dockCommand;
|
||||
|
||||
|
||||
public UndockedTabHostViewModel(ITabHost tabHost, ITab tab)
|
||||
{
|
||||
this.tabHost = tabHost;
|
||||
this.tab = tab;
|
||||
|
||||
tab.PropertyChanged += (_, args) =>
|
||||
{
|
||||
RaisePropertyChanged(args.PropertyName);
|
||||
if (args.PropertyName == nameof(ToolbarCommands))
|
||||
RaisePropertyChanged(nameof(ToolbarCommandsSeparatorVisibility));
|
||||
};
|
||||
|
||||
dockCommand = new DelegateCommand(DockCommandExecute);
|
||||
}
|
||||
|
||||
|
||||
private void DockCommandExecute()
|
||||
{
|
||||
tabHost.DockTab(tab);
|
||||
}
|
||||
|
||||
|
||||
public void WindowClosed()
|
||||
{
|
||||
tabHost.UndockedTabClosed(tab);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DesignTimeUndockedTabHostViewModel : UndockedTabHostViewModel
|
||||
{
|
||||
public DesignTimeUndockedTabHostViewModel() : base(null!, new DesignTimeTab())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
private class DesignTimeTab : ITab
|
||||
{
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands { get; } = Array.Empty<TabToolbarCommand>();
|
||||
|
||||
public string Title => "Design-time tab title";
|
||||
public ContentControl Content => null!;
|
||||
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
// Just to prevent the "PropertyChanged is never used" message
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
|
||||
}
|
||||
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml
Normal file
44
PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml
Normal file
@ -0,0 +1,44 @@
|
||||
<Window x:Class="PettingZoo.UI.Tab.Undocked.UndockedTabHostWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:undocked="clr-namespace:PettingZoo.UI.Tab.Undocked"
|
||||
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/"
|
||||
mc:Ignorable="d"
|
||||
d:DataContext="{d:DesignInstance undocked:DesignTimeUndockedTabHostViewModel, IsDesignTimeCreatable=True}"
|
||||
Title="{Binding Title}"
|
||||
Height="450"
|
||||
Width="800"
|
||||
WindowStyle="ThreeDBorderWindow">
|
||||
<DockPanel>
|
||||
<ToolBar DockPanel.Dock="Top" ToolBarTray.IsLocked="True">
|
||||
<Button Command="{Binding DockCommand}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{svgc:SvgImage Source=/Images/Dock.svg, AppName=PettingZoo}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{x:Static undocked:UndockedTabHostStrings.CommandDock}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Visibility="{Binding ToolbarCommandsSeparatorVisibility}" />
|
||||
<ItemsControl ItemsSource="{Binding ToolbarCommands}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Command="{Binding Command}" Style="{DynamicResource {x:Static ToolBar.ButtonStyleKey}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="{Binding Icon}" Width="16" Height="16" Style="{StaticResource ToolbarIcon}"/>
|
||||
<TextBlock Margin="3,0,0,0" Text="{Binding Caption}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToolBar>
|
||||
|
||||
<ContentControl Content="{Binding Content}" />
|
||||
</DockPanel>
|
||||
</Window>
|
34
PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml.cs
Normal file
34
PettingZoo/UI/Tab/Undocked/UndockedTabHostWindow.xaml.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace PettingZoo.UI.Tab.Undocked
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UndockedTabHostWindow.xaml
|
||||
/// </summary>
|
||||
public partial class UndockedTabHostWindow
|
||||
{
|
||||
public static UndockedTabHostWindow Create(ITabHost tabHost, ITab tab, double width, double height)
|
||||
{
|
||||
var viewModel = new UndockedTabHostViewModel(tabHost, tab);
|
||||
var window = new UndockedTabHostWindow(viewModel)
|
||||
{
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
||||
public UndockedTabHostWindow(UndockedTabHostViewModel viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
|
||||
Closed += (_, _) =>
|
||||
{
|
||||
viewModel.WindowClosed();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ namespace PettingZoo.UI.Tab
|
||||
{
|
||||
public string Title => getTitle(viewModel);
|
||||
public ContentControl Content { get; }
|
||||
public ICommand CloseTabCommand { get; }
|
||||
|
||||
public IEnumerable<TabToolbarCommand> ToolbarCommands => viewModel is ITabToolbarCommands tabToolbarCommands
|
||||
? tabToolbarCommands.ToolbarCommands
|
||||
@ -25,14 +24,13 @@ namespace PettingZoo.UI.Tab
|
||||
private readonly Func<TViewModel, string> getTitle;
|
||||
|
||||
|
||||
public ViewTab(ICommand closeTabCommand, TView view, TViewModel viewModel, Expression<Func<TViewModel, string>> title)
|
||||
public ViewTab(TView view, TViewModel viewModel, Expression<Func<TViewModel, string>> title)
|
||||
{
|
||||
if (title.Body is not MemberExpression titleMemberExpression)
|
||||
throw new ArgumentException(@"Invalid expression type, expected viewModel => viewModel.TitlePropertyName", nameof(title));
|
||||
|
||||
var titlePropertyName = titleMemberExpression.Member.Name;
|
||||
|
||||
CloseTabCommand = closeTabCommand;
|
||||
this.viewModel = viewModel;
|
||||
getTitle = title.Compile();
|
||||
Content = view;
|
||||
|
@ -8,13 +8,11 @@ namespace PettingZoo.UI.Tab
|
||||
public class ViewTabFactory : ITabFactory
|
||||
{
|
||||
private readonly ITabHost tabHost;
|
||||
private readonly ICommand closeTabCommand;
|
||||
|
||||
|
||||
public ViewTabFactory(ITabHost tabHost, ICommand closeTabCommand)
|
||||
public ViewTabFactory(ITabHost tabHost)
|
||||
{
|
||||
this.tabHost = tabHost;
|
||||
this.closeTabCommand = closeTabCommand;
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +20,6 @@ namespace PettingZoo.UI.Tab
|
||||
{
|
||||
var viewModel = new SubscriberViewModel(tabHost, this, connection, subscriber);
|
||||
return new ViewTab<SubscriberView, SubscriberViewModel>(
|
||||
closeTabCommand,
|
||||
new SubscriberView(viewModel),
|
||||
viewModel,
|
||||
vm => vm.Title);
|
||||
@ -33,7 +30,6 @@ namespace PettingZoo.UI.Tab
|
||||
{
|
||||
var viewModel = new PublisherViewModel(tabHost, this, connection, fromReceivedMessage);
|
||||
return new ViewTab<PublisherView, PublisherViewModel>(
|
||||
closeTabCommand,
|
||||
new PublisherView(viewModel),
|
||||
viewModel,
|
||||
vm => vm.Title);
|
||||
|
Loading…
Reference in New Issue
Block a user