feat(04-10): create TemplatesViewModel and TemplatesView

- TemplatesViewModel: list, capture with 5 options, apply, rename, delete, refresh
- TemplatesView: capture section with checkboxes, apply section, template DataGrid
- RenameInputDialog: simple WPF dialog (no Microsoft.VisualBasic dependency)
- Capture/Apply are separate async commands from RunCommand
This commit is contained in:
Dev
2026-04-03 10:24:23 +02:00
parent 87dd4bb3ef
commit a49bbb9f98
3 changed files with 372 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
<UserControl x:Class="SharepointToolbox.Views.Tabs.TemplatesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:SharepointToolbox.Localization">
<DockPanel Margin="10">
<!-- Left panel: Capture and Apply -->
<StackPanel DockPanel.Dock="Left" Width="320" Margin="0,0,10,0">
<!-- Capture Section -->
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.capture]}"
Margin="0,0,0,10">
<StackPanel Margin="5">
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.siteurl]}"
Margin="0,0,0,3" />
<TextBox Text="{Binding CaptureSiteUrl, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5" />
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.name]}"
Margin="0,0,0,3" />
<TextBox Text="{Binding TemplateName, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
<!-- Capture options checkboxes -->
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.options]}"
FontWeight="SemiBold" Margin="0,0,0,5" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.opt.libraries]}"
IsChecked="{Binding CaptureLibraries}" Margin="0,0,0,3" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.opt.folders]}"
IsChecked="{Binding CaptureFolders}" Margin="0,0,0,3" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.opt.permissions]}"
IsChecked="{Binding CapturePermissions}" Margin="0,0,0,3" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.opt.logo]}"
IsChecked="{Binding CaptureLogo}" Margin="0,0,0,3" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.opt.settings]}"
IsChecked="{Binding CaptureSettings}" Margin="0,0,0,10" />
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.capture]}"
Command="{Binding CaptureCommand}" />
</StackPanel>
</GroupBox>
<!-- Apply Section -->
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.apply]}"
Margin="0,0,0,10">
<StackPanel Margin="5">
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.newtitle]}"
Margin="0,0,0,3" />
<TextBox Text="{Binding NewSiteTitle, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5" />
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.newalias]}"
Margin="0,0,0,3" />
<TextBox Text="{Binding NewSiteAlias, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.apply]}"
Command="{Binding ApplyCommand}" />
</StackPanel>
</GroupBox>
<!-- Progress -->
<TextBlock Text="{Binding StatusMessage}" TextWrapping="Wrap" Margin="0,5,0,0" />
</StackPanel>
<!-- Right panel: Template list -->
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,5">
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.list]}"
FontWeight="Bold" FontSize="14" VerticalAlignment="Center" Margin="0,0,10,0" />
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.rename]}"
Command="{Binding RenameCommand}" Margin="0,0,5,0" Padding="10,3" />
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.delete]}"
Command="{Binding DeleteCommand}" Padding="10,3" />
</StackPanel>
<DataGrid ItemsSource="{Binding Templates}" SelectedItem="{Binding SelectedTemplate}"
AutoGenerateColumns="False" IsReadOnly="True"
SelectionMode="Single" CanUserSortColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="*" />
<DataGridTextColumn Header="Type" Binding="{Binding SiteType}" Width="100" />
<DataGridTextColumn Header="Source" Binding="{Binding SourceUrl}" Width="*" />
<DataGridTextColumn Header="Captured" Binding="{Binding CapturedAt, StringFormat=yyyy-MM-dd HH:mm}" Width="140" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</DockPanel>
</UserControl>

View File

@@ -0,0 +1,71 @@
using System.Windows;
using System.Windows.Controls;
namespace SharepointToolbox.Views.Tabs;
public partial class TemplatesView : UserControl
{
public TemplatesView(ViewModels.Tabs.TemplatesViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
// Wire rename dialog factory using a simple WPF input dialog
viewModel.RenameDialogFactory = currentName =>
{
var dialog = new RenameInputDialog(currentName) { Owner = Window.GetWindow(this) };
return dialog.ShowDialog() == true ? dialog.InputText : null;
};
// Load templates on first display
viewModel.RefreshCommand.ExecuteAsync(null);
}
}
/// <summary>
/// Simple WPF input dialog for renaming templates (replaces Microsoft.VisualBasic.Interaction.InputBox).
/// </summary>
internal class RenameInputDialog : Window
{
private readonly TextBox _inputBox;
public string InputText => _inputBox.Text;
public RenameInputDialog(string currentName)
{
Title = "Rename Template";
Width = 360;
Height = 140;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
ResizeMode = ResizeMode.NoResize;
var panel = new StackPanel { Margin = new Thickness(12) };
panel.Children.Add(new TextBlock { Text = "Enter new template name:", Margin = new Thickness(0, 0, 0, 6) });
_inputBox = new TextBox { Text = currentName };
_inputBox.SelectAll();
_inputBox.Focus();
_inputBox.KeyDown += (s, e) =>
{
if (e.Key == System.Windows.Input.Key.Enter) { DialogResult = true; Close(); }
if (e.Key == System.Windows.Input.Key.Escape) { DialogResult = false; Close(); }
};
panel.Children.Add(_inputBox);
var buttons = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
Margin = new Thickness(0, 10, 0, 0),
};
var ok = new Button { Content = "OK", Width = 75, Margin = new Thickness(0, 0, 8, 0), IsDefault = true };
ok.Click += (s, e) => { DialogResult = true; Close(); };
var cancel = new Button { Content = "Cancel", Width = 75, IsCancel = true };
cancel.Click += (s, e) => { DialogResult = false; Close(); };
buttons.Children.Add(ok);
buttons.Children.Add(cancel);
panel.Children.Add(buttons);
Content = panel;
}
}