1
0

feat: support view model closing view window

This commit is contained in:
2026-04-05 18:07:15 +08:00
parent a55a8c7456
commit a9fab50ada
27 changed files with 501 additions and 153 deletions

View File

@@ -17,13 +17,11 @@
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.4.0" /> <PackageReference Include="CommunityToolkit.HighPerformance" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="DotNetZip" Version="1.9.1.8" /> <PackageReference Include="DotNetZip" Version="1.9.1.8" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.142" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="10.0.5" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="10.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Frontend\Models\" /> <Folder Include="Frontend\Models\" />
<Folder Include="Frontend\Utils\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,49 +0,0 @@
using Microsoft.Xaml.Behaviors;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace BallanceTasEditor.Frontend.Behaviors {
public class ConfirmCloseBehavior : Behavior<Window> {
public ICommand ConfirmCommand {
get { return (ICommand)GetValue(ConfirmCommandProperty); }
set { SetValue(ConfirmCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for ConfirmCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ConfirmCommandProperty =
DependencyProperty.Register("ConfirmCommand", typeof(ICommand), typeof(ConfirmCloseBehavior));
protected override void OnAttached() {
base.OnAttached();
AssociatedObject.Closing += OnClosing;
}
protected override void OnDetaching() {
AssociatedObject.Closing -= OnClosing;
base.OnDetaching();
}
private void OnClosing(object? sender, CancelEventArgs e) {
if (ConfirmCommand?.CanExecute(null) == true) {
// 假设Command返回 bool 或通过回调/事件通知结果
bool allowClose = (Func<object?, bool>)ConfirmCommand.Execute(null);
e.Cancel = !allowClose;
}
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.Shared {
public static class BrowserHelper {
public static void OpenInDefaultBrowser(string url) {
if (string.IsNullOrWhiteSpace(url)) {
throw new ArgumentException("The content of URL should not be empty.", nameof(url));
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
// Windows 必须设置 UseShellExecute = true 才能识别 URL
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
Process.Start("xdg-open", url);
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
Process.Start("open", url);
} else {
throw new PlatformNotSupportedException("Not supported operating system.");
}
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.Shared {
public static class Constant {
public const string REPORT_BUG_URL = "https://github.com/yyc12345/BallanceTasToolbox/issues";
// YYC MARK:
// TAS usually used FPS is 132 or 264.
public const uint DEFAULT_FPS = 264;
public const uint DEFAULT_NEW_COUNT = 10000;
public const uint DEFAULT_INSERT_COUNT = 100;
}
}

View File

@@ -5,33 +5,71 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
namespace BallanceTasEditor.Frontend.Views { namespace BallanceTasEditor.Frontend.Shared {
public class DialogService : ViewModels.IDialogService { public interface IDialogService {
NewFileDialogResult? ShowNewFileDialog();
OpenFileDialogResult? ShowOpenFileDialog();
void ShowOpenFileFailedDialog(Exception e);
SaveFileDialogResult? ShowSaveFileDialog();
void ShowSaveFileFailedDialog(Exception e);
bool ShowConfirmCloseFileDialog(string message);
bool ShowConfirmExitWhenOpeningFileDialog();
bool ShowFileChangedDialog();
GotoDialogResult? ShowGotoDialog();
EditFpsDialogResult? ShowEditFpsDialog();
AddFrameDialogResult? ShowAddFrameDialog();
PreferenceDialogResult? ShowPreferenceDialog();
void ShowManuallyReportBugDialog();
void ShowAboutDialog();
}
public record NewFileDialogResult {
public required uint Fps { get; init; }
public required int Count { get; init; }
}
public record OpenFileDialogResult {
public required string Path { get; init; }
}
public record SaveFileDialogResult {
public required string Path { get; init; }
}
public record GotoDialogResult { }
public record EditFpsDialogResult { }
public record AddFrameDialogResult { }
public record PreferenceDialogResult { }
public class DialogService : IDialogService {
public DialogService(Window parent) { public DialogService(Window parent) {
m_Parent = parent; m_Parent = parent;
} }
private readonly Window m_Parent; private readonly Window m_Parent;
public ViewModels.NewFileDialogResult? ShowNewFileDialog() { public NewFileDialogResult? ShowNewFileDialog() {
var dialog = new NewFileDialog(); var dialog = new Views.NewFileDialog();
dialog.Owner = m_Parent; dialog.Owner = m_Parent;
if (dialog.ShowDialog() is true) { if (dialog.ShowDialog() is true) {
// TODO: Finish result extraction // TODO: Finish result extraction
return new ViewModels.NewFileDialogResult() { Count = 0, Fps = 60 }; return new NewFileDialogResult() { Count = 0, Fps = 60 };
} else { } else {
return null; return null;
} }
} }
public ViewModels.OpenFileDialogResult? ShowOpenFileDialog() { public OpenFileDialogResult? ShowOpenFileDialog() {
Microsoft.Win32.OpenFileDialog op = new Microsoft.Win32.OpenFileDialog(); Microsoft.Win32.OpenFileDialog op = new Microsoft.Win32.OpenFileDialog();
op.RestoreDirectory = true; op.RestoreDirectory = true;
op.Multiselect = false; op.Multiselect = false;
op.Filter = "TAS file(*.tas)|*.tas|All file(*.*)|*.*"; op.Filter = "TAS file(*.tas)|*.tas|All file(*.*)|*.*";
if (op.ShowDialog() is true) { if (op.ShowDialog() is true) {
return new ViewModels.OpenFileDialogResult() { Path = op.FileName }; return new OpenFileDialogResult() { Path = op.FileName };
} else { } else {
return null; return null;
} }
@@ -43,12 +81,12 @@ namespace BallanceTasEditor.Frontend.Views {
MessageBoxButton.OK, MessageBoxImage.Error); MessageBoxButton.OK, MessageBoxImage.Error);
} }
public ViewModels.SaveFileDialogResult? ShowSaveFileDialog() { public SaveFileDialogResult? ShowSaveFileDialog() {
Microsoft.Win32.SaveFileDialog op = new Microsoft.Win32.SaveFileDialog(); Microsoft.Win32.SaveFileDialog op = new Microsoft.Win32.SaveFileDialog();
op.RestoreDirectory = true; op.RestoreDirectory = true;
op.Filter = "TAS file(*.tas)|*.tas|All file(*.*)|*.*"; op.Filter = "TAS file(*.tas)|*.tas|All file(*.*)|*.*";
if (op.ShowDialog() is true) { if (op.ShowDialog() is true) {
return new ViewModels.SaveFileDialogResult() { Path = op.FileName }; return new SaveFileDialogResult() { Path = op.FileName };
} else { } else {
return null; return null;
} }
@@ -85,55 +123,62 @@ namespace BallanceTasEditor.Frontend.Views {
return rv == MessageBoxResult.Yes; return rv == MessageBoxResult.Yes;
} }
public ViewModels.GotoDialogResult? ShowGotoDialog() { public GotoDialogResult? ShowGotoDialog() {
var dialog = new GotoDialog(); var dialog = new Views.GotoDialog();
dialog.Owner = m_Parent; dialog.Owner = m_Parent;
if (dialog.ShowDialog() is true) { if (dialog.ShowDialog() is true) {
// TODO: Finish result extraction // TODO: Finish result extraction
return new ViewModels.GotoDialogResult(); return new GotoDialogResult();
} else { } else {
return null; return null;
} }
} }
public ViewModels.EditFpsDialogResult? ShowEditFpsDialog() { public EditFpsDialogResult? ShowEditFpsDialog() {
var dialog = new EditFpsDialog(); var dialog = new Views.EditFpsDialog();
dialog.Owner = m_Parent; dialog.Owner = m_Parent;
if (dialog.ShowDialog() is true) { if (dialog.ShowDialog() is true) {
// TODO: Finish result extraction // TODO: Finish result extraction
return new ViewModels.EditFpsDialogResult(); return new EditFpsDialogResult();
} else { } else {
return null; return null;
} }
} }
public ViewModels.AddFrameDialogResult? ShowAddFrameDialog() { public AddFrameDialogResult? ShowAddFrameDialog() {
var dialog = new AddFrameDialog(); var dialog = new Views.AddFrameDialog();
dialog.Owner = m_Parent; dialog.Owner = m_Parent;
if (dialog.ShowDialog() is true) { if (dialog.ShowDialog() is true) {
// TODO: Finish result extraction // TODO: Finish result extraction
return new ViewModels.AddFrameDialogResult(); return new AddFrameDialogResult();
} else { } else {
return null; return null;
} }
} }
public ViewModels.PreferenceDialogResult? ShowPreferenceDialog() { public PreferenceDialogResult? ShowPreferenceDialog() {
var dialog = new PreferenceDialog(); var dialog = new Views.PreferenceDialog();
dialog.Owner = m_Parent; dialog.Owner = m_Parent;
if (dialog.ShowDialog() is true) { if (dialog.ShowDialog() is true) {
// TODO: Finish result extraction // TODO: Finish result extraction
return new ViewModels.PreferenceDialogResult(); return new PreferenceDialogResult();
} else { } else {
return null; return null;
} }
} }
public void ShowManuallyReportBugDialog() {
MessageBox.Show($"We can not open browser automatically for you. Please visit {Shared.Constant.REPORT_BUG_URL} manually to report bug.",
"Can not Open Browser",
MessageBoxButton.OK, MessageBoxImage.Information);
}
public void ShowAboutDialog() { public void ShowAboutDialog() {
var dialog = new AboutDialog(); var dialog = new Views.AboutDialog();
dialog.Owner = m_Parent; dialog.Owner = m_Parent;
dialog.ShowDialog(); dialog.ShowDialog();
} }
} }
} }

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.Shared {
/// <summary>
/// The delegate used for view model requesting closing view window (non-modal window).
/// </summary>
public delegate void RequestCloseWindowEventHandler();
/// <summary>
/// The delegate used for view model requesting closing view dialog (modal window).
/// </summary>
public delegate void RequestCloseDialogEventHandler(RequestCloseDialogEventArgs e);
/// <summary>
/// The payload passed when requesting closing view dialog (modal window).
/// </summary>
public record RequestCloseDialogEventArgs {
/// <summary>
/// True if we want to close windows by clicking Ok button, otherwise false.
/// </summary>
public required bool Result { get; init; }
}
}

View File

@@ -0,0 +1,26 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels {
public partial class AboutDialog : ObservableObject {
public AboutDialog() { }
[RelayCommand]
private void Ok() {
OnRequestCloseDialog(true);
}
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result });
}
}
}

View File

@@ -0,0 +1,46 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels {
public partial class AddFrameDialog : ObservableObject {
public AddFrameDialog() {
Count = Shared.Constant.DEFAULT_INSERT_COUNT.ToString();
Fps = Shared.Constant.DEFAULT_FPS.ToString();
}
[RelayCommand(CanExecute = nameof(CanOk))]
private void Ok() {
OnRequestCloseDialog(true);
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string count;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string fps;
private bool CanOk() {
// TODO
return true;
}
[RelayCommand]
private void Cancel() {
OnRequestCloseDialog(false);
}
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result });
}
}
}

View File

@@ -0,0 +1,42 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels {
public partial class EditFpsDialog : ObservableObject {
public EditFpsDialog() {
Fps = Shared.Constant.DEFAULT_FPS.ToString();
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string fps;
[RelayCommand(CanExecute = nameof(CanOk))]
private void Ok() {
OnRequestCloseDialog(true);
}
private bool CanOk() {
// TODO
return true;
}
[RelayCommand]
private void Cancel() {
OnRequestCloseDialog(false);
}
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result });
}
}
}

View File

@@ -0,0 +1,42 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels {
public partial class GotoDialog : ObservableObject {
public GotoDialog() {
Index = 0.ToString();
}
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string index;
[RelayCommand(CanExecute = nameof(CanOk))]
private void Ok() {
OnRequestCloseDialog(true);
}
private bool CanOk() {
// TODO
return true;
}
[RelayCommand]
private void Cancel() {
OnRequestCloseDialog(false);
}
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result });
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels {
public interface IDialogService {
NewFileDialogResult? ShowNewFileDialog();
OpenFileDialogResult? ShowOpenFileDialog();
void ShowOpenFileFailedDialog(Exception e);
SaveFileDialogResult? ShowSaveFileDialog();
void ShowSaveFileFailedDialog(Exception e);
bool ShowConfirmCloseFileDialog(string message);
bool ShowConfirmExitWhenOpeningFileDialog();
bool ShowFileChangedDialog();
GotoDialogResult? ShowGotoDialog();
EditFpsDialogResult? ShowEditFpsDialog();
AddFrameDialogResult? ShowAddFrameDialog();
PreferenceDialogResult? ShowPreferenceDialog();
void ShowAboutDialog();
}
public record NewFileDialogResult {
public required uint Fps { get; init; }
public required int Count { get; init; }
}
public record OpenFileDialogResult {
public required string Path { get; init; }
}
public record SaveFileDialogResult {
public required string Path { get; init; }
}
public record GotoDialogResult { }
public record EditFpsDialogResult { }
public record AddFrameDialogResult { }
public record PreferenceDialogResult { }
}

View File

@@ -2,20 +2,23 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Policy;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels { namespace BallanceTasEditor.Frontend.ViewModels {
public partial class MainWindow : ObservableObject { public partial class MainWindow : ObservableObject {
public MainWindow(IDialogService dialogService) { public MainWindow(Shared.IDialogService dialogService) {
m_DialogService = dialogService; m_DialogService = dialogService;
this.TasFile = null; this.TasFile = null;
this.TasFilePath = null; this.TasFilePath = null;
} }
private IDialogService m_DialogService; private Shared.IDialogService m_DialogService;
#region File Operation #region File Operation
@@ -134,6 +137,40 @@ namespace BallanceTasEditor.Frontend.ViewModels {
#endregion #endregion
#region Exit Stuff
[RelayCommand]
private void Exit() {
// TODO
OnRequestCloseWindow();
}
public event Shared.RequestCloseWindowEventHandler? RequestCloseWindow;
private void OnRequestCloseWindow() {
RequestCloseWindow?.Invoke();
}
#endregion
#region Help Menu
[RelayCommand]
private void ReportBug() {
try {
Shared.BrowserHelper.OpenInDefaultBrowser(Shared.Constant.REPORT_BUG_URL);
} catch (Exception) {
m_DialogService.ShowManuallyReportBugDialog();
}
}
[RelayCommand]
private void About() {
m_DialogService.ShowAboutDialog();
}
#endregion
#region UI Only #region UI Only
public string WindowTitle { public string WindowTitle {

View File

@@ -9,11 +9,10 @@ using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels { namespace BallanceTasEditor.Frontend.ViewModels {
public partial class NewFileDialog : ObservableValidator { public partial class NewFileDialog : ObservableObject {
public NewFileDialog() { public NewFileDialog() {
Count = 10000.ToString(); Count = Shared.Constant.DEFAULT_NEW_COUNT.ToString();
// 132 or 264 Fps = Shared.Constant.DEFAULT_FPS.ToString();
Fps = 264.ToString();
} }
// YYC MARK: // YYC MARK:
@@ -39,33 +38,49 @@ namespace BallanceTasEditor.Frontend.ViewModels {
// 就直接把string绑定到TextBox.Text上然后再辅以我自己定义的一套可复用验证逻辑。 // 就直接把string绑定到TextBox.Text上然后再辅以我自己定义的一套可复用验证逻辑。
[ObservableProperty] [ObservableProperty]
//[CustomValidation(typeof(NewFileDialog), nameof(ValidateCount))]
[NotifyCanExecuteChangedFor(nameof(OkCommand))] [NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string count; private string count;
[ObservableProperty] [ObservableProperty]
//[CustomValidation(typeof(NewFileDialog), nameof(ValidateFps))]
[NotifyCanExecuteChangedFor(nameof(OkCommand))] [NotifyCanExecuteChangedFor(nameof(OkCommand))]
private string fps; private string fps;
//public static ValidationResult ValidateCount(string count, ValidationContext context) { //[ObservableProperty]
// return CountValidator.Instance.Validate(count); ////[CustomValidation(typeof(NewFileDialog), nameof(ValidateCount))]
//} //[NotifyCanExecuteChangedFor(nameof(OkCommand))]
//public static ValidationResult ValidateFps(string fps, ValidationContext context) { //private string count;
// return FpsValidator.Instance.Validate(fps);
//} //[ObservableProperty]
////[CustomValidation(typeof(NewFileDialog), nameof(ValidateFps))]
//[NotifyCanExecuteChangedFor(nameof(OkCommand))]
//private string fps;
////public static ValidationResult ValidateCount(string count, ValidationContext context) {
//// return CountValidator.Instance.Validate(count);
////}
////public static ValidationResult ValidateFps(string fps, ValidationContext context) {
//// return FpsValidator.Instance.Validate(fps);
////}
[RelayCommand(CanExecute = nameof(CanOk))] [RelayCommand(CanExecute = nameof(CanOk))]
private void Ok() { private void Ok() {
OnRequestCloseDialog(true);
} }
private bool CanOk() { private bool CanOk() {
return !HasErrors; // TODO
return true;
} }
[RelayCommand] [RelayCommand]
private void Cancel() { private void Cancel() {
OnRequestCloseDialog(false);
}
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result});
} }
//public NewFileDialogResult ToResult() { //public NewFileDialogResult ToResult() {

View File

@@ -0,0 +1,40 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BallanceTasEditor.Frontend.ViewModels {
public partial class PreferenceDialog : ObservableObject {
public PreferenceDialog() {
}
[RelayCommand(CanExecute = nameof(CanOk))]
private void Ok() {
OnRequestCloseDialog(true);
}
private bool CanOk() {
// TODO
return true;
}
[RelayCommand]
private void Cancel() {
OnRequestCloseDialog(false);
}
public event Shared.RequestCloseDialogEventHandler? RequestCloseDialog;
private void OnRequestCloseDialog(bool result) {
RequestCloseDialog?.Invoke(new Shared.RequestCloseDialogEventArgs { Result = result });
}
}
}

View File

@@ -4,6 +4,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views" xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
d:DataContext="{d:DesignInstance vm:AboutDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="About Ballance TAS Editor" Width="340" Height="480" Icon="/Frontend/Assets/About.ico"> Title="About Ballance TAS Editor" Width="340" Height="480" Icon="/Frontend/Assets/About.ico">
<Grid> <Grid>
@@ -24,7 +26,7 @@
<!-- 详情(版权信息等) --> <!-- 详情(版权信息等) -->
<TextBox Grid.Row="3" IsReadOnly="True" TextWrapping="Wrap" Margin="10" <TextBox Grid.Row="3" IsReadOnly="True" TextWrapping="Wrap" Margin="10"
ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Text="Copyright © 2021-2025 yyc12345. All rights reserved.&#13; Text="Copyright © 2021-2026 yyc12345. All rights reserved.&#13;
&#13; &#13;
Program: yyc12345 &#13; Program: yyc12345 &#13;
Icon design: plAer_2 &#13; Icon design: plAer_2 &#13;
@@ -33,6 +35,6 @@ Version: 1.2 stable&#13;
Build Date: October 21, 2025&#13;"/> Build Date: October 21, 2025&#13;"/>
<!-- 确认按钮 --> <!-- 确认按钮 -->
<Button Grid.Row="4" Content="OK" Margin="10" HorizontalAlignment="Center" Style="{StaticResource OkButtonStyle}" IsDefault="True"/> <Button Grid.Row="4" Content="OK" Margin="10" HorizontalAlignment="Center" Style="{StaticResource OkButtonStyle}" IsDefault="True" Command="{Binding OkCommand}"/>
</Grid> </Grid>
</Window> </Window>

View File

@@ -19,6 +19,15 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class AboutDialog : Window { public partial class AboutDialog : Window {
public AboutDialog() { public AboutDialog() {
InitializeComponent(); InitializeComponent();
var vm = new ViewModels.AboutDialog();
vm.RequestCloseDialog += ViewModel_RequestCloseDialog;
this.DataContext = vm;
}
private void ViewModel_RequestCloseDialog(Shared.RequestCloseDialogEventArgs e) {
this.DialogResult = e.Result;
this.Close();
} }
} }
} }

View File

@@ -4,6 +4,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views" xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
d:DataContext="{d:DesignInstance vm:AddFrameDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Add Frame" Height="250" Width="400" Icon="/Frontend/Assets/AddFrame.ico"> Title="Add Frame" Height="250" Width="400" Icon="/Frontend/Assets/AddFrame.ico">
<Grid> <Grid>
@@ -51,8 +53,10 @@
</Grid> </Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right" Margin="10"> <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right" Margin="10">
<Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"/> <Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"/> Command="{Binding OkCommand}"/>
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"
Command="{Binding CancelCommand}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -19,6 +19,14 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class AddFrameDialog : Window { public partial class AddFrameDialog : Window {
public AddFrameDialog() { public AddFrameDialog() {
InitializeComponent(); InitializeComponent();
var vm = new ViewModels.AddFrameDialog();
vm.RequestCloseDialog += ViewModel_RequestCloseDialog;
this.DataContext = vm;
}
private void ViewModel_RequestCloseDialog(Shared.RequestCloseDialogEventArgs e) {
this.DialogResult = e.Result;
this.Close();
} }
} }
} }

View File

@@ -4,6 +4,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views" xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
d:DataContext="{d:DesignInstance vm:EditFpsDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Edit FPS" Height="220" Width="400" Icon="/Frontend/Assets/SetFps.ico"> Title="Edit FPS" Height="220" Width="400" Icon="/Frontend/Assets/SetFps.ico">
<Grid> <Grid>
@@ -46,8 +48,10 @@
</Grid> </Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right" Margin="10"> <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right" Margin="10">
<Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"/> <Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"/> Command="{Binding OkCommand}"/>
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"
Command="{Binding CancelCommand}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -19,6 +19,15 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class EditFpsDialog : Window { public partial class EditFpsDialog : Window {
public EditFpsDialog() { public EditFpsDialog() {
InitializeComponent(); InitializeComponent();
var vm = new ViewModels.EditFpsDialog();
vm.RequestCloseDialog += ViewModel_RequestCloseDialog;
this.DataContext = vm;
}
private void ViewModel_RequestCloseDialog(Shared.RequestCloseDialogEventArgs e) {
this.DialogResult = e.Result;
this.Close();
} }
} }
} }

View File

@@ -4,6 +4,8 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views" xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
d:DataContext="{d:DesignInstance vm:GotoDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Goto..." Height="180" Width="400" Icon="/Frontend/Assets/Goto.ico"> Title="Goto..." Height="180" Width="400" Icon="/Frontend/Assets/Goto.ico">
<Grid> <Grid>
@@ -26,8 +28,10 @@
</Grid> </Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right" Margin="10"> <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right" Margin="10">
<Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"/> <Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"/> Command="{Binding OkCommand}"/>
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"
Command="{Binding CancelCommand}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -19,6 +19,15 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class GotoDialog : Window { public partial class GotoDialog : Window {
public GotoDialog() { public GotoDialog() {
InitializeComponent(); InitializeComponent();
var vm = new ViewModels.GotoDialog();
vm.RequestCloseDialog += ViewModel_RequestCloseDialog;
this.DataContext = vm;
}
private void ViewModel_RequestCloseDialog(Shared.RequestCloseDialogEventArgs e) {
this.DialogResult = e.Result;
this.Close();
} }
} }
} }

