669 lines
25 KiB
C#
669 lines
25 KiB
C#
using SharpDX.DirectInput;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Configuration;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using Telerik.WinControls;
|
|
using Telerik.WinControls.UI;
|
|
using Timer = System.Timers.Timer;
|
|
|
|
namespace PJ64Savestater
|
|
{
|
|
public partial class MainForm : Telerik.WinControls.UI.RadForm
|
|
{
|
|
private const string FILTER_INPUTPROFILE = "Json files (*.json)|*.json";
|
|
private const string PROFILE_CODE = "Project64 Savestater";
|
|
private const string DEFAULT_PROFILE_FILENAME = "Profile.json";
|
|
|
|
private Timer myTimer;
|
|
private DirectInput DInput = new DirectInput();
|
|
private DeviceInstance[] allDevices = Array.Empty<DeviceInstance>();
|
|
private Joystick curPad = null;
|
|
private RadTextBox focuesTextBox = null;
|
|
private InputProfile curProfile = null;
|
|
private bool enableActionExecution = false;
|
|
private int toleranceOffset = 1000;
|
|
|
|
public MainForm()
|
|
{
|
|
// Read configs
|
|
var reader = new AppSettingsReader();
|
|
toleranceOffset = (int)reader.GetValue("toleranceOffset", typeof(int));
|
|
|
|
// Init components
|
|
InitializeComponent();
|
|
|
|
// Init Timer
|
|
myTimer = new Timer(1)
|
|
{
|
|
SynchronizingObject = this,
|
|
AutoReset = true,
|
|
Enabled = true
|
|
};
|
|
myTimer.Elapsed += MyTimer_Elapsed;
|
|
}
|
|
|
|
private void LoadPads()
|
|
{
|
|
allDevices = DInput.GetDevices(DeviceClass.GameControl, DeviceEnumerationFlags.AttachedOnly).ToArray();
|
|
radDropDownList_Pads.Items.Clear();
|
|
foreach (DeviceInstance d in allDevices)
|
|
radDropDownList_Pads.Items.Add(d.InstanceName);
|
|
}
|
|
|
|
private void SetCurrentPad(Guid guid)
|
|
{
|
|
curPad = new Joystick(DInput, guid);
|
|
foreach (DeviceObjectInstance doi in curPad.GetObjects(DeviceObjectTypeFlags.Axis))
|
|
curPad.GetObjectPropertiesById(doi.ObjectId).Range = new InputRange(-5000, 5000);
|
|
curPad.Properties.AxisMode = DeviceAxisMode.Absolute;
|
|
curPad.SetCooperativeLevel(Handle, CooperativeLevel.NonExclusive | CooperativeLevel.Background);
|
|
curPad.Acquire();
|
|
if (curProfile is object)
|
|
curProfile.ControllerInstanceGuid = guid;
|
|
}
|
|
|
|
private static string GetDefaultProfileFilePath()
|
|
{
|
|
var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.ProductName);
|
|
Directory.CreateDirectory(dir);
|
|
return Path.Combine(dir, DEFAULT_PROFILE_FILENAME);
|
|
}
|
|
|
|
private void CheckForInput()
|
|
{
|
|
if (curPad is object)
|
|
{
|
|
var inputCodes = new List<string>();
|
|
var state = new JoystickState();
|
|
bool success;
|
|
|
|
try
|
|
{
|
|
curPad.Poll();
|
|
curPad.GetCurrentState(ref state);
|
|
success = true;
|
|
}
|
|
catch (SharpDX.SharpDXException)
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
|
|
for (int i = 0, loopTo = state.Buttons.Length - 1; i <= loopTo; i++)
|
|
{
|
|
if (state.Buttons[i])
|
|
inputCodes.Add("Button " + i);
|
|
}
|
|
|
|
for (int i = 0, loopTo1 = state.PointOfViewControllers.Length - 1; i <= loopTo1; i++)
|
|
{
|
|
int val = state.PointOfViewControllers[i];
|
|
if (val > -1)
|
|
inputCodes.Add("Point " + i + " " + val);
|
|
}
|
|
|
|
for (int i = 0, loopTo2 = state.Sliders.Length - 1; i <= loopTo2; i++)
|
|
{
|
|
int val = state.Sliders[i];
|
|
if (val > toleranceOffset)
|
|
inputCodes.Add("Slider " + i + " +");
|
|
if (val < -toleranceOffset)
|
|
inputCodes.Add("Slider " + i + " -");
|
|
}
|
|
|
|
for (int i = 0, loopTo3 = state.AccelerationSliders.Length - 1; i <= loopTo3; i++)
|
|
{
|
|
int val = state.AccelerationSliders[i];
|
|
if (val > toleranceOffset)
|
|
inputCodes.Add("ASlider " + i + " +");
|
|
if (val < -toleranceOffset)
|
|
inputCodes.Add("ASlider " + i + " -");
|
|
}
|
|
|
|
for (int i = 0, loopTo4 = state.ForceSliders.Length - 1; i <= loopTo4; i++)
|
|
{
|
|
int val = state.ForceSliders[i];
|
|
if (val > toleranceOffset)
|
|
inputCodes.Add("FSlider " + i + " +");
|
|
if (val < -toleranceOffset)
|
|
inputCodes.Add("FSlider " + i + " -");
|
|
}
|
|
|
|
for (int i = 0, loopTo5 = state.VelocitySliders.Length - 1; i <= loopTo5; i++)
|
|
{
|
|
int val = state.VelocitySliders[i];
|
|
if (val > toleranceOffset)
|
|
inputCodes.Add("VSlider " + i + " +");
|
|
if (val < -toleranceOffset)
|
|
inputCodes.Add("VSlider " + i + " -");
|
|
}
|
|
|
|
if (state.X > toleranceOffset)
|
|
inputCodes.Add("X +");
|
|
if (state.X < -toleranceOffset)
|
|
inputCodes.Add("X -");
|
|
|
|
if (state.Y > toleranceOffset)
|
|
inputCodes.Add("Y +");
|
|
if (state.Y < -toleranceOffset)
|
|
inputCodes.Add("Y -");
|
|
|
|
if (state.Z > toleranceOffset)
|
|
inputCodes.Add("Z +");
|
|
if (state.Z < -toleranceOffset)
|
|
inputCodes.Add("Z -");
|
|
|
|
if (state.AccelerationX > toleranceOffset)
|
|
inputCodes.Add("AX +");
|
|
if (state.AccelerationX < -toleranceOffset)
|
|
inputCodes.Add("AX -");
|
|
|
|
if (state.AccelerationY > toleranceOffset)
|
|
inputCodes.Add("AY +");
|
|
if (state.AccelerationY < -toleranceOffset)
|
|
inputCodes.Add("AY -");
|
|
|
|
if (state.AccelerationZ > toleranceOffset)
|
|
inputCodes.Add("AZ +");
|
|
if (state.AccelerationZ < -toleranceOffset)
|
|
inputCodes.Add("AZ -");
|
|
|
|
if (state.AngularAccelerationX > toleranceOffset)
|
|
inputCodes.Add("AAX +");
|
|
if (state.AngularAccelerationX < -toleranceOffset)
|
|
inputCodes.Add("AAX -");
|
|
|
|
if (state.AngularAccelerationY > toleranceOffset)
|
|
inputCodes.Add("AAY +");
|
|
if (state.AngularAccelerationY < -toleranceOffset)
|
|
inputCodes.Add("AAY -");
|
|
|
|
if (state.AngularAccelerationZ > toleranceOffset)
|
|
inputCodes.Add("AAZ +");
|
|
if (state.AngularAccelerationZ < -toleranceOffset)
|
|
inputCodes.Add("AAZ -");
|
|
|
|
if (state.ForceX > toleranceOffset)
|
|
inputCodes.Add("FX +");
|
|
if (state.ForceX < -toleranceOffset)
|
|
inputCodes.Add("FX -");
|
|
|
|
if (state.ForceY > toleranceOffset)
|
|
inputCodes.Add("FY +");
|
|
if (state.ForceY < -toleranceOffset)
|
|
inputCodes.Add("FY -");
|
|
|
|
if (state.ForceZ > toleranceOffset)
|
|
inputCodes.Add("FZ +");
|
|
if (state.ForceZ < -toleranceOffset)
|
|
inputCodes.Add("FZ -");
|
|
|
|
if (state.RotationX > toleranceOffset)
|
|
inputCodes.Add("RX +");
|
|
if (state.RotationX < -toleranceOffset)
|
|
inputCodes.Add("RX -");
|
|
|
|
if (state.RotationY > toleranceOffset)
|
|
inputCodes.Add("RY +");
|
|
if (state.RotationY < -toleranceOffset)
|
|
inputCodes.Add("RY -");
|
|
|
|
if (state.RotationZ > toleranceOffset)
|
|
inputCodes.Add("RZ +");
|
|
if (state.RotationZ < -toleranceOffset)
|
|
inputCodes.Add("RZ -");
|
|
|
|
if (state.TorqueX > toleranceOffset)
|
|
inputCodes.Add("TX +");
|
|
if (state.TorqueX < -toleranceOffset)
|
|
inputCodes.Add("TX -");
|
|
|
|
if (state.TorqueY > toleranceOffset)
|
|
inputCodes.Add("TY +");
|
|
if (state.TorqueY < -toleranceOffset)
|
|
inputCodes.Add("TY -");
|
|
|
|
if (state.TorqueZ > toleranceOffset)
|
|
inputCodes.Add("TZ +");
|
|
if (state.TorqueZ < -toleranceOffset)
|
|
inputCodes.Add("TZ -");
|
|
|
|
if (state.VelocityX > toleranceOffset)
|
|
inputCodes.Add("VX +");
|
|
if (state.VelocityX < -toleranceOffset)
|
|
inputCodes.Add("VX -");
|
|
|
|
if (state.VelocityY > toleranceOffset)
|
|
inputCodes.Add("VY +");
|
|
if (state.VelocityY < -toleranceOffset)
|
|
inputCodes.Add("VY -");
|
|
|
|
if (state.VelocityZ > toleranceOffset)
|
|
inputCodes.Add("VZ +");
|
|
if (state.VelocityZ < -toleranceOffset)
|
|
inputCodes.Add("VZ -");
|
|
|
|
if (inputCodes.Any())
|
|
{
|
|
if (focuesTextBox is object)
|
|
focuesTextBox.Text = inputCodes.Last();
|
|
else if (enableActionExecution)
|
|
CheckForActions(inputCodes.ToArray());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckForActions(string[] inputCodes)
|
|
{
|
|
foreach (var inputCode in inputCodes)
|
|
{
|
|
var inputControl = GetInputControlFromString(inputCode);
|
|
foreach (var ctrl in curProfile.Controls)
|
|
{
|
|
if (ctrl.Value == inputControl)
|
|
ExecuteAction(ctrl.Key);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ExecuteAction(string actionCode)
|
|
{
|
|
if (actionCode == ActionCodes.CreateSavestate)
|
|
SendKeys.Send("{F5}");
|
|
else if (actionCode == ActionCodes.LoadSavestate)
|
|
SendKeys.Send("{F7}");
|
|
}
|
|
|
|
private void SetAction(string actionCode, string inputCode)
|
|
{
|
|
if (curProfile is object)
|
|
curProfile[actionCode] = GetInputControlFromString(inputCode);
|
|
}
|
|
|
|
public static InputControl GetInputControlFromString(string str)
|
|
{
|
|
var input = new InputControl();
|
|
var p = str.Split(' ');
|
|
var switchExpr = p[0];
|
|
switch (switchExpr)
|
|
{
|
|
case "Button":
|
|
input.InputKey = InputKeys.Buttons;
|
|
input.KeyIndex = Convert.ToInt32(p[1]);
|
|
break;
|
|
case "Point":
|
|
input.InputKey = InputKeys.PointOfViewControllers;
|
|
input.KeyIndex = Convert.ToInt32(p[1]);
|
|
input.Value = p[2];
|
|
break;
|
|
case "Slider":
|
|
input.InputKey = InputKeys.Sliders;
|
|
input.KeyIndex = Convert.ToInt32(p[1]);
|
|
break;
|
|
case "ASlider":
|
|
input.InputKey = InputKeys.AccelerationSliders;
|
|
input.KeyIndex = Convert.ToInt32(p[1]);
|
|
break;
|
|
case "FSlider":
|
|
input.InputKey = InputKeys.ForceSliders;
|
|
input.KeyIndex = Convert.ToInt32(p[1]);
|
|
break;
|
|
case "VSlider":
|
|
input.InputKey = InputKeys.VelocitySliders;
|
|
input.KeyIndex = Convert.ToInt32(p[1]);
|
|
break;
|
|
case "X":
|
|
input.InputKey = InputKeys.X;
|
|
input.Value = p[1];
|
|
break;
|
|
case "Y":
|
|
input.InputKey = InputKeys.Y;
|
|
input.Value = p[1];
|
|
break;
|
|
case "Z":
|
|
input.InputKey = InputKeys.Z;
|
|
input.Value = p[1];
|
|
break;
|
|
case "AX":
|
|
input.InputKey = InputKeys.AccelerationX;
|
|
input.Value = p[1];
|
|
break;
|
|
case "AY":
|
|
input.InputKey = InputKeys.AccelerationY;
|
|
input.Value = p[1];
|
|
break;
|
|
case "AZ":
|
|
input.InputKey = InputKeys.AccelerationZ;
|
|
input.Value = p[1];
|
|
break;
|
|
case "AAX":
|
|
input.InputKey = InputKeys.AngularAccelerationX;
|
|
input.Value = p[1];
|
|
break;
|
|
case "AAY":
|
|
input.InputKey = InputKeys.AngularAccelerationY;
|
|
input.Value = p[1];
|
|
break;
|
|
case "AAZ":
|
|
input.InputKey = InputKeys.AngularAccelerationZ;
|
|
input.Value = p[1];
|
|
break;
|
|
case "FX":
|
|
input.InputKey = InputKeys.ForceX;
|
|
input.Value = p[1];
|
|
break;
|
|
case "FY":
|
|
input.InputKey = InputKeys.ForceY;
|
|
input.Value = p[1];
|
|
break;
|
|
case "FZ":
|
|
input.InputKey = InputKeys.ForceZ;
|
|
input.Value = p[1];
|
|
break;
|
|
case "RX":
|
|
input.InputKey = InputKeys.RotationX;
|
|
input.Value = p[1];
|
|
break;
|
|
case "RY":
|
|
input.InputKey = InputKeys.RotationY;
|
|
input.Value = p[1];
|
|
break;
|
|
case "RZ":
|
|
input.InputKey = InputKeys.RotationZ;
|
|
input.Value = p[1];
|
|
break;
|
|
case "TX":
|
|
input.InputKey = InputKeys.TorqueX;
|
|
input.Value = p[1];
|
|
break;
|
|
case "TY":
|
|
input.InputKey = InputKeys.TorqueY;
|
|
input.Value = p[1];
|
|
break;
|
|
case "TZ":
|
|
input.InputKey = InputKeys.TorqueZ;
|
|
input.Value = p[1];
|
|
break;
|
|
case "VX":
|
|
input.InputKey = InputKeys.VelocityX;
|
|
input.Value = p[1];
|
|
break;
|
|
case "VY":
|
|
input.InputKey = InputKeys.VelocityY;
|
|
input.Value = p[1];
|
|
break;
|
|
case "VZ":
|
|
input.InputKey = InputKeys.VelocityZ;
|
|
input.Value = p[1];
|
|
break;
|
|
}
|
|
|
|
return input;
|
|
}
|
|
|
|
public static string GetStringFromInputControl(InputControl input)
|
|
{
|
|
string str = string.Empty;
|
|
if (input.InputKey is null && input.KeyIndex is null && input.Value is null) return string.Empty;
|
|
|
|
switch (input.InputKey)
|
|
{
|
|
case var @case when @case == InputKeys.Buttons:
|
|
{
|
|
return $"Button {input.KeyIndex}";
|
|
}
|
|
|
|
case var case1 when case1 == InputKeys.PointOfViewControllers:
|
|
{
|
|
return $"Point {input.KeyIndex} {input.Value}";
|
|
}
|
|
|
|
case var case2 when case2 == InputKeys.Sliders:
|
|
{
|
|
return $"Slider {input.KeyIndex}";
|
|
}
|
|
|
|
case var case3 when case3 == InputKeys.AccelerationSliders:
|
|
{
|
|
return $"ASlider {input.KeyIndex}";
|
|
}
|
|
|
|
case var case4 when case4 == InputKeys.ForceSliders:
|
|
{
|
|
return $"FSlider {input.KeyIndex}";
|
|
}
|
|
|
|
case var case5 when case5 == InputKeys.VelocitySliders:
|
|
{
|
|
return $"VSlider {input.KeyIndex}";
|
|
}
|
|
|
|
case var case6 when case6 == InputKeys.X:
|
|
{
|
|
return $"X {input.Value}";
|
|
}
|
|
|
|
case var case7 when case7 == InputKeys.Y:
|
|
{
|
|
return $"Y {input.Value}";
|
|
}
|
|
|
|
case var case8 when case8 == InputKeys.Z:
|
|
{
|
|
return $"Z {input.Value}";
|
|
}
|
|
|
|
case var case9 when case9 == InputKeys.AccelerationX:
|
|
{
|
|
return $"AX {input.Value}";
|
|
}
|
|
|
|
case var case10 when case10 == InputKeys.AccelerationY:
|
|
{
|
|
return $"AY {input.Value}";
|
|
}
|
|
|
|
case var case11 when case11 == InputKeys.AccelerationZ:
|
|
{
|
|
return $"AZ {input.Value}";
|
|
}
|
|
|
|
case var case12 when case12 == InputKeys.AngularAccelerationX:
|
|
{
|
|
return $"AAX {input.Value}";
|
|
}
|
|
|
|
case var case13 when case13 == InputKeys.AngularAccelerationY:
|
|
{
|
|
return $"AAY {input.Value}";
|
|
}
|
|
|
|
case var case14 when case14 == InputKeys.AngularAccelerationZ:
|
|
{
|
|
return $"AAZ {input.Value}";
|
|
}
|
|
|
|
case var case15 when case15 == InputKeys.ForceX:
|
|
{
|
|
return $"FX {input.Value}";
|
|
}
|
|
|
|
case var case16 when case16 == InputKeys.ForceY:
|
|
{
|
|
return $"FY {input.Value}";
|
|
}
|
|
|
|
case var case17 when case17 == InputKeys.ForceZ:
|
|
{
|
|
return $"FZ {input.Value}";
|
|
}
|
|
|
|
case var case18 when case18 == InputKeys.RotationX:
|
|
{
|
|
return $"RX {input.Value}";
|
|
}
|
|
|
|
case var case19 when case19 == InputKeys.RotationY:
|
|
{
|
|
return $"RY {input.Value}";
|
|
}
|
|
|
|
case var case20 when case20 == InputKeys.RotationZ:
|
|
{
|
|
return $"RZ {input.Value}";
|
|
}
|
|
|
|
case var case21 when case21 == InputKeys.TorqueX:
|
|
{
|
|
return $"TX {input.Value}";
|
|
}
|
|
|
|
case var case22 when case22 == InputKeys.TorqueY:
|
|
{
|
|
return $"TY {input.Value}";
|
|
}
|
|
|
|
case var case23 when case23 == InputKeys.TorqueZ:
|
|
{
|
|
return $"TZ {input.Value}";
|
|
}
|
|
|
|
case var case24 when case24 == InputKeys.VelocityX:
|
|
{
|
|
return $"VX {input.Value}";
|
|
}
|
|
|
|
case var case25 when case25 == InputKeys.VelocityY:
|
|
{
|
|
return $"VY {input.Value}";
|
|
}
|
|
|
|
case var case26 when case26 == InputKeys.VelocityZ:
|
|
{
|
|
return $"VZ {input.Value}";
|
|
}
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
private void LoadProfile(string filePath)
|
|
{
|
|
var p = !string.IsNullOrEmpty(filePath) && System.IO.File.Exists(filePath) ? InputProfile.Load(filePath) : new InputProfile(PROFILE_CODE);
|
|
|
|
if (p.ProfileCode == PROFILE_CODE)
|
|
{
|
|
radTextBox_CreateSavestate.Text = GetStringFromInputControl(p[ActionCodes.CreateSavestate]);
|
|
radTextBox_LoadSavestate.Text = GetStringFromInputControl(p[ActionCodes.LoadSavestate]);
|
|
|
|
try
|
|
{
|
|
var tguid = allDevices.FirstOrDefault(n => Globals.CompareTwoByteArrays(n.InstanceGuid.ToByteArray(), p.ControllerInstanceGuid.ToByteArray()));
|
|
if (tguid is object)
|
|
radDropDownList_Pads.SelectedIndex = Array.IndexOf(allDevices, tguid);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
|
|
curProfile = p;
|
|
}
|
|
else
|
|
RadMessageBox.Show(this, LangRes.MsgBox_WrongProfileCode, LangRes.MsgBox_WrongProfileCode_Title, MessageBoxButtons.OK, RadMessageIcon.Exclamation);
|
|
}
|
|
|
|
private void MyTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
|
|
{
|
|
CheckForInput();
|
|
}
|
|
|
|
private void MainForm_Shown(object sender, EventArgs e)
|
|
{
|
|
LoadPads();
|
|
LoadProfile(GetDefaultProfileFilePath());
|
|
}
|
|
|
|
private void radButton_StartStopListening_Click(object sender, EventArgs e)
|
|
{
|
|
if (enableActionExecution)
|
|
{
|
|
radButton_StartStopListening.Image = Properties.Resources.icons8_play_button_circled_16px;
|
|
radButton_StartStopListening.Text = LangRes.Button_StartListening;
|
|
enableActionExecution = false;
|
|
}
|
|
else
|
|
{
|
|
radButton_StartStopListening.Image = Properties.Resources.icons8_stop_squared_16px;
|
|
radButton_StartStopListening.Text = LangRes.Button_StopListening;
|
|
enableActionExecution = true;
|
|
}
|
|
}
|
|
|
|
private void radButton3_Click(object sender, EventArgs e)
|
|
{
|
|
var sfd_SaveInputProfile = new RadSaveFileDialog
|
|
{
|
|
Filter = FILTER_INPUTPROFILE
|
|
};
|
|
if (sfd_SaveInputProfile.ShowDialog(this) == DialogResult.OK)
|
|
curProfile.Save(sfd_SaveInputProfile.FileName);
|
|
}
|
|
|
|
private void radButton4_Click(object sender, EventArgs e)
|
|
{
|
|
var ofd_SaveInputProfile = new RadOpenFileDialog
|
|
{
|
|
Filter = FILTER_INPUTPROFILE
|
|
};
|
|
if (ofd_SaveInputProfile.ShowDialog(this) == DialogResult.OK)
|
|
LoadProfile(ofd_SaveInputProfile.FileName);
|
|
}
|
|
|
|
private void radTextBox1_TextChanged(object sender, EventArgs e)
|
|
{
|
|
SetAction(ActionCodes.LoadSavestate, ((RadTextBox)sender).Text);
|
|
}
|
|
|
|
private void radTextBox2_TextChanged(object sender, EventArgs e)
|
|
{
|
|
SetAction(ActionCodes.CreateSavestate, ((RadTextBox)sender).Text);
|
|
}
|
|
|
|
private void radButton1_Click(object sender, EventArgs e)
|
|
{
|
|
LoadPads();
|
|
}
|
|
|
|
private void radDropDownList1_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e)
|
|
{
|
|
int index = radDropDownList_Pads.SelectedIndex;
|
|
if (index >= 0)
|
|
SetCurrentPad(allDevices[index].InstanceGuid);
|
|
}
|
|
|
|
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
curProfile.Save(GetDefaultProfileFilePath());
|
|
}
|
|
|
|
private void RadTextBox_Savestate_Clicked(object sender, EventArgs e)
|
|
{
|
|
if (curPad is object && curProfile is object)
|
|
focuesTextBox = (RadTextBox)sender;
|
|
}
|
|
|
|
private void RadTextBox_Savestate_LostFocus(object sender, EventArgs e)
|
|
{
|
|
focuesTextBox = null;
|
|
}
|
|
}
|
|
}
|