Custom input bar
This commit is contained in:
parent
d404cf2ca3
commit
a51aa6958b
184
FileSystemSuggestions.cs
Normal file
184
FileSystemSuggestions.cs
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace HashCalculator.GUI
|
||||||
|
{
|
||||||
|
internal class FileSystemSuggestions
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, InputEntryType> Mapping = new Dictionary<string, InputEntryType>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ ".big", InputEntryType.BigFile },
|
||||||
|
{ ".xml", InputEntryType.XmlFile },
|
||||||
|
{ ".w3x", InputEntryType.XmlFile },
|
||||||
|
{ ".manifest", InputEntryType.ManifestFile },
|
||||||
|
};
|
||||||
|
|
||||||
|
private Search _search = new Search();
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1031")]
|
||||||
|
public IEnumerable<InputEntry> ProvideFileSystemSuggestions(string? path)
|
||||||
|
{
|
||||||
|
var empty = Enumerable.Empty<InputEntry>();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(path) || path == null)
|
||||||
|
{
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = Environment.ExpandEnvironmentVariables(path);
|
||||||
|
|
||||||
|
_search.Update(path);
|
||||||
|
|
||||||
|
if (!_search.IsValidPath)
|
||||||
|
{
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentFiles = empty;
|
||||||
|
string? fileName;
|
||||||
|
string? currentFullPath;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
currentFullPath = Path.GetFullPath(path);
|
||||||
|
fileName = Path.GetFileName(path);
|
||||||
|
if (File.Exists(currentFullPath))
|
||||||
|
{
|
||||||
|
|
||||||
|
var type = CheckExtension(currentFullPath);
|
||||||
|
if (type.HasValue)
|
||||||
|
{
|
||||||
|
currentFiles.Append(new InputEntry(type.Value, path, currentFullPath));
|
||||||
|
}
|
||||||
|
currentFiles.Append(new InputEntry(InputEntryType.BinaryFile, path, currentFullPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var directories = from directory in _search.AllDirectories
|
||||||
|
where directory.Name.StartsWith(fileName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
select new InputEntry(InputEntryType.Path, _search.GetInputStyleName(directory), directory.FullName);
|
||||||
|
|
||||||
|
var otherFiles = from file in _search.AllFiles
|
||||||
|
where file.Name.StartsWith(fileName, StringComparison.OrdinalIgnoreCase)
|
||||||
|
where file.FullName != currentFullPath
|
||||||
|
select file;
|
||||||
|
|
||||||
|
var supportedFiles = empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
supportedFiles = from file in otherFiles
|
||||||
|
let type = CheckExtension(file.Extension)
|
||||||
|
where type.HasValue
|
||||||
|
select new InputEntry(type.Value, _search.GetInputStyleName(file), file.FullName);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
var binaryFiles = empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
binaryFiles = from file in otherFiles
|
||||||
|
select new InputEntry(InputEntryType.BinaryFile, _search.GetInputStyleName(file), file.FullName);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return currentFiles.Concat(supportedFiles).Concat(directories).Concat(binaryFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputEntryType? CheckExtension(string path)
|
||||||
|
{
|
||||||
|
if (Mapping.TryGetValue(path, out var type))
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class Search
|
||||||
|
{
|
||||||
|
private string? _inputBaseDirectory;
|
||||||
|
public bool IsValidPath => _inputBaseDirectory != null;
|
||||||
|
public IEnumerable<DirectoryInfo> AllDirectories { get; private set; }
|
||||||
|
public IEnumerable<FileInfo> AllFiles { get; private set; }
|
||||||
|
|
||||||
|
public Search()
|
||||||
|
{
|
||||||
|
AllDirectories = Array.Empty<DirectoryInfo>();
|
||||||
|
AllFiles = Array.Empty<FileInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetInputStyleName(FileSystemInfo entry)
|
||||||
|
{
|
||||||
|
if (_inputBaseDirectory == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Path.Combine(_inputBaseDirectory, entry.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1031")]
|
||||||
|
public void Update(string path)
|
||||||
|
{
|
||||||
|
string? newBaseDirectory = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newBaseDirectory = Path.GetDirectoryName(path);
|
||||||
|
var rootDirectory = Path.GetPathRoot(path);
|
||||||
|
if(string.IsNullOrEmpty(newBaseDirectory) && !string.IsNullOrEmpty(rootDirectory))
|
||||||
|
{
|
||||||
|
var last = rootDirectory.LastOrDefault();
|
||||||
|
if (last != Path.DirectorySeparatorChar && last != Path.AltDirectorySeparatorChar)
|
||||||
|
{
|
||||||
|
rootDirectory += Path.DirectorySeparatorChar;
|
||||||
|
}
|
||||||
|
newBaseDirectory = rootDirectory;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_inputBaseDirectory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBaseDirectory == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBaseDirectory == _inputBaseDirectory)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputBaseDirectory = newBaseDirectory;
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1031")]
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
AllDirectories = Enumerable.Empty<DirectoryInfo>();
|
||||||
|
AllFiles = Enumerable.Empty<FileInfo>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var actualPath = _inputBaseDirectory!.Length == 0
|
||||||
|
? "."
|
||||||
|
: _inputBaseDirectory;
|
||||||
|
var directory = new DirectoryInfo(actualPath);
|
||||||
|
if (directory.Exists)
|
||||||
|
{
|
||||||
|
AllDirectories = directory.EnumerateDirectories();
|
||||||
|
AllFiles = directory.EnumerateFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,25 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net461</TargetFramework>
|
<TargetFramework>net472</TargetFramework>
|
||||||
<RootNamespace>HashCalculator.GUI</RootNamespace>
|
<RootNamespace>HashCalculator.GUI</RootNamespace>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<UseWPF>true</UseWPF>
|
<UseWPF>true</UseWPF>
|
||||||
|
<Platforms>AnyCPU;x86</Platforms>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DotNetKit.Wpf.AutoCompleteComboBox" Version="1.2.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="TechnologyAssembler.Core">
|
||||||
|
<HintPath>TechnologyAssembler.Core.dll</HintPath>
|
||||||
|
<Private>true</Private>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
115
InputBar.xaml
Normal file
115
InputBar.xaml
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Class="HashCalculator.GUI.InputBar"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:HashCalculator.GUI"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
x:ClassModifier="internal"
|
||||||
|
x:Name="_this"
|
||||||
|
d:DesignHeight="100" d:DesignWidth="800"
|
||||||
|
>
|
||||||
|
<UserControl.Resources>
|
||||||
|
<local:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||||
|
<Style
|
||||||
|
x:Key="BlankListBoxContainerStyle"
|
||||||
|
TargetType="{x:Type ListBoxItem}"
|
||||||
|
>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="ListBoxItem">
|
||||||
|
<Border
|
||||||
|
Name="Border"
|
||||||
|
Padding="3, 2"
|
||||||
|
SnapsToDevicePixels="true"
|
||||||
|
>
|
||||||
|
<ContentPresenter />
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger
|
||||||
|
Property="IsSelected"
|
||||||
|
Value="true"
|
||||||
|
>
|
||||||
|
<Setter
|
||||||
|
TargetName="Border"
|
||||||
|
Property="Background"
|
||||||
|
Value="DarkSlateGray"
|
||||||
|
/>
|
||||||
|
<Setter
|
||||||
|
Property="Foreground"
|
||||||
|
Value="White"
|
||||||
|
/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger
|
||||||
|
Property="IsMouseOver"
|
||||||
|
Value="true"
|
||||||
|
>
|
||||||
|
<Setter
|
||||||
|
TargetName="Border"
|
||||||
|
Property="Background"
|
||||||
|
Value="DarkSlateGray"
|
||||||
|
/>
|
||||||
|
<Setter
|
||||||
|
Property="Foreground"
|
||||||
|
Value="White"
|
||||||
|
/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
<Grid>
|
||||||
|
<!--TextChanged="OnTextChanged"-->
|
||||||
|
<Grid>
|
||||||
|
<TextBox
|
||||||
|
x:Name="TextBox"
|
||||||
|
Text="{Binding Path=Text,
|
||||||
|
RelativeSource={RelativeSource AncestorType=local:InputBar},
|
||||||
|
UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
PreviewKeyDown="OnPreviewKeyDown"
|
||||||
|
Padding="2, 0"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
TextChanged="OnTextChanged"
|
||||||
|
LostFocus="OnLostFocus"
|
||||||
|
/>
|
||||||
|
<TextBlock
|
||||||
|
Text="{Binding Path=HintText,
|
||||||
|
RelativeSource={RelativeSource AncestorType=local:InputBar}}"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
Visibility="{Binding ElementName=TextBox, Path=Text, Converter={StaticResource NullToVisibilityConverter}}"
|
||||||
|
Padding="5, 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Popup
|
||||||
|
x:Name="DropDown"
|
||||||
|
StaysOpen="False"
|
||||||
|
MaxHeight="400"
|
||||||
|
Width="{Binding ElementName=TextBox, Path=ActualWidth}"
|
||||||
|
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||||
|
>
|
||||||
|
<ListBox
|
||||||
|
x:Name="ListBox"
|
||||||
|
ItemContainerStyle="{StaticResource ResourceKey=BlankListBoxContainerStyle}"
|
||||||
|
ItemsSource="{Binding Path=Collection,
|
||||||
|
RelativeSource={RelativeSource AncestorType=local:InputBar}}"
|
||||||
|
SelectionMode="Single"
|
||||||
|
SelectionChanged="OnSelectionChanged"
|
||||||
|
VirtualizingPanel.IsVirtualizing="True"
|
||||||
|
VirtualizingPanel.VirtualizationMode="Recycling"
|
||||||
|
>
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<TextBlock Text="{Binding Text}" FontWeight="Bold"></TextBlock>
|
||||||
|
<TextBlock Text="{Binding Type}"></TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</Popup>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
150
InputBar.xaml.cs
Normal file
150
InputBar.xaml.cs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace HashCalculator.GUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// InputBar.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1812")]
|
||||||
|
internal partial class InputBar : UserControl
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty HintTextProperty =
|
||||||
|
DependencyProperty.Register(nameof(HintText), typeof(string), typeof(InputBar), new FrameworkPropertyMetadata
|
||||||
|
{
|
||||||
|
BindsTwoWayByDefault = true,
|
||||||
|
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
|
||||||
|
});
|
||||||
|
public string? HintText
|
||||||
|
{
|
||||||
|
get => GetValue(HintTextProperty) as string;
|
||||||
|
set => SetValue(HintTextProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty TextProperty =
|
||||||
|
DependencyProperty.Register(nameof(Text), typeof(string), typeof(InputBar), new FrameworkPropertyMetadata
|
||||||
|
{
|
||||||
|
BindsTwoWayByDefault = true,
|
||||||
|
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
|
||||||
|
});
|
||||||
|
public string? Text
|
||||||
|
{
|
||||||
|
get => GetValue(TextProperty) as string;
|
||||||
|
set => SetValue(TextProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty CollectionProperty =
|
||||||
|
DependencyProperty.Register(nameof(Collection), typeof(IEnumerable<InputEntry>), typeof(InputBar), new FrameworkPropertyMetadata
|
||||||
|
{
|
||||||
|
BindsTwoWayByDefault = true,
|
||||||
|
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
|
||||||
|
CoerceValueCallback = (d, baseObject) =>
|
||||||
|
{
|
||||||
|
var value = baseObject as IEnumerable<InputEntry>;
|
||||||
|
if (value?.Count() > 100)
|
||||||
|
{
|
||||||
|
value = value
|
||||||
|
.Take(99)
|
||||||
|
.Append(new InputEntry(InputEntryType.Path, $"Other {value.Count() - 50} items...", string.Empty));
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
public IEnumerable<InputEntry>? Collection
|
||||||
|
{
|
||||||
|
get => GetValue(CollectionProperty) as IEnumerable<InputEntry>;
|
||||||
|
set => SetValue(CollectionProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DependencyProperty SelectedItemProperty =
|
||||||
|
DependencyProperty.Register(nameof(SelectedItem), typeof(InputEntry), typeof(InputBar), new FrameworkPropertyMetadata
|
||||||
|
{
|
||||||
|
BindsTwoWayByDefault = true,
|
||||||
|
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
|
||||||
|
});
|
||||||
|
public InputEntry? SelectedItem
|
||||||
|
{
|
||||||
|
get => GetValue(SelectedItemProperty) as InputEntry;
|
||||||
|
set => SetValue(SelectedItemProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event TextChangedEventHandler? TextChanged;
|
||||||
|
|
||||||
|
public InputBar()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPreviewKeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.Down:
|
||||||
|
ListBox.SelectedIndex = NormalizeIndex(ListBox.SelectedIndex + 1);
|
||||||
|
ListBox.ScrollIntoView(ListBox.SelectedItem);
|
||||||
|
DropDown.IsOpen = true;
|
||||||
|
break;
|
||||||
|
case Key.Up:
|
||||||
|
ListBox.SelectedIndex = NormalizeIndex(ListBox.SelectedIndex - 1);
|
||||||
|
ListBox.ScrollIntoView(ListBox.SelectedItem);
|
||||||
|
break;
|
||||||
|
case Key.Escape:
|
||||||
|
case Key.Enter:
|
||||||
|
DropDown.IsOpen = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DropDown.IsOpen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
SelectedItem = (InputEntry)ListBox.SelectedItem;
|
||||||
|
if (SelectedItem != null)
|
||||||
|
{
|
||||||
|
Text = SelectedItem.ToString();
|
||||||
|
TextBox.Select(Text.Length, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (Text != SelectedItem?.ToString())
|
||||||
|
{
|
||||||
|
TextChanged?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLostFocus(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
DropDown.IsOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int NormalizeIndex(int rawIndex)
|
||||||
|
{
|
||||||
|
if (Collection == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.Min(Math.Max(rawIndex, -1), Collection.Count() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
39
InputEntry.cs
Normal file
39
InputEntry.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TechnologyAssembler.Core.IO;
|
||||||
|
|
||||||
|
namespace HashCalculator.GUI
|
||||||
|
{
|
||||||
|
internal enum InputEntryType
|
||||||
|
{
|
||||||
|
Text,
|
||||||
|
BigFile,
|
||||||
|
ManifestFile,
|
||||||
|
XmlFile,
|
||||||
|
BinaryFile,
|
||||||
|
Path,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class InputEntry
|
||||||
|
{
|
||||||
|
public InputEntryType Type { get; }
|
||||||
|
public string Value { get; }
|
||||||
|
public string Text { get; }
|
||||||
|
|
||||||
|
public InputEntry(InputEntryType type, string text, string value)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
Text = text;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,12 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:dotNetKitControls="clr-namespace:DotNetKit.Windows.Controls;assembly=DotNetKit.Wpf.AutoCompleteComboBox"
|
|
||||||
xmlns:l="clr-namespace:HashCalculator.GUI"
|
xmlns:l="clr-namespace:HashCalculator.GUI"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Title="Sage FastHash 哈希计算器" Height="600" Width="600">
|
Title="Sage FastHash 哈希计算器" Height="600" Width="600">
|
||||||
|
<Window.Resources>
|
||||||
|
<l:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||||
|
</Window.Resources>
|
||||||
<Grid Margin="10, 10">
|
<Grid Margin="10, 10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
@ -18,36 +20,35 @@
|
|||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
<ColumnDefinition Width="92"/>
|
<ColumnDefinition Width="92"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<dotNetKitControls:AutoCompleteComboBox
|
<Grid>
|
||||||
SelectedValuePath="Id"
|
<!--<l:InputBox
|
||||||
TextSearch.TextPath="Name"
|
HintText="Select some big file"
|
||||||
Text="输入任意字符串来计算它的哈希值,也可以输入big/manifest/xml文件的路径"
|
Collection="{Binding Items}"
|
||||||
ItemsSource="{Binding Items}"
|
TextChanged="OnTextChanged"
|
||||||
SelectedItem="{Binding SelectedItem}"
|
>
|
||||||
SelectedValue="{Binding SelectedValue}"
|
</l:InputBox>-->
|
||||||
Grid.Column="0" Margin="0"
|
<l:InputBar
|
||||||
/>
|
HintText="Select some big file"
|
||||||
|
Text="{Binding Text}"
|
||||||
|
Collection="{Binding Items}"
|
||||||
|
TextChanged="OnTextChanged"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
<Button x:Name="button" Content="浏览文件"
|
<Button x:Name="button" Content="浏览文件"
|
||||||
Grid.Column="1" Margin="0"
|
Grid.Column="1" Margin="0"
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<dotNetKitControls:AutoCompleteComboBox
|
<l:InputBar
|
||||||
SelectedValuePath="Id"
|
HintText="输入big文件里包含的manifest文件的路径"
|
||||||
TextSearch.TextPath="Name"
|
Collection="{Binding Items}"
|
||||||
Text="输入big文件里包含的manifest文件的路径"
|
|
||||||
ItemsSource="{Binding Items}"
|
|
||||||
SelectedItem="{Binding SelectedItem}"
|
SelectedItem="{Binding SelectedItem}"
|
||||||
SelectedValue="{Binding SelectedValue}"
|
|
||||||
Margin="0,10,0,0"
|
Margin="0,10,0,0"
|
||||||
Height="25" VerticalAlignment="Top"
|
Height="25" VerticalAlignment="Top"
|
||||||
/>
|
/>
|
||||||
<dotNetKitControls:AutoCompleteComboBox
|
<l:InputBar
|
||||||
SelectedValuePath="Id"
|
HintText="过滤Asset ID(可选)"
|
||||||
TextSearch.TextPath="Name"
|
Collection="{Binding Items}"
|
||||||
Text="过滤Asset ID(可选)"
|
|
||||||
ItemsSource="{Binding Items}"
|
|
||||||
SelectedItem="{Binding SelectedItem}"
|
SelectedItem="{Binding SelectedItem}"
|
||||||
SelectedValue="{Binding SelectedValue}"
|
|
||||||
Margin="0,10,0,0"
|
Margin="0,10,0,0"
|
||||||
Height="25" VerticalAlignment="Top"
|
Height="25" VerticalAlignment="Top"
|
||||||
/>
|
/>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -23,19 +23,40 @@ namespace HashCalculator.GUI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
private readonly FileSystemSuggestions _suggestions = new FileSystemSuggestions();
|
||||||
|
private readonly ViewModel _model = new ViewModel();
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = new ViewModel();
|
DataContext = _model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is InputBar)
|
||||||
|
{
|
||||||
|
var inputValue = _model.Text;
|
||||||
|
if(inputValue != null)
|
||||||
|
{
|
||||||
|
_model.Items = _suggestions.ProvideFileSystemSuggestions(inputValue)
|
||||||
|
.Prepend(new InputEntry(InputEntryType.Text, inputValue, inputValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class ViewModel : INotifyPropertyChanged
|
internal class ViewModel : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
#region INotifyPropertyChanged
|
#region INotifyPropertyChanged
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
|
|
||||||
|
private void Notify(string name)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Debug.WriteLine($"Updating {name}");
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
||||||
|
}
|
||||||
|
|
||||||
private void SetField<X>(ref X field, X value, [CallerMemberName] string? propertyName = null)
|
private void SetField<X>(ref X field, X value, [CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
if (propertyName == null)
|
if (propertyName == null)
|
||||||
@ -50,104 +71,37 @@ namespace HashCalculator.GUI
|
|||||||
|
|
||||||
field = value;
|
field = value;
|
||||||
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
Notify(propertyName);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public IReadOnlyList<Person> Items
|
private IEnumerable<InputEntry>? _items;
|
||||||
|
public IEnumerable<InputEntry>? Items
|
||||||
{
|
{
|
||||||
get { return PersonModule.All; }
|
get => _items;
|
||||||
|
set => SetField(ref _items, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Person selectedItem;
|
private InputEntry? selectedItem;
|
||||||
public Person SelectedItem
|
public InputEntry? SelectedItem
|
||||||
{
|
{
|
||||||
get { return selectedItem; }
|
get { return selectedItem; }
|
||||||
set { SetField(ref selectedItem, value); }
|
set { SetField(ref selectedItem, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
long? selectedValue;
|
private string? selectedValue;
|
||||||
public long? SelectedValue
|
public string? SelectedValue
|
||||||
{
|
{
|
||||||
get { return selectedValue; }
|
get { return selectedValue; }
|
||||||
set { SetField(ref selectedValue, value); }
|
set { SetField(ref selectedValue, value); }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal enum InputEntryType
|
private string? _text;
|
||||||
{
|
public string? Text
|
||||||
Text,
|
|
||||||
SupportedFilePath,
|
|
||||||
BinaryFilePath,
|
|
||||||
Path,
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class InputEntry
|
|
||||||
{
|
|
||||||
public InputEntryType Type { get; }
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public InputEntry(InputEntryType type, string value)
|
|
||||||
{
|
{
|
||||||
Type = type;
|
get { return _text; }
|
||||||
Value = value;
|
set { SetField(ref _text, value); }
|
||||||
}
|
|
||||||
|
|
||||||
public static List<InputEntry> CreateSuggestion(string value)
|
|
||||||
{
|
|
||||||
var list = new List<InputEntry>();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var path = new FileInfo(value);
|
|
||||||
path.Directory.GetFiles()
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsSupportedFile(string value)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<InputEntry> ProvideFileSystemSuggestions(string path)
|
|
||||||
{
|
|
||||||
var empty = Enumerable.Empty<InputEntry>();
|
|
||||||
var list = empty;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
var directories = empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var directoryInfo = new DirectoryInfo(path);
|
|
||||||
if (directoryInfo.Exists)
|
|
||||||
{
|
|
||||||
directories = directoryInfo.GetDirectories()
|
|
||||||
.Select(info => new InputEntry(InputEntryType.Path, info.FullName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
var files = empty;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var fileInfo = new FileInfo(path);
|
|
||||||
if (fileInfo.Exists)
|
|
||||||
{
|
|
||||||
files = files.Concat()
|
|
||||||
}
|
|
||||||
if (fileInfo.Directory.Exists)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//fileInfo.Directory.get
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
22
NullToVisibilityConverter.cs
Normal file
22
NullToVisibilityConverter.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace HashCalculator.GUI
|
||||||
|
{
|
||||||
|
[ValueConversion(typeof(string), typeof(Visibility))]
|
||||||
|
public class NullToVisibilityConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
var text = value as string;
|
||||||
|
return string.IsNullOrEmpty(text) ? Visibility.Visible : Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace HashCalculator.GUI
|
namespace HashCalculator.GUI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Opens <see cref="Hyperlink.NavigateUri"/> in a default system browser
|
/// Opens <see cref="Hyperlink.NavigateUri"/> in a default system browser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[SuppressMessage("Microsoft.Performance", "CA1812")]
|
||||||
internal sealed class ShellLink : Hyperlink
|
internal sealed class ShellLink : Hyperlink
|
||||||
{
|
{
|
||||||
public ShellLink()
|
public ShellLink()
|
||||||
|
BIN
TechnologyAssembler.Core.dll
Normal file
BIN
TechnologyAssembler.Core.dll
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user