View File

@@ -55,7 +55,7 @@
<MenuItem Header="Save File then _Run Game" Icon="{StaticResource IconSaveFileThenRunGame}" InputGestureText="B"/> <MenuItem Header="Save File then _Run Game" Icon="{StaticResource IconSaveFileThenRunGame}" InputGestureText="B"/>
<Separator/> <Separator/>
<MenuItem Header="Close File" Icon="{StaticResource IconCloseFile}" Command="{Binding CloseFileCommand}"/> <MenuItem Header="Close File" Icon="{StaticResource IconCloseFile}" Command="{Binding CloseFileCommand}"/>
<MenuItem Header="Exit" Icon="{StaticResource IconExit}"/> <MenuItem Header="Exit" Icon="{StaticResource IconExit}" Command="{Binding ExitCommand}"/>
</MenuItem> </MenuItem>
<MenuItem Header="_Edit" Padding="5"> <MenuItem Header="_Edit" Padding="5">
<MenuItem Header="_Undo" Icon="{StaticResource IconUndo}" InputGestureText="Ctrl+Z"/> <MenuItem Header="_Undo" Icon="{StaticResource IconUndo}" InputGestureText="Ctrl+Z"/>
@@ -77,8 +77,8 @@
<MenuItem Header="Preference" Icon="{StaticResource IconPreference}" InputGestureText="Ctrl+P"/> <MenuItem Header="Preference" Icon="{StaticResource IconPreference}" InputGestureText="Ctrl+P"/>
</MenuItem> </MenuItem>
<MenuItem Header="_Help" Padding="5"> <MenuItem Header="_Help" Padding="5">
<MenuItem Header="Report Bug" Icon="{StaticResource IconReportBug}"/> <MenuItem Header="Report Bug" Icon="{StaticResource IconReportBug}" Command="{Binding ReportBugCommand}"/>
<MenuItem Header="About" Icon="{StaticResource IconAbout}"/> <MenuItem Header="About" Icon="{StaticResource IconAbout}" Command="{Binding AboutCommand}"/>
</MenuItem> </MenuItem>
</Menu> </Menu>

