基本的に、ユーザーがアプリケーションのウィンドウのサイズを変更した場合、アプリケーションを再度開いたときにアプリケーションを同じサイズにしたいと思います。
最初は、SizeChangedイベントを処理してHeightとWidthを保存しようと思っていましたが、もっと簡単な解決策があるはずだと思います。
かなり単純な問題ですが、簡単な解決策が見つかりません。
基本的に、ユーザーがアプリケーションのウィンドウのサイズを変更した場合、アプリケーションを再度開いたときにアプリケーションを同じサイズにしたいと思います。
最初は、SizeChangedイベントを処理してHeightとWidthを保存しようと思っていましたが、もっと簡単な解決策があるはずだと思います。
かなり単純な問題ですが、簡単な解決策が見つかりません。
回答:
値をuser.configファイルに保存します。
設定ファイルに値を作成する必要があります-プロパティフォルダーにあるはずです。5つの値を作成します。
Top
タイプの double
Left
タイプの double
Height
タイプの double
Width
タイプの double
Maximized
タイプbool
-ウィンドウが最大化されているかどうかを保持します。さらに多くの情報を保存する場合は、別のタイプまたは構造が必要になります。最初の2つを0に初期化し、次の2つをアプリケーションのデフォルトサイズに初期化し、最後の1つをfalseに初期化します。
Window_OnSourceInitializedイベントハンドラーを作成し、以下を追加します。
this.Top = Properties.Settings.Default.Top;
this.Left = Properties.Settings.Default.Left;
this.Height = Properties.Settings.Default.Height;
this.Width = Properties.Settings.Default.Width;
// Very quick and dirty - but it does the job
if (Properties.Settings.Default.Maximized)
{
WindowState = WindowState.Maximized;
}
注:設定されたウィンドウの配置は、コンストラクターではなく、ウィンドウのソースで初期化されたイベントに移動する必要があります。そうでない場合、2番目のモニターでウィンドウを最大化すると、常にプライマリモニターで最大化されて再起動し、これができなくなります。アクセスします。
Window_Closingイベントハンドラーを作成し、以下を追加します。
if (WindowState == WindowState.Maximized)
{
// Use the RestoreBounds as the current values will be 0, 0 and the size of the screen
Properties.Settings.Default.Top = RestoreBounds.Top;
Properties.Settings.Default.Left = RestoreBounds.Left;
Properties.Settings.Default.Height = RestoreBounds.Height;
Properties.Settings.Default.Width = RestoreBounds.Width;
Properties.Settings.Default.Maximized = true;
}
else
{
Properties.Settings.Default.Top = this.Top;
Properties.Settings.Default.Left = this.Left;
Properties.Settings.Default.Height = this.Height;
Properties.Settings.Default.Width = this.Width;
Properties.Settings.Default.Maximized = false;
}
Properties.Settings.Default.Save();
これは、ユーザーが画面を切断するか、画面の解像度を変更することによって表示領域を小さくすると、アプリケーションが閉じているときに失敗するため、値を適用する前に、目的の場所とサイズがまだ有効であることを確認する必要があります。
実際には、そのために分離コードを使用する必要はありません(設定の保存を除く)。カスタムマークアップ拡張機能を使用して、ウィンドウのサイズと位置を次のような設定にバインドできます。
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication1"
Title="Window1"
Height="{my:SettingBinding Height}"
Width="{my:SettingBinding Width}"
Left="{my:SettingBinding Left}"
Top="{my:SettingBinding Top}">
このマークアップ拡張機能のコードは、次の場所にあります。http: //www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
{Binding Settings.Height}
など、
「独自にロール」して手動で設定をどこかに保存することもできますが、一般的には機能しますが、すべてのケースを正しく処理しないのは非常に簡単です。終了時にGetWindowPlacement()を呼び出し、起動時にSetWindowPlacement()を呼び出すことにより、OSに作業を任せる方がはるかに優れています。発生する可能性のあるすべての異常なケース(複数のモニター、最大化されているときにウィンドウが閉じられている場合はウィンドウの通常のサイズを保存するなど)を処理するので、必要はありません。
このMSDNサンプルは、WPFアプリでこれらを使用する方法を示しています。サンプルは完全ではありません(最初の実行時にウィンドウが左上隅にできるだけ小さくなり、設定デザイナーがtypeの値を保存するときに奇妙な動作が発生しますWINDOWPLACEMENT
)が、少なくとも開始できるはずです。
上記でThomasが投稿した「長い形式」のバインディングはほとんどコーディングを必要としません。名前空間バインディングがあることを確認してください:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:p="clr-namespace:WpfApplication1.Properties"
Title="Window1"
Height="{Binding Source={x:Static p:Settings.Default}, Path=Height, Mode=TwoWay}"
Width="{Binding Source={x:Static p:Settings.Default}, Path=Width, Mode=TwoWay}"
Left="{Binding Source={x:Static p:Settings.Default}, Path=Left, Mode=TwoWay}"
Top="{Binding Source={x:Static p:Settings.Default}, Path=Top, Mode=TwoWay}">
次に、分離コードを保存します。
private void frmMain_Closed(object sender, EventArgs e)
{
Properties.Settings.Default.Save();
}
WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
または、次のアプローチもお勧めです(ソースを参照)。WindowSettingsクラスをプロジェクトに追加WindowSettings.Save="True"
し、メインウィンドウのヘッダーに挿入します。
<Window x:Class="YOURPROJECT.Views.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Services="clr-namespace:YOURNAMESPACE.Services"
Services:WindowSettings.Save="True">
ここで、WindowSettingsは次のように定義されています。
using System;
using System.ComponentModel;
using System.Configuration;
using System.Windows;
namespace YOURNAMESPACE.Services
{
/// <summary>
/// Persists a Window's Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettings
{
#region Fields
/// <summary>
/// Register the "Save" attached property and the "OnSaveInvalidated" callback
/// </summary>
public static readonly DependencyProperty SaveProperty = DependencyProperty.RegisterAttached("Save", typeof (bool), typeof (WindowSettings), new FrameworkPropertyMetadata(OnSaveInvalidated));
private readonly Window mWindow;
private WindowApplicationSettings mWindowApplicationSettings;
#endregion Fields
#region Constructors
public WindowSettings(Window pWindow) { mWindow = pWindow; }
#endregion Constructors
#region Properties
[Browsable(false)] public WindowApplicationSettings Settings {
get {
if (mWindowApplicationSettings == null) mWindowApplicationSettings = CreateWindowApplicationSettingsInstance();
return mWindowApplicationSettings;
}
}
#endregion Properties
#region Methods
public static void SetSave(DependencyObject pDependencyObject, bool pEnabled) { pDependencyObject.SetValue(SaveProperty, pEnabled); }
protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance() { return new WindowApplicationSettings(this); }
/// <summary>
/// Load the Window Size Location and State from the settings object
/// </summary>
protected virtual void LoadWindowState() {
Settings.Reload();
if (Settings.Location != Rect.Empty) {
mWindow.Left = Settings.Location.Left;
mWindow.Top = Settings.Location.Top;
mWindow.Width = Settings.Location.Width;
mWindow.Height = Settings.Location.Height;
}
if (Settings.WindowState != WindowState.Maximized) mWindow.WindowState = Settings.WindowState;
}
/// <summary>
/// Save the Window Size, Location and State to the settings object
/// </summary>
protected virtual void SaveWindowState() {
Settings.WindowState = mWindow.WindowState;
Settings.Location = mWindow.RestoreBounds;
Settings.Save();
}
/// <summary>
/// Called when Save is changed on an object.
/// </summary>
private static void OnSaveInvalidated(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs pDependencyPropertyChangedEventArgs) {
var window = pDependencyObject as Window;
if (window != null)
if ((bool) pDependencyPropertyChangedEventArgs.NewValue) {
var settings = new WindowSettings(window);
settings.Attach();
}
}
private void Attach() {
if (mWindow != null) {
mWindow.Closing += WindowClosing;
mWindow.Initialized += WindowInitialized;
mWindow.Loaded += WindowLoaded;
}
}
private void WindowClosing(object pSender, CancelEventArgs pCancelEventArgs) { SaveWindowState(); }
private void WindowInitialized(object pSender, EventArgs pEventArgs) { LoadWindowState(); }
private void WindowLoaded(object pSender, RoutedEventArgs pRoutedEventArgs) { if (Settings.WindowState == WindowState.Maximized) mWindow.WindowState = Settings.WindowState; }
#endregion Methods
#region Nested Types
public class WindowApplicationSettings : ApplicationSettingsBase
{
#region Constructors
public WindowApplicationSettings(WindowSettings pWindowSettings) { }
#endregion Constructors
#region Properties
[UserScopedSetting] public Rect Location {
get {
if (this["Location"] != null) return ((Rect) this["Location"]);
return Rect.Empty;
}
set { this["Location"] = value; }
}
[UserScopedSetting] public WindowState WindowState {
get {
if (this["WindowState"] != null) return (WindowState) this["WindowState"];
return WindowState.Normal;
}
set { this["WindowState"] = value; }
}
#endregion Properties
}
#endregion Nested Types
}
}
それを解決するデフォルトの方法は、設定ファイルを使用することです。設定ファイルの問題は、すべての設定を定義し、データを自分でやり取りするコードを記述する必要があることです。追跡するプロパティがたくさんある場合、かなり退屈です。
このために、非常に柔軟で非常に使いやすいライブラリを作成しました。追跡するオブジェクトのどのプロパティを追跡するだけで、あとは残ります。必要に応じて、がらくたの構成もできます。
ライブラリはJot(github)と呼ばれ、これについて私が書いた古いCodeProjectの記事です。
これを使用して、ウィンドウのサイズと位置を追跡します。
public MainWindow()
{
InitializeComponent();
_stateTracker.Configure(this)
.IdentifyAs("MyMainWindow")
.AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
.RegisterPersistTrigger(nameof(Closed))
.Apply();
}
Jotと設定ファイル: Jotを使用すると、コードが大幅に少なくなり、各プロパティについて一度だけ言及する必要があるため、エラーが発生しにくくなります。設定ファイルでは、各プロパティを5回記述する必要があります。1 回はプロパティを明示的に作成し、もう1回はコードで値を前後にコピーします。
ストレージ、シリアル化などは完全に構成可能です。また、IOCを使用する場合は、IOCをフックして、解決するすべてのオブジェクトに追跡を自動的に適用することもできます。これにより、プロパティを永続化するために必要なことは、その[Trackable]属性をたたくだけです。
ライブラリは一流であり、それについて口に出したいと思うので、私はこれらすべてを書いています。
これを行う簡単なクラスを書きました。これは、次のように呼び出されます。
public MainWindow()
{
FormSizeSaver.RegisterForm(this, () => Settings.Default.MainWindowSettings,
s =>
{
Settings.Default.MainWindowSettings = s;
Settings.Default.Save();
});
InitializeComponent();
...
そしてここにコードがあります:
public class FormSizeSaver
{
private readonly Window window;
private readonly Func<FormSizeSaverSettings> getSetting;
private readonly Action<FormSizeSaverSettings> saveSetting;
private FormSizeSaver(Window window, Func<string> getSetting, Action<string> saveSetting)
{
this.window = window;
this.getSetting = () => FormSizeSaverSettings.FromString(getSetting());
this.saveSetting = s => saveSetting(s.ToString());
window.Initialized += InitializedHandler;
window.StateChanged += StateChangedHandler;
window.SizeChanged += SizeChangedHandler;
window.LocationChanged += LocationChangedHandler;
}
public static FormSizeSaver RegisterForm(Window window, Func<string> getSetting, Action<string> saveSetting)
{
return new FormSizeSaver(window, getSetting, saveSetting);
}
private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
{
var s = getSetting();
s.Height = e.NewSize.Height;
s.Width = e.NewSize.Width;
saveSetting(s);
}
private void StateChangedHandler(object sender, EventArgs e)
{
var s = getSetting();
if (window.WindowState == WindowState.Maximized)
{
if (!s.Maximized)
{
s.Maximized = true;
saveSetting(s);
}
}
else if (window.WindowState == WindowState.Normal)
{
if (s.Maximized)
{
s.Maximized = false;
saveSetting(s);
}
}
}
private void InitializedHandler(object sender, EventArgs e)
{
var s = getSetting();
window.WindowState = s.Maximized ? WindowState.Maximized : WindowState.Normal;
if (s.Height != 0 && s.Width != 0)
{
window.Height = s.Height;
window.Width = s.Width;
window.WindowStartupLocation = WindowStartupLocation.Manual;
window.Left = s.XLoc;
window.Top = s.YLoc;
}
}
private void LocationChangedHandler(object sender, EventArgs e)
{
var s = getSetting();
s.XLoc = window.Left;
s.YLoc = window.Top;
saveSetting(s);
}
}
[Serializable]
internal class FormSizeSaverSettings
{
public double Height, Width, YLoc, XLoc;
public bool Maximized;
public override string ToString()
{
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, this);
ms.Position = 0;
byte[] buffer = new byte[(int)ms.Length];
ms.Read(buffer, 0, buffer.Length);
return Convert.ToBase64String(buffer);
}
}
internal static FormSizeSaverSettings FromString(string value)
{
try
{
using (var ms = new MemoryStream(Convert.FromBase64String(value)))
{
var bf = new BinaryFormatter();
return (FormSizeSaverSettings) bf.Deserialize(ms);
}
}
catch (Exception)
{
return new FormSizeSaverSettings();
}
}
}
ありますNuGetプロジェクトRestoreWindowPlaceが 上に表示githubの XMLファイル内の情報を保存し、あなたのためのすべてのこれを行います。
ウィンドウで機能させるには、次のように呼び出すだけです。
((App)Application.Current).WindowPlace.Register(this);
アプリでは、ウィンドウを管理するクラスを作成します。詳細については、上記のgithubリンクを参照してください。
あなたはこれが好きかもしれません:
public class WindowStateHelper
{
public static string ToXml(System.Windows.Window win)
{
XElement bounds = new XElement("Bounds");
if (win.WindowState == System.Windows.WindowState.Maximized)
{
bounds.Add(new XElement("Top", win.RestoreBounds.Top));
bounds.Add(new XElement("Left", win.RestoreBounds.Left));
bounds.Add(new XElement("Height", win.RestoreBounds.Height));
bounds.Add(new XElement("Width", win.RestoreBounds.Width));
}
else
{
bounds.Add(new XElement("Top", win.Top));
bounds.Add(new XElement("Left", win.Left));
bounds.Add(new XElement("Height", win.Height));
bounds.Add(new XElement("Width", win.Width));
}
XElement root = new XElement("WindowState",
new XElement("State", win.WindowState.ToString()),
new XElement("Visibility", win.Visibility.ToString()),
bounds);
return root.ToString();
}
public static void FromXml(string xml, System.Windows.Window win)
{
try
{
XElement root = XElement.Parse(xml);
string state = root.Descendants("State").FirstOrDefault().Value;
win.WindowState = (System.Windows.WindowState)Enum.Parse(typeof(System.Windows.WindowState), state);
state = root.Descendants("Visibility").FirstOrDefault().Value;
win.Visibility = (System.Windows.Visibility)Enum.Parse(typeof(System.Windows.Visibility), state);
XElement bounds = root.Descendants("Bounds").FirstOrDefault();
win.Top = Convert.ToDouble(bounds.Element("Top").Value);
win.Left = Convert.ToDouble(bounds.Element("Left").Value);
win.Height = Convert.ToDouble(bounds.Element("Height").Value);
win.Width = Convert.ToDouble(bounds.Element("Width").Value);
}
catch (Exception x)
{
System.Console.WriteLine(x.ToString());
}
}
}
アプリが閉じたとき:
Properties.Settings.Default.Win1Placement = WindowStateHelper.ToXml(win1);
Properties.Settings.Default.Win2Placement = WindowStateHelper.ToXml(win2);
...
アプリが起動すると:
WindowStateHelper.FromXml(Properties.Settings.Default.Win1Placement, win1);
WindowStateHelper.FromXml(Properties.Settings.Default.Win2Placement, win2);
...
デフォルト設定でWindowXmlという名前の文字列を作成します。
Window LoadedおよびClosingイベントでこの拡張メソッドを使用して、Windowのサイズと場所を復元および保存します。
using YourProject.Properties;
using System;
using System.Linq;
using System.Windows;
using System.Xml.Linq;
namespace YourProject.Extensions
{
public static class WindowExtensions
{
public static void SaveSizeAndLocation(this Window w)
{
try
{
var s = "<W>";
s += GetNode("Top", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Top : w.Top);
s += GetNode("Left", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Left : w.Left);
s += GetNode("Height", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Height : w.Height);
s += GetNode("Width", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Width : w.Width);
s += GetNode("WindowState", w.WindowState);
s += "</W>";
Settings.Default.WindowXml = s;
Settings.Default.Save();
}
catch (Exception)
{
}
}
public static void RestoreSizeAndLocation(this Window w)
{
try
{
var xd = XDocument.Parse(Settings.Default.WindowXml);
w.WindowState = (WindowState)Enum.Parse(typeof(WindowState), xd.Descendants("WindowState").FirstOrDefault().Value);
w.Top = Convert.ToDouble(xd.Descendants("Top").FirstOrDefault().Value);
w.Left = Convert.ToDouble(xd.Descendants("Left").FirstOrDefault().Value);
w.Height = Convert.ToDouble(xd.Descendants("Height").FirstOrDefault().Value);
w.Width = Convert.ToDouble(xd.Descendants("Width").FirstOrDefault().Value);
}
catch (Exception)
{
}
}
private static string GetNode(string name, object value)
{
return string.Format("<{0}>{1}</{0}>", name, value);
}
}
}
私はランスクリーブランドからの回答を使用して、設定をバインドします。しかし、ウィンドウが画面の外に出ないようにするために、もう少しコードを使用しています。
private void SetWindowSettingsIntoScreenArea()
{
// first detect Screen, where we will display the Window
// second correct bottom and right position
// then the top and left position.
// If Size is bigger than current Screen, it's still possible to move and size the Window
// get the screen to display the window
var screen = System.Windows.Forms.Screen.FromPoint(new System.Drawing.Point((int)Default.Left, (int)Default.Top));
// is bottom position out of screen for more than 1/3 Height of Window?
if (Default.Top + (Default.Height / 3) > screen.WorkingArea.Height)
Default.Top = screen.WorkingArea.Height - Default.Height;
// is right position out of screen for more than 1/2 Width of Window?
if (Default.Left + (Default.Width / 2) > screen.WorkingArea.Width)
Default.Left = screen.WorkingArea.Width - Default.Width;
// is top position out of screen?
if (Default.Top < screen.WorkingArea.Top)
Default.Top = screen.WorkingArea.Top;
// is left position out of screen?
if (Default.Left < screen.WorkingArea.Left)
Default.Left = screen.WorkingArea.Left;
}
RandomEngysの素晴らしい答えに基づいて、より一般的なソリューションを作成しました。実行中のフォルダーにファイルへの位置を保存し、作成する新しいウィンドウごとに新しいプロパティを作成する必要はありません。このソリューションは、コードビハインドの最小限のコードで私にとってはうまく機能します。
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Xml;
using System.Xml.Serialization;
namespace WindowPlacementNameSpace
{
// RECT structure required by WINDOWPLACEMENT structure
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left, int top, int right, int bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
}
// POINT structure required by WINDOWPLACEMENT structure
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
// WINDOWPLACEMENT stores the position, size, and state of a window
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public POINT minPosition;
public POINT maxPosition;
public RECT normalPosition;
}
public static class WindowPlacement
{
private static readonly Encoding Encoding = new UTF8Encoding();
private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(WINDOWPLACEMENT));
[DllImport("user32.dll")]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private static void SetPlacement(IntPtr windowHandle, string placementXml)
{
if (string.IsNullOrEmpty(placementXml))
{
return;
}
byte[] xmlBytes = Encoding.GetBytes(placementXml);
try
{
WINDOWPLACEMENT placement;
using (MemoryStream memoryStream = new MemoryStream(xmlBytes))
{
placement = (WINDOWPLACEMENT)Serializer.Deserialize(memoryStream);
}
placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
placement.flags = 0;
placement.showCmd = (placement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : placement.showCmd);
SetWindowPlacement(windowHandle, ref placement);
}
catch (InvalidOperationException)
{
// Parsing placement XML failed. Fail silently.
}
}
private static string GetPlacement(IntPtr windowHandle)
{
WINDOWPLACEMENT placement;
GetWindowPlacement(windowHandle, out placement);
using (MemoryStream memoryStream = new MemoryStream())
{
using (XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8))
{
Serializer.Serialize(xmlTextWriter, placement);
byte[] xmlBytes = memoryStream.ToArray();
return Encoding.GetString(xmlBytes);
}
}
}
public static void ApplyPlacement(this Window window)
{
var className = window.GetType().Name;
try
{
var pos = File.ReadAllText(Directory + "\\" + className + ".pos");
SetPlacement(new WindowInteropHelper(window).Handle, pos);
}
catch (Exception exception)
{
Log.Error("Couldn't read position for " + className, exception);
}
}
public static void SavePlacement(this Window window)
{
var className = window.GetType().Name;
var pos = GetPlacement(new WindowInteropHelper(window).Handle);
try
{
File.WriteAllText(Directory + "\\" + className + ".pos", pos);
}
catch (Exception exception)
{
Log.Error("Couldn't write position for " + className, exception);
}
}
private static string Directory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
}
コードビハインドでこれら2つのメソッドを追加します
///This method is save the actual position of the window to file "WindowName.pos"
private void ClosingTrigger(object sender, EventArgs e)
{
this.SavePlacement();
}
///This method is load the actual position of the window from the file
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
this.ApplyPlacement();
}
XAMLウィンドウでこれを追加します
Closing="ClosingTrigger"