more of the logic

This commit is contained in:
Asval 2024-10-02 21:08:41 +02:00
parent ddee7c1479
commit 2d001ff21f
15 changed files with 210 additions and 174 deletions

View File

@ -147,11 +147,17 @@
</Viewbox>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Changelog" Command="{Binding MenuCommand}" CommandParameter="Help_Changelog">
<MenuItem Header="Releases" Command="{Binding MenuCommand}" CommandParameter="Help_Releases">
<MenuItem.Icon>
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
<Path Fill="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="{StaticResource NoteIcon}" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M16 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M12 8m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M12 16m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M12 15v-6" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M15 11l-2 -2" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M11 7l-1.9 -1.9" />
<Path StrokeThickness="2" Stroke="{DynamicResource {x:Static adonisUi:Brushes.AccentForegroundBrush}}" Data="M13.446 2.6l7.955 7.954a2.045 2.045 0 0 1 0 2.892l-7.955 7.955a2.045 2.045 0 0 1 -2.892 0l-7.955 -7.955a2.045 2.045 0 0 1 0 -2.892l7.955 -7.955a2.045 2.045 0 0 1 2.892 0z" />
</Canvas>
</Viewbox>
</MenuItem.Icon>

View File

@ -45,8 +45,6 @@ public partial class MainWindow
private async void OnLoaded(object sender, RoutedEventArgs e)
{
var result = new UpdateView().ShowDialog();
var newOrUpdated = UserSettings.Default.ShowChangelog;
#if !DEBUG
ApplicationService.ApiEndpointView.FModelApi.CheckForUpdates(UserSettings.Default.UpdateMode, true);

View File

@ -20,6 +20,7 @@ public class ApiEndpointViewModel
public FortniteCentralApiEndpoint CentralApi { get; }
public EpicApiEndpoint EpicApi { get; }
public FModelApiEndpoint FModelApi { get; }
public GitHubApiEndpoint GitHubApi { get; }
public DynamicApiEndpoint DynamicApi { get; }
public ApiEndpointViewModel()
@ -29,6 +30,7 @@ public class ApiEndpointViewModel
CentralApi = new FortniteCentralApiEndpoint(_client);
EpicApi = new EpicApiEndpoint(_client);
FModelApi = new FModelApiEndpoint(_client);
GitHubApi = new GitHubApiEndpoint(_client);
DynamicApi = new DynamicApiEndpoint(_client);
}

View File