View File

@@ -20,9 +20,15 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class MainWindow : Window { public partial class MainWindow : Window {
public MainWindow() { public MainWindow() {
InitializeComponent(); InitializeComponent();
var dialogService = new DialogService(this);
this.DataContext = new ViewModels.MainWindow(dialogService); var dialogService = new Shared.DialogService(this);
var vm = new ViewModels.MainWindow(dialogService);
vm.RequestCloseWindow += ViewModel_RequestCloseWindow;
this.DataContext = vm;
} }
private void ViewModel_RequestCloseWindow() {
this.Close();
}
} }
} }

View File

@@ -19,7 +19,15 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class NewFileDialog : Window { public partial class NewFileDialog : Window {
public NewFileDialog() { public NewFileDialog() {
InitializeComponent(); InitializeComponent();
this.DataContext = new ViewModels.NewFileDialog();
var vm = new ViewModels.NewFileDialog();
vm.RequestCloseDialog += ViewModel_RequestCloseDialog;
this.DataContext = vm;
}
private void ViewModel_RequestCloseDialog(Shared.RequestCloseDialogEventArgs e) {
this.DialogResult = e.Result;
this.Close();
} }
} }
} }

View File

@@ -4,7 +4,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views" xmlns:local="clr-namespace:BallanceTasEditor.Frontend.Views"
xmlns:vm="clr-namespace:BallanceTasEditor.Frontend.ViewModels"
xmlns:styles="clr-namespace:BallanceTasEditor.Frontend.Styles" xmlns:styles="clr-namespace:BallanceTasEditor.Frontend.Styles"
d:DataContext="{d:DesignInstance vm:PreferenceDialog}"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False" mc:Ignorable="d" WindowStartupLocation="CenterOwner" ResizeMode="NoResize" ShowInTaskbar="False"
Title="Editor Preference" Height="450" Width="400" Icon="/Frontend/Assets/Preference.ico"> Title="Editor Preference" Height="450" Width="400" Icon="/Frontend/Assets/Preference.ico">
<Grid> <Grid>
@@ -57,8 +59,10 @@
</ScrollViewer> </ScrollViewer>
<StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Right" Margin="10"> <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Right" Margin="10">
<Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"/> <Button Content="OK" Margin="5" Style="{StaticResource OkButtonStyle}" IsDefault="True"
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"/> Command="{Binding OkCommand}"/>
<Button Content="Cancel" Margin="5" Style="{StaticResource CancelButtonStyle}"
Command="{Binding CancelCommand}"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -19,6 +19,15 @@ namespace BallanceTasEditor.Frontend.Views {
public partial class PreferenceDialog : Window { public partial class PreferenceDialog : Window {
public PreferenceDialog() { public PreferenceDialog() {
InitializeComponent(); InitializeComponent();
var vm = new ViewModels.PreferenceDialog();
vm.RequestCloseDialog += ViewModel_RequestCloseDialog;
this.DataContext = vm;
}
private void ViewModel_RequestCloseDialog(Shared.RequestCloseDialogEventArgs e) {
this.DialogResult = e.Result;
this.Close();
} }
} }
} }