@@ -118,7 +118,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
|
<Button Grid.Row="1" Content="Save Config" Margin="3" Click="Save_OnClick" />
|
||||||
</Grid>
|
</Grid>
|
||||||
<views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" />
|
<views:PropertyGrid Grid.Column="1" Margin="3" DataContext="{Binding SessionSettings}" IgnoreDisplay ="True" />
|
||||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True"
|
<GridSplitter Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Stretch" ShowsPreview="True"
|
||||||
Width="2" Background="Gray" />
|
Width="2" Background="Gray" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@@ -263,6 +263,9 @@
|
|||||||
<Compile Include="Views\ObjectCollectionEditor.xaml.cs">
|
<Compile Include="Views\ObjectCollectionEditor.xaml.cs">
|
||||||
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
|
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Views\ObjectEditor.xaml.cs">
|
||||||
|
<DependentUpon>ObjectEditor.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Views\PropertyGrid.xaml.cs">
|
<Compile Include="Views\PropertyGrid.xaml.cs">
|
||||||
<DependentUpon>PropertyGrid.xaml</DependentUpon>
|
<DependentUpon>PropertyGrid.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -291,6 +294,10 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\ObjectEditor.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\PropertyGrid.xaml">
|
<Page Include="Views\PropertyGrid.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
@@ -5,15 +5,24 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Torch.Views"
|
xmlns:local="clr-namespace:Torch.Views"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Height="370" Width="410" Title="Edit Collection">
|
Height="370" Width="400" Title="Edit Collection">
|
||||||
<Grid>
|
<Grid Width="Auto" Height="Auto">
|
||||||
<local:PropertyGrid x:Name="PGrid" VerticalAlignment="Top" Height="300" Width="190" Margin="200,0,0,0"
|
<Grid.ColumnDefinitions>
|
||||||
HorizontalAlignment="Right" />
|
<ColumnDefinition />
|
||||||
<ListBox x:Name="ElementList" Height="300" VerticalAlignment="Top" Width="190"
|
<ColumnDefinition />
|
||||||
HorizontalContentAlignment="Stretch" Margin="0,0,210,0" HorizontalAlignment="Left" />
|
</Grid.ColumnDefinitions>
|
||||||
<Button x:Name="AddButton" Content="Add" HorizontalAlignment="Left" Margin="0,305,0,0" VerticalAlignment="Top"
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="25"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ListBox Grid.Row="0" Grid.Column="0" x:Name="ElementList"
|
||||||
|
HorizontalContentAlignment="Stretch" Margin="0" VerticalContentAlignment="Stretch" />
|
||||||
|
<GridSplitter Grid.Column="1" Grid.Row="0" Width="2" HorizontalAlignment="Left" VerticalAlignment="Stretch" Background="Gray" ShowsPreview="True" VerticalContentAlignment="Stretch"/>
|
||||||
|
|
||||||
|
<local:PropertyGrid Grid.Row="0" Grid.Column="1" x:Name="PGrid" Margin="4,0,0,0" />
|
||||||
|
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="AddButton" Content="Add" HorizontalAlignment="Left" Margin="0" VerticalAlignment="Top"
|
||||||
Width="90" />
|
Width="90" />
|
||||||
<Button x:Name="RemoveButton" Content="Remove" HorizontalAlignment="Left" Margin="100,305,0,0"
|
<Button Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" x:Name="RemoveButton" Content="Remove" HorizontalAlignment="Left" Margin="100,0,0,0"
|
||||||
VerticalAlignment="Top" Width="90" />
|
VerticalAlignment="Top" Width="90" />
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
11
Torch/Views/ObjectEditor.xaml
Normal file
11
Torch/Views/ObjectEditor.xaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<Window x:Class="Torch.Views.ObjectEditor"
|
||||||
|
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:Torch.Views"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Height="400" Width="400" Title="Edit Object">
|
||||||
|
|
||||||
|
<local:PropertyGrid x:Name="PGrid" />
|
||||||
|
</Window>
|
36
Torch/Views/ObjectEditor.xaml.cs
Normal file
36
Torch/Views/ObjectEditor.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
namespace Torch.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for ObjectEditor.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class ObjectEditor : Window
|
||||||
|
{
|
||||||
|
public ObjectEditor()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Edit(object o, string title = "Edit Object")
|
||||||
|
{
|
||||||
|
PGrid.DataContext = o;
|
||||||
|
Title = title;
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
ShowDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition/>
|
<RowDefinition/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid Grid.Row="0">
|
<Grid Grid.Row="0">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
@@ -19,5 +20,6 @@
|
|||||||
<TextBox Grid.Column="1" Margin="3" TextChanged="UpdateFilter"/>
|
<TextBox Grid.Column="1" Margin="3" TextChanged="UpdateFilter"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<ScrollViewer Grid.Row="1" x:Name="ScrollViewer"/>
|
<ScrollViewer Grid.Row="1" x:Name="ScrollViewer"/>
|
||||||
|
<TextBlock x:Name="TbDescription" Grid.Row="2" MinHeight="18" Background="#FFBBB9B9"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -29,6 +30,14 @@ namespace Torch.Views
|
|||||||
{
|
{
|
||||||
private Dictionary<Type, Grid> _viewCache = new Dictionary<Type, Grid>();
|
private Dictionary<Type, Grid> _viewCache = new Dictionary<Type, Grid>();
|
||||||
|
|
||||||
|
public static readonly DependencyProperty IgnoreDisplayProperty = DependencyProperty.Register("IgnoreDisplay", typeof(bool), typeof(PropertyGrid));
|
||||||
|
|
||||||
|
public bool IgnoreDisplay
|
||||||
|
{
|
||||||
|
get => (bool)base.GetValue(IgnoreDisplayProperty);
|
||||||
|
set => base.SetValue(IgnoreDisplayProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public PropertyGrid()
|
public PropertyGrid()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -59,114 +68,171 @@ namespace Torch.Views
|
|||||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });
|
||||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||||
|
|
||||||
var curRow = 0;
|
var categories = new Dictionary<string, List<PropertyInfo>>();
|
||||||
foreach (var property in properties.OrderBy(x => x.Name))
|
var descriptors = new Dictionary<PropertyInfo, DisplayAttribute>(properties.Length);
|
||||||
|
|
||||||
|
foreach (var property in properties)
|
||||||
{
|
{
|
||||||
if (property.GetGetMethod() == null)
|
var a = property.GetCustomAttribute<DisplayAttribute>();
|
||||||
continue;
|
if (IgnoreDisplay)
|
||||||
|
a = null;
|
||||||
|
descriptors[property] = a;
|
||||||
|
string category = a?.GroupName ?? "Misc";
|
||||||
|
|
||||||
|
if (!categories.TryGetValue(category, out List<PropertyInfo> l))
|
||||||
|
{
|
||||||
|
l = new List<PropertyInfo>();
|
||||||
|
categories[category] = l;
|
||||||
|
}
|
||||||
|
l.Add(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
var curRow = 0;
|
||||||
|
foreach (var c in categories.OrderBy(x => x.Key))
|
||||||
|
{
|
||||||
grid.RowDefinitions.Add(new RowDefinition());
|
grid.RowDefinitions.Add(new RowDefinition());
|
||||||
|
var cl = new TextBlock
|
||||||
|
{
|
||||||
|
Text = c.Key,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
cl.SetValue(Grid.ColumnProperty, 0);
|
||||||
|
cl.SetValue(Grid.ColumnSpanProperty, 2);
|
||||||
|
cl.SetValue(Grid.RowProperty, curRow);
|
||||||
|
cl.Margin = new Thickness(3);
|
||||||
|
cl.FontWeight = FontWeights.Bold;
|
||||||
|
grid.Children.Add(cl);
|
||||||
|
curRow++;
|
||||||
|
|
||||||
var displayName = property.GetCustomAttribute<DisplayAttribute>()?.Name;
|
c.Value.Sort((a,b)=> string.Compare((descriptors[a]?.Name ?? a.Name), descriptors[b]?.Name ?? b.Name, StringComparison.Ordinal));
|
||||||
var propertyType = property.PropertyType;
|
|
||||||
|
|
||||||
var text = new TextBlock
|
foreach (var property in c.Value)
|
||||||
{
|
{
|
||||||
Text = property.Name,
|
if (property.GetGetMethod() == null)
|
||||||
ToolTip = displayName,
|
continue;
|
||||||
VerticalAlignment = VerticalAlignment.Center
|
|
||||||
};
|
|
||||||
text.SetValue(Grid.ColumnProperty, 0);
|
|
||||||
text.SetValue(Grid.RowProperty, curRow);
|
|
||||||
text.Margin = new Thickness(3);
|
|
||||||
grid.Children.Add(text);
|
|
||||||
|
|
||||||
FrameworkElement valueControl;
|
grid.RowDefinitions.Add(new RowDefinition());
|
||||||
if (property.GetSetMethod() == null)
|
|
||||||
{
|
var descriptor = descriptors[property];
|
||||||
valueControl = new TextBlock();
|
var displayName = descriptor?.Name;
|
||||||
var binding = new Binding(property.Name)
|
var propertyType = property.PropertyType;
|
||||||
|
|
||||||
|
var text = new TextBlock
|
||||||
|
{
|
||||||
|
Text = displayName ?? property.Name,
|
||||||
|
ToolTip = displayName,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center
|
||||||
|
};
|
||||||
|
text.SetValue(Grid.ColumnProperty, 0);
|
||||||
|
text.SetValue(Grid.RowProperty, curRow);
|
||||||
|
text.Margin = new Thickness(3);
|
||||||
|
text.Tag = $"{text.Text}: {descriptor?.Description}";
|
||||||
|
text.IsMouseDirectlyOverChanged += Text_IsMouseDirectlyOverChanged;
|
||||||
|
grid.Children.Add(text);
|
||||||
|
|
||||||
|
FrameworkElement valueControl;
|
||||||
|
if (property.GetSetMethod() == null)
|
||||||
{
|
{
|
||||||
Mode = BindingMode.OneWay
|
valueControl = new TextBlock();
|
||||||
};
|
var binding = new Binding(property.Name)
|
||||||
valueControl.SetBinding(TextBlock.TextProperty, binding);
|
{
|
||||||
}
|
Mode = BindingMode.OneWay
|
||||||
else if (propertyType == typeof(bool) || propertyType == typeof(bool?))
|
};
|
||||||
{
|
valueControl.SetBinding(TextBlock.TextProperty, binding);
|
||||||
valueControl = new CheckBox();
|
}
|
||||||
valueControl.SetBinding(CheckBox.IsCheckedProperty, property.Name);
|
else if (propertyType == typeof(bool) || propertyType == typeof(bool?))
|
||||||
}
|
|
||||||
else if (propertyType.IsEnum)
|
|
||||||
{
|
|
||||||
valueControl = new ComboBox
|
|
||||||
{
|
{
|
||||||
ItemsSource = Enum.GetValues(property.PropertyType)
|
valueControl = new CheckBox();
|
||||||
};
|
valueControl.SetBinding(CheckBox.IsCheckedProperty, property.Name);
|
||||||
valueControl.SetBinding(ComboBox.SelectedItemProperty, property.Name);
|
}
|
||||||
}
|
else if (propertyType.IsEnum)
|
||||||
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
|
||||||
{
|
|
||||||
var button = new Button
|
|
||||||
{
|
{
|
||||||
Content = "Edit Collection"
|
valueControl = new ComboBox
|
||||||
};
|
{
|
||||||
button.SetBinding(Button.DataContextProperty, property.Name);
|
ItemsSource = Enum.GetValues(property.PropertyType)
|
||||||
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext);
|
};
|
||||||
|
valueControl.SetBinding(ComboBox.SelectedItemProperty, property.Name);
|
||||||
valueControl = button;
|
}
|
||||||
}
|
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||||
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(SerializableDictionary<,>))
|
|
||||||
{
|
|
||||||
var button = new Button
|
|
||||||
{
|
{
|
||||||
Content = "Edit Collection"
|
|
||||||
};
|
|
||||||
button.SetBinding(Button.DataContextProperty, $"{property.Name}.Dictionary");
|
|
||||||
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext);
|
|
||||||
|
|
||||||
valueControl = button;
|
|
||||||
}
|
|
||||||
else if (propertyType.IsGenericType && typeof(ICollection).IsAssignableFrom(propertyType.GetGenericTypeDefinition()))
|
|
||||||
{
|
|
||||||
var button = new Button
|
var button = new Button
|
||||||
{
|
{
|
||||||
Content = "Edit Collection"
|
Content = "Edit Collection"
|
||||||
};
|
};
|
||||||
button.SetBinding(Button.DataContextProperty, $"{property.Name}");
|
button.SetBinding(Button.DataContextProperty, property.Name);
|
||||||
|
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext);
|
||||||
|
|
||||||
var gt = propertyType.GetGenericArguments()[0];
|
valueControl = button;
|
||||||
|
}
|
||||||
//TODO: Is this the best option? Probably not
|
else if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(SerializableDictionary<,>))
|
||||||
if (gt.IsPrimitive || gt == typeof(string))
|
|
||||||
{
|
{
|
||||||
button.Click += (sender, args) => EditPrimitiveCollection(((Button)sender).DataContext);
|
var button = new Button
|
||||||
|
{
|
||||||
|
Content = "Edit Collection"
|
||||||
|
};
|
||||||
|
button.SetBinding(Button.DataContextProperty, $"{property.Name}.Dictionary");
|
||||||
|
button.Click += (sender, args) => EditDictionary(((Button)sender).DataContext);
|
||||||
|
|
||||||
|
valueControl = button;
|
||||||
|
}
|
||||||
|
else if (propertyType.IsGenericType && typeof(ICollection).IsAssignableFrom(propertyType.GetGenericTypeDefinition()))
|
||||||
|
{
|
||||||
|
var button = new Button
|
||||||
|
{
|
||||||
|
Content = "Edit Collection"
|
||||||
|
};
|
||||||
|
button.SetBinding(Button.DataContextProperty, property.Name);
|
||||||
|
|
||||||
|
var gt = propertyType.GetGenericArguments()[0];
|
||||||
|
|
||||||
|
//TODO: Is this the best option? Probably not
|
||||||
|
if (gt.IsPrimitive || gt == typeof(string))
|
||||||
|
{
|
||||||
|
button.Click += (sender, args) => EditPrimitiveCollection(((Button)sender).DataContext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
button.Click += (sender, args) => EditObjectCollection(((Button)sender).DataContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
valueControl = button;
|
||||||
|
}
|
||||||
|
else if (propertyType.IsPrimitive || propertyType == typeof(string))
|
||||||
|
{
|
||||||
|
valueControl = new TextBox();
|
||||||
|
valueControl.SetBinding(TextBox.TextProperty, property.Name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
button.Click += (sender, args) => EditObjectCollection(((Button)sender).DataContext);
|
var button = new Button
|
||||||
|
{
|
||||||
|
Content = "Edit Object"
|
||||||
|
};
|
||||||
|
button.SetBinding(Button.DataContextProperty, property.Name);
|
||||||
|
button.Click += (sender, args) => EditObject(((Button)sender).DataContext);
|
||||||
|
valueControl = button;
|
||||||
}
|
}
|
||||||
|
|
||||||
valueControl = button;
|
valueControl.Margin = new Thickness(3);
|
||||||
}
|
valueControl.VerticalAlignment = VerticalAlignment.Center;
|
||||||
else
|
valueControl.SetValue(Grid.ColumnProperty, 1);
|
||||||
{
|
valueControl.SetValue(Grid.RowProperty, curRow);
|
||||||
valueControl = new TextBox();
|
valueControl.IsMouseDirectlyOverChanged += Text_IsMouseDirectlyOverChanged;
|
||||||
valueControl.SetBinding(TextBox.TextProperty, property.Name);
|
grid.Children.Add(valueControl);
|
||||||
}
|
|
||||||
|
|
||||||
valueControl.Margin = new Thickness(3);
|
curRow++;
|
||||||
valueControl.VerticalAlignment = VerticalAlignment.Center;
|
}
|
||||||
valueControl.SetValue(Grid.ColumnProperty, 1);
|
|
||||||
valueControl.SetValue(Grid.RowProperty, curRow);
|
|
||||||
grid.Children.Add(valueControl);
|
|
||||||
|
|
||||||
curRow++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_viewCache.Add(t, grid);
|
_viewCache.Add(t, grid);
|
||||||
return grid;
|
return grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Text_IsMouseDirectlyOverChanged(object sender, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
TbDescription.Text = (sender as FrameworkElement)?.Tag?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private void EditDictionary(object dict)
|
private void EditDictionary(object dict)
|
||||||
{
|
{
|
||||||
var dic = (IDictionary)dict;
|
var dic = (IDictionary)dict;
|
||||||
@@ -185,6 +251,11 @@ namespace Torch.Views
|
|||||||
new ObjectCollectionEditor().Edit(c, title);
|
new ObjectCollectionEditor().Edit(c, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EditObject(object o, string title = "Edit Object")
|
||||||
|
{
|
||||||
|
new ObjectEditor().Edit(o, title);
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateFilter(object sender, TextChangedEventArgs e)
|
private void UpdateFilter(object sender, TextChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var filterText = ((TextBox)sender).Text;
|
var filterText = ((TextBox)sender).Text;
|
||||||
|
Reference in New Issue
Block a user