Refactor ObjectCollectionEditor so it's embeddable in a propertygrid.
Make the listbox actually update when items are added/removed.
This commit is contained in:
@@ -273,6 +273,9 @@
|
|||||||
<DependentUpon>DictionaryEditor.xaml</DependentUpon>
|
<DependentUpon>DictionaryEditor.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Views\DisplayAttribute.cs" />
|
<Compile Include="Views\DisplayAttribute.cs" />
|
||||||
|
<Compile Include="Views\EmbeddedCollectionEditor.xaml.cs">
|
||||||
|
<DependentUpon>EmbeddedCollectionEditor.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Views\ObjectCollectionEditor.xaml.cs">
|
<Compile Include="Views\ObjectCollectionEditor.xaml.cs">
|
||||||
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
|
<DependentUpon>ObjectCollectionEditor.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -303,6 +306,10 @@
|
|||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Views\EmbeddedCollectionEditor.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Views\ObjectCollectionEditor.xaml">
|
<Page Include="Views\ObjectCollectionEditor.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
32
Torch/Views/EmbeddedCollectionEditor.xaml
Normal file
32
Torch/Views/EmbeddedCollectionEditor.xaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<UserControl x:Class="Torch.Views.EmbeddedCollectionEditor"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Torch.Views"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="300" d:DesignWidth="300">
|
||||||
|
<Grid Width="Auto" Height="Auto">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition />
|
||||||
|
<ColumnDefinition />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<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" />
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
126
Torch/Views/EmbeddedCollectionEditor.xaml.cs
Normal file
126
Torch/Views/EmbeddedCollectionEditor.xaml.cs
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
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 NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
|
||||||
|
namespace Torch.Views
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for EmbeddedCollectionEditor.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class EmbeddedCollectionEditor : UserControl
|
||||||
|
{
|
||||||
|
public EmbeddedCollectionEditor()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContextChanged += OnDataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
|
||||||
|
{
|
||||||
|
var c = dependencyPropertyChangedEventArgs.NewValue as ICollection;
|
||||||
|
//var c = DataContext as ICollection;
|
||||||
|
if (c != null)
|
||||||
|
Edit(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, MethodInfo> MethodCache = new Dictionary<Type, MethodInfo>();
|
||||||
|
private static readonly MethodInfo EditMethod;
|
||||||
|
|
||||||
|
|
||||||
|
static EmbeddedCollectionEditor()
|
||||||
|
{
|
||||||
|
var m = typeof(EmbeddedCollectionEditor).GetMethods();
|
||||||
|
EditMethod = m.First(mt => mt.Name == "Edit" && mt.GetGenericArguments().Length == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Edit(ICollection collection)
|
||||||
|
{
|
||||||
|
if (collection == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Cannot load null collection.", "Edit Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gt = collection.GetType().GenericTypeArguments[0];
|
||||||
|
|
||||||
|
//substitute for 'where T : new()'
|
||||||
|
if (gt.GetConstructor(Type.EmptyTypes) == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Unsupported collection type. Type must have paramaterless ctor.", "Edit Error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MethodCache.TryGetValue(gt, out MethodInfo gm))
|
||||||
|
{
|
||||||
|
gm = EditMethod.MakeGenericMethod(gt);
|
||||||
|
MethodCache.Add(gt, gm);
|
||||||
|
}
|
||||||
|
|
||||||
|
gm.Invoke(this, new object[] {collection});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Edit<T>(ICollection<T> collection) where T : new()
|
||||||
|
{
|
||||||
|
var oc = collection as ObservableCollection<T> ?? new ObservableCollection<T>(collection);
|
||||||
|
|
||||||
|
AddButton.Click += (sender, args) =>
|
||||||
|
{
|
||||||
|
var t = new T();
|
||||||
|
oc.Add(t);
|
||||||
|
ElementList.SelectedItem = t;
|
||||||
|
};
|
||||||
|
|
||||||
|
RemoveButton.Click += RemoveButton_OnClick<T>;
|
||||||
|
ElementList.SelectionChanged += ElementsList_OnSelected;
|
||||||
|
|
||||||
|
ElementList.ItemsSource = oc;
|
||||||
|
oc.CollectionChanged += (sender, args) => RefreshList();
|
||||||
|
|
||||||
|
if (!(collection is ObservableCollection<T>))
|
||||||
|
{
|
||||||
|
collection.Clear();
|
||||||
|
foreach (var o in oc)
|
||||||
|
collection.Add(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveButton_OnClick<T>(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
//this is kinda shitty, but item count is normally small, and it prevents CollectionModifiedExceptions
|
||||||
|
var l = (ObservableCollection<T>)ElementList.ItemsSource;
|
||||||
|
var r = new List<T>(ElementList.SelectedItems.Cast<T>());
|
||||||
|
foreach (var item in r)
|
||||||
|
l.Remove(item);
|
||||||
|
if (l.Any())
|
||||||
|
ElementList.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ElementsList_OnSelected(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var item = (sender as ListBox)?.SelectedItem;
|
||||||
|
PGrid.DataContext = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshList()
|
||||||
|
{
|
||||||
|
ElementList.Items.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -6,24 +6,5 @@
|
|||||||
xmlns:local="clr-namespace:Torch.Views"
|
xmlns:local="clr-namespace:Torch.Views"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Height="370" Width="400" Title="Edit Collection">
|
Height="370" Width="400" Title="Edit Collection">
|
||||||
<Grid Width="Auto" Height="Auto">
|
<local:EmbeddedCollectionEditor x:Name="Editor"/>
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<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" />
|
|
||||||
<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" />
|
|
||||||
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
</Window>
|
||||||
|
@@ -25,90 +25,18 @@ namespace Torch.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ObjectCollectionEditor : Window
|
public partial class ObjectCollectionEditor : Window
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<Type, MethodInfo> MethodCache = new Dictionary<Type, MethodInfo>();
|
|
||||||
private static readonly MethodInfo EditMethod;
|
|
||||||
|
|
||||||
public ObjectCollectionEditor()
|
public ObjectCollectionEditor()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ObjectCollectionEditor()
|
|
||||||
{
|
|
||||||
var m = typeof(ObjectCollectionEditor).GetMethods();
|
|
||||||
EditMethod = m.First(mt => mt.Name == "Edit" && mt.GetGenericArguments().Length == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Edit(ICollection collection, string title)
|
public void Edit(ICollection collection, string title)
|
||||||
{
|
{
|
||||||
if (collection == null)
|
Editor.Edit(collection);
|
||||||
{
|
|
||||||
MessageBox.Show("Cannot load null collection.", "Edit Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var gt = collection.GetType().GenericTypeArguments[0];
|
|
||||||
|
|
||||||
//substitute for 'where T : new()'
|
|
||||||
if (gt.GetConstructor(Type.EmptyTypes) == null)
|
|
||||||
{
|
|
||||||
MessageBox.Show("Unsupported collection type. Type must have paramaterless ctor.", "Edit Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MethodCache.TryGetValue(gt, out MethodInfo gm))
|
|
||||||
{
|
|
||||||
gm = EditMethod.MakeGenericMethod(gt);
|
|
||||||
MethodCache.Add(gt, gm);
|
|
||||||
}
|
|
||||||
|
|
||||||
gm.Invoke(this, new object[] {collection, title});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Edit<T>(ICollection<T> collection, string title) where T : new()
|
|
||||||
{
|
|
||||||
var oc = collection as ObservableCollection<T> ?? new ObservableCollection<T>(collection);
|
|
||||||
|
|
||||||
AddButton.Click += (sender, args) =>
|
|
||||||
{
|
|
||||||
var t = new T();
|
|
||||||
oc.Add(t);
|
|
||||||
ElementList.SelectedItem = t;
|
|
||||||
};
|
|
||||||
|
|
||||||
RemoveButton.Click += RemoveButton_OnClick<T>;
|
|
||||||
ElementList.SelectionChanged += ElementsList_OnSelected;
|
|
||||||
|
|
||||||
ElementList.ItemsSource = oc;
|
|
||||||
|
|
||||||
Title = title;
|
Title = title;
|
||||||
|
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
ShowDialog();
|
ShowDialog();
|
||||||
|
|
||||||
if (!(collection is ObservableCollection<T>))
|
|
||||||
{
|
|
||||||
collection.Clear();
|
|
||||||
foreach (var o in oc)
|
|
||||||
collection.Add(o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveButton_OnClick<T>(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
//this is kinda shitty, but item count is normally small, and it prevents CollectionModifiedExceptions
|
|
||||||
var l = (ObservableCollection<T>)ElementList.ItemsSource;
|
|
||||||
var r = new List<T>(ElementList.SelectedItems.Cast<T>());
|
|
||||||
foreach (var item in r)
|
|
||||||
l.Remove(item);
|
|
||||||
if (l.Any())
|
|
||||||
ElementList.SelectedIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ElementsList_OnSelected(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var item = (sender as ListBox)?.SelectedItem;
|
|
||||||
PGrid.DataContext = item;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user