Skip to content

Instantly share code, notes, and snippets.

@occar421
Last active August 29, 2015 14:01
Show Gist options
  • Save occar421/f3a02b4ba882671bd328 to your computer and use it in GitHub Desktop.
Save occar421/f3a02b4ba882671bd328 to your computer and use it in GitHub Desktop.
WPF with console remote

このGistについて

このGistはコンソールでのコマンドのみでWPFを変化させるものです。
自動化スクリプトなど、人がGUIに触れずに動くことを想定しています。
メモリリークや例外、ファイル分けなどは、あまり考慮していません。
極力MVVMに沿うように心掛けたので、ちょっと回りくどい実装です。

ファイル構成

  • Models
  • Collections.cs
    データの実態二つ
    CommandHandlerクラスに、データの処理方法を登録する(Event追加)。

  • CommandHander.cs
    コンソール画面からの指示を受け取ってコマンドを実行させるクラス
    Taskを使用して非同期処理をするので、GUIはフリーズしない。

  • Commands.cs
    各種コマンドのクラス群

  • ViewModels
  • CollectionsViewModel.cs
    データの実態のVM
    変更通知の仲介と、それぞれの子要素をListViewに表示するためのVMにするだけの処理をする。

  • ItemViewModel.cs
    ListViewの子要素のVM

  • Views
  • MainWindow.xaml
  • MainWindow.cs
    見た目
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Livet;
namespace WPFInteractive.Models
{
public abstract class CollectionBase : NotificationObject, IDisposable
{
protected CommandHandler Handler { get; set; }
public IEnumerable<string> Value { get; set; }
public CollectionBase()
{
this.Handler = new CommandHandler();
}
public void Dispose()
{
Handler.Dispose();
}
}
public class QueueData : CollectionBase
{
Queue<string> value = new Queue<string>();
public QueueData()
{
Value = value;
Handler.Add<string>("add", (newOne) =>
{
value.Enqueue(newOne);
RaisePropertyChanged();
});
Handler.Add<Action<string>>("delete", (newOne) =>
{
newOne(value.Dequeue());
RaisePropertyChanged();
});
Handler.Add<Action<string>>("clear", (newOne) =>
{
value.Clear();
newOne("Queue Cleared");
RaisePropertyChanged();
});
Handler.Start();
}
}
public class StackData : CollectionBase
{
Stack<string> value = new Stack<string>();
public StackData()
{
Value = value;
Handler.Add<string>("add", (newOne) =>
{
value.Push(newOne);
RaisePropertyChanged();
});
Handler.Add<Action<string>>("delete", (newOne) =>
{
newOne(value.Pop());
RaisePropertyChanged();
});
Handler.Add<Action<string>>("clear", (newOne) =>
{
value.Clear();
newOne("Stack Cleared");
RaisePropertyChanged();
});
Handler.Start();
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WPFInteractive.Models
{
public class CommandHandler : IDisposable
{
static KeyedCollection<string, CommandBase> commands = new CommandCollection
{
new AddCommand(),
new DeleteCommand(),
new ClearCommand()
};
static bool isStarted = false;
static bool isDisposed = false;
static int subscriberCount = 0;
public void Add<T>(string key, Action<T> action)
{
key = key.ToLowerInvariant();
if (commands.Contains(key))
{
if (typeof(CommandEventArg<T>).IsAssignableFrom(commands[key].GetType()))
{
(commands[key] as CommandEventArg<T>).OnExecuting += (sender, e) => { action(e); };
}
else
{
Console.WriteLine("error: @\"" + key + "\", Type is different.");
}
}
else
{
Console.WriteLine("error: CommandName \"" + key + "\" is undefined.");
}
}
public void Start()
{
subscriberCount++;
if (!isStarted)
{
StartStatic();
}
}
private static void StartStatic()
{
isStarted = true;
Task.Run(() =>
{
while (!isDisposed)
{
var line = Console.ReadLine().Split(' ');
var key = line[0].ToLowerInvariant();
if (commands.Contains(key))
{
commands[key].Execute(line);
}
else
{
Console.WriteLine("error: Command Not Found");
}
Thread.Sleep(100);
}
});
}
public void Dispose()
{
if (subscriberCount == 0)
{
isDisposed = true;
}
else
{
subscriberCount--;
}
}
}
class CommandCollection : KeyedCollection<string, CommandBase>
{
protected override string GetKeyForItem(CommandBase item)
{
return item.Name.ToLowerInvariant();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Livet;
namespace WPFInteractive.Models
{
interface CommandEventArg<T>
{
event EventHandler<T> OnExecuting;
}
abstract public class CommandBase
{
abstract public void Execute(IEnumerable<string> arg);
abstract public string Name { get; }
}
class AddCommand : CommandBase, CommandEventArg<string>
{
public event EventHandler<string> OnExecuting;
public override string Name
{
get
{
return "add";
}
}
public override void Execute(IEnumerable<string> arg)
{
if (arg.Skip(1).Count() == 1)
{
OnExecuting(this, arg.Skip(1).First());
}
else
{
Console.WriteLine("Usage: add hoge");
}
}
}
class DeleteCommand : CommandBase, CommandEventArg<Action<string>>
{
public event EventHandler<Action<string>> OnExecuting;
public override string Name
{
get { return "delete"; }
}
public override void Execute(IEnumerable<string> arg)
{
if (!arg.Skip(1).Any())
{
OnExecuting(this, Console.WriteLine);
}
else
{
Console.WriteLine("Usage: delete");
}
}
}
class ClearCommand : CommandBase, CommandEventArg<Action<string>>
{
public event EventHandler<Action<string>> OnExecuting;
public override string Name
{
get { return "clear"; }
}
public override void Execute(IEnumerable<string> arg)
{
if (!arg.Skip(1).Any())
{
OnExecuting(this, Console.WriteLine);
}
else
{
Console.WriteLine("Usage: clear");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using WPFInteractive.Models;
using Livet;
namespace WPFInteractive.ViewModels
{
abstract class CollectionViewModelBase : ViewModel
{
abstract protected string PropertyName { get; }
protected CollectionBase Model { get; private set; }
public CollectionViewModelBase(CollectionBase model)
{
this.Model = model;
this.Model.PropertyChanged += (sender, e) =>
{
RaisePropertyChanged(PropertyName);
};
}
protected override void Dispose(bool disposing)
{
Model.Dispose();
base.Dispose(disposing);
}
}
class QueueDataViewModel : CollectionViewModelBase
{
protected override string PropertyName { get { return "Queue"; } }//nameof(Queue)
public IEnumerable<ItemViewModel> Queue
{
get
{
return Model.Value.Select(x => new ItemViewModel(x));
}
}
public QueueDataViewModel()
: base(new QueueData())
{
}
}
class StackDataViewModel : CollectionViewModelBase
{
protected override string PropertyName { get { return "Stack"; } }//nameof(Stack)
public IEnumerable<ItemViewModel> Stack
{
get
{
return Model.Value.Select(x => new ItemViewModel(x));
}
}
public StackDataViewModel()
: base(new StackData())
{
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Livet;
using Livet.Commands;
using Livet.Messaging;
using Livet.Messaging.IO;
using Livet.EventListeners;
using Livet.Messaging.Windows;
using WPFInteractive.Models;
namespace WPFInteractive.ViewModels
{
public class ItemViewModel : ViewModel
{
public ItemViewModel(string val)
{
Value = val;
}
public string Value { get; private set; }
}
}
<Window x:Class="WPFInteractive.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:v="clr-namespace:WPFInteractive.Views"
xmlns:vm="clr-namespace:WPFInteractive.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView HorizontalAlignment="Left" Height="267" Margin="10,43,0,0" VerticalAlignment="Top" Width="240" ItemsSource="{Binding Path=Queue}">
<ListView.DataContext>
<vm:QueueDataViewModel />
</ListView.DataContext>
<ListView.ItemTemplate>
<ItemContainerTemplate>
<TextBlock Text="{Binding Value}" />
</ItemContainerTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView HorizontalAlignment="Left" Height="267" Margin="267,43,0,0" VerticalAlignment="Top" Width="240" ItemsSource="{Binding Path=Stack}">
<ListView.DataContext>
<vm:StackDataViewModel />
</ListView.DataContext>
<ListView.ItemTemplate>
<ItemContainerTemplate>
<TextBlock Text="{Binding Value}" />
</ItemContainerTemplate>
</ListView.ItemTemplate>
</ListView>
<Label Content="Queue" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<Label Content="Stack" HorizontalAlignment="Left" Margin="267,10,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
namespace WPFInteractive.Views
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment