Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Managed file dialog #2777

Merged
merged 39 commits into from Aug 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
95af64a
[WIP] Managed file dialog
kekekeks Dec 27, 2018
67f3b03
Merge branch 'managed-file-dialog' of https://github.com/AvaloniaUI/A…
Jul 23, 2019
63fb86b
Merge remote-tracking branch 'origin/master' into managed-file-dialog
Jul 25, 2019
44803ef
back port managed dialog work from wasabi wallet.
Jul 25, 2019
5cd0a43
fix managed file dialog demo
Jul 25, 2019
fc1e033
remove need for reactiveCommand.
Jul 25, 2019
d149a62
add grid splitter for resize of columns
Jul 25, 2019
c685a9e
fix for file dialog crashing with strange file name encoded strings.
Jul 27, 2019
f5c8567
allow use of custom window for hosting file dialogs
Jul 27, 2019
24ae6ce
[Managed Dialogs] make classes internal
Jul 27, 2019
91d570b
[Managed Dialogs] Dont depend on rx and rxui
Jul 27, 2019
09dd7c6
some polishing of file dialog
Jul 27, 2019
e9b0432
[ManagedDialogs] remove internal namespace.
Jul 27, 2019
1e91d3d
remove debug code.
Jul 27, 2019
9d8f936
rename class
Jul 27, 2019
79b3085
spaces not tabs!
Jul 27, 2019
f00f2df
Merge branch 'master' into managed-file-dialog
Jul 27, 2019
d6c633c
Merge remote-tracking branch 'origin/master' into managed-file-dialog
Jul 29, 2019
ff8ec72
revert rx and reactiveui
Jul 29, 2019
88ca24c
Add Platform Interface for Mounted drives service.
jmacato Jul 30, 2019
97a921a
Register on X11 Platform.
jmacato Jul 30, 2019
c9bb80e
Make dialogs use the service.
jmacato Jul 30, 2019
c2cbe44
Modify byte to readable translation.
jmacato Jul 30, 2019
3a798f7
Rename to MountedDrives
jmacato Aug 3, 2019
d391b62
add avalonia.freedesktop project
Aug 5, 2019
23c2087
Automatically refresh quick links when MountedDriveInfo changes
jmacato Aug 6, 2019
02a6d1b
Make icons work; Define a ItemType to correctly define the navigation…
jmacato Aug 7, 2019
4cb9e01
Make the listener active only when there's a dialog present;
jmacato Aug 9, 2019
b78ebb9
Merge remote-tracking branch 'origin/master' into managed-file-dialog
Aug 11, 2019
470f646
Merge branch 'master' into managed-file-dialog
Aug 11, 2019
25d168f
remove portable.xaml
Aug 11, 2019
a3f7d0b
Revert "remove portable.xaml"
Aug 11, 2019
ab2517e
remove portable.xaml
Aug 11, 2019
0dde108
Merge branch 'master' into managed-file-dialog
Aug 18, 2019
202b29d
Add Fallback on volume polling.
jmacato Aug 20, 2019
7efaa96
Remove DBus code. Use common linux kernel interfaces
jmacato Aug 21, 2019
a35efec
Decouple platform specific implementations
jmacato Aug 21, 2019
44a0dbd
Merge branch 'master' into managed-file-dialog
jmacato Aug 21, 2019
7b815f9
Fix namespace.
jmacato Aug 21, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 55 additions & 3 deletions Avalonia.sln
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2027
# Visual Studio Version 16
VisualStudioVersion = 16.0.29102.190
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}"
EndProject
Expand Down Expand Up @@ -197,7 +197,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "sam
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.FreeDesktop", "src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Expand Down Expand Up @@ -1842,6 +1846,54 @@ Global
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhone.Build.0 = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhone.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhone.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.Build.0 = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.ActiveCfg = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhone.Build.0 = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhone.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhone.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.Build.0 = Release|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.ActiveCfg = Release|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhone.Build.0 = Release|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions build/CoreLibraries.props
Expand Up @@ -13,6 +13,7 @@
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Styling/Avalonia.Styling.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Themes.Default/Avalonia.Themes.Default.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.OpenGL/Avalonia.OpenGL.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.Dialogs/Avalonia.Dialogs.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup/Avalonia.Markup.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)/../src/Avalonia.DesktopRuntime/Avalonia.DesktopRuntime.csproj" Condition="'$(TargetFramework)' != 'netstandard2.0'" />
Expand Down
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
<ProjectReference Include="..\..\src\Linux\Avalonia.LinuxFramebuffer\Avalonia.LinuxFramebuffer.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Desktop\Avalonia.Desktop.csproj" />
Expand Down
13 changes: 9 additions & 4 deletions samples/ControlCatalog.NetCore/Program.cs
Expand Up @@ -8,6 +8,9 @@
using Avalonia.LinuxFramebuffer.Output;
using Avalonia.Skia;
using Avalonia.ReactiveUI;
using Avalonia.Dialogs;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ControlCatalog.NetCore
{
Expand Down Expand Up @@ -51,21 +54,22 @@ static int Main(string[] args)
else
return builder.StartWithClassicDesktopLifetime(args);
}

