diff --git a/Sample/AvatarParameterSender/App.xaml b/Sample/AvatarParameterSender/App.xaml
new file mode 100644
index 0000000..7722a3d
--- /dev/null
+++ b/Sample/AvatarParameterSender/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/Sample/AvatarParameterSender/App.xaml.cs b/Sample/AvatarParameterSender/App.xaml.cs
new file mode 100644
index 0000000..e3d394e
--- /dev/null
+++ b/Sample/AvatarParameterSender/App.xaml.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace AvatarParameterSender;
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+}
diff --git a/Sample/AvatarParameterSender/AssemblyInfo.cs b/Sample/AvatarParameterSender/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/Sample/AvatarParameterSender/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/Sample/AvatarParameterSender/AvatarParameterSender.csproj b/Sample/AvatarParameterSender/AvatarParameterSender.csproj
new file mode 100644
index 0000000..d1d0a8e
--- /dev/null
+++ b/Sample/AvatarParameterSender/AvatarParameterSender.csproj
@@ -0,0 +1,14 @@
+
+
+
+ WinExe
+ net6.0-windows
+ enable
+ true
+
+
+
+
+
+
+
diff --git a/Sample/AvatarParameterSender/MainWindow.xaml b/Sample/AvatarParameterSender/MainWindow.xaml
new file mode 100644
index 0000000..acaba70
--- /dev/null
+++ b/Sample/AvatarParameterSender/MainWindow.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sample/AvatarParameterSender/MainWindow.xaml.cs b/Sample/AvatarParameterSender/MainWindow.xaml.cs
new file mode 100644
index 0000000..e82a95a
--- /dev/null
+++ b/Sample/AvatarParameterSender/MainWindow.xaml.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+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 BuildSoft.VRChat.Osc.Avatar;
+
+namespace AvatarParameterSender;
+///
+/// Interaction logic for MainWindow.xaml
+///
+public partial class MainWindow : Window
+{
+ public static readonly DependencyProperty CurrentAvatarNameProperty =
+ DependencyProperty.Register(nameof(CurrentAvatarName), typeof(string), typeof(MainWindow), new PropertyMetadata(string.Empty));
+
+ public string CurrentAvatarName
+ {
+ get { return (string)GetValue(CurrentAvatarNameProperty); }
+ set { SetValue(CurrentAvatarNameProperty, value); }
+ }
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ OscAvatarUtility.AvatarChanged += (sender, e) =>
+ {
+ Dispatcher.Invoke(() => LoadCurrentAvatarConfig());
+ };
+ }
+
+ private void LoadCurrentAvatarConfig()
+ {
+ var currentConfig = OscAvatarConfig.CreateAtCurrent();
+ if (currentConfig == null)
+ {
+ return;
+ }
+ CurrentAvatarName = currentConfig.Name;
+
+ var children = ParameterPanel.Children;
+ children.Clear();
+
+ var items = currentConfig.Parameters.Items;
+ for (int i = 0; i < items.Length; i++)
+ {
+ var input = items[i].Input;
+ if (input == null)
+ {
+ continue;
+ }
+
+ children.Add(new ParameterSenderItem() {
+ Address = input.Address,
+ ParameterName = items[i].Name,
+ Type = input.OscType,
+ });
+ }
+ }
+}
diff --git a/Sample/AvatarParameterSender/ParameterSenderItem.xaml b/Sample/AvatarParameterSender/ParameterSenderItem.xaml
new file mode 100644
index 0000000..f89d852
--- /dev/null
+++ b/Sample/AvatarParameterSender/ParameterSenderItem.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Sample/AvatarParameterSender/ParameterSenderItem.xaml.cs b/Sample/AvatarParameterSender/ParameterSenderItem.xaml.cs
new file mode 100644
index 0000000..5e84244
--- /dev/null
+++ b/Sample/AvatarParameterSender/ParameterSenderItem.xaml.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+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 BuildSoft.VRChat.Osc;
+
+namespace AvatarParameterSender;
+///
+/// Interaction logic for ParameterSenderItem.xaml
+///
+public partial class ParameterSenderItem : UserControl
+{
+ public static readonly DependencyProperty ParameterNameProperty =
+ DependencyProperty.Register(nameof(ParameterName), typeof(string), typeof(ParameterSenderItem), new PropertyMetadata(string.Empty));
+
+ public static readonly DependencyProperty AddressProperty =
+ DependencyProperty.Register(nameof(Address), typeof(string), typeof(ParameterSenderItem), new PropertyMetadata(string.Empty));
+
+ public static readonly DependencyProperty TypeProperty =
+ DependencyProperty.Register("MyProperty", typeof(OscType), typeof(ParameterSenderItem), new PropertyMetadata(OscType.Int, OnTypeChanged));
+
+ public static readonly DependencyProperty ValueProperty =
+ DependencyProperty.Register("Value", typeof(string), typeof(ParameterSenderItem), new PropertyMetadata(string.Empty));
+
+ private static void OnTypeChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+ {
+ var senderItem = (ParameterSenderItem)dependencyObject;
+
+ var items = senderItem.ValueBox.Items;
+ items.Clear();
+
+ senderItem.ValueBox.IsEditable = true;
+
+ if ((OscType)e.NewValue == OscType.Bool)
+ {
+ senderItem.ValueBox.IsEditable = false;
+ items.Add(bool.TrueString);
+ items.Add(bool.FalseString);
+ }
+ }
+
+
+ public string ParameterName
+ {
+ get { return (string)GetValue(ParameterNameProperty); }
+ set { SetValue(ParameterNameProperty, value); }
+ }
+
+ public string Address
+ {
+ get { return (string)GetValue(AddressProperty); }
+ set { SetValue(AddressProperty, value); }
+ }
+
+ public OscType Type
+ {
+ get { return (OscType)GetValue(TypeProperty); }
+ set { SetValue(TypeProperty, value); }
+ }
+
+ public string Value
+ {
+ get { return (string)GetValue(ValueProperty); }
+ set { SetValue(ValueProperty, value); }
+ }
+
+
+ public ParameterSenderItem()
+ {
+ InitializeComponent();
+ }
+
+
+ private void ValueBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
+ {
+ switch (Type)
+ {
+ case OscType.Int:
+ e.Handled = !int.TryParse(ValueBox.Text + e.Text, out _);
+ break;
+ case OscType.Float:
+ e.Handled = !float.TryParse(ValueBox.Text + e.Text, out _);
+ break;
+ }
+ }
+
+ private void SendButton_Click(object sender, RoutedEventArgs e)
+ {
+ var address = Address;
+ var value = Value;
+ if (string.IsNullOrEmpty(address) || string.IsNullOrEmpty(value))
+ {
+ return;
+ }
+
+ switch (Type)
+ {
+ case OscType.Bool:
+ if (bool.TryParse(value, out bool boolValue))
+ {
+ OscParameter.SendValue(address, boolValue);
+ }
+ break;
+ case OscType.Int:
+ if (int.TryParse(value, out int intValue))
+ {
+ OscParameter.SendValue(address, intValue);
+ }
+ break;
+ case OscType.Float:
+ if (float.TryParse(value, out float floatValue))
+ {
+ OscParameter.SendValue(address, floatValue);
+ }
+ break;
+ }
+ }
+}
diff --git a/vrcosclib.sln b/vrcosclib.sln
index 5c252de..653f497 100644
--- a/vrcosclib.sln
+++ b/vrcosclib.sln
@@ -24,42 +24,75 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuiController", "Sample\Gui
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0} = {3D5D91B8-5663-43AB-BD91-FD80502BDEF0}
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarParameterSender", "Sample\AvatarParameterSender\AvatarParameterSender.csproj", "{E3589FA0-A1ED-4986-B51F-7D8270A4656E}"
+ ProjectSection(ProjectDependencies) = postProject
+ {3D5D91B8-5663-43AB-BD91-FD80502BDEF0} = {3D5D91B8-5663-43AB-BD91-FD80502BDEF0}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
Debug|For Test = Debug|For Test
Debug|Full Build = Debug|Full Build
+ Release|Any CPU = Release|Any CPU
Release|For Test = Release|For Test
Release|Full Build = Release|Full Build
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Debug|For Test.ActiveCfg = Debug|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Debug|For Test.Build.0 = Debug|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Debug|Full Build.ActiveCfg = Debug|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Debug|Full Build.Build.0 = Debug|Any CPU
+ {3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Release|Any CPU.Build.0 = Release|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Release|For Test.ActiveCfg = Release|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Release|For Test.Build.0 = Release|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Release|Full Build.ActiveCfg = Release|Any CPU
{3D5D91B8-5663-43AB-BD91-FD80502BDEF0}.Release|Full Build.Build.0 = Release|Any CPU
+ {905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Debug|For Test.ActiveCfg = Debug|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Debug|For Test.Build.0 = Debug|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Debug|Full Build.ActiveCfg = Debug|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Debug|Full Build.Build.0 = Debug|Any CPU
+ {905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Release|Any CPU.Build.0 = Release|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Release|For Test.ActiveCfg = Release|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Release|For Test.Build.0 = Release|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Release|Full Build.ActiveCfg = Release|Any CPU
{905C0E65-4B84-40C9-9C6C-1A454BCB2D2A}.Release|Full Build.Build.0 = Release|Any CPU
+ {5729DE08-F01E-4091-8031-3FAE649F7627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5729DE08-F01E-4091-8031-3FAE649F7627}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5729DE08-F01E-4091-8031-3FAE649F7627}.Debug|For Test.ActiveCfg = Debug|Any CPU
{5729DE08-F01E-4091-8031-3FAE649F7627}.Debug|Full Build.ActiveCfg = Debug|Any CPU
{5729DE08-F01E-4091-8031-3FAE649F7627}.Debug|Full Build.Build.0 = Debug|Any CPU
+ {5729DE08-F01E-4091-8031-3FAE649F7627}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5729DE08-F01E-4091-8031-3FAE649F7627}.Release|Any CPU.Build.0 = Release|Any CPU
{5729DE08-F01E-4091-8031-3FAE649F7627}.Release|For Test.ActiveCfg = Release|Any CPU
{5729DE08-F01E-4091-8031-3FAE649F7627}.Release|Full Build.ActiveCfg = Release|Any CPU
{5729DE08-F01E-4091-8031-3FAE649F7627}.Release|Full Build.Build.0 = Release|Any CPU
+ {B214E3A8-C61B-4921-94A6-73CAAB358A14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B214E3A8-C61B-4921-94A6-73CAAB358A14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B214E3A8-C61B-4921-94A6-73CAAB358A14}.Debug|For Test.ActiveCfg = Debug|Any CPU
{B214E3A8-C61B-4921-94A6-73CAAB358A14}.Debug|Full Build.ActiveCfg = Debug|Any CPU
{B214E3A8-C61B-4921-94A6-73CAAB358A14}.Debug|Full Build.Build.0 = Debug|Any CPU
+ {B214E3A8-C61B-4921-94A6-73CAAB358A14}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B214E3A8-C61B-4921-94A6-73CAAB358A14}.Release|Any CPU.Build.0 = Release|Any CPU
{B214E3A8-C61B-4921-94A6-73CAAB358A14}.Release|For Test.ActiveCfg = Release|Any CPU
{B214E3A8-C61B-4921-94A6-73CAAB358A14}.Release|Full Build.ActiveCfg = Release|Any CPU
{B214E3A8-C61B-4921-94A6-73CAAB358A14}.Release|Full Build.Build.0 = Release|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Debug|For Test.ActiveCfg = Debug|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Debug|Full Build.ActiveCfg = Debug|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Debug|Full Build.Build.0 = Debug|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Release|For Test.ActiveCfg = Release|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Release|Full Build.ActiveCfg = Release|Any CPU
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E}.Release|Full Build.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -67,6 +100,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{5729DE08-F01E-4091-8031-3FAE649F7627} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5}
{B214E3A8-C61B-4921-94A6-73CAAB358A14} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5}
+ {E3589FA0-A1ED-4986-B51F-7D8270A4656E} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7ADDCAD0-5084-420D-96B4-54FD8F6A2FA5}