@ -10,6 +10,7 @@ using FModel.Framework;
using FModel.Services;
using FModel.Settings;
using FModel.ViewModels.ApiEndpoints.Models;
using FModel.Views;
using Newtonsoft.Json;
using RestSharp;
using Serilog;
@ -22,7 +23,6 @@ namespace FModel.ViewModels.ApiEndpoints;
public class FModelApiEndpoint : AbstractApiProvider
{
private GitHubCommit[] _commits;
private News _news;
private Info _infos;
private Donator[] _donators;
@ -33,23 +33,6 @@ public class FModelApiEndpoint : AbstractApiProvider
public FModelApiEndpoint(RestClient client) : base(client) { }
public async Task<GitHubCommit[]> GetGitHubCommitHistoryAsync(string branch = "dev", int page = 1, int limit = 20)
{
var request = new FRestRequest(Constants.GH_COMMITS_HISTORY);
request.AddParameter("sha", branch);
request.AddParameter("page", page);
request.AddParameter("per_page", limit);
var response = await _client.ExecuteAsync<GitHubCommit[]>(request).ConfigureAwait(false);
return response.Data;
}
public async Task<GitHubRelease> GetGitHubReleaseAsync(string tag)
{
var request = new FRestRequest($"{Constants.GH_RELEASES}/tags/{tag}");
var response = await _client.ExecuteAsync<GitHubRelease>(request).ConfigureAwait(false);
return response.Data;
}
public async Task<News> GetNewsAsync(CancellationToken token, string game)
{
var request = new FRestRequest($"https://api.fmodel.app/v1/news/{Constants.APP_VERSION}");
@ -177,33 +160,8 @@ public class FModelApiEndpoint : AbstractApiProvider
return;
}
var downgrade = currentVersion < args.InstalledVersion;
var messageBox = new MessageBoxModel
{
Text = $"The latest version of FModel {UserSettings.Default.UpdateMode.GetDescription()} is {(qa.Value ? qa.ShortCommitHash : args.CurrentVersion)}. You are using version {(qa.Value ? UserSettings.Default.ShortCommitHash : args.InstalledVersion)}. Do you want to {(downgrade ? "downgrade" : "update")} the application now?",
Caption = $"{(downgrade ? "Downgrade" : "Update")} Available",
Icon = MessageBoxImage.Question,
Buttons = MessageBoxButtons.YesNo(),
IsSoundEnabled = false
};
MessageBox.Show(messageBox);
if (messageBox.Result != MessageBoxResult.Yes) return;
try
{
if (AutoUpdater.DownloadUpdate(args))
{
UserSettings.Default.ShowChangelog = currentVersion != args.InstalledVersion;
UserSettings.Default.CommitHash = qa.CommitHash;
Application.Current.Shutdown();
}
}
catch (Exception exception)
{
UserSettings.Default.ShowChangelog = false;
MessageBox.Show(exception.Message, exception.GetType().ToString(), MessageBoxButton.OK, MessageBoxImage.Error);
}
const string message = "A new update is available!";
Helper.OpenWindow<AdonisWindow>(message, () => new UpdateView { Title = message, ResizeMode = ResizeMode.NoResize }.ShowDialog());
}
else
{

View File

@ -0,0 +1,36 @@
using System.Threading.Tasks;
using FModel.Framework;
using FModel.ViewModels.ApiEndpoints.Models;
using RestSharp;
namespace FModel.ViewModels.ApiEndpoints;
public class GitHubApiEndpoint : AbstractApiProvider
{
private GitHubCommit[] _commits;
public GitHubApiEndpoint(RestClient client) : base(client) { }
public async Task<GitHubCommit[]> GetCommitHistoryAsync(string branch = "dev", int page = 1, int limit = 20)
{
var request = new FRestRequest(Constants.GH_COMMITS_HISTORY);
request.AddParameter("sha", branch);
request.AddParameter("page", page);
request.AddParameter("per_page", limit);
var response = await _client.ExecuteAsync<GitHubCommit[]>(request).ConfigureAwait(false);
#if DEBUG
System.IO.File.WriteAllTextAsync(@"C:\Users\valen\Downloads\history.json", Newtonsoft.Json.JsonConvert.SerializeObject(response.Data));
#endif
return response.Data;
}
public async Task<GitHubRelease> GetReleaseAsync(string tag)
{
var request = new FRestRequest($"{Constants.GH_RELEASES}/tags/{tag}");
var response = await _client.ExecuteAsync<GitHubRelease>(request).ConfigureAwait(false);
#if DEBUG
System.IO.File.WriteAllTextAsync(@"C:\Users\valen\Downloads\qa.json", Newtonsoft.Json.JsonConvert.SerializeObject(response.Data));
#endif
return response.Data;
}
}

View File

@ -1,74 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using CUE4Parse.UE4.Versions;
using FModel.Creator;
using FModel.Extensions;
using FModel.Framework;
using FModel.Settings;
using SkiaSharp;
using J = Newtonsoft.Json.JsonPropertyAttribute;
namespace FModel.ViewModels.ApiEndpoints.Models;
public class GitHubRelease
{
[J("assets")] public GitHubAsset[] Assets { get; private set; }
}
public class GitHubAsset : ViewModel
{
[J("name")] public string Name { get; private set; }
[J("size")] public int Size { get; private set; }
[J("download_count")] public int DownloadCount { get; private set; }
[J("browser_download_url")] public string BrowserDownloadUrl { get; private set; }
[J("created_at")] public DateTime CreatedAt { get; private set; }
private bool _isLatest;
public bool IsLatest
{
get => _isLatest;
set => SetProperty(ref _isLatest, value);
}
}
public class GitHubCommit : ViewModel
{
[J("sha")] public string Sha { get; private set; }
[J("commit")] public Commit Commit { get; private set; }
[J("author")] public Author Author { get; private set; }
private GitHubAsset _asset;
public GitHubAsset Asset
{
get => _asset;
set
{
SetProperty(ref _asset, value);
RaisePropertyChanged(nameof(IsDownloadable));
}
}
public bool IsCurrent => Sha == UserSettings.Default.CommitHash;
public string ShortSha => Sha[..7];
public bool IsDownloadable => Asset != null;
}
public class Commit
{
[J("author")] public Author Author { get; set; }
[J("message")] public string Message { get; set; }
}
public class Author
{
[J("name")] public string Name { get; set; }
[J("date")] public DateTime Date { get; set; }
[J("avatar_url")] public string AvatarUrl { get; set; }
[J("html_url")] public string HtmlUrl { get; set; }
}
[DebuggerDisplay("{" + nameof(Messages) + "}")]
public class News
{

View File

@ -0,0 +1,75 @@
using System;
using FModel.Framework;
using FModel.Settings;
using J = Newtonsoft.Json.JsonPropertyAttribute;
namespace FModel.ViewModels.ApiEndpoints.Models;
public class GitHubRelease
{
[J("assets")] public GitHubAsset[] Assets { get; private set; }
}
public class GitHubAsset : ViewModel
{
[J("name")] public string Name { get; private set; }
[J("size")] public int Size { get; private set; }
[J("download_count")] public int DownloadCount { get; private set; }
[J("browser_download_url")] public string BrowserDownloadUrl { get; private set; }
[J("created_at")] public DateTime CreatedAt { get; private set; }
private bool _isLatest;
public bool IsLatest
{
get => _isLatest;
set => SetProperty(ref _isLatest, value);
}
}
public class GitHubCommit : ViewModel
{
private string _sha;
[J("sha")]
public string Sha
{
get => _sha;
set
{
SetProperty(ref _sha, value);
RaisePropertyChanged(nameof(IsCurrent));
RaisePropertyChanged(nameof(ShortSha));
}
}
[J("commit")] public Commit Commit { get; private set; }
[J("author")] public Author Author { get; private set; }
private GitHubAsset _asset;
public GitHubAsset Asset
{
get => _asset;
set
{
SetProperty(ref _asset, value);
RaisePropertyChanged(nameof(IsDownloadable));
}
}
public bool IsCurrent => Sha == UserSettings.Default.CommitHash;
public string ShortSha => Sha[..7];
public bool IsDownloadable => Asset != null;
}
public class Commit
{
[J("author")] public Author Author { get; set; }
[J("message")] public string Message { get; set; }
}
public class Author
{
[J("name")] public string Name { get; set; }
[J("date")] public DateTime Date { get; set; }
[J("avatar_url")] public string AvatarUrl { get; set; }
[J("html_url")] public string HtmlUrl { get; set; }
}

View File

@ -10,7 +10,6 @@ using CUE4Parse.Compression;
using CUE4Parse.Encryption.Aes;
using CUE4Parse.UE4.Objects.Core.Misc;
using CUE4Parse.UE4.VirtualFileSystem;
using FModel.Extensions;
using FModel.Framework;
using FModel.Services;
using FModel.Settings;
@ -50,7 +49,7 @@ public class ApplicationViewModel : ViewModel
public CopyCommand CopyCommand => _copyCommand ??= new CopyCommand(this);
private CopyCommand _copyCommand;
public string InitialWindowTitle => $"FModel {UserSettings.Default.UpdateMode.GetDescription()}";
public string InitialWindowTitle => $"FModel ({UserSettings.Default.ShortCommitHash})";
public string GameDisplayName => CUE4Parse.Provider.GameDisplayName ?? "Unknown";
public string TitleExtra => $"({UserSettings.Default.CurrentDir.UeVersion}){(Build != EBuildKind.Release ? $" ({Build})" : "")}";

View File

@ -54,9 +54,8 @@ public class MenuCommand : ViewModelCommand<ApplicationViewModel>
case "Help_Donate":
Process.Start(new ProcessStartInfo { FileName = Constants.DONATE_LINK, UseShellExecute = true });
break;
case "Help_Changelog":
UserSettings.Default.ShowChangelog = true;
ApplicationService.ApiEndpointView.FModelApi.CheckForUpdates(UserSettings.Default.UpdateMode);
case "Help_Releases":
Helper.OpenWindow<AdonisWindow>("Releases", () => new UpdateView().Show());
break;
case "Help_BugsReport":
Process.Start(new ProcessStartInfo { FileName = Constants.ISSUE_LINK, UseShellExecute = true });

View File

@ -1,5 +1,4 @@
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
@ -8,7 +7,6 @@ using FModel.Framework;
using FModel.Services;
using FModel.ViewModels.ApiEndpoints.Models;
using FModel.Views.Resources.Converters;
using Newtonsoft.Json;
namespace FModel.ViewModels;
@ -31,19 +29,11 @@ public class UpdateViewModel : ViewModel
public async Task Load()
{
#if DEBUG
Commits.AddRange(JsonConvert.DeserializeObject<GitHubCommit[]>(await File.ReadAllTextAsync(@"C:\Users\valen\Downloads\history.json")));
Commits.AddRange(Newtonsoft.Json.JsonConvert.DeserializeObject<GitHubCommit[]>(await System.IO.File.ReadAllTextAsync(@"C:\Users\valen\Downloads\history.json")));
var qa = Newtonsoft.Json.JsonConvert.DeserializeObject<GitHubRelease>(await System.IO.File.ReadAllTextAsync(@"C:\Users\valen\Downloads\qa.json"));
#else
Commits.AddRange(await _apiEndpointView.FModelApi.GetGitHubCommitHistoryAsync());
#endif
_ = PostLoad();
}
private async Task PostLoad()
{
#if DEBUG
var qa = JsonConvert.DeserializeObject<GitHubRelease>(await File.ReadAllTextAsync(@"C:\Users\valen\Downloads\qa.json"));
#else
var qa = await _apiEndpointView.FModelApi.GetGitHubReleaseAsync("qa");
Commits.AddRange(await _apiEndpointView.GitHubApi.GetCommitHistoryAsync());
var qa = await _apiEndpointView.GitHubApi.GetReleaseAsync("qa");
#endif
qa.Assets.OrderByDescending(x => x.CreatedAt).First().IsLatest = true;

View File

@ -65,7 +65,7 @@
</Grid>
</Grid>
<controls:CommitDownloaderControl Grid.Column="1" CommitAsset="{Binding Asset}" IsCurrent="{Binding IsCurrent}">
<controls:CommitDownloaderControl Grid.Column="1" Commit="{Binding}">
<controls:CommitDownloaderControl.Style>
<Style TargetType="controls:CommitDownloaderControl">
<Setter Property="Visibility" Value="Collapsed" />

View File

@ -32,7 +32,7 @@
<DataTrigger Binding="{Binding IsCurrent}" Value="True">
<Setter Property="Text" Value="Current" />
</DataTrigger>
<DataTrigger Binding="{Binding CommitAsset.IsLatest, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="True">
<DataTrigger Binding="{Binding Asset.IsLatest}" Value="True">
<Setter Property="Text" Value="Latest" />
</DataTrigger>
</Style.Triggers>
@ -47,10 +47,12 @@
<DataTrigger Binding="{Binding IsCurrent}" Value="True">
<Setter Property="Visibility" Value="Visible" />
<Setter Property="BorderBrush" Value="#3f92b9" />
<Setter Property="Background" Value="#0f3f92b9" />
</DataTrigger>
<DataTrigger Binding="{Binding CommitAsset.IsLatest, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="True">
<DataTrigger Binding="{Binding Asset.IsLatest}" Value="True">
<Setter Property="Visibility" Value="Visible" />
<Setter Property="BorderBrush" Value="#3fb950" />
<Setter Property="Background" Value="#0f3fb950" />
</DataTrigger>
</Style.Triggers>
</Style>
@ -72,7 +74,7 @@
</Viewbox>
<StackPanel Grid.Column="2">
<TextBlock Text="Size" FontSize="10" />
<TextBlock FontSize="10" Text="{Binding CommitAsset.Size, Converter={x:Static converters:SizeToStringConverter.Instance}, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<TextBlock FontSize="10" Text="{Binding Asset.Size, Converter={x:Static converters:SizeToStringConverter.Instance}}" />
</StackPanel>
</Grid>

View File

@ -1,7 +1,14 @@
using System.Windows;
using System;
using System.Windows;
using System.Windows.Controls;
using AdonisUI.Controls;
using AutoUpdaterDotNET;
using FModel.Settings;
using FModel.ViewModels.ApiEndpoints.Models;
using MessageBox = AdonisUI.Controls.MessageBox;
using MessageBoxButton = AdonisUI.Controls.MessageBoxButton;
using MessageBoxImage = AdonisUI.Controls.MessageBoxImage;
using MessageBoxResult = AdonisUI.Controls.MessageBoxResult;
namespace FModel.Views.Resources.Controls;
@ -12,27 +19,44 @@ public partial class CommitDownloaderControl : UserControl
InitializeComponent();
}
public static readonly DependencyProperty CommitAssetProperty =
DependencyProperty.Register("CommitAsset", typeof(GitHubAsset), typeof(CommitDownloaderControl), new PropertyMetadata(null));
public static readonly DependencyProperty CommitProperty =
DependencyProperty.Register(nameof(Commit), typeof(GitHubCommit), typeof(CommitDownloaderControl), new PropertyMetadata(null));
public GitHubAsset CommitAsset
public GitHubCommit Commit
{
get { return (GitHubAsset)GetValue(CommitAssetProperty); }
set { SetValue(CommitAssetProperty, value); }
}
public static readonly DependencyProperty IsCurrentProperty =
DependencyProperty.Register("IsCurrent", typeof(bool), typeof(CommitDownloaderControl), new PropertyMetadata(null));
public bool IsCurrent
{
get { return (bool)GetValue(IsCurrentProperty); }
set { SetValue(IsCurrentProperty, value); }
get { return (GitHubCommit)GetValue(CommitProperty); }
set { SetValue(CommitProperty, value); }
}
private void OnDownload(object sender, RoutedEventArgs e)
{
AutoUpdater.DownloadUpdate(new UpdateInfoEventArgs { DownloadURL = CommitAsset.BrowserDownloadUrl });
if (Commit.IsCurrent) return;
var messageBox = new MessageBoxModel
{
Text = $"Are you sure you want to update to version '{Commit.ShortSha}'?{(!Commit.Asset.IsLatest ? "\nThis is not the latest version." : "")}",
Caption = "Update FModel",
Icon = MessageBoxImage.Question,
Buttons = MessageBoxButtons.YesNo(),
IsSoundEnabled = false
};
MessageBox.Show(messageBox);
if (messageBox.Result != MessageBoxResult.Yes) return;
try
{
if (AutoUpdater.DownloadUpdate(new UpdateInfoEventArgs { DownloadURL = Commit.Asset.BrowserDownloadUrl }))
{
UserSettings.Default.CommitHash = Commit.Sha;
Application.Current.Shutdown();
}
}
catch (Exception exception)
{
UserSettings.Default.ShowChangelog = false;
MessageBox.Show(exception.Message, exception.GetType().ToString(), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}

View File

@ -5,12 +5,12 @@
xmlns:adonisExtensions="clr-namespace:AdonisUI.Extensions;assembly=AdonisUI"
xmlns:adonisControls="clr-namespace:AdonisUI.Controls;assembly=AdonisUI"
xmlns:controls="clr-namespace:FModel.Views.Resources.Controls"
MinHeight="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.20'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.50'}"
Loaded="OnLoaded">
WindowStartupLocation="CenterScreen" IconVisibility="Collapsed" ResizeMode="CanMinimize" Loaded="OnLoaded"
MinHeight="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenHeight}, Converter={converters:RatioConverter}, ConverterParameter='0.40'}"
Width="{Binding Source={x:Static SystemParameters.MaximizedPrimaryScreenWidth}, Converter={converters:RatioConverter}, ConverterParameter='0.35'}">
<adonisControls:AdonisWindow.Style>
<Style TargetType="adonisControls:AdonisWindow" BasedOn="{StaticResource {x:Type adonisControls:AdonisWindow}}" >
<Setter Property="Title" Value="Update Viewer for now" />
<Setter Property="Title" Value="Releases" />
</Style>
</adonisControls:AdonisWindow.Style>
<adonisControls:AdonisWindow.Resources>
@ -20,17 +20,35 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</adonisControls:AdonisWindow.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding Title, RelativeSource={RelativeSource AncestorType=adonisControls:AdonisWindow}}" Value="Releases">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Button Grid.Row="0">Download Latest</Button>
<Separator Grid.Row="1" Style="{StaticResource CustomSeparator}" Tag="History" />
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Button>Download Latest Release</Button>
<TextBlock Text="Remind Me:" />
<ComboBox>
<ComboBoxItem Content="In 3 Days" />
<ComboBoxItem Content="Next Week" />
<ComboBoxItem Content="Next Month" />
<!-- TODO: show that it can be manually updated via Help > Releases -->
<ComboBoxItem Content="Never" />
</ComboBox>
</WrapPanel>
<ItemsControl Grid.Row="2" ItemsSource="{Binding CommitsView}">
<Separator Style="{StaticResource CustomSeparator}" Tag="History" />
</StackPanel>
<ItemsControl DockPanel.Dock="Bottom" ItemsSource="{Binding CommitsView}">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
@ -69,6 +87,6 @@
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Grid>
</DockPanel>
</adonisControls:AdonisWindow>

View File

@ -1,8 +1,4 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Navigation;
using FModel.Services;
using System.Windows;
using FModel.ViewModels;
namespace FModel.Views;
@ -20,11 +16,5 @@ public partial class UpdateView
if (DataContext is not UpdateViewModel viewModel) return;
await viewModel.Load();
}
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
e.Handled = true;
}
}