[RegistryPreview] Extended preview for value data (#37689)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Add a button to the data grid that shows a windows for complex value
preview.

### Screenshots
**Button**

![image](https://github.com/user-attachments/assets/bb275831-be6d-490c-9193-5df719ce6c39)

**Context menu** (Usefull on long data, if button is scrolled out of
view.)

![image](https://github.com/user-attachments/assets/f5fb07ef-6f73-4eac-a289-9dce1c610ceb)

**Preview: REG_SZ**

![image](https://github.com/user-attachments/assets/e03fbbc7-adaa-40d0-967c-7783b1a97b74)

**Preview: REG_MULTI_SZ**

![image](https://github.com/user-attachments/assets/717590a6-7d91-4c9c-8e94-d875a5d2ba6b)

**Preview: REG_EXPAND_SZ**

![image](https://github.com/user-attachments/assets/20135b66-528f-40e7-beed-adfc2b50313d)

**Preview: REG_DWORD and REG_QWORD**

![image](https://github.com/user-attachments/assets/b60110ab-bfc7-40e7-ada3-d278a62b9d01)

**Preview: REG_BINARY**

![image](https://github.com/user-attachments/assets/95f81036-6833-439e-8c01-b3a45c2d8edd)

![image](https://github.com/user-attachments/assets/ce237664-da96-4dbd-835f-969982560b9f)


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #36877
- [x] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** All end user facing strings can be localized =>
missing yet
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

Additionally this PR updates the context menu for values.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Tested code with a local build.
This commit is contained in:
Heiko
2025-06-13 12:08:01 +02:00
committed by GitHub
parent ce058f1dc7
commit abc5c3e249
34 changed files with 4789 additions and 22 deletions

View File

@@ -76,3 +76,47 @@ using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "type", Target = "~T:Peek.UI.Views.TitleBar")]
[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")]
// HexBox control in RegistryPreviewUILib (We decided to copy the original code and not fix all theses problems for easier updating.)
[assembly: SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("Naming", "CA1720:Identifiers should not contain type names", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("Performance", "CA1805:Do not initialize unnecessarily", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1623:Property summary documentation should match accessors", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1642:Constructor summary documentation should begin with standard text", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1648:<inheritdoc> has been used on an element that doesn't inherit from a base class or implement an interface.", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500:Braces for multi-line statements should not share line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1502:Element should not be on a single line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1505:Opening braces should not be followed by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1507:Code should not contain multiple blank lines in a row", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1508:Closing braces should not be preceded by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1509:Opening braces should not be preceded by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1513:Closing brace should be followed by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1514:Element documentation header should be preceded by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1516:Elements should be separated by blank line", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1119:Statement should not use unnecessary parenthesis", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1407:Arithmetic expressions should declare precedence", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1413:Use trailing comma in multi-line initializers", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1306:Field names should begin with lower-case letter", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1312:Variable names should begin with lower-case letter", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1200:Using directives should be placed correctly", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1108:Block statements should not contain embedded comments", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1116:Split parameters should start on line after declaration", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1129:Do not use default value type constructor", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1003:Symbols should be spaced correctly", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1005:Single line comments should begin with single space", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1024:Colons Should Be Spaced Correctly", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1028:Code should not contain trailing whitespace", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]
[assembly: SuppressMessage("Usage", "CsWinRT1028:Class is not marked partial", Justification = "<Code port with style preservation>", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib.HexBox")]

View File

@@ -31,7 +31,7 @@
<ItemGroup>
<Folder Include="RegistryPreviewXAML\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Sizers" />

View File

@@ -10,7 +10,31 @@
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="HexBox_SelectionTextBrush" Color="{ThemeResource TextOnAccentFillColorSelectedText}" />
<SolidColorBrush x:Key="HexBox_SelectionBackgroundBrush" Color="{ThemeResource SystemAccentColor}" />
<SolidColorBrush x:Key="HexBox_VerticalLineBrush" Color="{ThemeResource DividerStrokeColorDefault}" />
<StaticResource x:Key="HexBox_ControlBorderBrush" ResourceKey="TextControlElevationBorderBrush" />
<StaticResource x:Key="HexBox_ControlBorderFocusedBrush" ResourceKey="TextControlElevationBorderFocusedBrush" />
<Thickness x:Key="HexBox_ControlBorderThickness">1</Thickness>
<Thickness x:Key="HexBox_ControlBorderFocusedThickness">1,1,1,2</Thickness>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<SolidColorBrush x:Key="HexBox_SelectionTextBrush" Color="{ThemeResource SystemColorHighlightTextColor}" />
<SolidColorBrush x:Key="HexBox_SelectionBackgroundBrush" Color="{ThemeResource SystemColorHighlightColor}" />
<SolidColorBrush x:Key="HexBox_VerticalLineBrush" Color="{ThemeResource SystemColorWindowTextColor}" />
<LinearGradientBrush x:Key="HexBox_ControlBorderBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="{ThemeResource SystemColorButtonTextColor}" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="HexBox_ControlBorderFocusedBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0" Color="{ThemeResource SystemColorHighlightColor}" />
</LinearGradientBrush>
<Thickness x:Key="HexBox_ControlBorderThickness">1</Thickness>
<Thickness x:Key="HexBox_ControlBorderFocusedThickness">2</Thickness>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox
{
/// <summary>
/// Enumerates the address column formatting options.
/// </summary>
public enum AddressFormat
{
/// <summary>
/// 16 bit HEX address "0000".
/// </summary>
Address16,
/// <summary>
/// 24 bit HEX address "00:0000".
/// </summary>
Address24,
/// <summary>
/// 32 bit HEX address "0000:0000".
/// </summary>
Address32,
/// <summary>
/// 48 bit HEX address "0000:00000000".
/// </summary>
Address48,
/// <summary>
/// 64 bit HEX address "00000000:00000000".
/// </summary>
Address64,
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
using System;
using System.Windows.Input;
namespace RegistryPreviewUILib.HexBox
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox
{
/// <summary>
/// Enumerates the format to display integral data in.
/// </summary>
public enum DataFormat
{
/// <summary>
/// Display the data in decimal format.
/// </summary>
Decimal,
/// <summary>
/// Display the data in hexadecimal format.
/// </summary>
Hexadecimal,
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox
{
/// <summary>
/// Enumerates the signedness of the data to display.
/// </summary>
public enum DataSignedness
{
/// <summary>
/// Display the data as signed values.
/// </summary>
Signed,
/// <summary>
/// Display the data as unsigned values.
/// </summary>
Unsigned,
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox
{
/// <summary>
/// Enumerates how the data (bytes read from the buffer) is to be interpreted when displayed.
/// </summary>
public enum DataType
{
/// <summary>
/// Display the data as integral (integer) values.
/// </summary>
Int_1 = 1,
/// <summary>
/// Display the data as integral (integer) values.
/// </summary>
Int_2 = 2,
/// <summary>
/// Display the data as integral (integer) values.
/// </summary>
Int_4 = 4,
/// <summary>
/// Display the data as integral (integer) values.
/// </summary>
Int_8 = 8,
/// <summary>
/// Display the data as floating point values.
/// </summary>
Float_32 = 32,
/// <summary>
/// Display the data as floating point values.
/// </summary>
Float_64 = 64,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox.Library.EndianConvert
{
using System;
using System.IO;
using System.Text;
/// <summary>
/// Reads primitive data types as binary values in a specific encoding and endianness.
/// </summary>
public class EndianBinaryReader : BinaryReader
{
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryReader"/> class based on the specified stream, endianness, and using UTF-8
/// encoding.
/// </summary>
///
/// <param name="input">
/// The input stream.
/// </param>
///
/// <param name="endianness">
/// The endianness of the data in the input stream.
/// </param>
///
/// <exception cref="System.ArgumentException">
/// The stream does not support reading, is null, or is already closed.
/// </exception>
public EndianBinaryReader(Stream input, Endianness endianness)
: this(input, endianness, Encoding.UTF8)
{
// Void
}
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryReader"/> class based on the specified stream, endianness, and character
/// encoding.
/// </summary>
///
/// <param name="input">
/// The input stream.
/// </param>
///
/// <param name="endianness">
/// The endianness of the data in the input stream.
/// </param>
///
/// <param name="encoding">
/// The character encoding to use.
/// </param>
///
/// <exception cref="System.ArgumentException">
/// The stream does not support reading, is null, or is already closed.
/// </exception>
public EndianBinaryReader(Stream input, Endianness endianness, Encoding encoding)
: this(input, endianness, encoding, false)
{
// Void
}
/// <summary>
/// Initializes a new instance of the <see cref="EndianBinaryReader"/> class based on the specified stream, endianness, and character
/// encoding, and optionally leaves the stream open.
/// </summary>
///
/// <param name="input">
/// The input stream.
/// </param>
///
/// <param name="endianness">
/// The endianness of the data in the input stream.
/// </param>
///
/// <param name="encoding">
/// The character encoding to use.
/// </param>
///
/// <param name="leaveOpen">
/// <c>true</c> to leave the stream open after the <see cref="EndianBinaryReader"/> object is disposed; <c>false</c> otherwise.
/// </param>
///
/// <exception cref="System.ArgumentException">
/// The stream does not support reading, is null, or is already closed.
/// </exception>
public EndianBinaryReader(Stream input, Endianness endianness, Encoding encoding, bool leaveOpen)
: base(input, encoding, leaveOpen)
{
Endianness = endianness;
}
/// <summary>
/// Gets the endianness of the data in the input stream.
/// </summary>
public Endianness Endianness
{
get;
}
/// <summary>
/// Reads a decimal value from the current stream and advances the current position of the stream by sixteen bytes.
/// </summary>
///
/// <returns>
/// A decimal value read from the current stream.
/// </returns>
public override decimal ReadDecimal()
{
throw new NotImplementedException();
}
/// <summary>
/// Reads an 8-byte floating point value from the current stream and advances the current position of the stream by eight bytes.
/// </summary>
///
/// <returns>
/// An 8-byte floating point value read from the current stream.
/// </returns>
public override double ReadDouble()
{
throw new NotImplementedException();
}
/// <summary>
/// Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes.
/// </summary>
///
/// <returns>
/// A 2-byte signed integer read from the current stream.
/// </returns>
public override short ReadInt16()
{
return EndianBitConverter.Convert(base.ReadInt16(), Endianness);
}
/// <summary>
/// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
/// </summary>
///
/// <returns>
/// A 4-byte signed integer read from the current stream.
/// </returns>
public override int ReadInt32()
{
return EndianBitConverter.Convert(base.ReadInt32(), Endianness);
}
/// <summary>
/// Reads an 8-byte signed integer from the current stream and advances the current position of the stream by eight bytes.
/// </summary>
///
/// <returns>
/// An 8-byte signed integer read from the current stream.
/// </returns>
public override long ReadInt64()
{
return EndianBitConverter.Convert(base.ReadInt64(), Endianness);
}
/// <summary>
/// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
/// </summary>
///
/// <returns>
/// A 4-byte floating point value read from the current stream.
/// </returns>
public override float ReadSingle()
{
throw new NotImplementedException();
}
/// <summary>
/// Reads a string from the current stream. The string is prefixed with the length, encoded as an integer seven bits at a time.
/// </summary>
///
/// <returns>
/// The string being read.
/// </returns>
public override string ReadString()
{
throw new NotImplementedException();
}
/// <summary>
/// Reads a 2-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by
/// two bytes.
/// </summary>
///
/// <returns>
/// A 2-byte unsigned integer read from this stream.
/// </returns>
public override ushort ReadUInt16()
{
return EndianBitConverter.Convert(base.ReadUInt16(), Endianness);
}
/// <summary>
/// Reads a 4-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by
/// four bytes.
/// </summary>
///
/// <returns>
/// A 4-byte unsigned integer read from this stream.
/// </returns>
public override uint ReadUInt32()
{
return EndianBitConverter.Convert(base.ReadUInt32(), Endianness);
}
/// <summary>
/// Reads an 8-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by
/// eight bytes.
/// </summary>
///
/// <returns>
/// An 8-byte unsigned integer read from this stream.
/// </returns>
public override ulong ReadUInt64()
{
return EndianBitConverter.Convert(base.ReadUInt64(), Endianness);
}
}
}

View File

@@ -0,0 +1,186 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox.Library.EndianConvert
{
using System;
/// <summary>
/// Converts integral values to the native endianness of this computer architecture.
/// </summary>
public static class EndianBitConverter
{
/// <summary>
/// Gets the native endianness of this computer architecture.
/// </summary>
public static readonly Endianness NativeEndianness = BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian;
/// <summary>
/// Converts a value from the specified endianness to the native endianness.
/// </summary>
///
/// <param name="value">
/// The value to convert.
/// </param>
///
/// <param name="endianness">
/// The endianness of <paramref name="value"/>.
/// </param>
///
/// <returns>
/// The value converted from the specified endianness to the native endianness (<see cref="NativeEndianness"/>).
/// </returns>
public static ushort Convert(ushort value, Endianness endianness)
{
if (endianness == NativeEndianness)
{
return value;
}
else
{
unchecked
{
return (ushort)((value & 0x00FFU) << 8 |
(value & 0xFF00U) >> 8);
}
}
}
/// <summary>
/// Converts a value from the specified endianness to the native endianness.
/// </summary>
///
/// <param name="value">
/// The value to convert.
/// </param>
///
/// <param name="endianness">
/// The endianness of <paramref name="value"/>.
/// </param>
///
/// <returns>
/// The value converted from the specified endianness to the native endianness (<see cref="NativeEndianness"/>).
/// </returns>
public static uint Convert(uint value, Endianness endianness)
{
if (endianness == NativeEndianness)
{
return value;
}
else
{
unchecked
{
return (value & 0x000000FFU) << 24 |
(value & 0xFF000000U) >> 24 |
(value & 0x0000FF00U) << 8 |
(value & 0x00FF0000U) >> 8;
}
}
}
/// <summary>
/// Converts a value from the specified endianness to the native endianness.
/// </summary>
///
/// <param name="value">
/// The value to convert.
/// </param>
///
/// <param name="endianness">
/// The endianness of <paramref name="value"/>.
/// </param>
///
/// <returns>
/// The value converted from the specified endianness to the native endianness (<see cref="NativeEndianness"/>).
/// </returns>
public static ulong Convert(ulong value, Endianness endianness)
{
if (endianness == NativeEndianness)
{
return value;
}
else
{
unchecked
{
return (value & 0x00000000000000FFUL) << 56 |
(value & 0xFF00000000000000UL) >> 56 |
(value & 0x000000000000FF00UL) << 40 |
(value & 0x00FF000000000000UL) >> 40 |
(value & 0x0000000000FF0000UL) << 24 |
(value & 0x0000FF0000000000UL) >> 24 |
(value & 0x00000000FF000000UL) << 8 |
(value & 0x000000FF00000000UL) >> 8;
}
}
}
/// <summary>
/// Converts a value from the specified endianness to the native endianness.
/// </summary>
///
/// <param name="value">
/// The value to convert.
/// </param>
///
/// <param name="endianness">
/// The endianness of <paramref name="value"/>.
/// </param>
///
/// <returns>
/// The value converted from the specified endianness to the native endianness (<see cref="NativeEndianness"/>).
/// </returns>
public static short Convert(short value, Endianness endianness)
{
return (short)Convert((ushort)value, endianness);
}
/// <summary>
/// Converts a value from the specified endianness to the native endianness.
/// </summary>
///
/// <param name="value">
/// The value to convert.
/// </param>
///
/// <param name="endianness">
/// The endianness of <paramref name="value"/>.
/// </param>
///
/// <returns>
/// The value converted from the specified endianness to the native endianness (<see cref="NativeEndianness"/>).
/// </returns>
public static int Convert(int value, Endianness endianness)
{
return (int)Convert((uint)value, endianness);
}
/// <summary>
/// Converts a value from the specified endianness to the native endianness.
/// </summary>
///
/// <param name="value">
/// The value to convert.
/// </param>
///
/// <param name="endianness">
/// The endianness of <paramref name="value"/>.
/// </param>
///
/// <returns>
/// The value converted from the specified endianness to the native endianness (<see cref="NativeEndianness"/>).
/// </returns>
public static long Convert(long value, Endianness endianness)
{
return (long)Convert((ulong)value, endianness);
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox.Library.EndianConvert
{
/// <summary>
/// Represents the endianness of a value in a computer architecture.
/// </summary>
public enum Endianness
{
/// <summary>
/// Most significant byte first.
/// </summary>
BigEndian,
/// <summary>
/// Least significant byte first.
/// </summary>
LittleEndian,
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox.Library.EndianConvert
{
using System;
/// <summary>
/// The exception that is thrown when an input file or a data stream is malformed.
/// </summary>
[Serializable]
public sealed class FileFormatException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="FileFormatException"/> class.
/// </summary>
public FileFormatException()
{
// Void
}
/// <summary>
/// Initializes a new instance of the <see cref="FileFormatException"/> class with a specified error message.
/// </summary>
///
/// <param name="message">
/// The message that describes the error.
/// </param>
public FileFormatException(string message)
: base(message)
{
// Void
}
/// <summary>
/// Initializes a new instance of the <see cref="FileFormatException"/> class with a specified error message and a reference to the inner
/// exception that is the cause of this exception.
/// </summary>
///
/// <param name="message">
/// The message that describes the error.
/// </param>
///
/// <param name="innerException">
/// The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
/// </param>
public FileFormatException(string message, Exception innerException)
: base(message, innerException)
{
// Void
}
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox
{
/// <summary>
/// Enumerates the text section encodings/formats that the control is able to display.
/// </summary>
public enum TextFormat
{
/// <summary>
/// Display data in ASCII (ISO-8859-1) encoding.
/// </summary>
Ascii,
}
}

View File

@@ -0,0 +1,170 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:convert="using:CommunityToolkit.WinUI.Converters"
xmlns:local="using:RegistryPreviewUILib.HexBox"
xmlns:skia="using:SkiaSharp.Views.Windows">
<Style TargetType="local:HexBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:HexBox">
<Grid
Margin="{TemplateBinding Margin}"
Padding="4"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
IsTabStop="False">
<Grid.Resources>
<convert:BoolNegationConverter x:Key="BoolNegationConverter" />
<local:BigEndianConverter x:Key="BigEndianConverter" />
<local:HexboxDataFormatConverter x:Key="HexboxDataFormatConverter" />
<local:HexboxDataSignednessConverter x:Key="HexboxDataSignednessConverter" />
<local:HexboxDataFormatBoolConverter x:Key="HexboxDataFormatBoolConverter" />
<local:HexboxDataTypeConverter x:Key="HexboxDataTypeConverter" />
<MenuFlyout x:Key="DataMenuFlyout">
<!-- New code for PowerToys implementation -->
<MenuFlyoutItem
x:Uid="/PowerToys.RegistryPreviewUILib/Resources/HexBox_CopyCommand"
Command="{Binding CopyCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Icon="Copy">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="C" Modifiers="Control" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem
x:Uid="/PowerToys.RegistryPreviewUILib/Resources/HexBox_CopyTextCommand"
Command="{Binding CopyTextCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Icon="Font">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="C" Modifiers="Control,Shift" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem
x:Uid="/PowerToys.RegistryPreviewUILib/Resources/HexBox_SelectAllCommand"
Command="{Binding SelectAllCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Icon="SelectAll">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="A" Modifiers="Control" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<!-- Original source code from HexBox.WinUI.HexBox
<MenuFlyoutItem
Command="{Binding CopyCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Icon="Copy"
Text="Copy">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="C" Modifiers="Control" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem
Command="{Binding CopyTextCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Icon="Font"
Text="Copy text">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="C" Modifiers="Control,Shift"/>
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutItem
Command="{Binding SelectAllCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Icon="SelectAll"
Text="Select all">
<MenuFlyoutItem.KeyboardAccelerators>
<KeyboardAccelerator Key="A" Modifiers="Control" />
</MenuFlyoutItem.KeyboardAccelerators>
</MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutSubItem IsEnabled="{Binding EnforceProperties, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" Text="Address properties">
<ToggleMenuFlyoutItem IsChecked="{Binding ShowAddress, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource BoolNegationConverter}}" Text="No Address" />
</MenuFlyoutSubItem>
<MenuFlyoutSubItem IsEnabled="{Binding EnforceProperties, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" Text="Data properties">
<ToggleMenuFlyoutItem IsChecked="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource BoolNegationConverter}}" Text="No Data" />
<RadioMenuFlyoutItem
GroupName="DataType"
IsChecked="{Binding DataType, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataTypeConverter}, ConverterParameter=Int_1}"
IsEnabled="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Text="1 byte-integer" />
<RadioMenuFlyoutItem
GroupName="DataType"
IsChecked="{Binding DataType, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataTypeConverter}, ConverterParameter=Int_2}"
IsEnabled="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Text="2 byte-integer" />
<RadioMenuFlyoutItem
GroupName="DataType"
IsChecked="{Binding DataType, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataTypeConverter}, ConverterParameter=Int_4}"
IsEnabled="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Text="4 byte-integer" />
<RadioMenuFlyoutItem
GroupName="DataType"
IsChecked="{Binding DataType, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataTypeConverter}, ConverterParameter=Int_8}"
IsEnabled="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Text="8 byte-integer" />
<RadioMenuFlyoutItem
GroupName="DataType"
IsChecked="{Binding DataType, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataTypeConverter}, ConverterParameter=Float_32}"
IsEnabled="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Text="32-bit Floating Point" />
<RadioMenuFlyoutItem
GroupName="DataType"
IsChecked="{Binding DataType, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataTypeConverter}, ConverterParameter=Float_64}"
IsEnabled="{Binding ShowData, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Text="64-bit Floating Point" />
<MenuFlyoutSeparator />
<ToggleMenuFlyoutItem
IsChecked="{Binding DataSignedness, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataSignednessConverter}, ConverterParameter=Signed}"
IsEnabled="{Binding DataFormat, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource HexboxDataFormatBoolConverter}, ConverterParameter={Binding DataFormat}}"
Text="Signed" />
<ToggleMenuFlyoutItem
IsChecked="{Binding DataSignedness, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataSignednessConverter}, ConverterParameter=Unsigned}"
IsEnabled="{Binding DataFormat, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource HexboxDataFormatBoolConverter}, ConverterParameter={Binding DataFormat}}"
Text="Unsigned" />
<MenuFlyoutSeparator />
<ToggleMenuFlyoutItem IsChecked="{Binding DataFormat, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataFormatConverter}, ConverterParameter=Decimal}" Text="Decimal" />
<ToggleMenuFlyoutItem IsChecked="{Binding DataFormat, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource HexboxDataFormatConverter}, ConverterParameter=Hexadecimal}" Text="Hexadecimal" />
<MenuFlyoutSeparator />
<ToggleMenuFlyoutItem IsChecked="{Binding Endianness, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource BigEndianConverter}, ConverterParameter=BigEndian}" Text="Big-Endian" />
<ToggleMenuFlyoutItem IsChecked="{Binding Endianness, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource BigEndianConverter}, ConverterParameter=LittleEndian}" Text="Little-Endian" />
</MenuFlyoutSubItem>
<MenuFlyoutSubItem IsEnabled="{Binding EnforceProperties, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}" Text="Text properties">
<ToggleMenuFlyoutItem IsChecked="{Binding ShowText, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay, Converter={StaticResource BoolNegationConverter}}" Text="No Text" />
<ToggleMenuFlyoutItem
IsChecked="{Binding ShowText, RelativeSource={RelativeSource Mode=TemplatedParent}, Mode=TwoWay}"
IsEnabled="False"
Text="ASCII" />
</MenuFlyoutSubItem>-->
</MenuFlyout>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="24" />
</Grid.ColumnDefinitions>
<skia:SKXamlCanvas
Name="ElementCanvas"
Grid.Column="0"
Margin="2,0,2,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ContextFlyout="{StaticResource DataMenuFlyout}" />
<ScrollBar
Name="ElementScrollBar"
Grid.Column="1"
Margin="0,0,2,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
IndicatorMode="MouseIndicator"
Orientation="Vertical" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,264 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
#pragma warning disable SA1210 // Using directives should be ordered alphabetically by namespace
#pragma warning disable SA1208 // System using directives should be placed before other using directives
using RegistryPreviewUILib.HexBox.Library.EndianConvert;
using Microsoft.UI.Xaml.Data;
using System;
#pragma warning restore SA1208 // System using directives should be placed before other using directives
#pragma warning restore SA1210 // Using directives should be ordered alphabetically by namespace
namespace RegistryPreviewUILib.HexBox
{
public partial class HexboxDataTypeConverter : IValueConverter
{
/// <summary>
/// Convert a DataType value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataType"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is DataType b)
{
if (parameter is string c)
{
return c == b.ToString();
}
}
throw new NotImplementedException();
}
/// <summary>
/// Convert back a DataType value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataType"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is bool b && parameter is string c)
{
if(c == "Int_1")
{
return DataType.Int_1;
}
else if (c == "Int_2")
{
return DataType.Int_2;
}
else if (c == "Int_4")
{
return DataType.Int_4;
}
else if (c == "Int_8")
{
return DataType.Int_8;
}
else if (c == "Float_32")
{
return DataType.Float_32;
}
else /*if (c == "Float_64")*/
{
return DataType.Float_64;
}
}
throw new NotImplementedException();
}
}
public class HexboxDataSignednessConverter : IValueConverter
{
/// <summary>
/// Convert a DataSignedness value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataSignedness"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is DataSignedness b)
{
if (parameter is string c)
{
var end = c == "Signed" ? DataSignedness.Signed : DataSignedness.Unsigned;
return (b == end);
}
}
throw new NotImplementedException();
}
/// <summary>
/// Convert back a DataSignedness value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataSignedness"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is bool b && parameter is string c)
{
var end = c == "Signed" ? DataSignedness.Signed : DataSignedness.Unsigned;
if (b)
{
return end;
}
else
{
return c == "Signed" ? DataSignedness.Unsigned : DataSignedness.Signed;
}
}
throw new NotImplementedException();
}
}
public class HexboxDataFormatBoolConverter : IValueConverter
{
/// <summary>
/// Convert a DataFormat value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataFormat"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object Convert(object value, Type targetType, object parameter, string language)
{
if(value is DataFormat f)
{
return f != DataFormat.Hexadecimal;
}
throw new NotImplementedException();
}
/// <summary>
/// Convert back a DataFormat value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataFormat"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
public class HexboxDataFormatConverter : IValueConverter
{
/// <summary>
/// Convert a DataFormat value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataFormat"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is DataFormat b)
{
if (parameter is string c)
{
var end = c == "Decimal" ? DataFormat.Decimal: DataFormat.Hexadecimal;
return (b == end);
}
}
throw new NotImplementedException();
}
/// <summary>
/// Convert back a DataFormat value to its negation.
/// </summary>
/// <param name="value">The <see cref="DataFormat"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is bool b && parameter is string c)
{
var end = c == "Decimal" ? DataFormat.Decimal : DataFormat.Hexadecimal;
if (b)
{
return end;
}
else
{
return end == DataFormat.Decimal ? DataFormat.Hexadecimal : DataFormat.Decimal;
}
}
throw new NotImplementedException();
}
}
public class BigEndianConverter : IValueConverter
{
/// <summary>
/// Convert a Endian value to its negation.
/// </summary>
/// <param name="value">The <see cref="Endian"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Endianness b)
{
if (parameter is string c)
{
var end = c == "BigEndian" ? Endianness.BigEndian : Endianness.LittleEndian;
return (b == end);
}
}
throw new NotImplementedException();
}
/// <summary>
/// Convert back a Endian value to its negation.
/// </summary>
/// <param name="value">The <see cref="Endian"/> value to negate.</param>
/// <param name="targetType">The type of the target property, as a type reference.</param>
/// <param name="parameter">Optional parameter. Not used.</param>
/// <param name="language">The language of the conversion. Not used</param>
/// <returns>The value to be passed to the target dependency property.</returns>
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
if (value is bool b && parameter is string c)
{
var end = c == "BigEndian" ? Endianness.BigEndian : Endianness.LittleEndian;
if (b)
{
return end;
}
else
{
return end == Endianness.BigEndian ? Endianness.LittleEndian : Endianness.BigEndian;
}
}
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// <history>
// 2020-... created by Filip Jeremic (fjeremic) as "HexView.Wpf".
// 2024-... republished by @hotkidfamily as "HexBox.WinUI".
// 2025 Included in PowerToys. (Branch master; commit 72dcf64dc858c693a7a16887004c8ddbab61fce7.)
// </history>
namespace RegistryPreviewUILib.HexBox
{
using System;
/// <summary>
/// A utility class with miscellaneous methods.
/// </summary>
internal static class Utilities
{
/// <summary>
/// Clamps the <paramref name="value"/> to the range [<paramref name="min"/>, <paramref name="max"/>].
/// </summary>
///
/// <typeparam name="T">
/// The type of the value to clamp.
/// </typeparam>
///
/// <param name="value">
/// The value to clamp.
/// </param>
///
/// <param name="min">
/// The upper bound on the clamped value.
/// </param>
///
/// <param name="max">
/// The lower bound on the clmaped value.
/// </param>
///
/// <returns>
/// The nearest value of <paramref name="value"/> in the range [<paramref name="min"/>,
/// <paramref name="max"/>].
/// </returns>
public static T Clamp<T>(this T value, T min, T max)
where T : IComparable<T>
{
return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
}
/// <summary>
/// Calculates the arithmetic modulus of <paramref name="n"/> modulo <paramref name="m"/>.
/// </summary>
///
/// <typeparam name="T">
/// The type of the values.
/// </typeparam>
///
/// <param name="n">
/// The value to compute the modulus of.
/// </param>
///
/// <param name="m">
/// The modulus.
/// </param>
///
/// <returns>
/// The non-negative value <c>r</c> such that for some integral value <c>q</c>:
/// <c><paramref name="n"/> = q*m + r</c>.
/// </returns>
public static T Mod<T>(this T n, T m)
where T : IComparable<T>
{
dynamic dn = n;
dynamic dm = m;
dynamic dr = dn % dm;
return dr.CompareTo(0) < 0 ? dr + dm : dr;
}
}
}

View File

@@ -0,0 +1,341 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.Windows.ApplicationModel.Resources;
using Windows.Foundation.Metadata;
using HB = RegistryPreviewUILib.HexBox;
namespace RegistryPreviewUILib
{
public sealed partial class RegistryPreviewMainPage : Page
{
private static bool _isDataPreviewHexBoxLoaded;
internal async Task ShowExtendedDataPreview(string name, string type, string value)
{
// Create dialog
_isDataPreviewHexBoxLoaded = false;
var panel = new StackPanel()
{
Spacing = 16,
Padding = new Thickness(0),
};
ContentDialog contentDialog = new ContentDialog()
{
Title = resourceLoader.GetString("DataPreviewTitle") + " - " + name,
Content = panel,
CloseButtonText = resourceLoader.GetString("DataPreviewClose"),
DefaultButton = ContentDialogButton.Primary,
Padding = new Thickness(0),
};
contentDialog.Opened += ExtendedDataPreview_Opened;
// Add content based on value type
switch (type)
{
case "REG_DWORD":
case "REG_QWORD":
AddHexView(ref panel, ref resourceLoader, value);
break;
case "REG_NONE":
case "REG_BINARY":
// Convert value to BinaryReader
byte[] byteArray = Convert.FromHexString(value.Replace(" ", string.Empty));
MemoryStream memoryStream = new MemoryStream(byteArray);
BinaryReader binaryData = new BinaryReader(memoryStream);
binaryData.ReadBytes(byteArray.Length);
// Convert value to text
// For more printable asci characters the following code lines are required:
// var cpW1252 = CodePagesEncodingProvider.Instance.GetEncoding(1252);
// || b == 128 || (b >= 130 && b <= 140) || b == 142 || (b >= 145 & b <= 156) || b >= 158
// cpW1252.GetString([b]);
string binaryDataText = string.Empty;
foreach (byte b in byteArray)
{
// ASCII codes:
// 9, 10, 13: Space, Line Feed, Carriage Return
// 32-126: Printable characters
// 128, 130-140, 142, 145-156, 158-255: Extended printable characters
if (b == 9 || b == 10 || b == 13 || (b >= 32 && b <= 126))
{
binaryDataText += Convert.ToChar(b);
}
}
// Add controls
AddBinaryView(ref panel, ref resourceLoader, ref binaryData, binaryDataText);
break;
case "REG_MULTI_SZ":
var multiLineBox = new TextBox()
{
IsReadOnly = true,
AcceptsReturn = true,
TextWrapping = TextWrapping.NoWrap,
MaxHeight = 200,
FontSize = 14,
RequestedTheme = panel.ActualTheme,
Text = value,
};
ScrollViewer.SetVerticalScrollBarVisibility(multiLineBox, ScrollBarVisibility.Auto);
ScrollViewer.SetHorizontalScrollBarVisibility(multiLineBox, ScrollBarVisibility.Auto);
AutomationProperties.SetName(multiLineBox, resourceLoader.GetString("DataPreview_AutomationPropertiesName_MultilineTextValue"));
panel.Children.Add(multiLineBox);
break;
case "REG_EXPAND_SZ":
AddExpandStringView(ref panel, ref resourceLoader, value);
break;
default: // REG_SZ
var stringBox = new TextBox()
{
IsReadOnly = true,
FontSize = 14,
RequestedTheme = panel.ActualTheme,
Text = value,
};
AutomationProperties.SetName(stringBox, resourceLoader.GetString("DataPreview_AutomationPropertiesName_TextValue"));
panel.Children.Add(stringBox);
break;
}
// Use this code to associate the dialog to the appropriate AppWindow by setting
// the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow.
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
{
contentDialog.XamlRoot = this.Content.XamlRoot;
}
// Show dialog and wait.
ChangeCursor(gridPreview, false);
_ = await contentDialog.ShowAsync();
}
private static void AddHexView(ref StackPanel panel, ref ResourceLoader resourceLoader, string value)
{
var hexBox = new TextBox()
{
Header = resourceLoader.GetString("DataPreviewHex"),
IsReadOnly = true,
FontSize = 14,
RequestedTheme = panel.ActualTheme,
Text = value.Split(" ")[0],
};
var decimalBox = new TextBox()
{
Header = resourceLoader.GetString("DataPreviewDec"),
IsReadOnly = true,
FontSize = 14,
RequestedTheme = panel.ActualTheme,
Text = value.Split(" ")[1].TrimStart('(').TrimEnd(')'),
};
panel.Children.Add(hexBox);
panel.Children.Add(decimalBox);
}
private static void AddBinaryView(ref StackPanel panel, ref ResourceLoader resourceLoader, ref BinaryReader data, string dataText)
{
// Create SelectorBar
var navBar = new SelectorBar()
{
RequestedTheme = panel.ActualTheme,
};
navBar.SelectionChanged += BinaryPreview_SelectorChanged;
navBar.Items.Add(new SelectorBarItem()
{
Text = resourceLoader.GetString("DataPreviewDataView"),
Tag = "DataView",
FontSize = 14,
RequestedTheme = panel.ActualTheme,
IsSelected = true,
});
navBar.Items.Add(new SelectorBarItem()
{
Text = resourceLoader.GetString("DataPreviewVisibleText"),
Tag = "TextView",
FontSize = 14,
RequestedTheme = panel.ActualTheme,
IsSelected = false,
IsEnabled = !string.IsNullOrWhiteSpace(dataText),
});
// Create HexBox
var binaryPreviewBox = new HB.HexBox()
{
Height = 300,
Width = 495,
ShowAddress = true,
ShowData = true,
ShowText = true,
Columns = 8,
FontSize = 13,
RequestedTheme = panel.ActualTheme,
AddressBrush = (SolidColorBrush)Application.Current.Resources["AccentTextFillColorPrimaryBrush"],
AlternatingDataColumnTextBrush = (SolidColorBrush)Application.Current.Resources["TextFillColorSecondaryBrush"],
SelectionTextBrush = (SolidColorBrush)Application.Current.Resources["HexBox_SelectionTextBrush"],
SelectionBrush = (SolidColorBrush)Application.Current.Resources["HexBox_SelectionBackgroundBrush"],
VerticalSeparatorLineBrush = (SolidColorBrush)Application.Current.Resources["HexBox_VerticalLineBrush"],
BorderBrush = (LinearGradientBrush)Application.Current.Resources["HexBox_ControlBorderBrush"],
BorderThickness = (Thickness)Application.Current.Resources["HexBox_ControlBorderThickness"],
CornerRadius = (CornerRadius)Application.Current.Resources["ControlCornerRadius"],
DataFormat = HB.DataFormat.Hexadecimal,
DataSignedness = HB.DataSignedness.Unsigned,
DataType = HB.DataType.Int_1,
EnforceProperties = true,
Visibility = Visibility.Collapsed,
DataSource = data,
};
AutomationProperties.SetName(binaryPreviewBox, resourceLoader.GetString("DataPreview_AutomationPropertiesName_BinaryDataPreview"));
binaryPreviewBox.Loaded += BinaryPreview_HexBoxLoaded;
binaryPreviewBox.GotFocus += BinaryPreview_HexBoxFocused;
binaryPreviewBox.LostFocus += BinaryPreview_HexBoxFocusLost;
// Create TextBox
var visibleText = new TextBox()
{
IsReadOnly = true,
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
Height = 300,
Width = 495,
FontSize = 13,
Text = dataText,
RequestedTheme = panel.ActualTheme,
Visibility = Visibility.Collapsed,
};
AutomationProperties.SetName(visibleText, resourceLoader.GetString("DataPreview_AutomationPropertiesName_VisibleTextPreview"));
// Add controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox
panel.Children.Add(navBar);
panel.Children.Add(new ProgressRing());
panel.Children.Add(binaryPreviewBox);
panel.Children.Add(visibleText);
}
private static void AddExpandStringView(ref StackPanel panel, ref ResourceLoader resourceLoader, string value)
{
var stringBoxRaw = new TextBox()
{
Header = resourceLoader.GetString("DataPreviewRawValue"),
IsReadOnly = true,
FontSize = 14,
RequestedTheme = panel.ActualTheme,
Text = value,
};
var stringBoxExp = new TextBox()
{
Header = resourceLoader.GetString("DataPreviewExpandedValue"),
IsReadOnly = true,
FontSize = 14,
RequestedTheme = panel.ActualTheme,
Text = Environment.ExpandEnvironmentVariables(value),
};
panel.Children.Add(stringBoxRaw);
panel.Children.Add(stringBoxExp);
}
private static void BinaryPreview_SelectorChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs args)
{
// Child controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox
var stackPanel = sender.Parent as StackPanel;
var progressRing = (ProgressRing)stackPanel.Children[1];
var hexBox = (HB.HexBox)stackPanel.Children[2];
var textBox = (TextBox)stackPanel.Children[3];
if (sender.SelectedItem.Tag.ToString() == "DataView")
{
textBox.Visibility = Visibility.Collapsed;
if (_isDataPreviewHexBoxLoaded)
{
progressRing.Visibility = Visibility.Collapsed;
hexBox.Visibility = Visibility.Visible;
// Clear selection aligned to TextBox
hexBox.ClearSelection();
hexBox.Focus(FocusState.Programmatic);
}
else
{
hexBox.Visibility = Visibility.Collapsed;
progressRing.Visibility = Visibility.Visible;
progressRing.Focus(FocusState.Programmatic);
}
}
else
{
progressRing.Visibility = Visibility.Collapsed;
hexBox.Visibility = Visibility.Collapsed;
textBox.Visibility = Visibility.Visible;
// Workaround for wrong text selection (color) after switching back to "Visible text"
textBox.Focus(FocusState.Programmatic);
textBox.Select(0, 0);
}
}
private static void BinaryPreview_HexBoxLoaded(object sender, RoutedEventArgs e)
{
_isDataPreviewHexBoxLoaded = true;
// Child controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox
var hexBox = (HB.HexBox)sender;
var stackPanel = hexBox.Parent as StackPanel;
var selectorBar = stackPanel.Children[0] as SelectorBar;
var progressRing = stackPanel.Children[1] as ProgressRing;
if (selectorBar.SelectedItem.Tag.ToString() == "DataView")
{
progressRing.Visibility = Visibility.Collapsed;
hexBox.Visibility = Visibility.Visible;
}
}
/// <summary>
/// Event handler to set correct control border if focused.
/// </summary>
private static void BinaryPreview_HexBoxFocused(object sender, RoutedEventArgs e)
{
var hexBox = (HB.HexBox)sender;
hexBox.BorderThickness = (Thickness)Application.Current.Resources["HexBox_ControlBorderFocusedThickness"];
hexBox.BorderBrush = (LinearGradientBrush)Application.Current.Resources["HexBox_ControlBorderFocusedBrush"];
}
/// <summary>
/// Event handler to set correct control border if not focused.
/// </summary>
private static void BinaryPreview_HexBoxFocusLost(object sender, RoutedEventArgs e)
{
var hexBox = (HB.HexBox)sender;
// Workaround: Verify that the newly focused control isn't the context menu of the HexBox control
if (FocusManager.GetFocusedElement(hexBox.XamlRoot).GetType() != typeof(MenuFlyoutPresenter))
{
hexBox.BorderThickness = (Thickness)Application.Current.Resources["HexBox_ControlBorderThickness"];
hexBox.BorderBrush = (LinearGradientBrush)Application.Current.Resources["HexBox_ControlBorderBrush"];
}
}
/// <summary>
/// Make sure that for REG_Binary preview the HexBox control is focused after opening.
/// </summary>
private static void ExtendedDataPreview_Opened(ContentDialog sender, ContentDialogOpenedEventArgs e)
{
// If <_isDataPreviewHexBoxLoaded == true> then we have the right content on the dialog.
if (_isDataPreviewHexBoxLoaded)
{
// Child controls: 0 = SelectorBar, 1 = ProgressRing, 2 = HexBox, 3 = TextBox
(sender.Content as StackPanel).Children[2].Focus(FocusState.Programmatic);
}
}
}
}

View File

@@ -8,7 +8,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
@@ -415,5 +414,48 @@ namespace RegistryPreviewUILib
saveButton.IsEnabled = true;
});
}
// Commands to show data preview
public void ButtonExtendedPreview_Click(object sender, RoutedEventArgs e)
{
var data = ((Button)sender).DataContext as RegistryValue;
InvokeExtendedDataPreview(data);
}
public void MenuExtendedPreview_Click(object sender, RoutedEventArgs e)
{
var data = ((MenuFlyoutItem)sender).DataContext as RegistryValue;
InvokeExtendedDataPreview(data);
}
private async void InvokeExtendedDataPreview(RegistryValue valueData)
{
// Only one content dialog can be open at the same time and multiple instances of data preview can crash the app.
if (_dialogSemaphore.CurrentCount == 0)
{
return;
}
try
{
// Lock ui and request dialog lock
_dialogSemaphore.Wait();
ChangeCursor(gridPreview, true);
await ShowExtendedDataPreview(valueData.Name, valueData.Type, valueData.Value);
}
catch
{
#if DEBUG
throw;
#endif
}
finally
{
// Unblock ui and release dialog lock
ChangeCursor(gridPreview, false);
_dialogSemaphore.Release();
}
}
}
}

View File

@@ -503,6 +503,7 @@ namespace RegistryPreviewUILib
case "REG_NONE":
if (value.Length <= 0)
{
registryValue.IsEmptyBinary = true;
value = resourceLoader.GetString("ZeroLength");
}
else

View File

@@ -2,6 +2,7 @@
x:Class="RegistryPreviewUILib.RegistryPreviewMainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:RegistryPreviewUILib"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -10,6 +11,12 @@
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<Page.Resources>
<ResourceDictionary>
<converter:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</ResourceDictionary>
</Page.Resources>
<Grid
x:Name="gridPreview"
Grid.Row="1"
@@ -178,7 +185,7 @@
Spacing="8">
<StackPanel.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="ContextMenu_CopyName" Command="{Binding Content.CopyToClipboardName_Click}" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyKeyName" Command="{Binding Content.CopyToClipboardName_Click}" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyKeyPath" Command="{Binding Content.CopyToClipboardKeyPath_Click}" />
</MenuFlyout>
</StackPanel.ContextFlyout>
@@ -225,10 +232,18 @@
Spacing="8">
<StackPanel.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="ContextMenu_CopyName" Command="{Binding CopyToClipboardName_Click}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="ContextMenu_CopyName"
Command="{Binding CopyToClipboardName_Click}"
Icon="Copy" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyValue" Command="{Binding CopyToClipboardEntry_Click}" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyValueWithPath" Command="{Binding CopyToClipboardWithPath_Click}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="ContextMenu_DataPreview"
Click="MenuExtendedPreview_Click"
Icon="Zoom"
IsEnabled="{Binding ShowPreviewButton}" />
</MenuFlyout>
</StackPanel.ContextFlyout>
<Image
@@ -255,10 +270,18 @@
<Setter Property="ContextFlyout">
<Setter.Value>
<MenuFlyout>
<MenuFlyoutItem x:Uid="ContextMenu_CopyType" Command="{Binding CopyToClipboardType_Click}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="ContextMenu_CopyType"
Command="{Binding CopyToClipboardType_Click}"
Icon="Copy" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyValue" Command="{Binding CopyToClipboardEntry_Click}" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyValueWithPath" Command="{Binding CopyToClipboardWithPath_Click}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="ContextMenu_DataPreview"
Click="MenuExtendedPreview_Click"
Icon="Zoom"
IsEnabled="{Binding ShowPreviewButton}" />
</MenuFlyout>
</Setter.Value>
</Setter>
@@ -274,20 +297,36 @@
<StackPanel
Margin="4"
VerticalAlignment="Center"
Orientation="Horizontal">
Orientation="Horizontal"
Spacing="6">
<StackPanel.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem x:Uid="ContextMenu_CopyData" Command="{Binding CopyToClipboardData_Click}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="ContextMenu_CopyData"
Command="{Binding CopyToClipboardData_Click}"
Icon="Copy" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyValue" Command="{Binding CopyToClipboardEntry_Click}" />
<MenuFlyoutItem x:Uid="ContextMenu_CopyValueWithPath" Command="{Binding CopyToClipboardWithPath_Click}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem
x:Uid="ContextMenu_DataPreview"
Click="MenuExtendedPreview_Click"
Icon="Zoom"
IsEnabled="{Binding ShowPreviewButton}" />
</MenuFlyout>
</StackPanel.ContextFlyout>
<Button
x:Uid="ShowDataPreviewButton"
Padding="2"
Click="ButtonExtendedPreview_Click"
Content="&#xe71e;"
FontFamily="{StaticResource SymbolThemeFontFamily}"
Visibility="{Binding ShowPreviewButton, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
VerticalAlignment="Center"
IsTabStop="False"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding ValueOneLine}"
ToolTipService.ToolTip="{Binding Value}" />
Text="{Binding ValueOneLine}" />
</StackPanel>
</DataTemplate>
</tk7controls:DataGridTemplateColumn.CellTemplate>

View File

@@ -40,7 +40,9 @@
</ItemGroup>
<ItemGroup>
<None Remove="MonacoEditorControl.xaml" />
<None Remove="Controls\MonacoEditor\MonacoEditorControl.xaml" />
<None Remove="Controls\HexBox\Themes\Generic.xaml" />
<None Remove="Themes\Generic.xaml" />
</ItemGroup>
<ItemGroup>
@@ -51,6 +53,7 @@
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="SkiaSharp.Views.WinUI" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
@@ -59,10 +62,21 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Page Update="MonacoEditorControl.xaml">
<Page Update="Controls\MonacoEditor\MonacoEditorControl.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Controls\HexBox\Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Themes\Generic.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Folder Include="Controls\MonacoEditor\" />
<Folder Include="Controls\HexBox\" />
</ItemGroup>
</Project>

View File

@@ -6,7 +6,6 @@ using System;
using System.Windows.Input;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml;
using Windows.ApplicationModel.DataTransfer;
namespace RegistryPreviewUILib
{
@@ -29,6 +28,8 @@ namespace RegistryPreviewUILib
public string Value { get; set; }
public bool IsEmptyBinary { private get; set; }
public string ValueOneLine => Value.Replace('\r', ' ');
public string ToolTipText { get; set; }
@@ -54,6 +55,10 @@ namespace RegistryPreviewUILib
}
}
public bool ShowPreviewButton =>
Type != "ERROR" && Type != string.Empty &&
Value != string.Empty && IsEmptyBinary != true;
public RegistryValue(string name, string type, string value, string key)
{
this.Name = name;

View File

@@ -277,16 +277,20 @@
<value>Copy path</value>
<comment>Like "Copy item"</comment>
</data>
<data name="ContextMenu_CopyName.Text" xml:space="preserve">
<data name="ContextMenu_CopyKeyName.Text" xml:space="preserve">
<value>Copy name</value>
<comment>Like "Copy item"</comment>
</data>
<data name="ContextMenu_CopyName.Text" xml:space="preserve">
<value>Copy</value>
<comment>Like "Copy item"</comment>
</data>
<data name="ContextMenu_CopyType.Text" xml:space="preserve">
<value>Copy type</value>
<value>Copy</value>
<comment>Like "Copy item"</comment>
</data>
<data name="ContextMenu_CopyData.Text" xml:space="preserve">
<value>Copy data</value>
<value>Copy</value>
<comment>Like "Copy item"</comment>
</data>
<data name="ContextMenu_CopyValue.Text" xml:space="preserve">
@@ -297,6 +301,65 @@
<value>Copy value with key path</value>
<comment>Like "Copy item"</comment>
</data>
<data name="ShowDataPreviewButton.ToolTipService.ToolTip" xml:space="preserve">
<value>Show extended preview</value>
</data>
<data name="ShowDataPreviewButton.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Show extended preview</value>
</data>
<data name="DataPreviewTitle" xml:space="preserve">
<value>View data</value>
</data>
<data name="DataPreviewClose" xml:space="preserve">
<value>Close</value>
<comment>like "Close window"</comment>
</data>
<data name="DataPreviewHex" xml:space="preserve">
<value>Hexadecimal</value>
</data>
<data name="DataPreviewDec" xml:space="preserve">
<value>Decimal</value>
</data>
<data name="DataPreviewRawValue" xml:space="preserve">
<value>Raw value</value>
</data>
<data name="DataPreviewExpandedValue" xml:space="preserve">
<value>Expanded value</value>
</data>
<data name="DataPreviewVisibleText" xml:space="preserve">
<value>Readable text</value>
<comment>Means text that is human readable and not only control characters.</comment>
</data>
<data name="DataPreviewDataView" xml:space="preserve">
<value>Data</value>
<comment>Like "binary data"</comment>
</data>
<data name="DataPreview_AutomationPropertiesName_MultilineTextValue" xml:space="preserve">
<value>Multiline text value</value>
</data>
<data name="DataPreview_AutomationPropertiesName_TextValue" xml:space="preserve">
<value>Text value</value>
</data>
<data name="DataPreview_AutomationPropertiesName_BinaryDataPreview" xml:space="preserve">
<value>Binary data preview</value>
</data>
<data name="DataPreview_AutomationPropertiesName_VisibleTextPreview" xml:space="preserve">
<value>Preview of readable text</value>
</data>
<data name="HexBox_CopyCommand.Text" xml:space="preserve">
<value>Copy</value>
<comment>Like "copy the value"</comment>
</data>
<data name="HexBox_CopyTextCommand.Text" xml:space="preserve">
<value>Copy text</value>
<comment>Like "Copy the text"</comment>
</data>
<data name="HexBox_SelectAllCommand.Text" xml:space="preserve">
<value>Select all</value>
</data>
<data name="ContextMenu_DataPreview.Text" xml:space="preserve">
<value>Extended data preview</value>
</data>
<data name="NewButton.Label" xml:space="preserve">
<value>New</value>
</data>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RegistryPreviewUILib">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PowerToys.RegistryPreviewUILib/Controls/HexBox/Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>