/// <summary>
/// This method is needed for IDE previewer infrastructure
/// </summary>
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.With(new X11PlatformOptions {EnableMultiTouch = true})
.With(new X11PlatformOptions { EnableMultiTouch = true })
.With(new Win32PlatformOptions
{
EnableMultitouch = true,
AllowEglInitialization = true
})
.UseSkia()
.UseReactiveUI();
.UseReactiveUI()
.UseManagedSystemDialogs();

static void SilenceConsole()
{
Expand All @@ -74,7 +78,8 @@ static void SilenceConsole()
Console.CursorVisible = false;
while (true)
Console.ReadKey(true);
}) {IsBackground = true}.Start();
})
{ IsBackground = true }.Start();
}
}
}
1 change: 1 addition & 0 deletions samples/ControlCatalog/MainWindow.xaml.cs
Expand Up @@ -6,6 +6,7 @@
using Avalonia.Threading;
using ControlCatalog.ViewModels;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ControlCatalog
Expand Down
11 changes: 11 additions & 0 deletions src/Avalonia.Controls/AppBuilderBase.cs
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using Avalonia.Controls.ApplicationLifetimes;
Expand Down Expand Up @@ -59,6 +60,8 @@ public abstract class AppBuilderBase<TAppBuilder> where TAppBuilder : AppBuilder
public Action<TAppBuilder> AfterSetupCallback { get; private set; } = builder => { };


public Action<TAppBuilder> AfterPlatformServicesSetupCallback { get; private set; } = builder => { };

protected AppBuilderBase(IRuntimePlatform platform, Action<TAppBuilder> platformServices)
{
RuntimePlatform = platform;
Expand Down Expand Up @@ -97,6 +100,13 @@ public TAppBuilder AfterSetup(Action<TAppBuilder> callback)
AfterSetupCallback = (Action<TAppBuilder>)Delegate.Combine(AfterSetupCallback, callback);
return Self;
}


public TAppBuilder AfterPlatformServicesSetup(Action<TAppBuilder> callback)
{
AfterPlatformServicesSetupCallback = (Action<TAppBuilder>)Delegate.Combine(AfterPlatformServicesSetupCallback, callback);
return Self;
}

/// <summary>
/// Starts the application with an instance of <typeparamref name="TMainWindow"/>.
Expand Down Expand Up @@ -274,6 +284,7 @@ private void Setup()
RuntimePlatformServicesInitializer();
WindowingSubsystemInitializer();
RenderingSubsystemInitializer();
AfterPlatformServicesSetupCallback(Self);
Instance.RegisterServices();
Instance.Initialize();
AfterSetupCallback(Self);
Expand Down
23 changes: 23 additions & 0 deletions src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs
@@ -0,0 +1,23 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.

using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Avalonia.Platform;

