リストボックスにアイコンの画像を表示するとアプリケーションランチャーのような見た目が作れると思い試作してみました。
機能
- アプリケーションの登録…ウィンドウに実行ファイル(.exe)をドラッグアンドドロップしてください。
- アプリケーションの起動…アイコンをダブルクリックしてください。
- アプリケーションの削除…アプリケーションが選択された状態でDeleteキーを押してください。
プロジェクトの作成
mkdir AppLanch
cd AppLanch
dotnet new wpf
dotnet add package Microsoft.Xaml.Behaviors.Wpf
dotnet add package ReactiveProperty.WPF
dotnet add package System.Drawing.Common
code.
ソースコード
<Window x:Class="AppLanch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AppLanch"
xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.InputBindings>
<KeyBinding
Gesture="Delete"
Command="{Binding DeleteCommand}"/>
</Window.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closed">
<interactivity:EventToReactiveCommand Command="{Binding WindowClosedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid AllowDrop="True">
<ListBox
x:Name="ListBox1"
SelectedIndex="{Binding AppListIndex.Value}"
ItemsSource="{Binding AppList}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<interactivity:EventToReactiveCommand Command="{Binding MouseDoubleClickCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Ico}"
Width="50"
Height="50"
Margin="10"
/>
<TextBlock Text="{Binding Name}"
Margin="10"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="DragOver">
<interactivity:EventToReactiveCommand Command="{Binding DragOverCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="Drop">
<interactivity:EventToReactiveCommand Command="{Binding DropCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</Window>
using System.Diagnostics;
using System;
using System.ComponentModel;
using Reactive.Bindings;
using System.Reactive.Disposables;
using System.Windows.Media.Imaging;
using Reactive.Bindings.Extensions;
using System.Windows;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AppLanch
{
public class MainWindowViewModel : INotifyPropertyChanged, IDisposable
{
public static string IniPath = @".\AppLanch.json";
public event PropertyChangedEventHandler? PropertyChanged;
private CompositeDisposable Disposable { get; } = new ();
public ReactiveCommand<EventArgs> WindowClosedCommand { get; }
protected virtual void OnPropertyChanged(string name)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public class AppInfo
{
public string? Name {get; set;}
public string? FullName {get; set;}
public BitmapImage? Ico {get; set;}
public AppInfo(string name, string fullName)
{
Name = name;
FullName = fullName;
}
}
public ReactiveProperty<int> AppListIndex { get; set; } = new ReactiveProperty<int>(-1);
public ReactiveCollection<AppInfo> AppList { get; set; } = new ReactiveCollection<AppInfo>();
public ReactiveCommand<DragEventArgs>? DragOverCommand { get; }
public ReactiveCommand<DragEventArgs>? DropCommand { get; }
public ReactiveCommand<EventArgs> MouseDoubleClickCommand { get; }
public ReactiveCommand? DeleteCommand { get; }
BitmapImage? GetIcon(string filePath)
{
var ico = System.Drawing.Icon.ExtractAssociatedIcon(filePath);
if (ico == null) return null;
using (var ms = new System.IO.MemoryStream())
{
var bitmap = ico.ToBitmap();
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.CreateOptions = BitmapCreateOptions.None;
bitmapImage.StreamSource = ms;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
public MainWindowViewModel()
{
PropertyChanged += (o, e) => {};
// アプリ情報の読込
if (File.Exists(IniPath))
{
using (var fs = File.OpenRead(IniPath))
{
using (var stream = new StreamReader(fs, System.Text.Encoding.UTF8))
{
while (!stream.EndOfStream)
{
var s = stream.ReadLine();
if (s != null)
{
var app = JsonSerializer.Deserialize<AppInfo>(s);
if (app != null)
{
if (app.FullName != null)
{
app.Ico = GetIcon(app.FullName);
}
AppList.AddOnScheduler(app);
}
}
}
}
}
}
WindowClosedCommand = new ReactiveCommand<EventArgs>()
.WithSubscribe(e =>this.Dispose());
DragOverCommand = new ReactiveCommand<DragEventArgs>()
.WithSubscribe(e=>
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
else
{
e.Effects = DragDropEffects.None;
e.Handled = false;
}
}).AddTo(Disposable);
// アプリの登録
DropCommand = new ReactiveCommand<DragEventArgs>()
.WithSubscribe(e=>
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
var f = files[0];
if (Path.GetExtension(f).ToUpper() != ".EXE") return;
var name = System.IO.Path.GetFileNameWithoutExtension(f);
var app = new AppInfo(name, f);
if (app.FullName != null)
{
app.Ico = GetIcon(app.FullName);
}
AppList.AddOnScheduler(app);
}).AddTo(Disposable);
// アプリの起動
MouseDoubleClickCommand = new ReactiveCommand<EventArgs>()
.WithSubscribe(e =>
{
if (AppListIndex.Value < 0) return;
var path = AppList[AppListIndex.Value].FullName;
if (path == null) return;
Debug.WriteLine(path);
Process.Start(path);
}).AddTo(Disposable);
// 要素の削除
DeleteCommand = new ReactiveCommand()
.WithSubscribe(()=>{
if (AppList.Count == 0) return;
if (AppListIndex.Value < 0) return;
AppList.RemoveAtOnScheduler(AppListIndex.Value);
}).AddTo(Disposable);
}
// 終了処理
public void Dispose()
{
// アプリ情報の保存
var options = new JsonSerializerOptions() {
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.
Create(System.Text.Unicode.UnicodeRanges.All)};
using (var fs = File.Create(IniPath))
{
using (var stream = new StreamWriter(fs, System.Text.Encoding.UTF8))
{
foreach (var app in AppList)
{
app.Ico = null;
string str = JsonSerializer.Serialize<AppInfo>(app, options);
stream.WriteLine(str);
}
}
}
Debug.WriteLine("Dispose");
Disposable.Dispose();
}
}
}
コメント