Commitok összehasonlítása
24 Commit-ok
v1.21
...
ddc5a70719
| Szerző | SHA1 | Dátum | |
|---|---|---|---|
|
|
ddc5a70719 | ||
|
|
12b86e0707 | ||
|
|
813e08b7d4 | ||
|
|
292a5a093e | ||
|
|
8e4ad331f1 | ||
|
|
53ae21dd87 | ||
|
|
642b19274b | ||
|
|
fb7ce8b34e | ||
|
|
486589f9bc | ||
|
|
ae4d7f82bc | ||
|
|
0bc3bd2588 | ||
|
|
ea8fa98bd6 | ||
|
|
9c835871ec | ||
|
|
0e4253ba37 | ||
|
|
eac8813cc0 | ||
|
|
f41b821d24 | ||
|
|
c23865d5f5 | ||
|
|
b412baaa5c | ||
|
|
24b41b0e63 | ||
|
|
0401910711 | ||
|
|
56d5c38ab1 | ||
|
|
0794d40054 | ||
|
|
e8abb03eee | ||
|
|
4937ac4b1c |
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
**/bin/
|
||||||
|
**/obj/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
.vs/
|
||||||
|
*.DotSettings.user
|
||||||
|
packages/
|
||||||
14
App.xaml
Normal file
14
App.xaml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Application x:Class="InstaSoftOfficeTool.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="Styles/FluentTheme.xaml"/>
|
||||||
|
<ResourceDictionary Source="Styles/ButtonStyles.xaml"/>
|
||||||
|
<ResourceDictionary Source="Styles/ControlStyles.xaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
8
App.xaml.cs
Normal file
8
App.xaml.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool
|
||||||
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
27
InstaSoftOfficeTool.csproj
Normal file
27
InstaSoftOfficeTool.csproj
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net48</TargetFramework>
|
||||||
|
<UseWPF>true</UseWPF>
|
||||||
|
<RootNamespace>InstaSoftOfficeTool</RootNamespace>
|
||||||
|
<AssemblyName>InstaSoftOfficeTool</AssemblyName>
|
||||||
|
<ApplicationIcon>Resources\app.ico</ApplicationIcon>
|
||||||
|
<Company>InstaSoft Zrt.</Company>
|
||||||
|
<Product>InstaSoft Office Tool</Product>
|
||||||
|
<Copyright>Copyright (c) InstaSoft Zrt. 2026</Copyright>
|
||||||
|
<Version>1.22</Version>
|
||||||
|
<AssemblyVersion>1.22.0.0</AssemblyVersion>
|
||||||
|
<FileVersion>1.22.0.0</FileVersion>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\app.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
83
MainWindow.xaml
Normal file
83
MainWindow.xaml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<Window x:Class="InstaSoftOfficeTool.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:pages="clr-namespace:InstaSoftOfficeTool.Pages"
|
||||||
|
Title="InstaSoft Office Tool"
|
||||||
|
Width="800" Height="550"
|
||||||
|
ResizeMode="NoResize"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
Background="{StaticResource BackgroundBrush}"
|
||||||
|
TextElement.Foreground="{StaticResource TextPrimaryBrush}">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<!-- Main content -->
|
||||||
|
<Grid x:Name="MainGrid">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="64"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="60"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Header bar -->
|
||||||
|
<Border Grid.Row="0" Background="{StaticResource HeaderBarBrush}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="0,0,0,1">
|
||||||
|
<Grid Margin="24,0">
|
||||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
|
<!-- Microsoft 4-color logo -->
|
||||||
|
<Grid Width="24" Height="24" Margin="0,0,12,0">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Rectangle Grid.Row="0" Grid.Column="0" Fill="#F35325" Margin="0,0,1,1"/>
|
||||||
|
<Rectangle Grid.Row="0" Grid.Column="1" Fill="#81BC06" Margin="1,0,0,1"/>
|
||||||
|
<Rectangle Grid.Row="1" Grid.Column="0" Fill="#05A6F0" Margin="0,1,1,0"/>
|
||||||
|
<Rectangle Grid.Row="1" Grid.Column="1" Fill="#FFBA08" Margin="1,1,0,0"/>
|
||||||
|
</Grid>
|
||||||
|
<StackPanel VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="InstaSoft Office Tool" FontSize="16" FontWeight="SemiBold"
|
||||||
|
Foreground="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<TextBlock Text="Microsoft Partner" FontSize="11"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,-2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="v1.22" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Content area -->
|
||||||
|
<Frame x:Name="ContentFrame" Grid.Row="1" NavigationUIVisibility="Hidden"
|
||||||
|
Margin="0" Background="Transparent"/>
|
||||||
|
|
||||||
|
<!-- Bottom bar: step dots + navigation -->
|
||||||
|
<Border Grid.Row="2" Background="{StaticResource HeaderBarBrush}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="0,1,0,0">
|
||||||
|
<Grid Margin="24,0">
|
||||||
|
<!-- Step indicator dots -->
|
||||||
|
<StackPanel x:Name="StepIndicator" Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Left" VerticalAlignment="Center">
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Navigation buttons -->
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<Button x:Name="BtnBack" Content="← Vissza" Style="{StaticResource SecondaryButton}"
|
||||||
|
Click="BtnBack_Click" Margin="0,0,8,0" Visibility="Collapsed"/>
|
||||||
|
<Button x:Name="BtnNext" Content="Tovább →" Style="{StaticResource PrimaryButton}"
|
||||||
|
Click="BtnNext_Click" Visibility="Collapsed"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Confirm dialog overlay -->
|
||||||
|
<pages:ConfirmDialog x:Name="Dialog"/>
|
||||||
|
<!-- Product key dialog overlay -->
|
||||||
|
<pages:ProductKeyDialog x:Name="KeyDialog"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
230
MainWindow.xaml.cs
Normal file
230
MainWindow.xaml.cs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
using InstaSoftOfficeTool.Pages;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool
|
||||||
|
{
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
private InstallConfig _config = new InstallConfig();
|
||||||
|
private List<Page> _wizardPages;
|
||||||
|
private int _currentPageIndex = -1;
|
||||||
|
private string _currentFlow; // "install", "remove", "license"
|
||||||
|
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
NavigateToWelcome();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NavigateToWelcome()
|
||||||
|
{
|
||||||
|
_currentFlow = null;
|
||||||
|
_currentPageIndex = -1;
|
||||||
|
_config = new InstallConfig();
|
||||||
|
BtnBack.Visibility = Visibility.Collapsed;
|
||||||
|
BtnNext.Visibility = Visibility.Collapsed;
|
||||||
|
StepIndicator.Children.Clear();
|
||||||
|
ContentFrame.Navigate(new WelcomePage(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartInstallFlow()
|
||||||
|
{
|
||||||
|
_currentFlow = "install";
|
||||||
|
_config = new InstallConfig();
|
||||||
|
_wizardPages = new List<Page>
|
||||||
|
{
|
||||||
|
new VersionPage(this, _config), // 0
|
||||||
|
new EditionPage(this, _config), // 1
|
||||||
|
new ConfigPage(this, _config), // 2
|
||||||
|
new ProductKeyPage(this, _config), // 3
|
||||||
|
new SummaryPage(this, _config), // 4
|
||||||
|
new PreInstallCheckPage(this), // 5 — remove old Office
|
||||||
|
new ProgressPage(this, _config) // 6
|
||||||
|
};
|
||||||
|
_currentPageIndex = 0;
|
||||||
|
ShowCurrentPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartRemoveFlow()
|
||||||
|
{
|
||||||
|
_currentFlow = "remove";
|
||||||
|
_wizardPages = new List<Page>
|
||||||
|
{
|
||||||
|
new RemovePage(this)
|
||||||
|
};
|
||||||
|
_currentPageIndex = 0;
|
||||||
|
BtnNext.Visibility = Visibility.Collapsed;
|
||||||
|
BtnBack.Visibility = Visibility.Visible;
|
||||||
|
StepIndicator.Children.Clear();
|
||||||
|
ContentFrame.Navigate(_wizardPages[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartLicenseFlow()
|
||||||
|
{
|
||||||
|
_currentFlow = "license";
|
||||||
|
_wizardPages = new List<Page>
|
||||||
|
{
|
||||||
|
new TroubleshootPage(this)
|
||||||
|
};
|
||||||
|
_currentPageIndex = 0;
|
||||||
|
BtnNext.Visibility = Visibility.Collapsed;
|
||||||
|
BtnBack.Visibility = Visibility.Visible;
|
||||||
|
StepIndicator.Children.Clear();
|
||||||
|
ContentFrame.Navigate(_wizardPages[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowCurrentPage()
|
||||||
|
{
|
||||||
|
if (_currentPageIndex < 0 || _currentPageIndex >= _wizardPages.Count) return;
|
||||||
|
|
||||||
|
ContentFrame.Navigate(_wizardPages[_currentPageIndex]);
|
||||||
|
UpdateStepIndicator();
|
||||||
|
UpdateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStepIndicator()
|
||||||
|
{
|
||||||
|
StepIndicator.Children.Clear();
|
||||||
|
if (_currentFlow != "install") return;
|
||||||
|
|
||||||
|
// Only show dots for install flow (exclude PreInstallCheck + Progress)
|
||||||
|
int totalDots = _wizardPages.Count - 2;
|
||||||
|
for (int i = 0; i < totalDots; i++)
|
||||||
|
{
|
||||||
|
var dot = new Ellipse
|
||||||
|
{
|
||||||
|
Margin = new Thickness(4, 0, 4, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (i <= _currentPageIndex)
|
||||||
|
{
|
||||||
|
dot.Width = 10;
|
||||||
|
dot.Height = 10;
|
||||||
|
dot.Fill = (SolidColorBrush)FindResource("AccentBrush");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dot.Width = 8;
|
||||||
|
dot.Height = 8;
|
||||||
|
dot.Fill = (SolidColorBrush)FindResource("BorderBrush");
|
||||||
|
}
|
||||||
|
|
||||||
|
StepIndicator.Children.Add(dot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateButtons()
|
||||||
|
{
|
||||||
|
BtnBack.Visibility = _currentPageIndex > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
BtnNext.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
// On SummaryPage = "Tov\u00e1bb"
|
||||||
|
// On PreInstallCheckPage or ProgressPage = hide
|
||||||
|
if (_wizardPages[_currentPageIndex] is PreInstallCheckPage ||
|
||||||
|
_wizardPages[_currentPageIndex] is ProgressPage)
|
||||||
|
{
|
||||||
|
BtnNext.Visibility = Visibility.Collapsed;
|
||||||
|
BtnBack.Visibility = Visibility.Collapsed;
|
||||||
|
StepIndicator.Children.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BtnNext.Content = "Tov\u00e1bb \u2192";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnBack_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Reset window height in case a page enlarged it
|
||||||
|
Height = 550;
|
||||||
|
|
||||||
|
if (_currentPageIndex > 0 && _currentFlow == "install")
|
||||||
|
{
|
||||||
|
_currentPageIndex--;
|
||||||
|
ShowCurrentPage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NavigateToWelcome();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnNext_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_currentFlow != "install") return;
|
||||||
|
|
||||||
|
// Validate current page
|
||||||
|
var currentPage = _wizardPages[_currentPageIndex];
|
||||||
|
if (currentPage is IWizardPage wizardPage && !wizardPage.Validate())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_currentPageIndex < _wizardPages.Count - 1)
|
||||||
|
{
|
||||||
|
_currentPageIndex++;
|
||||||
|
|
||||||
|
// Refresh edition page when version changes
|
||||||
|
if (_currentPageIndex == 1)
|
||||||
|
{
|
||||||
|
_wizardPages[1] = new EditionPage(this, _config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh config page when edition changes (MSI/ODT mode may differ)
|
||||||
|
if (_currentPageIndex == 2)
|
||||||
|
{
|
||||||
|
_wizardPages[2] = new ConfigPage(this, _config);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowCurrentPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ProceedToInstall()
|
||||||
|
{
|
||||||
|
// Jump to ProgressPage (last page in the wizard)
|
||||||
|
_currentPageIndex = _wizardPages.Count - 1;
|
||||||
|
ShowCurrentPage();
|
||||||
|
|
||||||
|
if (_wizardPages[_currentPageIndex] is ProgressPage progressPage)
|
||||||
|
{
|
||||||
|
progressPage.StartInstallation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowCloseButton()
|
||||||
|
{
|
||||||
|
BtnNext.Content = "Bez\u00e1r\u00e1s";
|
||||||
|
BtnNext.Visibility = Visibility.Visible;
|
||||||
|
BtnNext.Click -= BtnNext_Click;
|
||||||
|
BtnNext.Click += (s, e) => Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> ConfirmAsync(string title, string message,
|
||||||
|
string confirmText = "Igen", string cancelText = "M\u00e9gse",
|
||||||
|
DialogType type = DialogType.Warning)
|
||||||
|
{
|
||||||
|
return Dialog.ShowAsync(title, message, confirmText, cancelText, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> AskProductKeyAsync()
|
||||||
|
{
|
||||||
|
return KeyDialog.ShowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowBackToHomeButton()
|
||||||
|
{
|
||||||
|
BtnBack.Content = "\u2190 F\u0151oldal";
|
||||||
|
BtnBack.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IWizardPage
|
||||||
|
{
|
||||||
|
bool Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
94
Models/InstallConfig.cs
Normal file
94
Models/InstallConfig.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Models
|
||||||
|
{
|
||||||
|
public class InstallConfig
|
||||||
|
{
|
||||||
|
public OfficeVersion Version { get; set; }
|
||||||
|
public OfficeEdition Edition { get; set; }
|
||||||
|
public string Architecture { get; set; } = "64";
|
||||||
|
public string Language { get; set; } = "hu-hu";
|
||||||
|
public string ProductKey { get; set; }
|
||||||
|
public List<string> ExcludedApps { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public bool IsMsiInstall => Edition != null && Edition.IsMsiInstall;
|
||||||
|
|
||||||
|
public string GetIsoUrl()
|
||||||
|
{
|
||||||
|
return Edition?.GetIsoUrl(Architecture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetVersionDisplayName()
|
||||||
|
{
|
||||||
|
switch (Version)
|
||||||
|
{
|
||||||
|
case OfficeVersion.Office2024: return "Office 2024";
|
||||||
|
case OfficeVersion.Office2021: return "Office 2021";
|
||||||
|
case OfficeVersion.Office2019: return "Office 2019";
|
||||||
|
case OfficeVersion.Office2016: return "Office 2016";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLanguageDisplayName()
|
||||||
|
{
|
||||||
|
return LanguageList.GetDisplayName(Language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LanguageList
|
||||||
|
{
|
||||||
|
public static readonly (string Code, string Name)[] Languages = new[]
|
||||||
|
{
|
||||||
|
("hu-hu", "Magyar"),
|
||||||
|
("en-us", "English (US)"),
|
||||||
|
("de-de", "Deutsch"),
|
||||||
|
("fr-fr", "Fran\u00e7ais"),
|
||||||
|
("it-it", "Italiano"),
|
||||||
|
("es-es", "Espa\u00f1ol"),
|
||||||
|
("pt-pt", "Portugu\u00eas"),
|
||||||
|
("nl-nl", "Nederlands"),
|
||||||
|
("pl-pl", "Polski"),
|
||||||
|
("cs-cz", "\u010ce\u0161tina"),
|
||||||
|
("sk-sk", "Sloven\u010dina"),
|
||||||
|
("ro-ro", "Rom\u00e2n\u0103"),
|
||||||
|
("hr-hr", "Hrvatski"),
|
||||||
|
("sl-si", "Sloven\u0161\u010dina"),
|
||||||
|
("sr-latn-rs", "Srpski"),
|
||||||
|
("bg-bg", "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"),
|
||||||
|
("uk-ua", "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"),
|
||||||
|
("ru-ru", "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"),
|
||||||
|
("tr-tr", "T\u00fcrk\u00e7e"),
|
||||||
|
("ja-jp", "\u65e5\u672c\u8a9e"),
|
||||||
|
("zh-cn", "\u4e2d\u6587 (\u7b80\u4f53)"),
|
||||||
|
("ko-kr", "\ud55c\uad6d\uc5b4"),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetDisplayName(string code)
|
||||||
|
{
|
||||||
|
foreach (var lang in Languages)
|
||||||
|
{
|
||||||
|
if (lang.Code == code) return lang.Name;
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExcludableApps
|
||||||
|
{
|
||||||
|
// Id, DisplayName, DefaultChecked (true = telep\u00edt, false = kiz\u00e1r)
|
||||||
|
// MinEdition: "all", "standard+" (Standard \u00e9s ProPlus), "proplus" (csak ProPlus)
|
||||||
|
public static readonly (string Id, string DisplayName, bool DefaultChecked, string MinEdition)[] Apps = new[]
|
||||||
|
{
|
||||||
|
("Word", "Word", true, "all"),
|
||||||
|
("Excel", "Excel", true, "all"),
|
||||||
|
("PowerPoint", "PowerPoint", true, "all"),
|
||||||
|
("Outlook", "Outlook", true, "all"),
|
||||||
|
("OneNote", "OneNote", true, "all"),
|
||||||
|
("Access", "Access", true, "proplus"),
|
||||||
|
("Publisher", "Publisher", true, "standard+"),
|
||||||
|
("Teams", "Teams", false, "all"),
|
||||||
|
("Lync", "Skype for Business", false, "all"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Models/InstalledOffice.cs
Normal file
11
Models/InstalledOffice.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace InstaSoftOfficeTool.Models
|
||||||
|
{
|
||||||
|
public class InstalledOffice
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
public string ProductCode { get; set; }
|
||||||
|
public bool IsClickToRun { get; set; }
|
||||||
|
public bool IsSelected { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
147
Models/OfficeEdition.cs
Normal file
147
Models/OfficeEdition.cs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
namespace InstaSoftOfficeTool.Models
|
||||||
|
{
|
||||||
|
public class OfficeEdition
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string ProductId { get; set; }
|
||||||
|
public string Channel { get; set; }
|
||||||
|
public bool IsVolume { get; set; }
|
||||||
|
|
||||||
|
// MSI ISO telepítés (Office 2016)
|
||||||
|
public string IsoUrl32 { get; set; }
|
||||||
|
public string IsoUrl64 { get; set; }
|
||||||
|
|
||||||
|
public bool IsMsiInstall => IsoUrl64 != null || IsoUrl32 != null;
|
||||||
|
|
||||||
|
public string GetIsoUrl(string architecture)
|
||||||
|
{
|
||||||
|
return architecture == "32" ? IsoUrl32 : IsoUrl64;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasArchitecture(string architecture)
|
||||||
|
{
|
||||||
|
return architecture == "32" ? IsoUrl32 != null : IsoUrl64 != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OfficeEdition[] GetEditions(OfficeVersion version)
|
||||||
|
{
|
||||||
|
switch (version)
|
||||||
|
{
|
||||||
|
case OfficeVersion.Office2024:
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Standard",
|
||||||
|
Description = "Alapvet\u0151 irodai alkalmaz\u00e1sok: Word, Excel, PowerPoint, Outlook, OneNote",
|
||||||
|
ProductId = "Standard2024Volume",
|
||||||
|
Channel = "PerpetualVL2024",
|
||||||
|
IsVolume = true
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Professional Plus",
|
||||||
|
Description = "Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote",
|
||||||
|
ProductId = "ProPlus2024Volume",
|
||||||
|
Channel = "PerpetualVL2024",
|
||||||
|
IsVolume = true
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Otthoni \u00e9s kisv\u00e1llalati verzi\u00f3",
|
||||||
|
Description = "Word, Excel, PowerPoint, Outlook, OneNote \u2014 v\u00e1llalkoz\u00e1sokban is haszn\u00e1lhat\u00f3",
|
||||||
|
ProductId = "HomeBusiness2024Retail",
|
||||||
|
Channel = "Current",
|
||||||
|
IsVolume = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case OfficeVersion.Office2021:
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Standard",
|
||||||
|
Description = "Alapvet\u0151 irodai alkalmaz\u00e1sok: Word, Excel, PowerPoint, Outlook, OneNote",
|
||||||
|
ProductId = "Standard2021Volume",
|
||||||
|
Channel = "PerpetualVL2021",
|
||||||
|
IsVolume = true
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Professional Plus",
|
||||||
|
Description = "Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote",
|
||||||
|
ProductId = "ProPlus2021Volume",
|
||||||
|
Channel = "PerpetualVL2021",
|
||||||
|
IsVolume = true
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Otthoni \u00e9s kisv\u00e1llalati verzi\u00f3",
|
||||||
|
Description = "Word, Excel, PowerPoint, Outlook, OneNote \u2014 v\u00e1llalkoz\u00e1sokban is haszn\u00e1lhat\u00f3",
|
||||||
|
ProductId = "HomeBusiness2021Retail",
|
||||||
|
Channel = "Current",
|
||||||
|
IsVolume = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case OfficeVersion.Office2019:
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Standard",
|
||||||
|
Description = "Alapvet\u0151 irodai alkalmaz\u00e1sok: Word, Excel, PowerPoint, Outlook, OneNote",
|
||||||
|
ProductId = "Standard2019Volume",
|
||||||
|
Channel = "PerpetualVL2019",
|
||||||
|
IsVolume = true
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Professional Plus",
|
||||||
|
Description = "Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote, Skype for Business",
|
||||||
|
ProductId = "ProPlus2019Volume",
|
||||||
|
Channel = "PerpetualVL2019",
|
||||||
|
IsVolume = true
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Otthoni \u00e9s kisv\u00e1llalati verzi\u00f3",
|
||||||
|
Description = "Word, Excel, PowerPoint, Outlook, OneNote \u2014 v\u00e1llalkoz\u00e1sokban is haszn\u00e1lhat\u00f3",
|
||||||
|
ProductId = "HomeBusiness2019Retail",
|
||||||
|
Channel = "Current",
|
||||||
|
IsVolume = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
case OfficeVersion.Office2016:
|
||||||
|
return new[]
|
||||||
|
{
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Standard",
|
||||||
|
Description = "Alapvető irodai alkalmazások: Word, Excel, PowerPoint, Outlook, OneNote",
|
||||||
|
ProductId = "Standard2016Volume",
|
||||||
|
Channel = "PerpetualVL2019",
|
||||||
|
IsVolume = true,
|
||||||
|
IsoUrl32 = "https://soft.direct/Install/SW_DVD5_Office_2016_W32_Hungarian_MLF_X20-41370.iso",
|
||||||
|
IsoUrl64 = "https://soft.direct/Install/SW_DVD5_Office_2016_W64_Hungarian_MLF_X20-41370.iso"
|
||||||
|
},
|
||||||
|
new OfficeEdition
|
||||||
|
{
|
||||||
|
DisplayName = "Professional Plus",
|
||||||
|
Description = "Teljes csomag: Word, Excel, PowerPoint, Outlook, Access, Publisher, OneNote, Skype for Business",
|
||||||
|
ProductId = "ProPlus2016Volume",
|
||||||
|
Channel = "PerpetualVL2019",
|
||||||
|
IsVolume = true,
|
||||||
|
IsoUrl64 = "https://soft.direct/Install/SW_DVD5_Office_Professional_Plus_2016_64Bit_Hungarian_MLF_X20-42439.ISO"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new OfficeEdition[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Models/OfficeVersion.cs
Normal file
10
Models/OfficeVersion.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace InstaSoftOfficeTool.Models
|
||||||
|
{
|
||||||
|
public enum OfficeVersion
|
||||||
|
{
|
||||||
|
Office2024,
|
||||||
|
Office2021,
|
||||||
|
Office2019,
|
||||||
|
Office2016
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Pages/ConfigPage.xaml
Normal file
53
Pages/ConfigPage.xaml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.ConfigPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="40,30">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Beállítások" FontSize="24" FontWeight="Light" Margin="0,0,0,20"/>
|
||||||
|
|
||||||
|
<!-- Arch + Language side by side -->
|
||||||
|
<Grid Margin="0,0,0,20">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="0" Margin="0,0,16,0">
|
||||||
|
<TextBlock Text="Architektúra" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,8"/>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<RadioButton x:Name="Rb64" GroupName="Arch" IsChecked="True"
|
||||||
|
Style="{StaticResource CardRadioButton}" Margin="0,0,10,0" MinWidth="140">
|
||||||
|
<TextBlock Text="64-bit (ajánlott)" FontSize="14" Margin="6,0"/>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton x:Name="Rb32" GroupName="Arch"
|
||||||
|
Style="{StaticResource CardRadioButton}" MinWidth="100">
|
||||||
|
<TextBlock Text="32-bit" FontSize="14" Margin="6,0"/>
|
||||||
|
</RadioButton>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Column="1">
|
||||||
|
<TextBlock Text="Telepítési nyelv" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,8"/>
|
||||||
|
<ComboBox x:Name="CbLanguage" Style="{StaticResource FluentComboBox}"
|
||||||
|
HorizontalAlignment="Stretch"/>
|
||||||
|
<TextBlock x:Name="LanguageNote" Visibility="Collapsed"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Margin="0,4,0,0" TextWrapping="Wrap"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock x:Name="ArchNote" Visibility="Collapsed"
|
||||||
|
FontSize="12" Foreground="#E65100" Margin="0,-12,0,16" TextWrapping="Wrap"/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="AppTitle" Text="Telepítendő alkalmazások" FontSize="15" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<WrapPanel x:Name="AppCheckBoxes" Orientation="Horizontal"/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="AppNote" Visibility="Collapsed"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Margin="0,8,0,0" TextWrapping="Wrap"/>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Page>
|
||||||
142
Pages/ConfigPage.xaml.cs
Normal file
142
Pages/ConfigPage.xaml.cs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class ConfigPage : Page, IWizardPage
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly InstallConfig _config;
|
||||||
|
private readonly List<CheckBox> _appCheckBoxes = new List<CheckBox>();
|
||||||
|
|
||||||
|
public ConfigPage(MainWindow main, InstallConfig config)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
_config = config;
|
||||||
|
|
||||||
|
bool isMsi = _config.IsMsiInstall;
|
||||||
|
|
||||||
|
// Restore arch
|
||||||
|
if (_config.Architecture == "32")
|
||||||
|
{
|
||||||
|
Rb32.IsChecked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSI: handle architecture constraints
|
||||||
|
if (isMsi && _config.Edition != null && !_config.Edition.HasArchitecture("32"))
|
||||||
|
{
|
||||||
|
Rb32.IsEnabled = false;
|
||||||
|
Rb64.IsChecked = true;
|
||||||
|
ArchNote.Text = "A Professional Plus kiadáshoz csak 64-bites telepítő érhető el.";
|
||||||
|
ArchNote.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate language combo
|
||||||
|
foreach (var lang in LanguageList.Languages)
|
||||||
|
{
|
||||||
|
CbLanguage.Items.Add(new ComboBoxItem
|
||||||
|
{
|
||||||
|
Content = lang.Name + " (" + lang.Code + ")",
|
||||||
|
Tag = lang.Code
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select current language
|
||||||
|
for (int i = 0; i < CbLanguage.Items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = (ComboBoxItem)CbLanguage.Items[i];
|
||||||
|
if ((string)item.Tag == _config.Language)
|
||||||
|
{
|
||||||
|
CbLanguage.SelectedIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (CbLanguage.SelectedIndex < 0) CbLanguage.SelectedIndex = 0;
|
||||||
|
|
||||||
|
// MSI: language is baked into the ISO
|
||||||
|
if (isMsi)
|
||||||
|
{
|
||||||
|
CbLanguage.IsEnabled = false;
|
||||||
|
// Force Hungarian selection
|
||||||
|
for (int i = 0; i < CbLanguage.Items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = (ComboBoxItem)CbLanguage.Items[i];
|
||||||
|
if ((string)item.Tag == "hu-hu")
|
||||||
|
{
|
||||||
|
CbLanguage.SelectedIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LanguageNote.Text = "Az Office 2016 telepítő ISO magyar nyelvű. A nyelv nem módosítható.";
|
||||||
|
LanguageNote.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSI: app exclusion not available through our tool
|
||||||
|
if (isMsi)
|
||||||
|
{
|
||||||
|
AppTitle.Visibility = Visibility.Collapsed;
|
||||||
|
AppCheckBoxes.Visibility = Visibility.Collapsed;
|
||||||
|
AppNote.Text = "Az alkalmazások kiválasztása a Microsoft telepítő felületén lehetséges.";
|
||||||
|
AppNote.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Populate app checkboxes (ODT mode)
|
||||||
|
foreach (var app in ExcludableApps.Apps)
|
||||||
|
{
|
||||||
|
bool isChecked = _config.ExcludedApps.Count > 0
|
||||||
|
? !_config.ExcludedApps.Contains(app.Id)
|
||||||
|
: app.DefaultChecked;
|
||||||
|
|
||||||
|
bool isProPlus = _config.Edition != null &&
|
||||||
|
_config.Edition.ProductId.Contains("ProPlus");
|
||||||
|
bool isStandard = _config.Edition != null &&
|
||||||
|
_config.Edition.ProductId.Contains("Standard");
|
||||||
|
|
||||||
|
bool unavailable = false;
|
||||||
|
if (app.MinEdition == "proplus" && !isProPlus)
|
||||||
|
unavailable = true;
|
||||||
|
else if (app.MinEdition == "standard+" && !isProPlus && !isStandard)
|
||||||
|
unavailable = true;
|
||||||
|
|
||||||
|
var cb = new CheckBox
|
||||||
|
{
|
||||||
|
Content = app.DisplayName,
|
||||||
|
IsChecked = unavailable ? false : isChecked,
|
||||||
|
IsEnabled = !unavailable,
|
||||||
|
Tag = app.Id,
|
||||||
|
Style = (Style)FindResource("FluentCheckBox"),
|
||||||
|
Margin = new Thickness(0, 4, 24, 4),
|
||||||
|
MinWidth = 160
|
||||||
|
};
|
||||||
|
_appCheckBoxes.Add(cb);
|
||||||
|
AppCheckBoxes.Children.Add(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
_config.Architecture = Rb64.IsChecked == true ? "64" : "32";
|
||||||
|
|
||||||
|
if (CbLanguage.SelectedItem is ComboBoxItem selected)
|
||||||
|
{
|
||||||
|
_config.Language = (string)selected.Tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
_config.ExcludedApps.Clear();
|
||||||
|
foreach (var cb in _appCheckBoxes)
|
||||||
|
{
|
||||||
|
if (cb.IsChecked != true || !cb.IsEnabled)
|
||||||
|
{
|
||||||
|
_config.ExcludedApps.Add((string)cb.Tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Pages/ConfirmDialog.xaml
Normal file
41
Pages/ConfirmDialog.xaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<UserControl x:Class="InstaSoftOfficeTool.Pages.ConfirmDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
|
||||||
|
<!-- Backdrop overlay -->
|
||||||
|
<Grid>
|
||||||
|
<Border Background="#66000000" MouseDown="Backdrop_MouseDown"/>
|
||||||
|
|
||||||
|
<!-- Dialog card -->
|
||||||
|
<Border Background="{StaticResource CardBrush}" CornerRadius="12"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
MinWidth="400" MaxWidth="500" Padding="28,24">
|
||||||
|
<Border.Effect>
|
||||||
|
<DropShadowEffect ShadowDepth="8" BlurRadius="32" Opacity="0.2" Color="Black"/>
|
||||||
|
</Border.Effect>
|
||||||
|
|
||||||
|
<StackPanel>
|
||||||
|
<!-- Icon + Title -->
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
|
||||||
|
<TextBlock x:Name="DialogIcon" FontFamily="Segoe MDL2 Assets" FontSize="22"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,12,0"/>
|
||||||
|
<TextBlock x:Name="DialogTitle" FontSize="18" FontWeight="SemiBold"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Message -->
|
||||||
|
<TextBlock x:Name="DialogMessage" FontSize="14" TextWrapping="Wrap"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,0,0,24"/>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button x:Name="BtnCancel" Style="{StaticResource SecondaryButton}"
|
||||||
|
Click="BtnCancel_Click" Margin="0,0,8,0"/>
|
||||||
|
<Button x:Name="BtnConfirm" Style="{StaticResource PrimaryButton}"
|
||||||
|
Click="BtnConfirm_Click"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
75
Pages/ConfirmDialog.xaml.cs
Normal file
75
Pages/ConfirmDialog.xaml.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class ConfirmDialog : UserControl
|
||||||
|
{
|
||||||
|
private TaskCompletionSource<bool> _tcs;
|
||||||
|
|
||||||
|
public ConfirmDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<bool> ShowAsync(string title, string message,
|
||||||
|
string confirmText = "Igen", string cancelText = "M\u00e9gse",
|
||||||
|
DialogType type = DialogType.Warning)
|
||||||
|
{
|
||||||
|
DialogTitle.Text = title;
|
||||||
|
DialogMessage.Text = message;
|
||||||
|
BtnConfirm.Content = confirmText;
|
||||||
|
BtnCancel.Content = cancelText;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DialogType.Warning:
|
||||||
|
DialogIcon.Text = "\uE7BA";
|
||||||
|
DialogIcon.Foreground = (Brush)FindResource("WarningBrush");
|
||||||
|
break;
|
||||||
|
case DialogType.Question:
|
||||||
|
DialogIcon.Text = "\uE9CE";
|
||||||
|
DialogIcon.Foreground = (Brush)FindResource("AccentBrush");
|
||||||
|
break;
|
||||||
|
case DialogType.Error:
|
||||||
|
DialogIcon.Text = "\uE711";
|
||||||
|
DialogIcon.Foreground = (Brush)FindResource("ErrorBrush");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Visibility = Visibility.Visible;
|
||||||
|
_tcs = new TaskCompletionSource<bool>();
|
||||||
|
return _tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnConfirm_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
_tcs?.TrySetResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnCancel_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
_tcs?.TrySetResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Backdrop_MouseDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
// click outside = cancel
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
_tcs?.TrySetResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DialogType
|
||||||
|
{
|
||||||
|
Warning,
|
||||||
|
Question,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Pages/EditionPage.xaml
Normal file
20
Pages/EditionPage.xaml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.EditionPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,24">
|
||||||
|
<TextBlock x:Name="TitleText" Text="Válasszon kiadást" FontSize="24" FontWeight="Light"/>
|
||||||
|
<TextBlock x:Name="SubtitleText" FontSize="14"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel x:Name="EditionPanel" Grid.Row="1" VerticalAlignment="Top"/>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
80
Pages/EditionPage.xaml.cs
Normal file
80
Pages/EditionPage.xaml.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class EditionPage : Page, IWizardPage
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly InstallConfig _config;
|
||||||
|
private readonly OfficeEdition[] _editions;
|
||||||
|
|
||||||
|
public EditionPage(MainWindow main, InstallConfig config)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
_config = config;
|
||||||
|
_editions = OfficeEdition.GetEditions(_config.Version);
|
||||||
|
|
||||||
|
SubtitleText.Text = _config.GetVersionDisplayName() + " \u2014 melyik kiad\u00e1st szeretn\u00e9?";
|
||||||
|
|
||||||
|
BuildEditionCards();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildEditionCards()
|
||||||
|
{
|
||||||
|
EditionPanel.Children.Clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < _editions.Length; i++)
|
||||||
|
{
|
||||||
|
var edition = _editions[i];
|
||||||
|
var rb = new RadioButton
|
||||||
|
{
|
||||||
|
GroupName = "Edition",
|
||||||
|
IsChecked = i == 0 || (_config.Edition != null && _config.Edition.ProductId == edition.ProductId),
|
||||||
|
Style = (Style)FindResource("CardRadioButton"),
|
||||||
|
Margin = new Thickness(0, 0, 0, 10),
|
||||||
|
Tag = i
|
||||||
|
};
|
||||||
|
|
||||||
|
var sp = new StackPanel { Margin = new Thickness(8, 2, 8, 2) };
|
||||||
|
|
||||||
|
var titleBlock = new TextBlock
|
||||||
|
{
|
||||||
|
Text = edition.DisplayName,
|
||||||
|
FontSize = 18,
|
||||||
|
FontWeight = FontWeights.SemiBold
|
||||||
|
};
|
||||||
|
sp.Children.Add(titleBlock);
|
||||||
|
|
||||||
|
var descBlock = new TextBlock
|
||||||
|
{
|
||||||
|
Text = edition.Description,
|
||||||
|
FontSize = 12,
|
||||||
|
Foreground = (System.Windows.Media.Brush)FindResource("TextSecondaryBrush"),
|
||||||
|
TextWrapping = TextWrapping.Wrap,
|
||||||
|
Margin = new Thickness(0, 2, 0, 0)
|
||||||
|
};
|
||||||
|
sp.Children.Add(descBlock);
|
||||||
|
|
||||||
|
rb.Content = sp;
|
||||||
|
EditionPanel.Children.Add(rb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
foreach (var child in EditionPanel.Children)
|
||||||
|
{
|
||||||
|
if (child is RadioButton rb && rb.IsChecked == true)
|
||||||
|
{
|
||||||
|
int idx = (int)rb.Tag;
|
||||||
|
_config.Edition = _editions[idx];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
Pages/PreInstallCheckPage.xaml
Normal file
67
Pages/PreInstallCheckPage.xaml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.PreInstallCheckPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,16">
|
||||||
|
<TextBlock Text="Meglévő Office ellenőrzése" FontSize="24" FontWeight="Light"/>
|
||||||
|
<TextBlock x:Name="SubtitleText" FontSize="14"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel x:Name="OfficeListPanel"/>
|
||||||
|
|
||||||
|
<!-- No office found card -->
|
||||||
|
<Border x:Name="NoOfficeCard" Visibility="Collapsed"
|
||||||
|
Background="{StaticResource CardBrush}" CornerRadius="8"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
Padding="16,14">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="18"
|
||||||
|
Foreground="{StaticResource SuccessBrush}" VerticalAlignment="Center"
|
||||||
|
Margin="0,0,12,0"/>
|
||||||
|
<TextBlock Text="Nem található telepített Office. A telepítés indítható."
|
||||||
|
FontSize="14" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- License cleanup -->
|
||||||
|
<Border x:Name="LicenseCleanupPanel" Margin="0,12,0,0" Visibility="Collapsed"
|
||||||
|
Background="{StaticResource CardBrush}" CornerRadius="8"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<CheckBox x:Name="CbCleanLicense" Style="{StaticResource FluentCheckBox}"
|
||||||
|
Content="Licenc-adatbázis tisztítása is (ajánlott)"
|
||||||
|
IsChecked="True"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Log -->
|
||||||
|
<Border x:Name="LogPanel" Margin="0,12,0,0" Background="#F8F8F8"
|
||||||
|
CornerRadius="6" Padding="12" Visibility="Collapsed"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1">
|
||||||
|
<TextBox x:Name="LogText" FontFamily="Consolas" FontSize="11"
|
||||||
|
TextWrapping="Wrap" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
IsReadOnly="True" Background="Transparent" BorderThickness="0"
|
||||||
|
VerticalScrollBarVisibility="Auto" AcceptsReturn="True" MaxHeight="150"/>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
|
||||||
|
<Button x:Name="BtnSkip" Content="Kihagyás, telepítés folytatása"
|
||||||
|
Style="{StaticResource SecondaryButton}" Click="BtnSkip_Click" Margin="0,0,8,0"/>
|
||||||
|
<Button x:Name="BtnRemove" Content="Eltávolítás, majd telepítés"
|
||||||
|
Style="{StaticResource PrimaryButton}" Click="BtnRemove_Click" Visibility="Collapsed"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
164
Pages/PreInstallCheckPage.xaml.cs
Normal file
164
Pages/PreInstallCheckPage.xaml.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
using InstaSoftOfficeTool.Services;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class PreInstallCheckPage : Page
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private List<InstalledOffice> _detected;
|
||||||
|
|
||||||
|
public PreInstallCheckPage(MainWindow main)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
Loaded += (s, e) => DetectOffice();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DetectOffice()
|
||||||
|
{
|
||||||
|
_detected = OfficeDetector.Detect();
|
||||||
|
OfficeListPanel.Children.Clear();
|
||||||
|
|
||||||
|
if (_detected.Count == 0)
|
||||||
|
{
|
||||||
|
SubtitleText.Text = "Nincs kor\u00e1bbi Office telep\u00edt\u00e9s a g\u00e9pen.";
|
||||||
|
NoOfficeCard.Visibility = Visibility.Visible;
|
||||||
|
BtnRemove.Visibility = Visibility.Collapsed;
|
||||||
|
LicenseCleanupPanel.Visibility = Visibility.Collapsed;
|
||||||
|
BtnSkip.Content = "Tov\u00e1bb a telep\u00edt\u00e9shez \u2192";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SubtitleText.Text = "A sz\u00e1m\u00edt\u00f3g\u00e9pen tal\u00e1lt Office telep\u00edt\u00e9sek. Az \u00faj Office telep\u00edt\u00e9se el\u0151tt aj\u00e1nlott elt\u00e1vol\u00edtani.";
|
||||||
|
BtnRemove.Visibility = Visibility.Visible;
|
||||||
|
LicenseCleanupPanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
foreach (var office in _detected)
|
||||||
|
{
|
||||||
|
var cb = new CheckBox
|
||||||
|
{
|
||||||
|
Content = office.DisplayName +
|
||||||
|
(string.IsNullOrEmpty(office.Version) ? "" : " (" + office.Version + ")"),
|
||||||
|
IsChecked = true,
|
||||||
|
Style = (Style)FindResource("FluentCheckBox"),
|
||||||
|
Tag = office,
|
||||||
|
Margin = new Thickness(0, 4, 0, 4),
|
||||||
|
FontSize = 14
|
||||||
|
};
|
||||||
|
OfficeListPanel.Children.Add(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnSkip_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_main.ProceedToInstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnRemove_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
bool confirmed = await _main.ConfirmAsync(
|
||||||
|
"Office elt\u00e1vol\u00edt\u00e1s",
|
||||||
|
"Biztosan el szeretn\u00e9 t\u00e1vol\u00edtani a kiv\u00e1lasztott Office telep\u00edt\u00e9seket az \u00faj verzi\u00f3 telep\u00edt\u00e9se el\u0151tt?",
|
||||||
|
"Elt\u00e1vol\u00edt\u00e1s", "M\u00e9gse");
|
||||||
|
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
BtnRemove.IsEnabled = false;
|
||||||
|
BtnSkip.IsEnabled = false;
|
||||||
|
LogPanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var c2rProducts = new System.Collections.Generic.List<string>();
|
||||||
|
|
||||||
|
foreach (UIElement child in OfficeListPanel.Children)
|
||||||
|
{
|
||||||
|
if (child is CheckBox cb && cb.IsChecked == true)
|
||||||
|
{
|
||||||
|
var office = (InstalledOffice)cb.Tag;
|
||||||
|
if (office.IsClickToRun)
|
||||||
|
{
|
||||||
|
c2rProducts.Add(office.ProductCode);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(office.ProductCode))
|
||||||
|
{
|
||||||
|
AppendLog("MSI elt\u00e1vol\u00edt\u00e1s: " + office.DisplayName);
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
runner.OutputReceived += msg => Dispatcher.Invoke(() => AppendLog(msg));
|
||||||
|
int code = await runner.RunAsync("msiexec", "/x " + office.ProductCode + " /qb");
|
||||||
|
AppendLog("MSI exit code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c2rProducts.Count > 0)
|
||||||
|
{
|
||||||
|
AppendLog("Click-to-Run term\u00e9kek elt\u00e1vol\u00edt\u00e1sa: " + string.Join(", ", c2rProducts));
|
||||||
|
|
||||||
|
var downloader = new OdtDownloader();
|
||||||
|
downloader.StatusChanged += msg => Dispatcher.Invoke(() => AppendLog(msg));
|
||||||
|
|
||||||
|
bool ok = await downloader.DownloadAndExtractAsync();
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
AppendLog("HIBA: Az ODT let\u00f6lt\u00e9se sikertelen.");
|
||||||
|
BtnRemove.IsEnabled = true;
|
||||||
|
BtnSkip.IsEnabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string removeXml = OdtXmlGenerator.GenerateRemoveProducts(c2rProducts.ToArray());
|
||||||
|
string xmlPath = Path.Combine(downloader.OdtFolder, "remove.xml");
|
||||||
|
File.WriteAllText(xmlPath, removeXml);
|
||||||
|
|
||||||
|
AppendLog("Futtat\u00e1s: setup.exe /configure remove.xml");
|
||||||
|
AppendLog("Ez eltarthat n\u00e9h\u00e1ny percig...");
|
||||||
|
|
||||||
|
int exitCode = await downloader.RunRemoveAsync(xmlPath,
|
||||||
|
msg => Dispatcher.Invoke(() => AppendLog(msg)));
|
||||||
|
|
||||||
|
AppendLog("setup.exe exit code: " + exitCode);
|
||||||
|
AppendLog(exitCode == 0
|
||||||
|
? "Office sikeresen elt\u00e1vol\u00edtva."
|
||||||
|
: "FIGYELEM: Az elt\u00e1vol\u00edt\u00e1s nem siker\u00fclt (k\u00f3d: " + exitCode + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CbCleanLicense.IsChecked == true)
|
||||||
|
{
|
||||||
|
AppendLog("Licenc-adatb\u00e1zis tiszt\u00edt\u00e1sa...");
|
||||||
|
var lm = new LicenseManager();
|
||||||
|
if (lm.FindOspp())
|
||||||
|
{
|
||||||
|
var cleanResult = await lm.RemoveAllKeysAsync();
|
||||||
|
AppendLog(cleanResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendLog("Az ospp.vbs nem tal\u00e1lhat\u00f3 \u2014 licenc-tiszt\u00edt\u00e1s kihagyva.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendLog("K\u00e9sz. Tov\u00e1bbl\u00e9p\u00e9s az \u00faj Office telep\u00edt\u00e9s\u00e9hez...");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppendLog("V\u00e1ratlan hiba: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
await System.Threading.Tasks.Task.Delay(2000);
|
||||||
|
_main.ProceedToInstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendLog(string text)
|
||||||
|
{
|
||||||
|
LogText.Text += DateTime.Now.ToString("HH:mm:ss") + " " + text + "\n";
|
||||||
|
LogText.ScrollToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Pages/ProductKeyDialog.xaml
Normal file
69
Pages/ProductKeyDialog.xaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<UserControl x:Class="InstaSoftOfficeTool.Pages.ProductKeyDialog"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Border Background="#66000000" MouseDown="Backdrop_MouseDown"/>
|
||||||
|
|
||||||
|
<Border Background="{StaticResource CardBrush}" CornerRadius="12"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
MinWidth="480" Padding="28,24">
|
||||||
|
<Border.Effect>
|
||||||
|
<DropShadowEffect ShadowDepth="8" BlurRadius="32" Opacity="0.2" Color="Black"/>
|
||||||
|
</Border.Effect>
|
||||||
|
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="22"
|
||||||
|
Foreground="{StaticResource AccentBrush}"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,12,0"/>
|
||||||
|
<TextBlock Text="Termékkulcs megadása" FontSize="18" FontWeight="SemiBold"
|
||||||
|
VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock Text="Adja meg a 25 karakteres termékkulcsot az aktiváláshoz."
|
||||||
|
FontSize="13" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Margin="0,0,0,16"/>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,20">
|
||||||
|
<TextBox x:Name="DKey1" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="76" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
FontFamily="Consolas" FontSize="15" TextAlignment="Center"
|
||||||
|
TextChanged="KeyBox_TextChanged"/>
|
||||||
|
<TextBlock Text="-" FontSize="18" VerticalAlignment="Center" Margin="4,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="DKey2" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="76" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
FontFamily="Consolas" FontSize="15" TextAlignment="Center"
|
||||||
|
TextChanged="KeyBox_TextChanged"/>
|
||||||
|
<TextBlock Text="-" FontSize="18" VerticalAlignment="Center" Margin="4,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="DKey3" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="76" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
FontFamily="Consolas" FontSize="15" TextAlignment="Center"
|
||||||
|
TextChanged="KeyBox_TextChanged"/>
|
||||||
|
<TextBlock Text="-" FontSize="18" VerticalAlignment="Center" Margin="4,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="DKey4" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="76" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
FontFamily="Consolas" FontSize="15" TextAlignment="Center"
|
||||||
|
TextChanged="KeyBox_TextChanged"/>
|
||||||
|
<TextBlock Text="-" FontSize="18" VerticalAlignment="Center" Margin="4,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="DKey5" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="76" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
FontFamily="Consolas" FontSize="15" TextAlignment="Center"
|
||||||
|
TextChanged="KeyBox_TextChanged"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||||
|
<Button Content="Mégse" Style="{StaticResource SecondaryButton}"
|
||||||
|
Click="BtnCancel_Click" Margin="0,0,8,0"/>
|
||||||
|
<Button x:Name="BtnOk" Content="Aktiválás" Style="{StaticResource PrimaryButton}"
|
||||||
|
Click="BtnOk_Click" IsEnabled="False"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
124
Pages/ProductKeyDialog.xaml.cs
Normal file
124
Pages/ProductKeyDialog.xaml.cs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class ProductKeyDialog : UserControl
|
||||||
|
{
|
||||||
|
private TaskCompletionSource<string> _tcs;
|
||||||
|
private readonly TextBox[] _keyBoxes;
|
||||||
|
private bool _suppress;
|
||||||
|
|
||||||
|
public ProductKeyDialog()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_keyBoxes = new[] { DKey1, DKey2, DKey3, DKey4, DKey5 };
|
||||||
|
|
||||||
|
foreach (var box in _keyBoxes)
|
||||||
|
{
|
||||||
|
DataObject.AddPastingHandler(box, OnPaste);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> ShowAsync()
|
||||||
|
{
|
||||||
|
foreach (var box in _keyBoxes) box.Text = "";
|
||||||
|
BtnOk.IsEnabled = false;
|
||||||
|
Visibility = Visibility.Visible;
|
||||||
|
DKey1.Focus();
|
||||||
|
_tcs = new TaskCompletionSource<string>();
|
||||||
|
return _tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetFullKey()
|
||||||
|
{
|
||||||
|
return string.Join("-", DKey1.Text, DKey2.Text, DKey3.Text, DKey4.Text, DKey5.Text).ToUpper();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsKeyComplete()
|
||||||
|
{
|
||||||
|
foreach (var box in _keyBoxes)
|
||||||
|
{
|
||||||
|
if (box.Text.Length != 5 || !Regex.IsMatch(box.Text, "^[A-Za-z0-9]{5}$"))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KeyBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppress) return;
|
||||||
|
var tb = (TextBox)sender;
|
||||||
|
|
||||||
|
var cleaned = Regex.Replace(tb.Text, "[^A-Za-z0-9]", "");
|
||||||
|
if (cleaned != tb.Text)
|
||||||
|
{
|
||||||
|
_suppress = true;
|
||||||
|
tb.Text = cleaned;
|
||||||
|
tb.CaretIndex = cleaned.Length;
|
||||||
|
_suppress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb.Text.Length == 5)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _keyBoxes.Length - 1; i++)
|
||||||
|
{
|
||||||
|
if (_keyBoxes[i] == tb)
|
||||||
|
{
|
||||||
|
_keyBoxes[i + 1].Focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BtnOk.IsEnabled = IsKeyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPaste(object sender, DataObjectPastingEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.DataObject.GetDataPresent(typeof(string))) return;
|
||||||
|
var pasted = (string)e.DataObject.GetData(typeof(string));
|
||||||
|
var allAlphaNum = Regex.Replace(pasted, "[^A-Za-z0-9]", "");
|
||||||
|
|
||||||
|
if (allAlphaNum.Length <= 5) return;
|
||||||
|
|
||||||
|
e.CancelCommand();
|
||||||
|
_suppress = true;
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
int start = i * 5;
|
||||||
|
if (start < allAlphaNum.Length)
|
||||||
|
{
|
||||||
|
int len = Math.Min(5, allAlphaNum.Length - start);
|
||||||
|
_keyBoxes[i].Text = allAlphaNum.Substring(start, len).ToUpper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_suppress = false;
|
||||||
|
_keyBoxes[4].Focus();
|
||||||
|
_keyBoxes[4].CaretIndex = _keyBoxes[4].Text.Length;
|
||||||
|
BtnOk.IsEnabled = IsKeyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnOk_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
_tcs?.TrySetResult(GetFullKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnCancel_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
_tcs?.TrySetResult(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Backdrop_MouseDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
Visibility = Visibility.Collapsed;
|
||||||
|
_tcs?.TrySetResult(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Pages/ProductKeyPage.xaml
Normal file
62
Pages/ProductKeyPage.xaml
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.ProductKeyPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,24">
|
||||||
|
<TextBlock Text="Termékkulcs" FontSize="24" FontWeight="Light"/>
|
||||||
|
<TextBlock Text="Adja meg a 25 karakteres termékkulcsot, vagy hagyja üresen."
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="1" VerticalAlignment="Top">
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||||
|
<TextBox x:Name="Key1" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="110" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
TextChanged="KeyBox_TextChanged" FontFamily="Consolas" TextAlignment="Center"/>
|
||||||
|
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="Key2" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="110" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
TextChanged="KeyBox_TextChanged" FontFamily="Consolas" TextAlignment="Center"/>
|
||||||
|
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="Key3" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="110" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
TextChanged="KeyBox_TextChanged" FontFamily="Consolas" TextAlignment="Center"/>
|
||||||
|
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="Key4" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="110" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
TextChanged="KeyBox_TextChanged" FontFamily="Consolas" TextAlignment="Center"/>
|
||||||
|
<TextBlock Text="-" FontSize="20" VerticalAlignment="Center" Margin="6,0"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
<TextBox x:Name="Key5" Style="{StaticResource FluentTextBox}"
|
||||||
|
Width="110" MaxLength="5" CharacterCasing="Upper"
|
||||||
|
TextChanged="KeyBox_TextChanged" FontFamily="Consolas" TextAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<CheckBox x:Name="CbSkipKey" Style="{StaticResource FluentCheckBox}"
|
||||||
|
Content="Később szeretném megadni"
|
||||||
|
Margin="0,20,0,0" Checked="CbSkipKey_Changed" Unchecked="CbSkipKey_Changed"/>
|
||||||
|
|
||||||
|
<Border Background="#FFF8E1" CornerRadius="6" Padding="16,12" Margin="0,24,0,0"
|
||||||
|
BorderBrush="#FFE082" BorderThickness="1" MaxWidth="600" HorizontalAlignment="Left">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Tudnivaló" FontWeight="SemiBold" FontSize="13" Margin="0,0,0,4"/>
|
||||||
|
<TextBlock TextWrapping="Wrap" FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Text="Ha megadja a termékkulcsot, az Office automatikusan aktiválódik a telepítés után. Ha nem adja meg, későbbi időpontban is megadható az Office alkalmazáson belül (Fájl > Fiók > Termékkulcs módosítása)."/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<TextBlock x:Name="ValidationMessage" FontSize="12" Margin="0,12,0,0"
|
||||||
|
Foreground="{StaticResource ErrorBrush}" Visibility="Collapsed"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
149
Pages/ProductKeyPage.xaml.cs
Normal file
149
Pages/ProductKeyPage.xaml.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class ProductKeyPage : Page, IWizardPage
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly InstallConfig _config;
|
||||||
|
private readonly TextBox[] _keyBoxes;
|
||||||
|
private bool _suppressAutoTab;
|
||||||
|
|
||||||
|
public ProductKeyPage(MainWindow main, InstallConfig config)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
_config = config;
|
||||||
|
_keyBoxes = new[] { Key1, Key2, Key3, Key4, Key5 };
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_config.ProductKey))
|
||||||
|
{
|
||||||
|
var parts = _config.ProductKey.Split('-');
|
||||||
|
for (int i = 0; i < parts.Length && i < 5; i++)
|
||||||
|
{
|
||||||
|
_keyBoxes[i].Text = parts[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intercept paste on all boxes
|
||||||
|
foreach (var box in _keyBoxes)
|
||||||
|
{
|
||||||
|
DataObject.AddPastingHandler(box, OnPaste);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPaste(object sender, DataObjectPastingEventArgs e)
|
||||||
|
{
|
||||||
|
if (!e.DataObject.GetDataPresent(typeof(string))) return;
|
||||||
|
|
||||||
|
var pasted = (string)e.DataObject.GetData(typeof(string));
|
||||||
|
var allAlphaNum = Regex.Replace(pasted, "[^A-Za-z0-9]", "");
|
||||||
|
|
||||||
|
// Only intercept if it looks like a full key (more than 5 chars)
|
||||||
|
if (allAlphaNum.Length <= 5) return;
|
||||||
|
|
||||||
|
e.CancelCommand(); // prevent default paste
|
||||||
|
|
||||||
|
_suppressAutoTab = true;
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
int start = i * 5;
|
||||||
|
if (start < allAlphaNum.Length)
|
||||||
|
{
|
||||||
|
int len = System.Math.Min(5, allAlphaNum.Length - start);
|
||||||
|
_keyBoxes[i].Text = allAlphaNum.Substring(start, len).ToUpper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_suppressAutoTab = false;
|
||||||
|
_keyBoxes[4].Focus();
|
||||||
|
_keyBoxes[4].CaretIndex = _keyBoxes[4].Text.Length;
|
||||||
|
ValidationMessage.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void KeyBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_suppressAutoTab) return;
|
||||||
|
|
||||||
|
var tb = (TextBox)sender;
|
||||||
|
var raw = tb.Text;
|
||||||
|
|
||||||
|
// Clean non-alphanumeric chars
|
||||||
|
var cleaned = Regex.Replace(raw, "[^A-Za-z0-9]", "");
|
||||||
|
if (cleaned != raw)
|
||||||
|
{
|
||||||
|
_suppressAutoTab = true;
|
||||||
|
tb.Text = cleaned;
|
||||||
|
tb.CaretIndex = cleaned.Length;
|
||||||
|
_suppressAutoTab = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-tab to next box when 5 chars entered
|
||||||
|
if (tb.Text.Length == 5)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _keyBoxes.Length - 1; i++)
|
||||||
|
{
|
||||||
|
if (_keyBoxes[i] == tb)
|
||||||
|
{
|
||||||
|
_keyBoxes[i + 1].Focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationMessage.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CbSkipKey_Changed(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
bool skip = CbSkipKey.IsChecked == true;
|
||||||
|
foreach (var box in _keyBoxes)
|
||||||
|
{
|
||||||
|
box.IsEnabled = !skip;
|
||||||
|
if (skip) box.Text = "";
|
||||||
|
}
|
||||||
|
ValidationMessage.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
if (CbSkipKey.IsChecked == true)
|
||||||
|
{
|
||||||
|
_config.ProductKey = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allEmpty = true;
|
||||||
|
foreach (var box in _keyBoxes)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(box.Text))
|
||||||
|
{
|
||||||
|
allEmpty = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allEmpty)
|
||||||
|
{
|
||||||
|
_config.ProductKey = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var box in _keyBoxes)
|
||||||
|
{
|
||||||
|
if (box.Text.Length != 5 || !Regex.IsMatch(box.Text, "^[A-Za-z0-9]{5}$"))
|
||||||
|
{
|
||||||
|
ValidationMessage.Text = "A term\u00e9kkulcsnak 5 x 5 alfanumerikus karakterb\u0151l kell \u00e1llnia.";
|
||||||
|
ValidationMessage.Visibility = Visibility.Visible;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_config.ProductKey = string.Join("-",
|
||||||
|
Key1.Text, Key2.Text, Key3.Text, Key4.Text, Key5.Text).ToUpper();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
Pages/ProgressPage.xaml
Normal file
90
Pages/ProgressPage.xaml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.ProgressPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="40,30">
|
||||||
|
<StackPanel>
|
||||||
|
|
||||||
|
<TextBlock x:Name="TitleText" Text="Telepítés folyamatban..."
|
||||||
|
FontSize="24" FontWeight="Light" Margin="0,0,0,20"/>
|
||||||
|
|
||||||
|
<StackPanel Margin="0,0,0,16">
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,4">
|
||||||
|
<TextBlock x:Name="Step1Icon" Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Width="24"/>
|
||||||
|
<TextBlock x:Name="Step1Text" Text="Office Deployment Tool letöltése..."
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,4">
|
||||||
|
<TextBlock x:Name="Step2Icon" Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Width="24"/>
|
||||||
|
<TextBlock x:Name="Step2Text" Text="Konfiguráció generálása..."
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,4">
|
||||||
|
<TextBlock x:Name="Step3Icon" Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Width="24"/>
|
||||||
|
<TextBlock x:Name="Step3Text" Text="Office telepítése... (akár 30-40 percet is igénybe vehet)"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ProgressBar x:Name="MainProgress" Style="{StaticResource FluentProgressBar}"
|
||||||
|
IsIndeterminate="True" Margin="0,0,0,0"/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="DownloadInfo" FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Margin="0,4,0,0" Visibility="Collapsed"/>
|
||||||
|
|
||||||
|
<Border Background="#F8F8F8" CornerRadius="6" Padding="12" Margin="0,12,0,0"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1">
|
||||||
|
<TextBox x:Name="LogText" FontFamily="Consolas" FontSize="11"
|
||||||
|
TextWrapping="Wrap" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
IsReadOnly="True" Background="Transparent" BorderThickness="0"
|
||||||
|
VerticalScrollBarVisibility="Auto" AcceptsReturn="True"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Done panel -->
|
||||||
|
<StackPanel x:Name="DonePanel" Margin="0,12,0,0" Visibility="Collapsed">
|
||||||
|
<!-- Status message -->
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
|
||||||
|
<TextBlock x:Name="DoneIcon" FontFamily="Segoe MDL2 Assets" FontSize="20"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||||
|
<TextBlock x:Name="DoneText" FontSize="15" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Activate card (only if no key was provided) -->
|
||||||
|
<Border x:Name="ActivateCard" Visibility="Collapsed"
|
||||||
|
Background="{StaticResource CardBrush}" CornerRadius="8"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
Padding="16,12" Margin="0,0,0,12">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="Termékkulcs megadása" FontSize="14" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="Az Office aktiválásához adja meg a termékkulcsot."
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<Button x:Name="BtnActivateNow" Grid.Column="1" Content="Aktiválás"
|
||||||
|
Style="{StaticResource PrimaryButton}" Click="BtnActivateNow_Click"
|
||||||
|
VerticalAlignment="Center" Padding="18,8"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Launch apps -->
|
||||||
|
<Border x:Name="LaunchCard" Visibility="Collapsed"
|
||||||
|
Background="{StaticResource CardBrush}" CornerRadius="8"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
Padding="16,12" Margin="0,0,0,8">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Alkalmazás indítása" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,10"/>
|
||||||
|
<WrapPanel x:Name="LaunchButtons" Orientation="Horizontal"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Page>
|
||||||
386
Pages/ProgressPage.xaml.cs
Normal file
386
Pages/ProgressPage.xaml.cs
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
using InstaSoftOfficeTool.Services;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class ProgressPage : Page
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly InstallConfig _config;
|
||||||
|
|
||||||
|
public ProgressPage(MainWindow main, InstallConfig config)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void StartInstallation()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_config.IsMsiInstall)
|
||||||
|
{
|
||||||
|
await StartMsiInstallation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await StartOdtInstallation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppendLog("HIBA: " + ex.Message);
|
||||||
|
ShowDone(false, "Váratlan hiba történt.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartOdtInstallation()
|
||||||
|
{
|
||||||
|
// Step 1: Download ODT
|
||||||
|
SetStepActive(Step1Icon, Step1Text);
|
||||||
|
AppendLog("ODT letöltése indul...");
|
||||||
|
|
||||||
|
var downloader = new OdtDownloader();
|
||||||
|
downloader.StatusChanged += msg => Dispatcher.Invoke(() => AppendLog(msg));
|
||||||
|
|
||||||
|
bool downloaded = await downloader.DownloadAndExtractAsync();
|
||||||
|
if (!downloaded)
|
||||||
|
{
|
||||||
|
SetStepError(Step1Icon, Step1Text);
|
||||||
|
ShowDone(false, "Az ODT letöltése sikertelen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStepDone(Step1Icon, Step1Text);
|
||||||
|
|
||||||
|
// Step 2: Generate config XML
|
||||||
|
SetStepActive(Step2Icon, Step2Text);
|
||||||
|
AppendLog("Konfigurációs XML generálása...");
|
||||||
|
|
||||||
|
string xml = OdtXmlGenerator.Generate(_config);
|
||||||
|
string xmlPath = Path.Combine(downloader.OdtFolder, "configuration.xml");
|
||||||
|
File.WriteAllText(xmlPath, xml);
|
||||||
|
AppendLog("XML mentve: " + xmlPath);
|
||||||
|
|
||||||
|
SetStepDone(Step2Icon, Step2Text);
|
||||||
|
|
||||||
|
// Step 3: Run setup.exe /configure
|
||||||
|
SetStepActive(Step3Icon, Step3Text);
|
||||||
|
AppendLog("Office telepítés indítása...");
|
||||||
|
AppendLog("setup.exe /configure \"" + xmlPath + "\"");
|
||||||
|
|
||||||
|
int exitCode = await downloader.RunSetupAsync(xmlPath, msg =>
|
||||||
|
Dispatcher.Invoke(() => AppendLog(msg)));
|
||||||
|
|
||||||
|
if (exitCode == 0)
|
||||||
|
{
|
||||||
|
SetStepDone(Step3Icon, Step3Text);
|
||||||
|
ShowDone(true, "Az Office sikeresen települt!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetStepError(Step3Icon, Step3Text);
|
||||||
|
ShowDone(false, "A telepítés hibakóddal fejeződött be: " + exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartMsiInstallation()
|
||||||
|
{
|
||||||
|
string isoUrl = _config.GetIsoUrl();
|
||||||
|
if (string.IsNullOrEmpty(isoUrl))
|
||||||
|
{
|
||||||
|
ShowDone(false, "Nincs elérhető ISO a kiválasztott architektúrához.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update step labels for MSI flow
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
Step1Text.Text = "Telepítő ISO letöltése...";
|
||||||
|
Step2Text.Text = "ISO csatolása...";
|
||||||
|
Step3Text.Text = "Office 2016 telepítése...";
|
||||||
|
});
|
||||||
|
|
||||||
|
var installer = new MsiInstaller();
|
||||||
|
installer.StatusChanged += msg => Dispatcher.Invoke(() => AppendLog(msg));
|
||||||
|
installer.DownloadProgress += (received, total) =>
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
long receivedMb = received / 1024 / 1024;
|
||||||
|
if (total > 0)
|
||||||
|
{
|
||||||
|
long totalMb = total / 1024 / 1024;
|
||||||
|
int percent = (int)(received * 100 / total);
|
||||||
|
DownloadInfo.Text = receivedMb + " MB / " + totalMb + " MB (" + percent + "%)";
|
||||||
|
MainProgress.IsIndeterminate = false;
|
||||||
|
MainProgress.Value = percent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DownloadInfo.Text = receivedMb + " MB letöltve...";
|
||||||
|
}
|
||||||
|
DownloadInfo.Visibility = Visibility.Visible;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 1: Download ISO
|
||||||
|
SetStepActive(Step1Icon, Step1Text);
|
||||||
|
AppendLog("ISO letöltése indul...");
|
||||||
|
|
||||||
|
bool downloaded = await installer.DownloadIsoAsync(isoUrl);
|
||||||
|
if (!downloaded)
|
||||||
|
{
|
||||||
|
SetStepError(Step1Icon, Step1Text);
|
||||||
|
ShowDone(false, "Az ISO letöltése sikertelen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStepDone(Step1Icon, Step1Text);
|
||||||
|
|
||||||
|
// Hide download progress, reset progress bar
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
DownloadInfo.Visibility = Visibility.Collapsed;
|
||||||
|
MainProgress.IsIndeterminate = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 2: Mount ISO
|
||||||
|
SetStepActive(Step2Icon, Step2Text);
|
||||||
|
bool mounted = await installer.MountIsoAsync();
|
||||||
|
if (!mounted)
|
||||||
|
{
|
||||||
|
SetStepError(Step2Icon, Step2Text);
|
||||||
|
ShowDone(false, "Az ISO csatolása sikertelen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetStepDone(Step2Icon, Step2Text);
|
||||||
|
|
||||||
|
// Step 3: Run setup.exe
|
||||||
|
SetStepActive(Step3Icon, Step3Text);
|
||||||
|
int exitCode = await installer.RunSetupAsync(msg =>
|
||||||
|
Dispatcher.Invoke(() => AppendLog(msg)));
|
||||||
|
|
||||||
|
// Dismount ISO regardless of result
|
||||||
|
await installer.DismountIsoAsync();
|
||||||
|
|
||||||
|
if (exitCode == 0)
|
||||||
|
{
|
||||||
|
SetStepDone(Step3Icon, Step3Text);
|
||||||
|
|
||||||
|
// Apply product key if provided
|
||||||
|
if (!string.IsNullOrEmpty(_config.ProductKey))
|
||||||
|
{
|
||||||
|
AppendLog("Termékkulcs telepítése...");
|
||||||
|
var lm = new LicenseManager();
|
||||||
|
if (lm.FindOspp())
|
||||||
|
{
|
||||||
|
string inpResult = await lm.InstallKeyAsync(_config.ProductKey);
|
||||||
|
AppendLog(inpResult);
|
||||||
|
AppendLog("Aktiválás...");
|
||||||
|
string actResult = await lm.ActivateAsync();
|
||||||
|
AppendLog(actResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendLog("Az ospp.vbs nem található — a kulcsot később a Licenc-kezelésben adhatja meg.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowDone(true, "Az Office 2016 sikeresen települt!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetStepError(Step3Icon, Step3Text);
|
||||||
|
ShowDone(false, "A telepítés hibakóddal fejeződött be: " + exitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendLog(string text)
|
||||||
|
{
|
||||||
|
LogText.Text += DateTime.Now.ToString("HH:mm:ss") + " " + text + "\n";
|
||||||
|
LogText.ScrollToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetStepActive(TextBlock icon, TextBlock text)
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
icon.Text = "\uE72A";
|
||||||
|
icon.Foreground = (Brush)FindResource("AccentBrush");
|
||||||
|
text.Foreground = (Brush)FindResource("TextPrimaryBrush");
|
||||||
|
text.FontWeight = FontWeights.SemiBold;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetStepDone(TextBlock icon, TextBlock text)
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
icon.Text = "\uE73E";
|
||||||
|
icon.Foreground = (Brush)FindResource("SuccessBrush");
|
||||||
|
text.Foreground = (Brush)FindResource("SuccessBrush");
|
||||||
|
text.FontWeight = FontWeights.Normal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetStepError(TextBlock icon, TextBlock text)
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
icon.Text = "\uE711";
|
||||||
|
icon.Foreground = (Brush)FindResource("ErrorBrush");
|
||||||
|
text.Foreground = (Brush)FindResource("ErrorBrush");
|
||||||
|
text.FontWeight = FontWeights.Normal;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowDone(bool success, string message)
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MainProgress.IsIndeterminate = false;
|
||||||
|
MainProgress.Value = 100;
|
||||||
|
TitleText.Text = success ? "Telep\u00edt\u00e9s befejezve" : "Telep\u00edt\u00e9s sikertelen";
|
||||||
|
|
||||||
|
DoneIcon.Text = success ? "\uE73E" : "\uE711";
|
||||||
|
DoneIcon.Foreground = success
|
||||||
|
? (Brush)FindResource("SuccessBrush")
|
||||||
|
: (Brush)FindResource("ErrorBrush");
|
||||||
|
DoneText.Text = message;
|
||||||
|
DoneText.Foreground = DoneIcon.Foreground;
|
||||||
|
DonePanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
// Show activate card if no product key was provided
|
||||||
|
if (string.IsNullOrEmpty(_config.ProductKey))
|
||||||
|
{
|
||||||
|
ActivateCard.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show launch buttons
|
||||||
|
BuildLaunchButtons();
|
||||||
|
LaunchCard.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
_main.ShowCloseButton();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildLaunchButtons()
|
||||||
|
{
|
||||||
|
LaunchButtons.Children.Clear();
|
||||||
|
|
||||||
|
var apps = new[]
|
||||||
|
{
|
||||||
|
("Word", "WINWORD.EXE"),
|
||||||
|
("Excel", "EXCEL.EXE"),
|
||||||
|
("PowerPoint", "POWERPNT.EXE"),
|
||||||
|
("Outlook", "OUTLOOK.EXE"),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var (name, exe) in apps)
|
||||||
|
{
|
||||||
|
// Skip if excluded
|
||||||
|
if (_config.ExcludedApps.Contains(name == "Word" ? "Word" :
|
||||||
|
name == "Excel" ? "Excel" :
|
||||||
|
name == "PowerPoint" ? "PowerPoint" :
|
||||||
|
name == "Outlook" ? "Outlook" : ""))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var btn = new Button
|
||||||
|
{
|
||||||
|
Content = name,
|
||||||
|
Style = (Style)FindResource("SecondaryButton"),
|
||||||
|
Padding = new Thickness(18, 8, 18, 8),
|
||||||
|
FontSize = 13,
|
||||||
|
Margin = new Thickness(0, 0, 8, 0),
|
||||||
|
Tag = exe
|
||||||
|
};
|
||||||
|
btn.Click += LaunchApp_Click;
|
||||||
|
LaunchButtons.Children.Add(btn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LaunchApp_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var btn = (Button)sender;
|
||||||
|
var exe = (string)btn.Tag;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Try common Office paths
|
||||||
|
var paths = new[]
|
||||||
|
{
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||||
|
"Microsoft Office", "root", "Office16", exe),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
|
||||||
|
"Microsoft Office", "root", "Office16", exe),
|
||||||
|
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||||
|
"Microsoft Office", "Office16", exe),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var path in paths)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
Process.Start(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: let Windows find it
|
||||||
|
Process.Start(exe);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
AppendLog("Nem siker\u00fclt elind\u00edtani: " + exe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnActivateNow_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string key = await _main.AskProductKeyAsync();
|
||||||
|
if (string.IsNullOrEmpty(key)) return;
|
||||||
|
|
||||||
|
BtnActivateNow.IsEnabled = false;
|
||||||
|
BtnActivateNow.Content = "Aktiv\u00e1l\u00e1s...";
|
||||||
|
|
||||||
|
var lm = new LicenseManager();
|
||||||
|
if (!lm.FindOspp())
|
||||||
|
{
|
||||||
|
AppendLog("Az ospp.vbs nem tal\u00e1lhat\u00f3.");
|
||||||
|
BtnActivateNow.IsEnabled = true;
|
||||||
|
BtnActivateNow.Content = "Aktiv\u00e1l\u00e1s";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendLog("Term\u00e9kkulcs telep\u00edt\u00e9se: " + key);
|
||||||
|
string inpResult = await lm.InstallKeyAsync(key);
|
||||||
|
AppendLog(inpResult);
|
||||||
|
|
||||||
|
AppendLog("Aktiv\u00e1l\u00e1s...");
|
||||||
|
string actResult = await lm.ActivateAsync();
|
||||||
|
AppendLog(actResult);
|
||||||
|
|
||||||
|
if (actResult.Contains("successful") || actResult.Contains("sikeres"))
|
||||||
|
{
|
||||||
|
BtnActivateNow.Content = "Aktiv\u00e1lva \u2713";
|
||||||
|
AppendLog("Az Office sikeresen aktiv\u00e1lva!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BtnActivateNow.IsEnabled = true;
|
||||||
|
BtnActivateNow.Content = "Aktiv\u00e1l\u00e1s";
|
||||||
|
AppendLog("Az aktiv\u00e1l\u00e1s eredm\u00e9ny\u00e9t ellen\u0151rizze a kimenetben.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Pages/RemovePage.xaml
Normal file
52
Pages/RemovePage.xaml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.RemovePage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,20">
|
||||||
|
<TextBlock Text="Office eltávolítás" FontSize="24" FontWeight="Light"/>
|
||||||
|
<TextBlock Text="A számítógépen talált Office telepítések:"
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel x:Name="OfficeListPanel"/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="NoOfficeText" Text="Nem található telepített Office."
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
Margin="0,20,0,0" Visibility="Collapsed"/>
|
||||||
|
|
||||||
|
<Border x:Name="LicenseCleanupPanel" Margin="0,20,0,0"
|
||||||
|
Background="{StaticResource CardBrush}" CornerRadius="8"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
Padding="16,12">
|
||||||
|
<CheckBox x:Name="CbCleanLicense" Style="{StaticResource FluentCheckBox}"
|
||||||
|
Content="Licenc-adatbázis tisztítása is (ajánlott)"
|
||||||
|
IsChecked="True"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border x:Name="LogPanel" Margin="0,16,0,0" Background="#F8F8F8"
|
||||||
|
CornerRadius="6" Padding="12" Visibility="Collapsed"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1">
|
||||||
|
<TextBox x:Name="LogText" FontFamily="Consolas" FontSize="11"
|
||||||
|
TextWrapping="Wrap" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
IsReadOnly="True" Background="Transparent" BorderThickness="0"
|
||||||
|
VerticalScrollBarVisibility="Auto" AcceptsReturn="True" MaxHeight="200"/>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
|
||||||
|
<Button x:Name="BtnRemove" Content="Eltávolítás" Style="{StaticResource PrimaryButton}"
|
||||||
|
Click="BtnRemove_Click"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
170
Pages/RemovePage.xaml.cs
Normal file
170
Pages/RemovePage.xaml.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
using InstaSoftOfficeTool.Services;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class RemovePage : Page
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private List<InstalledOffice> _detected;
|
||||||
|
|
||||||
|
public RemovePage(MainWindow main)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
Loaded += (s, e) => DetectOffice();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DetectOffice()
|
||||||
|
{
|
||||||
|
_detected = OfficeDetector.Detect();
|
||||||
|
|
||||||
|
OfficeListPanel.Children.Clear();
|
||||||
|
|
||||||
|
if (_detected.Count == 0)
|
||||||
|
{
|
||||||
|
NoOfficeText.Visibility = Visibility.Visible;
|
||||||
|
LicenseCleanupPanel.Visibility = Visibility.Collapsed;
|
||||||
|
BtnRemove.IsEnabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var office in _detected)
|
||||||
|
{
|
||||||
|
var cb = new CheckBox
|
||||||
|
{
|
||||||
|
Content = office.DisplayName + (string.IsNullOrEmpty(office.Version) ? "" : " (" + office.Version + ")"),
|
||||||
|
IsChecked = true,
|
||||||
|
Style = (Style)FindResource("FluentCheckBox"),
|
||||||
|
Tag = office,
|
||||||
|
Margin = new Thickness(0, 4, 0, 4),
|
||||||
|
FontSize = 14
|
||||||
|
};
|
||||||
|
OfficeListPanel.Children.Add(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnRemove_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
bool confirmed = await _main.ConfirmAsync(
|
||||||
|
"Office elt\u00e1vol\u00edt\u00e1s",
|
||||||
|
"Biztosan el szeretn\u00e9 t\u00e1vol\u00edtani a kiv\u00e1lasztott Office telep\u00edt\u00e9seket?",
|
||||||
|
"Elt\u00e1vol\u00edt\u00e1s", "M\u00e9gse");
|
||||||
|
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
BtnRemove.IsEnabled = false;
|
||||||
|
LogPanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var c2rProducts = new System.Collections.Generic.List<string>();
|
||||||
|
|
||||||
|
foreach (UIElement child in OfficeListPanel.Children)
|
||||||
|
{
|
||||||
|
if (child is CheckBox cb && cb.IsChecked == true)
|
||||||
|
{
|
||||||
|
var office = (InstalledOffice)cb.Tag;
|
||||||
|
if (office.IsClickToRun)
|
||||||
|
{
|
||||||
|
c2rProducts.Add(office.ProductCode);
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(office.ProductCode))
|
||||||
|
{
|
||||||
|
AppendLog("MSI elt\u00e1vol\u00edt\u00e1s: " + office.DisplayName);
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
runner.OutputReceived += msg => Dispatcher.Invoke(() => AppendLog(msg));
|
||||||
|
int code = await runner.RunAsync("msiexec", "/x " + office.ProductCode + " /qb");
|
||||||
|
AppendLog("MSI exit code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c2rProducts.Count > 0)
|
||||||
|
{
|
||||||
|
AppendLog("Click-to-Run term\u00e9kek elt\u00e1vol\u00edt\u00e1sa: " + string.Join(", ", c2rProducts));
|
||||||
|
|
||||||
|
var downloader = new OdtDownloader();
|
||||||
|
downloader.StatusChanged += msg => Dispatcher.Invoke(() => AppendLog(msg));
|
||||||
|
|
||||||
|
bool ok = await downloader.DownloadAndExtractAsync();
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
AppendLog("HIBA: Az ODT let\u00f6lt\u00e9se sikertelen.");
|
||||||
|
BtnRemove.IsEnabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string removeXml = OdtXmlGenerator.GenerateRemoveProducts(c2rProducts.ToArray());
|
||||||
|
string xmlPath = Path.Combine(downloader.OdtFolder, "remove.xml");
|
||||||
|
File.WriteAllText(xmlPath, removeXml);
|
||||||
|
|
||||||
|
AppendLog("Remove XML:");
|
||||||
|
AppendLog(removeXml);
|
||||||
|
AppendLog("");
|
||||||
|
AppendLog("Futtat\u00e1s: setup.exe /configure remove.xml");
|
||||||
|
AppendLog("Ez eltarthat n\u00e9h\u00e1ny percig...");
|
||||||
|
|
||||||
|
int exitCode = await downloader.RunRemoveAsync(xmlPath,
|
||||||
|
msg => Dispatcher.Invoke(() => AppendLog(msg)));
|
||||||
|
|
||||||
|
AppendLog("setup.exe exit code: " + exitCode);
|
||||||
|
|
||||||
|
if (exitCode == 0)
|
||||||
|
{
|
||||||
|
AppendLog("Office sikeresen elt\u00e1vol\u00edtva.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendLog("FIGYELEM: Az elt\u00e1vol\u00edt\u00e1s nem siker\u00fclt (k\u00f3d: " + exitCode + ")");
|
||||||
|
AppendLog("Pr\u00f3b\u00e1lja meg manu\u00e1lisan: Vez\u00e9rl\u0151pult > Programok elt\u00e1vol\u00edt\u00e1sa");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CbCleanLicense.IsChecked == true)
|
||||||
|
{
|
||||||
|
AppendLog("");
|
||||||
|
AppendLog("Licenc-adatb\u00e1zis tiszt\u00edt\u00e1sa...");
|
||||||
|
var lm = new LicenseManager();
|
||||||
|
if (lm.FindOspp())
|
||||||
|
{
|
||||||
|
var cleanResult = await lm.RemoveAllKeysAsync();
|
||||||
|
AppendLog(cleanResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppendLog("Az ospp.vbs nem tal\u00e1lhat\u00f3 \u2014 licenc-tiszt\u00edt\u00e1s kihagyva.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendLog("");
|
||||||
|
AppendLog("K\u00e9sz. Lista friss\u00edt\u00e9se...");
|
||||||
|
DetectOffice();
|
||||||
|
|
||||||
|
if (_detected.Count == 0)
|
||||||
|
{
|
||||||
|
AppendLog("Nincs t\u00f6bb telep\u00edtett Office.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppendLog("V\u00e1ratlan hiba: " + ex.Message);
|
||||||
|
AppendLog(ex.StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
BtnRemove.IsEnabled = _detected.Count > 0;
|
||||||
|
_main.ShowCloseButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendLog(string text)
|
||||||
|
{
|
||||||
|
LogText.Text += DateTime.Now.ToString("HH:mm:ss") + " " + text + "\n";
|
||||||
|
LogText.ScrollToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Pages/SummaryPage.xaml
Normal file
63
Pages/SummaryPage.xaml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.SummaryPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="40,30">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Összegzés" FontSize="24" FontWeight="Light" Margin="0,0,0,20"/>
|
||||||
|
<TextBlock Text="Ellenőrizze a beállításokat a telepítés indítása előtt."
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,0,0,16"/>
|
||||||
|
|
||||||
|
<Border Background="{StaticResource CardBrush}" CornerRadius="8"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
Padding="20,16" Margin="0,0,0,16">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="180"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Verzió:" FontWeight="SemiBold" Margin="0,4"/>
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" x:Name="SumVersion" Margin="0,4"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Kiadás:" FontWeight="SemiBold" Margin="0,4"/>
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="SumEdition" Margin="0,4"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Architektúra:" FontWeight="SemiBold" Margin="0,4"/>
|
||||||
|
<TextBlock Grid.Row="2" Grid.Column="1" x:Name="SumArch" Margin="0,4"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Nyelv:" FontWeight="SemiBold" Margin="0,4"/>
|
||||||
|
<TextBlock Grid.Row="3" Grid.Column="1" x:Name="SumLanguage" Margin="0,4"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="0" Text="Termékkulcs:" FontWeight="SemiBold" Margin="0,4"/>
|
||||||
|
<TextBlock Grid.Row="4" Grid.Column="1" x:Name="SumKey" Margin="0,4"/>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="5" Grid.Column="0" Text="Kizárt alkalmazások:" FontWeight="SemiBold" Margin="0,4"/>
|
||||||
|
<TextBlock Grid.Row="5" Grid.Column="1" x:Name="SumExcluded" Margin="0,4" TextWrapping="Wrap"/>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Expander Header="Konfigurációs XML megtekintése" FontSize="13" Margin="0,0,0,10">
|
||||||
|
<Border Background="#F8F8F8" CornerRadius="4" Padding="12" Margin="0,8,0,0"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1">
|
||||||
|
<TextBox x:Name="XmlPreview" IsReadOnly="True" TextWrapping="Wrap"
|
||||||
|
FontFamily="Consolas" FontSize="12" BorderThickness="0"
|
||||||
|
Background="Transparent" VerticalScrollBarVisibility="Auto"
|
||||||
|
MaxHeight="200"/>
|
||||||
|
</Border>
|
||||||
|
</Expander>
|
||||||
|
|
||||||
|
<Button x:Name="BtnSaveXml" Content="XML mentése fájlba..." Style="{StaticResource SecondaryButton}"
|
||||||
|
HorizontalAlignment="Left" Click="BtnSaveXml_Click" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Page>
|
||||||
80
Pages/SummaryPage.xaml.cs
Normal file
80
Pages/SummaryPage.xaml.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
using InstaSoftOfficeTool.Services;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class SummaryPage : Page, IWizardPage
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly InstallConfig _config;
|
||||||
|
private string _generatedXml;
|
||||||
|
|
||||||
|
public SummaryPage(MainWindow main, InstallConfig config)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
_config = config;
|
||||||
|
|
||||||
|
Loaded += (s, e) => RefreshSummary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshSummary()
|
||||||
|
{
|
||||||
|
SumVersion.Text = _config.GetVersionDisplayName();
|
||||||
|
SumEdition.Text = _config.Edition?.DisplayName ?? "-";
|
||||||
|
SumArch.Text = _config.Architecture + "-bit";
|
||||||
|
SumLanguage.Text = _config.GetLanguageDisplayName() + " (" + _config.Language + ")";
|
||||||
|
SumKey.Text = string.IsNullOrEmpty(_config.ProductKey) ? "Nincs megadva" : _config.ProductKey;
|
||||||
|
|
||||||
|
if (_config.IsMsiInstall)
|
||||||
|
{
|
||||||
|
// MSI mode: no app exclusion, show ISO info instead of XML
|
||||||
|
SumExcluded.Text = "(a Microsoft telepítőben választható)";
|
||||||
|
|
||||||
|
string isoUrl = _config.GetIsoUrl();
|
||||||
|
XmlPreview.Text = "Telepítési mód: ISO letöltés + MSI telepítő\n" +
|
||||||
|
"ISO URL: " + (isoUrl ?? "Nem elérhető") + "\n\n" +
|
||||||
|
"A telepítő ISO fájl letöltése után a Microsoft Office\n" +
|
||||||
|
"saját telepítője indul el automatikusan.";
|
||||||
|
|
||||||
|
BtnSaveXml.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_config.ExcludedApps.Count > 0)
|
||||||
|
SumExcluded.Text = string.Join(", ", _config.ExcludedApps);
|
||||||
|
else
|
||||||
|
SumExcluded.Text = "Nincs (minden alkalmazás települ)";
|
||||||
|
|
||||||
|
_generatedXml = OdtXmlGenerator.Generate(_config);
|
||||||
|
XmlPreview.Text = _generatedXml;
|
||||||
|
BtnSaveXml.Visibility = System.Windows.Visibility.Visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BtnSaveXml_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var dlg = new SaveFileDialog
|
||||||
|
{
|
||||||
|
Filter = "XML f\u00e1jl (*.xml)|*.xml",
|
||||||
|
FileName = "configuration.xml"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dlg.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
System.IO.File.WriteAllText(dlg.FileName, _generatedXml);
|
||||||
|
MessageBox.Show("XML sikeresen mentve:\n" + dlg.FileName,
|
||||||
|
"Ment\u00e9s", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
51
Pages/TroubleshootPage.xaml
Normal file
51
Pages/TroubleshootPage.xaml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.TroubleshootPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,16">
|
||||||
|
<TextBlock Text="Licenc-kezelés" FontSize="24" FontWeight="Light"/>
|
||||||
|
<TextBlock Text="Office licenc állapot lekérdezése, aktiválás és termékkulcsok kezelése"
|
||||||
|
FontSize="14" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Key cards (scrollable) -->
|
||||||
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Margin="0,0,0,8">
|
||||||
|
<StackPanel x:Name="KeyCardsPanel"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Details expander -->
|
||||||
|
<Expander x:Name="DetailsExpander" Grid.Row="2" Header="Részletek" FontSize="13"
|
||||||
|
Margin="0,0,0,8" Expanded="DetailsExpander_Expanded" Collapsed="DetailsExpander_Collapsed">
|
||||||
|
<StackPanel Margin="0,8,0,0">
|
||||||
|
<TextBlock x:Name="OsppPathText" FontSize="12"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,0,0,6"/>
|
||||||
|
<Border Background="#F8F8F8" CornerRadius="4" Padding="10"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1">
|
||||||
|
<TextBox x:Name="OutputText" FontFamily="Consolas" FontSize="11"
|
||||||
|
TextWrapping="Wrap" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
IsReadOnly="True" Background="Transparent" BorderThickness="0"
|
||||||
|
VerticalScrollBarVisibility="Auto" AcceptsReturn="True"
|
||||||
|
Height="85"/>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</Expander>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,4,0,0">
|
||||||
|
<Button x:Name="BtnRefresh" Content="Állapot frissítése" Style="{StaticResource SecondaryButton}"
|
||||||
|
Click="BtnRefresh_Click" Margin="0,0,8,0"/>
|
||||||
|
<Button x:Name="BtnRemoveAll" Content="Összes kulcs eltávolítása" Style="{StaticResource PrimaryButton}"
|
||||||
|
Click="BtnRemoveAll_Click" IsEnabled="False" Visibility="Collapsed"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
311
Pages/TroubleshootPage.xaml.cs
Normal file
311
Pages/TroubleshootPage.xaml.cs
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using InstaSoftOfficeTool.Services;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class TroubleshootPage : Page
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly LicenseManager _licenseManager = new LicenseManager();
|
||||||
|
private List<LicenseEntry> _entries = new List<LicenseEntry>();
|
||||||
|
|
||||||
|
public TroubleshootPage(MainWindow main)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
Loaded += async (s, e) => await RefreshStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async System.Threading.Tasks.Task RefreshStatus()
|
||||||
|
{
|
||||||
|
OutputText.Text = "";
|
||||||
|
OsppPathText.Text = "";
|
||||||
|
KeyCardsPanel.Children.Clear();
|
||||||
|
BtnRemoveAll.IsEnabled = false;
|
||||||
|
BtnRemoveAll.Visibility = Visibility.Collapsed;
|
||||||
|
BtnRefresh.IsEnabled = false;
|
||||||
|
|
||||||
|
var loadingText = new TextBlock
|
||||||
|
{
|
||||||
|
Text = "Keres\u00e9s folyamatban...",
|
||||||
|
FontSize = 13,
|
||||||
|
Foreground = (Brush)FindResource("TextSecondaryBrush"),
|
||||||
|
Margin = new Thickness(0, 8, 0, 8)
|
||||||
|
};
|
||||||
|
KeyCardsPanel.Children.Add(loadingText);
|
||||||
|
|
||||||
|
bool found = _licenseManager.FindOspp();
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
KeyCardsPanel.Children.Clear();
|
||||||
|
var errorCard = new Border
|
||||||
|
{
|
||||||
|
Background = (Brush)FindResource("CardBrush"),
|
||||||
|
BorderBrush = (Brush)FindResource("BorderBrush"),
|
||||||
|
BorderThickness = new Thickness(1),
|
||||||
|
CornerRadius = new CornerRadius(8),
|
||||||
|
Padding = new Thickness(16, 14, 16, 14),
|
||||||
|
Margin = new Thickness(0, 0, 0, 6)
|
||||||
|
};
|
||||||
|
var errorPanel = new StackPanel();
|
||||||
|
errorPanel.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = "Az ospp.vbs nem tal\u00e1lhat\u00f3",
|
||||||
|
FontSize = 14, FontWeight = FontWeights.SemiBold,
|
||||||
|
Foreground = (Brush)FindResource("ErrorBrush")
|
||||||
|
});
|
||||||
|
errorPanel.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = "Nincs telep\u00edtett Microsoft Office, vagy nem a szok\u00e1sos helyre lett telep\u00edtve.",
|
||||||
|
FontSize = 12, Foreground = (Brush)FindResource("TextSecondaryBrush"),
|
||||||
|
TextWrapping = TextWrapping.Wrap, Margin = new Thickness(0, 4, 0, 0)
|
||||||
|
});
|
||||||
|
errorCard.Child = errorPanel;
|
||||||
|
KeyCardsPanel.Children.Add(errorCard);
|
||||||
|
BtnRefresh.IsEnabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OsppPathText.Text = "ospp.vbs helye: " + _licenseManager.OsppPath;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string status = await _licenseManager.GetStatusAsync();
|
||||||
|
OutputText.Text = status;
|
||||||
|
|
||||||
|
_entries = _licenseManager.ParseLicenseEntries(status);
|
||||||
|
BuildKeyCards();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
KeyCardsPanel.Children.Clear();
|
||||||
|
OutputText.Text = "Hiba a lek\u00e9rdez\u00e9s sor\u00e1n: " + ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
BtnRefresh.IsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildKeyCards()
|
||||||
|
{
|
||||||
|
KeyCardsPanel.Children.Clear();
|
||||||
|
|
||||||
|
if (_entries.Count == 0)
|
||||||
|
{
|
||||||
|
var noKeyCard = new Border
|
||||||
|
{
|
||||||
|
Background = (Brush)FindResource("CardBrush"),
|
||||||
|
BorderBrush = (Brush)FindResource("BorderBrush"),
|
||||||
|
BorderThickness = new Thickness(1),
|
||||||
|
CornerRadius = new CornerRadius(8),
|
||||||
|
Padding = new Thickness(16, 14, 16, 14)
|
||||||
|
};
|
||||||
|
noKeyCard.Child = new TextBlock
|
||||||
|
{
|
||||||
|
Text = "Nincs telep\u00edtett term\u00e9kkulcs.",
|
||||||
|
FontSize = 13,
|
||||||
|
Foreground = (Brush)FindResource("TextSecondaryBrush")
|
||||||
|
};
|
||||||
|
KeyCardsPanel.Children.Add(noKeyCard);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entries.Count > 1)
|
||||||
|
{
|
||||||
|
BtnRemoveAll.Visibility = Visibility.Visible;
|
||||||
|
BtnRemoveAll.IsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in _entries)
|
||||||
|
{
|
||||||
|
KeyCardsPanel.Children.Add(CreateKeyCard(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Border CreateKeyCard(LicenseEntry entry)
|
||||||
|
{
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||||
|
|
||||||
|
var infoPanel = new StackPanel { VerticalAlignment = VerticalAlignment.Center };
|
||||||
|
|
||||||
|
infoPanel.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = string.IsNullOrEmpty(entry.LicenseName) ? "Ismeretlen licenc" : entry.LicenseName,
|
||||||
|
FontSize = 13, FontWeight = FontWeights.SemiBold,
|
||||||
|
TextTrimming = TextTrimming.CharacterEllipsis
|
||||||
|
});
|
||||||
|
|
||||||
|
var statusLine = !string.IsNullOrEmpty(entry.ErrorDescription)
|
||||||
|
? entry.ErrorDescription
|
||||||
|
: (!string.IsNullOrEmpty(entry.Status) ? entry.Status : entry.Description);
|
||||||
|
|
||||||
|
infoPanel.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = statusLine,
|
||||||
|
FontSize = 11, Foreground = (Brush)FindResource("TextSecondaryBrush"),
|
||||||
|
TextTrimming = TextTrimming.CharacterEllipsis
|
||||||
|
});
|
||||||
|
|
||||||
|
infoPanel.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = "Kulcs: *****-" + entry.Last5,
|
||||||
|
FontSize = 11, FontFamily = new FontFamily("Consolas"),
|
||||||
|
Foreground = (Brush)FindResource("TextSecondaryBrush"),
|
||||||
|
Margin = new Thickness(0, 2, 0, 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
Grid.SetColumn(infoPanel, 0);
|
||||||
|
grid.Children.Add(infoPanel);
|
||||||
|
|
||||||
|
// Right side: Activate + Remove buttons
|
||||||
|
var btnPanel = new StackPanel
|
||||||
|
{
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
var activateBtn = new Button
|
||||||
|
{
|
||||||
|
Content = "Aktiv\u00e1l\u00e1s",
|
||||||
|
Style = (Style)FindResource("PrimaryButton"),
|
||||||
|
Padding = new Thickness(14, 6, 14, 6),
|
||||||
|
FontSize = 12,
|
||||||
|
Tag = entry.Last5
|
||||||
|
};
|
||||||
|
activateBtn.Click += BtnActivateSingle_Click;
|
||||||
|
btnPanel.Children.Add(activateBtn);
|
||||||
|
|
||||||
|
var removeBtn = new Button
|
||||||
|
{
|
||||||
|
Content = "Elt\u00e1vol\u00edt\u00e1s",
|
||||||
|
Style = (Style)FindResource("SecondaryButton"),
|
||||||
|
Padding = new Thickness(14, 6, 14, 6),
|
||||||
|
FontSize = 12,
|
||||||
|
Tag = entry.Last5,
|
||||||
|
Margin = new Thickness(6, 0, 0, 0)
|
||||||
|
};
|
||||||
|
removeBtn.Click += BtnRemoveSingle_Click;
|
||||||
|
btnPanel.Children.Add(removeBtn);
|
||||||
|
|
||||||
|
Grid.SetColumn(btnPanel, 1);
|
||||||
|
grid.Children.Add(btnPanel);
|
||||||
|
|
||||||
|
return new Border
|
||||||
|
{
|
||||||
|
Background = (Brush)FindResource("CardBrush"),
|
||||||
|
BorderBrush = (Brush)FindResource("BorderBrush"),
|
||||||
|
BorderThickness = new Thickness(1),
|
||||||
|
CornerRadius = new CornerRadius(8),
|
||||||
|
Padding = new Thickness(16, 10, 16, 10),
|
||||||
|
Margin = new Thickness(0, 0, 0, 6),
|
||||||
|
Child = grid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnActivateSingle_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
string key = await _main.AskProductKeyAsync();
|
||||||
|
if (string.IsNullOrEmpty(key)) return;
|
||||||
|
|
||||||
|
var btn = (Button)sender;
|
||||||
|
btn.IsEnabled = false;
|
||||||
|
btn.Content = "...";
|
||||||
|
|
||||||
|
DetailsExpander.IsExpanded = true;
|
||||||
|
OutputText.Text = "Term\u00e9kkulcs telep\u00edt\u00e9se: " + key + "\n";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string inpResult = await _licenseManager.InstallKeyAsync(key);
|
||||||
|
OutputText.Text += inpResult + "\n";
|
||||||
|
|
||||||
|
OutputText.Text += "Aktiv\u00e1l\u00e1s...\n";
|
||||||
|
string actResult = await _licenseManager.ActivateAsync();
|
||||||
|
OutputText.Text += actResult;
|
||||||
|
|
||||||
|
await RefreshStatus();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OutputText.Text += "\nHiba: " + ex.Message;
|
||||||
|
btn.IsEnabled = true;
|
||||||
|
btn.Content = "Aktiv\u00e1l\u00e1s";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnRemoveSingle_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var btn = (Button)sender;
|
||||||
|
var last5 = (string)btn.Tag;
|
||||||
|
|
||||||
|
bool confirmed = await _main.ConfirmAsync(
|
||||||
|
"Kulcs elt\u00e1vol\u00edt\u00e1sa",
|
||||||
|
"Biztosan el szeretn\u00e9 t\u00e1vol\u00edtani a *****-" + last5 + " kulcsot?",
|
||||||
|
"Elt\u00e1vol\u00edt\u00e1s", "M\u00e9gse", DialogType.Question);
|
||||||
|
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
btn.IsEnabled = false;
|
||||||
|
btn.Content = "...";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _licenseManager.RemoveKeyAsync(last5);
|
||||||
|
await RefreshStatus();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OutputText.Text += "\nHiba: " + ex.Message;
|
||||||
|
btn.IsEnabled = true;
|
||||||
|
btn.Content = "Elt\u00e1vol\u00edt\u00e1s";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnRefresh_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
await RefreshStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void BtnRemoveAll_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
bool confirmed = await _main.ConfirmAsync(
|
||||||
|
"\u00d6sszes kulcs elt\u00e1vol\u00edt\u00e1sa",
|
||||||
|
"Biztosan el szeretn\u00e9 t\u00e1vol\u00edtani az \u00f6sszes telep\u00edtett term\u00e9kkulcsot (" + _entries.Count + " db)?\n\n" +
|
||||||
|
"Ez nem t\u00f6r\u00f6l adatot, csak az aktiv\u00e1ci\u00f3s \u00e1llapotot \u00e1ll\u00edtja vissza.",
|
||||||
|
"\u00d6sszes elt\u00e1vol\u00edt\u00e1sa", "M\u00e9gse");
|
||||||
|
|
||||||
|
if (!confirmed) return;
|
||||||
|
|
||||||
|
BtnRemoveAll.IsEnabled = false;
|
||||||
|
BtnRefresh.IsEnabled = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _licenseManager.RemoveAllKeysAsync();
|
||||||
|
await RefreshStatus();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
OutputText.Text += "\nHiba: " + ex.Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
BtnRefresh.IsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DetailsExpander_Expanded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_main.Height = 680;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DetailsExpander_Collapsed(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_main.Height = 550;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Pages/VersionPage.xaml
Normal file
58
Pages/VersionPage.xaml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.VersionPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,24">
|
||||||
|
<TextBlock Text="Válasszon Office verziót" FontSize="24" FontWeight="Light"/>
|
||||||
|
<TextBlock Text="Melyik évjáratot szeretné telepíteni?" FontSize="14"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Padding="0,0,4,0">
|
||||||
|
<StackPanel VerticalAlignment="Top">
|
||||||
|
<RadioButton x:Name="Rb2024" Style="{StaticResource CardRadioButton}"
|
||||||
|
GroupName="Version" IsChecked="True" Margin="0,0,0,10">
|
||||||
|
<StackPanel Margin="8,2">
|
||||||
|
<TextBlock Text="Office 2024" FontSize="18" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="2024. októberi kiadás · Windows 10 / 11"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
<RadioButton x:Name="Rb2021" Style="{StaticResource CardRadioButton}"
|
||||||
|
GroupName="Version" Margin="0,0,0,10">
|
||||||
|
<StackPanel Margin="8,2">
|
||||||
|
<TextBlock Text="Office 2021" FontSize="18" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="2021. októberi kiadás · Windows 10 / 11"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
<RadioButton x:Name="Rb2019" Style="{StaticResource CardRadioButton}"
|
||||||
|
GroupName="Version" Margin="0,0,0,10">
|
||||||
|
<StackPanel Margin="8,2">
|
||||||
|
<TextBlock Text="Office 2019" FontSize="18" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="2019. szeptemberi kiadás · Windows 7 / 8.1 / 10 / 11 · Támogatás lejárt: 2025.10."
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
|
||||||
|
<RadioButton x:Name="Rb2016" Style="{StaticResource CardRadioButton}"
|
||||||
|
GroupName="Version" Margin="0,0,0,10">
|
||||||
|
<StackPanel Margin="8,2">
|
||||||
|
<TextBlock Text="Office 2016" FontSize="18" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="2016. szeptemberi kiadás · Windows 7 / 8.1 / 10 · Támogatás lejárt: 2025.10."
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}" Margin="0,2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</RadioButton>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
36
Pages/VersionPage.xaml.cs
Normal file
36
Pages/VersionPage.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class VersionPage : Page, IWizardPage
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
private readonly InstallConfig _config;
|
||||||
|
|
||||||
|
public VersionPage(MainWindow main, InstallConfig config)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
_config = config;
|
||||||
|
|
||||||
|
// Restore selection
|
||||||
|
switch (_config.Version)
|
||||||
|
{
|
||||||
|
case OfficeVersion.Office2024: Rb2024.IsChecked = true; break;
|
||||||
|
case OfficeVersion.Office2021: Rb2021.IsChecked = true; break;
|
||||||
|
case OfficeVersion.Office2019: Rb2019.IsChecked = true; break;
|
||||||
|
case OfficeVersion.Office2016: Rb2016.IsChecked = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Validate()
|
||||||
|
{
|
||||||
|
if (Rb2024.IsChecked == true) _config.Version = OfficeVersion.Office2024;
|
||||||
|
else if (Rb2021.IsChecked == true) _config.Version = OfficeVersion.Office2021;
|
||||||
|
else if (Rb2019.IsChecked == true) _config.Version = OfficeVersion.Office2019;
|
||||||
|
else if (Rb2016.IsChecked == true) _config.Version = OfficeVersion.Office2016;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
Pages/WelcomePage.xaml
Normal file
59
Pages/WelcomePage.xaml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<Page x:Class="InstaSoftOfficeTool.Pages.WelcomePage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<Grid Margin="40,30">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,30">
|
||||||
|
<TextBlock Text="Üdvözöljük!" FontSize="28" FontWeight="Light"
|
||||||
|
Foreground="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<TextBlock Text="Válassza ki a kívánt műveletet:" FontSize="15"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<UniformGrid Grid.Row="1" Columns="3" Margin="0,0,0,0">
|
||||||
|
|
||||||
|
<Button Style="{StaticResource CardButton}" Margin="0,0,10,0"
|
||||||
|
Click="InstallClick">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="28" Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
Foreground="{StaticResource AccentBrush}" Margin="0,0,0,12"/>
|
||||||
|
<TextBlock Text="Office telepítés" FontSize="16" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="Új Microsoft Office telepítése a számítógépre"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap" Margin="0,6,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Style="{StaticResource CardButton}" Margin="5,0,5,0"
|
||||||
|
Click="RemoveClick">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="28" Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
Foreground="{StaticResource WarningBrush}" Margin="0,0,0,12"/>
|
||||||
|
<TextBlock Text="Office eltávolítás" FontSize="16" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="Meglévő Microsoft Office eltávolítása"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap" Margin="0,6,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Style="{StaticResource CardButton}" Margin="10,0,0,0"
|
||||||
|
Click="LicenseClick">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="28" Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
Foreground="{StaticResource SuccessBrush}" Margin="0,0,0,12"/>
|
||||||
|
<TextBlock Text="Licenc-kezelés" FontSize="16" FontWeight="SemiBold"/>
|
||||||
|
<TextBlock Text="Licenc állapot lekérdezése, termékkulcsok törlése"
|
||||||
|
FontSize="12" Foreground="{StaticResource TextSecondaryBrush}"
|
||||||
|
TextWrapping="Wrap" Margin="0,6,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
</UniformGrid>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
31
Pages/WelcomePage.xaml.cs
Normal file
31
Pages/WelcomePage.xaml.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Pages
|
||||||
|
{
|
||||||
|
public partial class WelcomePage : Page
|
||||||
|
{
|
||||||
|
private readonly MainWindow _main;
|
||||||
|
|
||||||
|
public WelcomePage(MainWindow main)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_main = main;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InstallClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_main.StartInstallFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_main.StartRemoveFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LicenseClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_main.StartLicenseFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ Fejlesztő: **InstaSoft Informatikai Zrt.** | Microsoft Partner
|
|||||||
|
|
||||||
## Letöltés
|
## Letöltés
|
||||||
|
|
||||||
### [Legfrissebb verzió letöltése](https://github.com/InstaSoft-hu/OfficeTool/releases/latest)
|
A legfrissebb verzió a **Releases** oldalon érhető el.
|
||||||
|
|
||||||
> Az alkalmazás futtatásához **.NET Framework 4.8** szükséges (Windows 10/11 rendszereken alapértelmezetten telepítve van).
|
> Az alkalmazás futtatásához **.NET Framework 4.8** szükséges (Windows 10/11 rendszereken alapértelmezetten telepítve van).
|
||||||
> A program rendszergazdai jogosultságot kér induláskor.
|
> A program rendszergazdai jogosultságot kér induláskor.
|
||||||
@@ -36,7 +36,7 @@ Első futtatáskor a Windows SmartScreen figyelmeztetést jeleníthet meg, mivel
|
|||||||
## Funkciók
|
## Funkciók
|
||||||
|
|
||||||
### Office telepítés
|
### Office telepítés
|
||||||
- Támogatott verziók: **Office 2024**, **Office 2021**, **Office 2019**
|
- Támogatott verziók: **Office 2024**, **Office 2021**, **Office 2019**, **Office 2016**
|
||||||
- Kiadások: **Standard**, **Professional Plus**, **Otthoni és kisvállalati verzió**
|
- Kiadások: **Standard**, **Professional Plus**, **Otthoni és kisvállalati verzió**
|
||||||
- Architektúra választás (64-bit / 32-bit)
|
- Architektúra választás (64-bit / 32-bit)
|
||||||
- 22 telepítési nyelv
|
- 22 telepítési nyelv
|
||||||
@@ -45,6 +45,8 @@ Első futtatáskor a Windows SmartScreen figyelmeztetést jeleníthet meg, mivel
|
|||||||
- Meglévő Office automatikus felismerése és eltávolítási lehetőség telepítés előtt
|
- Meglévő Office automatikus felismerése és eltávolítási lehetőség telepítés előtt
|
||||||
- A telepítés végén alkalmazások közvetlen indítása
|
- A telepítés végén alkalmazások közvetlen indítása
|
||||||
|
|
||||||
|
> **Office 2016** telepítése ISO letöltéssel történik (a Microsoft CDN már nem szolgálja ki a 2016-os Click-to-Run fájlokat). A Standard kiadás 32-bit és 64-bit, a Professional Plus kiadás 64-bit változatban érhető el, magyar nyelven.
|
||||||
|
|
||||||
### Office eltávolítás
|
### Office eltávolítás
|
||||||
- Telepített Office verziók automatikus felismerése (Click-to-Run és MSI)
|
- Telepített Office verziók automatikus felismerése (Click-to-Run és MSI)
|
||||||
- Szelektív eltávolítás — minden termék külön kiválasztható
|
- Szelektív eltávolítás — minden termék külön kiválasztható
|
||||||
@@ -93,7 +95,7 @@ Első futtatáskor a Windows SmartScreen figyelmeztetést jeleníthet meg, mivel
|
|||||||
|
|
||||||
## Verziótörténet
|
## Verziótörténet
|
||||||
|
|
||||||
A részletes verziótörténet a [Releases](https://github.com/InstaSoft-hu/OfficeTool/releases) oldalon érhető el.
|
A részletes verziótörténet a Releases oldalon érhető el.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
BINáris
Resources/app.ico
Normal file
BINáris
Resources/app.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Méret: 2.4 KiB |
185
Services/LicenseManager.cs
Normal file
185
Services/LicenseManager.cs
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Services
|
||||||
|
{
|
||||||
|
public class LicenseEntry
|
||||||
|
{
|
||||||
|
public string LicenseName { get; set; } = "";
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
public string Status { get; set; } = "";
|
||||||
|
public string ErrorDescription { get; set; } = "";
|
||||||
|
public string Last5 { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LicenseManager
|
||||||
|
{
|
||||||
|
public string OsppPath { get; private set; }
|
||||||
|
|
||||||
|
public bool FindOspp()
|
||||||
|
{
|
||||||
|
var searchPaths = new[]
|
||||||
|
{
|
||||||
|
// Click-to-Run (leggyakoribb — Office 365, 2019, 2021, 2024)
|
||||||
|
@"C:\Program Files\Microsoft Office\root\Office16\OSPP.VBS",
|
||||||
|
@"C:\Program Files (x86)\Microsoft Office\root\Office16\OSPP.VBS",
|
||||||
|
// Hagyományos MSI
|
||||||
|
@"C:\Program Files\Microsoft Office\Office16\ospp.vbs",
|
||||||
|
@"C:\Program Files (x86)\Microsoft Office\Office16\ospp.vbs",
|
||||||
|
@"C:\Program Files\Microsoft Office\Office15\ospp.vbs",
|
||||||
|
@"C:\Program Files (x86)\Microsoft Office\Office15\ospp.vbs",
|
||||||
|
@"C:\Program Files\Microsoft Office\Office14\ospp.vbs",
|
||||||
|
@"C:\Program Files (x86)\Microsoft Office\Office14\ospp.vbs",
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var path in searchPaths)
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
OsppPath = path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var c2rPath = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
|
||||||
|
@"SOFTWARE\Microsoft\Office\ClickToRun\Configuration")
|
||||||
|
?.GetValue("InstallationPath") as string;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(c2rPath))
|
||||||
|
{
|
||||||
|
// C2R: root\Office16 vagy sima Office16
|
||||||
|
var candidates = new[]
|
||||||
|
{
|
||||||
|
Path.Combine(c2rPath, "root", "Office16", "OSPP.VBS"),
|
||||||
|
Path.Combine(c2rPath, "Office16", "OSPP.VBS"),
|
||||||
|
};
|
||||||
|
foreach (var candidate in candidates)
|
||||||
|
{
|
||||||
|
if (File.Exists(candidate))
|
||||||
|
{
|
||||||
|
OsppPath = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetStatusAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(OsppPath))
|
||||||
|
return "Az ospp.vbs nem tal\u00e1lhat\u00f3.";
|
||||||
|
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
return await runner.RunAndCaptureAsync("cscript",
|
||||||
|
"//Nologo \"" + OsppPath + "\" /dstatus");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> ParseLicenseKeys(string dstatusOutput)
|
||||||
|
{
|
||||||
|
var keys = new List<string>();
|
||||||
|
var regex = new Regex(@"Last 5 characters of installed product key:\s*(\S+)",
|
||||||
|
RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
foreach (Match match in regex.Matches(dstatusOutput))
|
||||||
|
{
|
||||||
|
keys.Add(match.Groups[1].Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LicenseEntry> ParseLicenseEntries(string dstatusOutput)
|
||||||
|
{
|
||||||
|
var entries = new List<LicenseEntry>();
|
||||||
|
|
||||||
|
// Split by "-------" separator blocks
|
||||||
|
var blocks = Regex.Split(dstatusOutput, @"-{10,}");
|
||||||
|
|
||||||
|
foreach (var block in blocks)
|
||||||
|
{
|
||||||
|
var trimmed = block.Trim();
|
||||||
|
if (string.IsNullOrEmpty(trimmed)) continue;
|
||||||
|
if (trimmed.StartsWith("---Processing") || trimmed.StartsWith("---Exiting")) continue;
|
||||||
|
|
||||||
|
var entry = new LicenseEntry();
|
||||||
|
|
||||||
|
var nameMatch = Regex.Match(trimmed, @"LICENSE NAME:\s*(.+)", RegexOptions.IgnoreCase);
|
||||||
|
if (nameMatch.Success) entry.LicenseName = nameMatch.Groups[1].Value.Trim();
|
||||||
|
|
||||||
|
var descMatch = Regex.Match(trimmed, @"LICENSE DESCRIPTION:\s*(.+)", RegexOptions.IgnoreCase);
|
||||||
|
if (descMatch.Success) entry.Description = descMatch.Groups[1].Value.Trim();
|
||||||
|
|
||||||
|
var statusMatch = Regex.Match(trimmed, @"LICENSE STATUS:\s*(.+)", RegexOptions.IgnoreCase);
|
||||||
|
if (statusMatch.Success) entry.Status = statusMatch.Groups[1].Value.Trim();
|
||||||
|
|
||||||
|
var errorMatch = Regex.Match(trimmed, @"ERROR DESCRIPTION:\s*(.+)", RegexOptions.IgnoreCase);
|
||||||
|
if (errorMatch.Success) entry.ErrorDescription = errorMatch.Groups[1].Value.Trim();
|
||||||
|
|
||||||
|
var keyMatch = Regex.Match(trimmed, @"Last 5 characters of installed product key:\s*(\S+)", RegexOptions.IgnoreCase);
|
||||||
|
if (keyMatch.Success) entry.Last5 = keyMatch.Groups[1].Value.Trim();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(entry.Last5))
|
||||||
|
entries.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> RemoveKeyAsync(string last5Chars)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(OsppPath))
|
||||||
|
return "Az ospp.vbs nem tal\u00e1lhat\u00f3.";
|
||||||
|
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
return await runner.RunAndCaptureAsync("cscript",
|
||||||
|
"//Nologo \"" + OsppPath + "\" /unpkey:" + last5Chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> RemoveAllKeysAsync()
|
||||||
|
{
|
||||||
|
var status = await GetStatusAsync();
|
||||||
|
var keys = ParseLicenseKeys(status);
|
||||||
|
|
||||||
|
if (keys.Count == 0)
|
||||||
|
return "Nem tal\u00e1lhat\u00f3 telep\u00edtett term\u00e9kkulcs.";
|
||||||
|
|
||||||
|
var results = new List<string>();
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
var result = await RemoveKeyAsync(key);
|
||||||
|
results.Add(key + ": " + result.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join("\n", results);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> InstallKeyAsync(string productKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(OsppPath))
|
||||||
|
return "Az ospp.vbs nem tal\u00e1lhat\u00f3.";
|
||||||
|
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
return await runner.RunAndCaptureAsync("cscript",
|
||||||
|
"//Nologo \"" + OsppPath + "\" /inpkey:" + productKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> ActivateAsync()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(OsppPath))
|
||||||
|
return "Az ospp.vbs nem tal\u00e1lhat\u00f3.";
|
||||||
|
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
return await runner.RunAndCaptureAsync("cscript",
|
||||||
|
"//Nologo \"" + OsppPath + "\" /act");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
192
Services/MsiInstaller.cs
Normal file
192
Services/MsiInstaller.cs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Services
|
||||||
|
{
|
||||||
|
public class MsiInstaller
|
||||||
|
{
|
||||||
|
public event Action<string> StatusChanged;
|
||||||
|
public event Action<long, long> DownloadProgress;
|
||||||
|
|
||||||
|
private string _isoPath;
|
||||||
|
private string _mountedDrive;
|
||||||
|
|
||||||
|
public string WorkFolder { get; private set; }
|
||||||
|
|
||||||
|
public MsiInstaller()
|
||||||
|
{
|
||||||
|
WorkFolder = Path.Combine(Path.GetTempPath(), "InstaSoftODT");
|
||||||
|
Directory.CreateDirectory(WorkFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DownloadIsoAsync(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string fileName = Path.GetFileName(new Uri(url).AbsolutePath);
|
||||||
|
_isoPath = Path.Combine(WorkFolder, fileName);
|
||||||
|
|
||||||
|
// Check if already downloaded
|
||||||
|
if (File.Exists(_isoPath))
|
||||||
|
{
|
||||||
|
var fi = new FileInfo(_isoPath);
|
||||||
|
if (fi.Length > 100_000_000)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("ISO már letöltve: " + fileName + " (" + (fi.Length / 1024 / 1024) + " MB)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
File.Delete(_isoPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("ISO letöltése: " + fileName);
|
||||||
|
StatusChanged?.Invoke("URL: " + url);
|
||||||
|
|
||||||
|
using (var handler = new HttpClientHandler { AllowAutoRedirect = true })
|
||||||
|
using (var client = new HttpClient(handler))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "InstaSoftOfficeTool/1.0");
|
||||||
|
client.Timeout = TimeSpan.FromMinutes(30);
|
||||||
|
|
||||||
|
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
long totalBytes = response.Content.Headers.ContentLength ?? -1;
|
||||||
|
|
||||||
|
using (var stream = await response.Content.ReadAsStreamAsync())
|
||||||
|
using (var fileStream = new FileStream(_isoPath, FileMode.Create, FileAccess.Write, FileShare.None, 65536))
|
||||||
|
{
|
||||||
|
var buffer = new byte[65536];
|
||||||
|
long totalRead = 0;
|
||||||
|
int bytesRead;
|
||||||
|
|
||||||
|
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
|
||||||
|
{
|
||||||
|
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||||
|
totalRead += bytesRead;
|
||||||
|
DownloadProgress?.Invoke(totalRead, totalBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var downloadedSize = new FileInfo(_isoPath).Length;
|
||||||
|
StatusChanged?.Invoke("ISO letöltve: " + (downloadedSize / 1024 / 1024) + " MB");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Hálózati hiba: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Időtúllépés: A letöltés túl sokáig tartott.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("ISO letöltési hiba: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> MountIsoAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("ISO csatolása...");
|
||||||
|
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "powershell",
|
||||||
|
Arguments = "-NoProfile -Command \"" +
|
||||||
|
"$result = Mount-DiskImage -ImagePath '" + _isoPath + "' -PassThru; " +
|
||||||
|
"$vol = $result | Get-Volume; " +
|
||||||
|
"Write-Output $vol.DriveLetter\"",
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var process = Process.Start(psi))
|
||||||
|
{
|
||||||
|
string output = await Task.Run(() => process.StandardOutput.ReadToEnd());
|
||||||
|
await Task.Run(() => process.WaitForExit());
|
||||||
|
|
||||||
|
string driveLetter = output.Trim();
|
||||||
|
if (process.ExitCode != 0 || driveLetter.Length != 1 || !char.IsLetter(driveLetter[0]))
|
||||||
|
{
|
||||||
|
string error = await Task.Run(() => process.StandardError.ReadToEnd());
|
||||||
|
StatusChanged?.Invoke("Hiba: Az ISO csatolása sikertelen.");
|
||||||
|
if (!string.IsNullOrEmpty(error))
|
||||||
|
StatusChanged?.Invoke(error.Trim());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mountedDrive = driveLetter + ":\\";
|
||||||
|
StatusChanged?.Invoke("ISO csatolva: " + _mountedDrive);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("ISO csatolási hiba: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> RunSetupAsync(Action<string> outputCallback)
|
||||||
|
{
|
||||||
|
string setupExe = Path.Combine(_mountedDrive, "setup.exe");
|
||||||
|
if (!File.Exists(setupExe))
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Hiba: setup.exe nem található: " + setupExe);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("Office 2016 telepítő indítása: " + setupExe);
|
||||||
|
outputCallback?.Invoke("A Microsoft Office telepítő ablaka megjelenik...");
|
||||||
|
outputCallback?.Invoke("Kérjük, kövesse a telepítő utasításait.");
|
||||||
|
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = setupExe,
|
||||||
|
UseShellExecute = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var process = Process.Start(psi))
|
||||||
|
{
|
||||||
|
await Task.Run(() => process.WaitForExit());
|
||||||
|
return process.ExitCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DismountIsoAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_isoPath) || !File.Exists(_isoPath)) return;
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("ISO leválasztása...");
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "powershell",
|
||||||
|
Arguments = "-NoProfile -Command \"Dismount-DiskImage -ImagePath '" + _isoPath + "'\"",
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var process = Process.Start(psi))
|
||||||
|
{
|
||||||
|
await Task.Run(() => process.WaitForExit());
|
||||||
|
}
|
||||||
|
StatusChanged?.Invoke("ISO leválasztva.");
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
Services/OdtDownloader.cs
Normal file
124
Services/OdtDownloader.cs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Services
|
||||||
|
{
|
||||||
|
public class OdtDownloader
|
||||||
|
{
|
||||||
|
// Direct CDN link to ODT setup.exe (no self-extractor needed)
|
||||||
|
private const string OdtDownloadUrl = "https://officecdn.microsoft.com/pr/wsus/setup.exe";
|
||||||
|
|
||||||
|
public event Action<string> StatusChanged;
|
||||||
|
|
||||||
|
public string OdtFolder { get; private set; }
|
||||||
|
public string SetupExePath { get; private set; }
|
||||||
|
|
||||||
|
public OdtDownloader()
|
||||||
|
{
|
||||||
|
OdtFolder = Path.Combine(Path.GetTempPath(), "InstaSoftODT");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DownloadAndExtractAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetupExePath = Path.Combine(OdtFolder, "setup.exe");
|
||||||
|
|
||||||
|
// Check if valid setup.exe already exists
|
||||||
|
if (File.Exists(SetupExePath))
|
||||||
|
{
|
||||||
|
var existingSize = new FileInfo(SetupExePath).Length;
|
||||||
|
if (existingSize > 1000000) // valid setup.exe is ~7MB
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Az ODT setup.exe m\u00e1r el\u00e9rhet\u0151 (" +
|
||||||
|
(existingSize / 1024 / 1024) + " MB).");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete corrupted file
|
||||||
|
StatusChanged?.Invoke("S\u00e9r\u00fclt setup.exe t\u00f6rl\u00e9se...");
|
||||||
|
File.Delete(SetupExePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(OdtFolder);
|
||||||
|
|
||||||
|
// Download setup.exe directly from CDN
|
||||||
|
StatusChanged?.Invoke("Office Deployment Tool let\u00f6lt\u00e9se...");
|
||||||
|
StatusChanged?.Invoke("URL: " + OdtDownloadUrl);
|
||||||
|
|
||||||
|
using (var handler = new HttpClientHandler { AllowAutoRedirect = true })
|
||||||
|
using (var client = new HttpClient(handler))
|
||||||
|
{
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "InstaSoftOfficeTool/1.0");
|
||||||
|
client.Timeout = TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
|
var response = await client.GetAsync(OdtDownloadUrl);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Hiba: HTTP " + (int)response.StatusCode + " " + response.StatusCode);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentType = response.Content.Headers.ContentType?.MediaType ?? "";
|
||||||
|
if (contentType.Contains("text/html"))
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Hiba: HTML oldal \u00e9rkezett exe helyett.");
|
||||||
|
StatusChanged?.Invoke("T\u00f6ltse le manu\u00e1lisan: https://www.microsoft.com/en-us/download/details.aspx?id=49117");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = await response.Content.ReadAsByteArrayAsync();
|
||||||
|
File.WriteAllBytes(SetupExePath, bytes);
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("Let\u00f6ltve: " + (bytes.Length / 1024) + " KB");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(SetupExePath) || new FileInfo(SetupExePath).Length < 1000000)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Hiba: A let\u00f6lt\u00f6tt f\u00e1jl s\u00e9r\u00fclt vagy t\u00fal kicsi.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("ODT setup.exe k\u00e9sz.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (System.Net.Http.HttpRequestException ex)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("H\u00e1l\u00f3zati hiba: Nincs internetkapcsolat vagy a szerver nem el\u00e9rhet\u0151.");
|
||||||
|
StatusChanged?.Invoke("R\u00e9szletek: " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Id\u0151t\u00fall\u00e9p\u00e9s: A let\u00f6lt\u00e9s t\u00fal sok\u00e1ig tartott. Ellen\u0151rizze az internetkapcsolatot.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusChanged?.Invoke("Hiba: " + ex.GetType().Name + ": " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> RunSetupAsync(string configXmlPath, Action<string> outputCallback)
|
||||||
|
{
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
runner.OutputReceived += line => outputCallback?.Invoke(line);
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("Office telep\u00edt\u00e9s ind\u00edt\u00e1sa...");
|
||||||
|
return await runner.RunAsync(SetupExePath, "/configure \"" + configXmlPath + "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> RunRemoveAsync(string configXmlPath, Action<string> outputCallback)
|
||||||
|
{
|
||||||
|
var runner = new ProcessRunner();
|
||||||
|
runner.OutputReceived += line => outputCallback?.Invoke(line);
|
||||||
|
|
||||||
|
StatusChanged?.Invoke("Office elt\u00e1vol\u00edt\u00e1s ind\u00edt\u00e1sa...");
|
||||||
|
return await runner.RunAsync(SetupExePath, "/configure \"" + configXmlPath + "\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
Services/OdtXmlGenerator.cs
Normal file
79
Services/OdtXmlGenerator.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using System.Xml.Linq;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Services
|
||||||
|
{
|
||||||
|
public static class OdtXmlGenerator
|
||||||
|
{
|
||||||
|
public static string Generate(InstallConfig config)
|
||||||
|
{
|
||||||
|
var product = new XElement("Product",
|
||||||
|
new XAttribute("ID", config.Edition.ProductId));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(config.ProductKey))
|
||||||
|
{
|
||||||
|
product.Add(new XAttribute("PIDKEY", config.ProductKey.Replace("-", "").Trim()));
|
||||||
|
}
|
||||||
|
|
||||||
|
product.Add(new XElement("Language", new XAttribute("ID", config.Language)));
|
||||||
|
|
||||||
|
foreach (var app in config.ExcludedApps)
|
||||||
|
{
|
||||||
|
product.Add(new XElement("ExcludeApp", new XAttribute("ID", app)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var add = new XElement("Add",
|
||||||
|
new XAttribute("OfficeClientEdition", config.Architecture),
|
||||||
|
new XAttribute("Channel", config.Edition.Channel),
|
||||||
|
product);
|
||||||
|
|
||||||
|
var configuration = new XElement("Configuration",
|
||||||
|
add,
|
||||||
|
new XElement("Display",
|
||||||
|
new XAttribute("Level", "Full"),
|
||||||
|
new XAttribute("AcceptEULA", "TRUE")),
|
||||||
|
new XElement("Property",
|
||||||
|
new XAttribute("Name", "FORCEAPPSHUTDOWN"),
|
||||||
|
new XAttribute("Value", "TRUE")));
|
||||||
|
|
||||||
|
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null), configuration);
|
||||||
|
return doc.Declaration + "\n" + doc.Root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateRemoveAll()
|
||||||
|
{
|
||||||
|
var configuration = new XElement("Configuration",
|
||||||
|
new XElement("Remove", new XAttribute("All", "TRUE")),
|
||||||
|
new XElement("Display",
|
||||||
|
new XAttribute("Level", "Full"),
|
||||||
|
new XAttribute("AcceptEULA", "TRUE")),
|
||||||
|
new XElement("Property",
|
||||||
|
new XAttribute("Name", "FORCEAPPSHUTDOWN"),
|
||||||
|
new XAttribute("Value", "TRUE")));
|
||||||
|
|
||||||
|
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null), configuration);
|
||||||
|
return doc.Declaration + "\n" + doc.Root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GenerateRemoveProducts(string[] productIds)
|
||||||
|
{
|
||||||
|
var remove = new XElement("Remove");
|
||||||
|
foreach (var id in productIds)
|
||||||
|
{
|
||||||
|
remove.Add(new XElement("Product", new XAttribute("ID", id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
var configuration = new XElement("Configuration",
|
||||||
|
remove,
|
||||||
|
new XElement("Display",
|
||||||
|
new XAttribute("Level", "Full"),
|
||||||
|
new XAttribute("AcceptEULA", "TRUE")),
|
||||||
|
new XElement("Property",
|
||||||
|
new XAttribute("Name", "FORCEAPPSHUTDOWN"),
|
||||||
|
new XAttribute("Value", "TRUE")));
|
||||||
|
|
||||||
|
var doc = new XDocument(new XDeclaration("1.0", "utf-8", null), configuration);
|
||||||
|
return doc.Declaration + "\n" + doc.Root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
116
Services/OfficeDetector.cs
Normal file
116
Services/OfficeDetector.cs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using InstaSoftOfficeTool.Models;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Services
|
||||||
|
{
|
||||||
|
public static class OfficeDetector
|
||||||
|
{
|
||||||
|
public static List<InstalledOffice> Detect()
|
||||||
|
{
|
||||||
|
var results = new List<InstalledOffice>();
|
||||||
|
|
||||||
|
// Check Click-to-Run
|
||||||
|
DetectClickToRun(results);
|
||||||
|
|
||||||
|
// Check MSI-based installs
|
||||||
|
DetectMsi(results);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DetectClickToRun(List<InstalledOffice> results)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var key = Registry.LocalMachine.OpenSubKey(
|
||||||
|
@"SOFTWARE\Microsoft\Office\ClickToRun\Configuration"))
|
||||||
|
{
|
||||||
|
if (key == null) return;
|
||||||
|
|
||||||
|
var productIds = key.GetValue("ProductReleaseIds") as string;
|
||||||
|
var versionToReport = key.GetValue("VersionToReport") as string;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(productIds)) return;
|
||||||
|
|
||||||
|
// Split into individual products (e.g. "O365BusinessRetail,VisioProRetail")
|
||||||
|
var ids = productIds.Split(',');
|
||||||
|
foreach (var id in ids)
|
||||||
|
{
|
||||||
|
var trimmedId = id.Trim();
|
||||||
|
if (string.IsNullOrEmpty(trimmedId)) continue;
|
||||||
|
|
||||||
|
results.Add(new InstalledOffice
|
||||||
|
{
|
||||||
|
DisplayName = "Microsoft Office Click-to-Run (" + trimmedId + ")",
|
||||||
|
Version = versionToReport ?? "",
|
||||||
|
ProductCode = trimmedId,
|
||||||
|
IsClickToRun = true,
|
||||||
|
IsSelected = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DetectMsi(List<InstalledOffice> results)
|
||||||
|
{
|
||||||
|
var uninstallPaths = new[]
|
||||||
|
{
|
||||||
|
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
|
||||||
|
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var path in uninstallPaths)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var key = Registry.LocalMachine.OpenSubKey(path))
|
||||||
|
{
|
||||||
|
if (key == null) continue;
|
||||||
|
|
||||||
|
foreach (var subKeyName in key.GetSubKeyNames())
|
||||||
|
{
|
||||||
|
using (var subKey = key.OpenSubKey(subKeyName))
|
||||||
|
{
|
||||||
|
if (subKey == null) continue;
|
||||||
|
|
||||||
|
var displayName = subKey.GetValue("DisplayName") as string;
|
||||||
|
var publisher = subKey.GetValue("Publisher") as string;
|
||||||
|
|
||||||
|
if (displayName != null &&
|
||||||
|
publisher != null &&
|
||||||
|
publisher.Contains("Microsoft") &&
|
||||||
|
(displayName.Contains("Microsoft Office") ||
|
||||||
|
displayName.Contains("Microsoft 365")))
|
||||||
|
{
|
||||||
|
var version = subKey.GetValue("DisplayVersion") as string ?? "";
|
||||||
|
|
||||||
|
// Skip Click-to-Run updater entries
|
||||||
|
if (displayName.Contains("Click-to-Run") &&
|
||||||
|
!displayName.Contains("Microsoft Office"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Only accept {GUID} product codes for MSI uninstall
|
||||||
|
bool isGuid = Regex.IsMatch(subKeyName, @"^\{[0-9A-Fa-f\-]+\}$");
|
||||||
|
|
||||||
|
results.Add(new InstalledOffice
|
||||||
|
{
|
||||||
|
DisplayName = displayName,
|
||||||
|
Version = version,
|
||||||
|
ProductCode = isGuid ? subKeyName : null,
|
||||||
|
IsClickToRun = false,
|
||||||
|
IsSelected = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Services/ProcessRunner.cs
Normal file
63
Services/ProcessRunner.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace InstaSoftOfficeTool.Services
|
||||||
|
{
|
||||||
|
public class ProcessRunner
|
||||||
|
{
|
||||||
|
public event Action<string> OutputReceived;
|
||||||
|
|
||||||
|
public async Task<int> RunAsync(string fileName, string arguments, bool redirectOutput = true)
|
||||||
|
{
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = fileName,
|
||||||
|
Arguments = arguments,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardOutput = redirectOutput,
|
||||||
|
RedirectStandardError = redirectOutput,
|
||||||
|
StandardOutputEncoding = redirectOutput ? Encoding.UTF8 : null,
|
||||||
|
StandardErrorEncoding = redirectOutput ? Encoding.UTF8 : null
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var process = new Process { StartInfo = psi })
|
||||||
|
{
|
||||||
|
if (redirectOutput)
|
||||||
|
{
|
||||||
|
process.OutputDataReceived += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.Data != null)
|
||||||
|
OutputReceived?.Invoke(e.Data);
|
||||||
|
};
|
||||||
|
process.ErrorDataReceived += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.Data != null)
|
||||||
|
OutputReceived?.Invoke("[HIBA] " + e.Data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
if (redirectOutput)
|
||||||
|
{
|
||||||
|
process.BeginOutputReadLine();
|
||||||
|
process.BeginErrorReadLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Run(() => process.WaitForExit());
|
||||||
|
return process.ExitCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> RunAndCaptureAsync(string fileName, string arguments)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
OutputReceived += line => sb.AppendLine(line);
|
||||||
|
await RunAsync(fileName, arguments);
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
120
Styles/ButtonStyles.xaml
Normal file
120
Styles/ButtonStyles.xaml
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<!-- Primary (accent) button -->
|
||||||
|
<Style x:Key="PrimaryButton" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextOnAccentBrush}"/>
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Padding" Value="24,10"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="border"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
CornerRadius="4"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
SnapsToDevicePixels="True"
|
||||||
|
TextElement.Foreground="{StaticResource TextOnAccentBrush}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="border" Property="Background" Value="{StaticResource AccentHoverBrush}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter TargetName="border" Property="Background" Value="{StaticResource AccentPressedBrush}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter TargetName="border" Property="Opacity" Value="0.4"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Secondary (outline) button -->
|
||||||
|
<Style x:Key="SecondaryButton" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="24,10"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="border"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="4"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="border" Property="Background" Value="{StaticResource CardHoverBrush}"/>
|
||||||
|
<Setter TargetName="border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter TargetName="border" Property="Background" Value="{StaticResource BorderBrush}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter TargetName="border" Property="Opacity" Value="0.4"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Card button (for WelcomePage action cards) -->
|
||||||
|
<Style x:Key="CardButton" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="{StaticResource CardBrush}"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="20,24"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="border"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<Border.Effect>
|
||||||
|
<DropShadowEffect ShadowDepth="1" BlurRadius="8" Opacity="0.08" Color="Black"/>
|
||||||
|
</Border.Effect>
|
||||||
|
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="border" Property="Background" Value="#FAFCFF"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter TargetName="border" Property="Background" Value="#F0F4FA"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
259
Styles/ControlStyles.xaml
Normal file
259
Styles/ControlStyles.xaml
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<!-- Fluent ComboBox with rounded corners and custom dropdown -->
|
||||||
|
<Style x:Key="FluentComboBox" TargetType="ComboBox">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="Background" Value="{StaticResource CardBrush}"/>
|
||||||
|
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="Padding" Value="12,9"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="ItemContainerStyle">
|
||||||
|
<Setter.Value>
|
||||||
|
<Style TargetType="ComboBoxItem">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="12,8"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="ComboBoxItem">
|
||||||
|
<Border x:Name="itemBorder" Background="Transparent"
|
||||||
|
Padding="{TemplateBinding Padding}" CornerRadius="4" Margin="2,1">
|
||||||
|
<ContentPresenter/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="itemBorder" Property="Background" Value="#E8E8E8"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter TargetName="itemBorder" Property="Background" Value="#DCE8F8"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="ComboBox">
|
||||||
|
<Grid>
|
||||||
|
<ToggleButton x:Name="ToggleButton" Focusable="False"
|
||||||
|
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
ClickMode="Press">
|
||||||
|
<ToggleButton.Template>
|
||||||
|
<ControlTemplate TargetType="ToggleButton">
|
||||||
|
<Border x:Name="toggleBorder"
|
||||||
|
Background="{StaticResource CardBrush}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
BorderThickness="1" CornerRadius="6"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition Width="36"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Border Grid.Column="1">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="10" HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Foreground="{StaticResource TextSecondaryBrush}"/>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="toggleBorder" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsChecked" Value="True">
|
||||||
|
<Setter TargetName="toggleBorder" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="toggleBorder" Property="BorderThickness" Value="1,1,1,2"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</ToggleButton.Template>
|
||||||
|
</ToggleButton>
|
||||||
|
<ContentPresenter x:Name="ContentSite" IsHitTestVisible="False"
|
||||||
|
Content="{TemplateBinding SelectionBoxItem}"
|
||||||
|
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
|
||||||
|
Margin="14,9,36,9"
|
||||||
|
VerticalAlignment="Center" HorizontalAlignment="Left"/>
|
||||||
|
<Popup x:Name="Popup" Placement="Bottom"
|
||||||
|
IsOpen="{TemplateBinding IsDropDownOpen}"
|
||||||
|
AllowsTransparency="True" Focusable="False"
|
||||||
|
PopupAnimation="Slide">
|
||||||
|
<Border x:Name="DropDownBorder" Background="{StaticResource CardBrush}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
CornerRadius="8" Padding="4"
|
||||||
|
MinWidth="{TemplateBinding ActualWidth}"
|
||||||
|
MaxHeight="{TemplateBinding MaxDropDownHeight}"
|
||||||
|
Margin="0,2,0,0"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<Border.Effect>
|
||||||
|
<DropShadowEffect ShadowDepth="4" BlurRadius="16" Opacity="0.14" Color="Black"/>
|
||||||
|
</Border.Effect>
|
||||||
|
<ScrollViewer SnapsToDevicePixels="True">
|
||||||
|
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
</Grid>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Fluent TextBox with rounded corners -->
|
||||||
|
<Style x:Key="FluentTextBox" TargetType="TextBox">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="16"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="CaretBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter Property="SelectionBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="TextBox">
|
||||||
|
<Border x:Name="border" Background="{StaticResource CardBrush}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}" BorderThickness="1"
|
||||||
|
CornerRadius="6" Padding="10,8" SnapsToDevicePixels="True">
|
||||||
|
<ScrollViewer x:Name="PART_ContentHost" Focusable="False"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="border" Property="BorderBrush" Value="#BBBBBB"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsFocused" Value="True">
|
||||||
|
<Setter TargetName="border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="border" Property="BorderThickness" Value="1,1,1,2"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter TargetName="border" Property="Background" Value="#F0F0F0"/>
|
||||||
|
<Setter Property="Foreground" Value="#999999"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Card-style RadioButton -->
|
||||||
|
<Style x:Key="CardRadioButton" TargetType="RadioButton">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="15"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Padding" Value="16,14"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="RadioButton">
|
||||||
|
<Border x:Name="border"
|
||||||
|
Background="{StaticResource CardBrush}"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="{TemplateBinding Padding}"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<Border.Effect>
|
||||||
|
<DropShadowEffect ShadowDepth="1" BlurRadius="6" Opacity="0.06" Color="Black"/>
|
||||||
|
</Border.Effect>
|
||||||
|
<ContentPresenter VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="border" Property="Background" Value="#FAFCFF"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsChecked" Value="True">
|
||||||
|
<Setter TargetName="border" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="border" Property="BorderThickness" Value="2"/>
|
||||||
|
<Setter TargetName="border" Property="Background" Value="#F0F6FF"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Fluent CheckBox with custom box -->
|
||||||
|
<Style x:Key="FluentCheckBox" TargetType="CheckBox">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="CheckBox">
|
||||||
|
<Border x:Name="rootBorder" Background="Transparent" Padding="6,8" CornerRadius="6"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Border x:Name="checkBorder" Width="20" Height="20"
|
||||||
|
CornerRadius="4" BorderThickness="2"
|
||||||
|
BorderBrush="{StaticResource BorderBrush}"
|
||||||
|
Background="{StaticResource CardBrush}"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,10,0">
|
||||||
|
<TextBlock x:Name="checkMark" Text=""
|
||||||
|
FontFamily="Segoe MDL2 Assets" FontSize="12"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
Foreground="White" Visibility="Collapsed"/>
|
||||||
|
</Border>
|
||||||
|
<ContentPresenter VerticalAlignment="Center" RecognizesAccessKey="True"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="rootBorder" Property="Background" Value="#F5F5F5"/>
|
||||||
|
<Setter TargetName="checkBorder" Property="BorderBrush" Value="#999"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsChecked" Value="True">
|
||||||
|
<Setter TargetName="checkBorder" Property="Background" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="checkBorder" Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter TargetName="checkMark" Property="Visibility" Value="Visible"/>
|
||||||
|
</Trigger>
|
||||||
|
<MultiTrigger>
|
||||||
|
<MultiTrigger.Conditions>
|
||||||
|
<Condition Property="IsChecked" Value="True"/>
|
||||||
|
<Condition Property="IsMouseOver" Value="True"/>
|
||||||
|
</MultiTrigger.Conditions>
|
||||||
|
<Setter TargetName="checkBorder" Property="Background" Value="{StaticResource AccentHoverBrush}"/>
|
||||||
|
<Setter TargetName="checkBorder" Property="BorderBrush" Value="{StaticResource AccentHoverBrush}"/>
|
||||||
|
<Setter TargetName="rootBorder" Property="Background" Value="#F5F5F5"/>
|
||||||
|
</MultiTrigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter TargetName="rootBorder" Property="Opacity" Value="0.5"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Step indicator dot (active) -->
|
||||||
|
<Style x:Key="StepDotActive" TargetType="Ellipse">
|
||||||
|
<Setter Property="Width" Value="10"/>
|
||||||
|
<Setter Property="Height" Value="10"/>
|
||||||
|
<Setter Property="Fill" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter Property="Margin" Value="4,0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Step indicator dot (inactive) -->
|
||||||
|
<Style x:Key="StepDotInactive" TargetType="Ellipse">
|
||||||
|
<Setter Property="Width" Value="8"/>
|
||||||
|
<Setter Property="Height" Value="8"/>
|
||||||
|
<Setter Property="Fill" Value="{StaticResource BorderBrush}"/>
|
||||||
|
<Setter Property="Margin" Value="4,0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Progress bar -->
|
||||||
|
<Style x:Key="FluentProgressBar" TargetType="ProgressBar">
|
||||||
|
<Setter Property="Height" Value="4"/>
|
||||||
|
<Setter Property="Background" Value="{StaticResource BorderBrush}"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource AccentBrush}"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
48
Styles/FluentTheme.xaml
Normal file
48
Styles/FluentTheme.xaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
|
||||||
|
<!-- Colors -->
|
||||||
|
<Color x:Key="AccentColor">#0078D4</Color>
|
||||||
|
<Color x:Key="AccentHoverColor">#106EBE</Color>
|
||||||
|
<Color x:Key="AccentPressedColor">#005A9E</Color>
|
||||||
|
<Color x:Key="BackgroundColor">#F3F3F3</Color>
|
||||||
|
<Color x:Key="CardColor">#FFFFFF</Color>
|
||||||
|
<Color x:Key="CardHoverColor">#F8F8F8</Color>
|
||||||
|
<Color x:Key="BorderColor">#E0E0E0</Color>
|
||||||
|
<Color x:Key="TextPrimaryColor">#1A1A1A</Color>
|
||||||
|
<Color x:Key="TextSecondaryColor">#666666</Color>
|
||||||
|
<Color x:Key="TextOnAccentColor">#FFFFFF</Color>
|
||||||
|
<Color x:Key="SuccessColor">#107C10</Color>
|
||||||
|
<Color x:Key="ErrorColor">#D13438</Color>
|
||||||
|
<Color x:Key="WarningColor">#CA5010</Color>
|
||||||
|
<Color x:Key="HeaderBarColor">#FAFAFA</Color>
|
||||||
|
|
||||||
|
<!-- Brushes -->
|
||||||
|
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}"/>
|
||||||
|
<SolidColorBrush x:Key="AccentHoverBrush" Color="{StaticResource AccentHoverColor}"/>
|
||||||
|
<SolidColorBrush x:Key="AccentPressedBrush" Color="{StaticResource AccentPressedColor}"/>
|
||||||
|
<SolidColorBrush x:Key="BackgroundBrush" Color="{StaticResource BackgroundColor}"/>
|
||||||
|
<SolidColorBrush x:Key="CardBrush" Color="{StaticResource CardColor}"/>
|
||||||
|
<SolidColorBrush x:Key="CardHoverBrush" Color="{StaticResource CardHoverColor}"/>
|
||||||
|
<SolidColorBrush x:Key="BorderBrush" Color="{StaticResource BorderColor}"/>
|
||||||
|
<SolidColorBrush x:Key="TextPrimaryBrush" Color="{StaticResource TextPrimaryColor}"/>
|
||||||
|
<SolidColorBrush x:Key="TextSecondaryBrush" Color="{StaticResource TextSecondaryColor}"/>
|
||||||
|
<SolidColorBrush x:Key="TextOnAccentBrush" Color="{StaticResource TextOnAccentColor}"/>
|
||||||
|
<SolidColorBrush x:Key="SuccessBrush" Color="{StaticResource SuccessColor}"/>
|
||||||
|
<SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}"/>
|
||||||
|
<SolidColorBrush x:Key="WarningBrush" Color="{StaticResource WarningColor}"/>
|
||||||
|
<SolidColorBrush x:Key="HeaderBarBrush" Color="{StaticResource HeaderBarColor}"/>
|
||||||
|
|
||||||
|
<!-- Global font — no Foreground here, it breaks inherited colors in buttons -->
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style TargetType="Label">
|
||||||
|
<Setter Property="FontFamily" Value="Segoe UI Variable Display, Segoe UI, Arial"/>
|
||||||
|
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
26
app.manifest
Normal file
26
app.manifest
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="InstaSoftOfficeTool"/>
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
||||||
Reference in New Issue
Block a user