namespace Avalonia.Controls.Platform
{
/// <summary>
/// Defines a platform-specific mount volumes info provider implementation.
/// </summary>
public interface IMountedVolumeInfoProvider
{
/// <summary>
/// Listens to any changes in volume mounts and
/// forwards updates to the referenced
/// <see cref="ObservableCollection{MountedDriveInfo}"/>.
/// </summary>
IDisposable Listen(ObservableCollection<MountedVolumeInfo> mountedDrives);
}
}
24 changes: 24 additions & 0 deletions src/Avalonia.Controls/Platform/MountedDriveInfo.cs
@@ -0,0 +1,24 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.

using System;

namespace Avalonia.Controls.Platform
{
/// <summary>
/// Describes a Drive's properties.
/// </summary>
public class MountedVolumeInfo : IEquatable<MountedVolumeInfo>
{
public string VolumeLabel { get; set; }
public string VolumePath { get; set; }
public ulong VolumeSizeBytes { get; set; }

public bool Equals(MountedVolumeInfo other)
{
return this.VolumeSizeBytes.Equals(other.VolumeSizeBytes) &&
this.VolumePath.Equals(other.VolumePath) &&
(this.VolumeLabel ?? string.Empty).Equals(other.VolumeLabel ?? string.Empty);
}
}
}
16 changes: 13 additions & 3 deletions src/Avalonia.Controls/SystemDialog.cs
Expand Up @@ -14,7 +14,13 @@ public abstract class FileDialog : FileSystemDialog

public abstract class FileSystemDialog : SystemDialog
{
public string InitialDirectory { get; set; }
[Obsolete("Use Directory")]
public string InitialDirectory
{
get => Directory;
set => Directory = value;
}
public string Directory { get; set; }
}

public class SaveFileDialog : FileDialog
Expand Down Expand Up @@ -45,8 +51,12 @@ public Task<string[]> ShowAsync(Window parent)

public class OpenFolderDialog : FileSystemDialog
{
public string DefaultDirectory { get; set; }

[Obsolete("Use Directory")]
public string DefaultDirectory
{
get => Directory;
set => Directory = value;
}
public Task<string> ShowAsync(Window parent)
{
if(parent == null)
Expand Down
19 changes: 19 additions & 0 deletions src/Avalonia.Dialogs/Avalonia.Dialogs.csproj
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</AvaloniaResource>
</ItemGroup>

<Import Project="..\..\build\BuildTargets.targets" />

<ItemGroup>
<ProjectReference Include="..\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
</ItemGroup>
</Project>
40 changes: 40 additions & 0 deletions src/Avalonia.Dialogs/ByteSizeHelper.cs
@@ -0,0 +1,40 @@
using System;

namespace Avalonia.Dialogs
{
internal static class ByteSizeHelper
{
private const string formatTemplate = "{0}{1:0.#} {2}";

private static readonly string[] Prefixes =
{
"B",
"KB",
"MB",
"GB",
"TB",
"PB",
"EB",
"ZB",
"YB"
};

public static string ToString(ulong bytes)
{
if (bytes == 0)
{
return string.Format(formatTemplate, null, 0, Prefixes[0]);
}

var absSize = Math.Abs((double)bytes);
var fpPower = Math.Log(absSize, 1000);
var intPower = (int)fpPower;
var iUnit = intPower >= Prefixes.Length
? Prefixes.Length - 1
: intPower;
var normSize = absSize / Math.Pow(1000, iUnit);

return string.Format(formatTemplate,bytes < 0 ? "-" : null, normSize, Prefixes[iUnit]);
}
}
}
21 changes: 21 additions & 0 deletions src/Avalonia.Dialogs/ChildFitter.cs
@@ -0,0 +1,21 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;

namespace Avalonia.Dialogs
{
internal class ChildFitter : Decorator
{
protected override Size MeasureOverride(Size availableSize)
{
return new Size(0, 0);
}

protected override Size ArrangeOverride(Size finalSize)
{
Child.Measure(finalSize);
base.ArrangeOverride(finalSize);
return finalSize;
}
}
}
26 changes: 26 additions & 0 deletions src/Avalonia.Dialogs/FileSizeStringConverter.cs
@@ -0,0 +1,26 @@
using Avalonia.Data.Converters;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace Avalonia.Dialogs
{
internal class FileSizeStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is long size && size > 0)
{
return ByteSizeHelper.ToString((ulong)size);
}

return "";
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}