I am using WPF Live Charts Library. I am trying to create a bar chart generically.
Following is my code. Can't do it in XAML as it doesn't support generics.
public abstract class AbstractGenericBarChart<T> : UserControl, INotifyPropertyChanged
{
SeriesCollection _SeriesCollection;
public SeriesCollection SeriesCollection { get { return _SeriesCollection; } set { _SeriesCollection = value; notifyPropertyChanged("SeriesCollection"); } }
string[] _Labels;
public string[] Labels { get { return _Labels; } set { _Labels = value; notifyPropertyChanged("Labels"); } }
Func<int, string> _Formatter;
public Func<int, string> Formatter { get { return _Formatter; } set { _Formatter = value; notifyPropertyChanged("Formatter"); } }
public abstract void constructChart(List<T> chartItems);
public void init(string xLabel, string yLabel)
{
renderChart(xLabel, yLabel);
}
public void renderChart(string xLabel, string yLabel)
{
CartesianChart chart = new CartesianChart { Margin = new Thickness(10, 10, 10, 10), LegendLocation = LegendLocation.Bottom, DataTooltip = new DefaultTooltip { SelectionMode = TooltipSelectionMode.SharedYValues } };
Axis xAxis = new Axis { Foreground = Brushes.Black, FontSize = 14d, Title = xLabel };
Axis yAxis = new Axis { Foreground = Brushes.Black, FontSize = 14d, Title = yLabel };
chart.AxisX.Add(xAxis);
chart.AxisY.Add(yAxis);
setBinding("LabelFormatter", Formatter, xAxis, Axis.LabelFormatterProperty);
setBinding("Labels", Labels, yAxis, Axis.LabelsProperty);
setBinding("Series", SeriesCollection, chart, CartesianChart.SeriesProperty);
Content = chart;
}
public void setBinding(string propertyName, object source, FrameworkElement control, DependencyProperty dependencyProperty)
{
Binding binding = new Binding(propertyName)
{
Source = source
};
control.SetBinding(dependencyProperty, binding);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void notifyPropertyChanged(string prop)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
}
internal class BarChart : AbstractGenericBarChart<TopTransactingCount>
{
public override void constructChart(List<TopTransactingCount> chartItems)
{
SeriesCollection = new SeriesCollection
{
new RowSeries
{
Title = "Transaction Count",
Values = new ChartValues<long>(chartItems.Select(x=>x.TransCount))
}
};
Labels = chartItems.Select(x => x.Date.ToShortDateString()).ToArray();
Formatter = value => value.ToString();
DataContext = this;
}
}
At startup I see an empty chart control which is expected.
I call constructChart method on submit button click.
public partial class TotalTransCountsChart : UserControl, IChart
{
private BarChart chart = new BarChart();
List<object> chartData;
public TotalTransCountsChart()
{
InitializeComponent();
}
public void init(List<object> chartData)
{
this.chartData = chartData;
chart.init("Transaction Count", "Date");
chart.constructChart(chartData.Cast<TopTransactingCount>().ToList());
grid.Children.Add(chart);
Grid.SetRow(chart, 3);
}
private void CmdSubmit_Click(object sender, System.Windows.RoutedEventArgs e)
{
chart.constructChart(chartData.Cast<TopTransactingCount>().ToList());
}
}
However, the chart still remains empty. I think the binding part in the code is not working as expected. I am stuck at this point.
it looks like you are creating Bindings incorrectly (try to confirm it from Visual Studio Output window - it reports messages about incorrect bindings).
for example:
setBinding("Labels", Labels, yAxis, Axis.LabelsProperty);
public void setBinding(string propertyName, object source, FrameworkElement control, DependencyProperty dependencyProperty)
{
Binding binding = new Binding(propertyName)
{
Source = source
};
control.SetBinding(dependencyProperty, binding);
}
Labels is a string[], and you have a binding , which attempts to use "Labels" property - which doesn't exist.
you need a valid binding source, most likely DataContext:
setBinding("LabelFormatter", DataContext, xAxis, Axis.LabelFormatterProperty);
setBinding("Labels", DataContext, yAxis, Axis.LabelsProperty);
setBinding("Series", DataContext, chart, CartesianChart.SeriesProperty);
or better yet - don't specify source and all bindings will connect to current DataContext, even if it updates:
public void setBinding(string propertyName, object source, FrameworkElement control, DependencyProperty dependencyProperty)
{
Binding binding = new Binding(propertyName);
control.SetBinding(dependencyProperty, binding);
}
Related
I am using the VS2017 IDE, and DotNet4.5.2 and WinForm frameworks.
I want to display a value with a progress bar, so I wrote the following code and it works:
progressBar1.DataBindings.Add(nameof(ProgressBar.Maximum), source, nameof(source.Maximum));
progressBar1.DataBindings.Add(nameof(ProgressBar.Minimum), source, nameof(source.Minimum));
progressBar1.DataBindings.Add(nameof(ProgressBar.Value), source, nameof(source.Status)).Format += (sender,e) => {
e.Value = (int)(e.Value as IStatus).Value;
};
I want to use the ToolStripProgressBar to display it, but because it cannot perform data binding, I defined a new class to inherit it and implement IBindableComponent as follows:
public class BindableToolStripProgressBar : ToolStripProgressBar, IBindableComponent
{
private BindingContext bindingContext;
private ControlBindingsCollection dataBindings;
public BindingContext BindingContext
{
get
{
if (bindingContext == null)
{
bindingContext = new BindingContext();
}
return bindingContext;
}
set
{
bindingContext = value;
}
}
public ControlBindingsCollection DataBindings
{
get
{
if (dataBindings == null)
{
dataBindings = new ControlBindingsCollection(this);
}
return dataBindings;
}
}
}
Then I changed the data binding object from ProgressBar to the newly defined BindableToolStripProgressBar, but this time running the application results in the throwing of a System.FormatException:
tspb.DataBindings.Add(nameof(ProgressBar.Maximum), source, nameof(source.Maximum));
tspb.DataBindings.Add(nameof(ProgressBar.Minimum), source, nameof(source.Minimum));
tspb.DataBindings.Add(nameof(ProgressBar.Value), source, nameof(source.Status)).Format += (sender, e) => {
e.Value = (int)(e.Value as IStatus).Value; // Will be thrown FormatException
};
Is there anything wrong with my implementation of IBindableComponent?
Thanks in advace!
ToolStripProgressBar has a ProgessBar property which exposes the ProgressBar control which is hosted in the ToolStripControlHost. Maximum, Minimum and Value properties of the ToolStripProgressBar basically get or set corresponding property of the underlying ProgressBar.
So you can setup data-binding to the underlying ProgressBar:
Source source = new Source() { Minimum = 0, Maximum = 100, Value = 50 };
private void Form1_Load(object sender, EventArgs e)
{
var p = toolStripProgressBar1.ProgressBar;
p.DataBindings.Add(nameof(p.Maximum), source, nameof(source.Maximum));
p.DataBindings.Add(nameof(p.Minimum), source, nameof(source.Minimum));
p.DataBindings.Add(nameof(p.Value), source, nameof(source.Value));
}
Making a Windows 8: Universal Application, I have the following question:
Why the Height of each control in MyGridView.Items does not change after the new value is assigned to height in MyGridView_SelectionChanged method? Within the OnNavigatedTo method it sets the Height value correctly...
public sealed partial class NewPage : Page
{
public double height;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
foreach (var card in e.Parameter as List<MyClass>)
{
MyControl myControl = new MyControl(card);
BindingOperations.SetBinding(myControl, HeightProperty, new Binding { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, Mode = BindingMode.TwoWay, Source = this, Path = new PropertyPath("height") });
MyGridView.Items.Add(myControl);
}
}
private void MyGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
height = 100;
}
}
I also tried with the DependencyProperty:
public static readonly DependencyProperty MyGridViewItemHeightProperty = DependencyProperty.Register("MyGridViewItemHeight", typeof(double), typeof(GridViewItem), null);
public double MyGridViewItemHeight
{
set { SetValue(MyGridViewItemHeightProperty, value); }
get { return (double)GetValue(MyGridViewItemHeightProperty); }
}
and
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
foreach (var card in e.Parameter as List<MyClass>)
{
MyControl myControl = new MyControl(card);
BindingOperations.SetBinding(myControl, HeightProperty, new Binding { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, Mode = BindingMode.TwoWay, Source = this, Path = new PropertyPath("MyGridViewItemHeightProperty") });
}
}
but it gave the same result...
I am new to programming. I am trying to bing index value of an element in scrollview to a scrollview property called ScrollToAsync, so that it will automatically centre the selected element of scrollview.
Below is my code for INotifyPropertyChanged
class LVItem : INotifyPropertyChanged
{
private int _tIndex;
public int TIndex
{
get { return _tIndex; }
internal set
{
_tIndex = value;
OnPropertyChanged("TIndex");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and the ScrollToAsnyc property of ScrollView
scroll.ScrollToAsync(sLayout.Children[index], ScrollToPosition.Center, true);
I want here 'index' to be binded..
can we bind it? If yes, how? please help me on this..
Below is the code of horizontal scrollview
//horizontal list
StackLayout sLayout = new StackLayout()
{
Orientation = StackOrientation.Horizontal
};
for (int i = 0; i<itemLst.Count; i++)
{
Label label = new Label()
{
HorizontalTextAlignment = TextAlignment.Center,
TextColor = Color.Black,
FontSize = Device.GetNamedSize(NamedSize.Medium, new Label())
};
label.Text = itemLst[i];
gestureRecognizer = new TapGestureRecognizer
{
Command = new Command(TapL_Tapped),
CommandParameter = label,
};
label.GestureRecognizers.Add(gestureRecognizer);
sLayout.Children.Add(label);
}
ScrollView scroll = new ScrollView
{
Orientation = ScrollOrientation.Horizontal,
Content = new StackLayout
{
Children =
{
sLayout
}
}
};
You can create a custom ScrollView with a custom bindable Index property.
public class CustomScrollView : ScrollView
{
public static readonly BindableProperty IndexProperty = BindableProperty.Create (
propertyName: nameof (Index),
returnType: typeof (int),
declaringType: typeof (CustomScrollView),
defaultValue: 0,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: HandleIndexChanged
);
static void HandleIndexChanged (BindableObject bindable, object oldValue, object newValue)
{
var view = (CustomScrollView)bindable;
view.Index = (int)newValue;
// Call your view.ScrollToAsync here.
// Depending on the structure of your XAML you could call
// it using view.Content to go through the control tree.
// Such as (view.Content as StackLayout).Children[newValue]
}
public int Index
{
get { return (int)GetValue (IndexProperty); }
set { SetValue (IndexProperty, value); }
}
}
You can then reference this custom ScrollView in your XAML page instead of the normal one by adding this part to your ContentPage declaration:
xmlns:custom="clr-namespace:[YourNamespace];assembly=[YourAssembly]"
And referencing the control as follows:
<custom:CustomScrollView Index="{Binding TIndex}" />
Please assume this entire question deals in code, without any XAML.
I have a static ObservableCollection named myStaticList. It's a part of a non-static class named myClass.
public class myClass
{
public static ObservableCollection<CheckBoxStructure> myStaticList { get; set; }
static myClass()
{
myStaticList = new ObservableCollection<CheckBoxStructure>();
}
}
And the definition of CheckBoxStructure:
public class CheckBoxStructure
{
public string Description { get; set; }
public bool IsSelected { get; set; }
}
In addition, there's an array of checkboxes called checkBoxArray[], holding 3 elements. each checkbox has as content a textbox.
What I want to do is programmatically bind (two-way) these two, in such a manner that the IsChecked property of the checkboxes in the checkBoxArray[] array will bind to the IsSelected property of the myStaticList's CheckBoxStructure, and similarly so between the text of the textboxes inthe checkboxes' content and the Description property of the myStaticList's CheckBoxStructure.
In addition, I would like to avoid using loops, since it is preferable that this two lists will update each other if they change in size.
How is this possible?
Thanks!
Using XAML, an easy way would be to the declare an ItemsControl and a DataTemplate for it so that you can have a UserControl (CheckBox and TextBox inside) with its DataContext being a CheckBoxStructure. This way the bindings work between CheckBox.IsChecked and IsSelected property and between TextBox.Text and Description property.
If you need to this only in code then you would have to create same behavior (ItemsControl with a DataTemplate). You have at least 2 options
1.
DataTemplate template = new DataTemplate();
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(StackPanel));
template.VisualTree = factory;
FrameworkElementFactory childFactory = new FrameworkElementFactory(typeof(CheckBox));
childFactory.SetBinding(CheckBox.IsChecked, new Binding("IsSelected"));
factory.AppendChild(childFactory);
childFactory = new FrameworkElementFactory(typeof(TextBox));
childFactory.SetBinding(Label.ContentProperty, new Binding("Description"));
factory.AppendChild(childFactory);
2.
MemoryStream sr = null;
ParserContext pc = null;
string xaml = string.Empty;
xaml = "<DataTemplate><StackPanel><TextBlock Text="{Binding Description"/><CheckBox IsChecked="{Binding IsSelected"/></StackPanel></DataTemplate>";
sr = new MemoryStream(Encoding.ASCII.GetBytes(xaml));
pc = new ParserContext();
pc.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
pc.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
DataTemplate datatemplate = (DataTemplate)XamlReader.Load(sr, pc);
this.Resources.Add("dt", datatemplate);
Later edit, after discussion from comments; this example works only one way of binding but is easily to make it two ways. Please note that this is only a trivial example of a concept and is not complete: you need to modify the list classes to suit how you wish for objects to be paired, you may need to add more guards for corner cases, you may need to make it thread safe and so on...
First the basic binding objects:
class Binder
{
public Binder()
{
_bindings = new Dictionary<string, List<string>>();
}
private INotifyPropertyChanged _dataContext;
public INotifyPropertyChanged DataContext
{
get { return _dataContext; }
set
{
if (_dataContext != null)
{
_dataContext.PropertyChanged -= _dataContext_PropertyChanged;
}
_dataContext = value;
_dataContext.PropertyChanged += _dataContext_PropertyChanged;
}
}
void _dataContext_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_bindings.ContainsKey(e.PropertyName))
{
var bindableType = _dataContext.GetType();
var bindableProp = bindableType.GetProperty(e.PropertyName);
if (bindableProp == null)
{
return;
}
var binderType = this.GetType();
foreach (var binderPropName in _bindings[e.PropertyName])
{
var binderProp = binderType.GetProperty(binderPropName);
if (binderProp == null)
{
continue;
}
var value = bindableProp.GetValue(_dataContext);
binderProp.SetValue(this, value);
}
}
}
Dictionary<string, List<string>> _bindings;
public void AddBinding(string binderPropertyName, string bindablePropertyName)
{
if (!_bindings.ContainsKey(bindablePropertyName))
{
_bindings.Add(bindablePropertyName, new List<string>());
}
_bindings[bindablePropertyName].Add(bindablePropertyName);
}
}
class Bindable : INotifyPropertyChanged
{
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then the holding lists for them:
class BindableList<T> : List<T> where T : Bindable
{
public event Action<T> ItemAdded;
public new void Add(T item)
{
base.Add(item);
NotifyItemAdded(item);
}
private void NotifyItemAdded(T item)
{
if (ItemAdded != null)
{
ItemAdded(item);
}
}
}
class BinderList<T> : List<T> where T : Binder
{
public BinderList()
{
_bindingRules = new Dictionary<string, string>();
}
private BindableList<Bindable> _dataContextList;
public BindableList<Bindable> DataContextList
{
get { return _dataContextList; }
set
{
if (_dataContextList != null)
{
_dataContextList.ItemAdded -= _dataContextList_ItemAdded;
}
_dataContextList = value;
_dataContextList.ItemAdded += _dataContextList_ItemAdded;
}
}
void _dataContextList_ItemAdded(Bindable obj)
{
foreach (var pair in _bindingRules)
{
this[Count-1].AddBinding(pair.Key, pair.Value);
this[Count - 1].DataContext = obj;
}
}
private Dictionary<string, string> _bindingRules;
public void AddBindingRule(string binderPropertyName, string bindablePropertyName)
{
_bindingRules.Add(binderPropertyName, bindablePropertyName);
}
}
Now the actual classes with properties:
class BinderElement : Binder
{
private string _description;
public string Description
{
get { return _description; }
set { _description = value; }
}
}
class BindableElement : Bindable
{
private string _description;
public string Description
{
get
{
return _description;
}
set
{
_description = value;
NotifyPropertyChanged("Description");
}
}
}
And an example to use them:
static void Main(string[] args)
{
var bindableList = new BindableList<Bindable>();
var binderList = new BinderList<BinderElement>()
{
new BinderElement(),
new BinderElement()
};
binderList.DataContextList = bindableList;
binderList.AddBindingRule("Description", "Description");
bindableList.Add(new BindableElement());
bindableList.Add(new BindableElement());
((BindableElement)bindableList[1]).Description = "This should arrive in BinderElement Description property";
Console.WriteLine(binderList[1].Description);
Console.ReadLine();
}
I'm using the WPF Extended Toolkit Property Grid. I'm using an editor user control to display a list of objects, it looks like this:
My user wants to be able to change the number in the "Elements" description, and have the code adjust the number of elements in the list.
{ removed incomplete code, see answer below for working code }
Is there some way to put my own control to replace the label that says "3 elements" with a TextBox so I can process changes to the text and change my arrays?
Thanks,
David
Ok, sorry for the delay, but here is the working code for my question...I hope it helps someone.
As a recap, what I wanted to do, was produce a PropertyGrid Entry that looks like this:
In summary, what you need to do is create two User Control property Grid Editors, one for the summary line (the spin box and element label above), and another for the data list. All of the associated code is below:
So, to start, here is the Element class:
public class Element
{
public Element(int number, double wtf)
{
Number = number;
WTF = wtf;
}
public int Number { get; set; }
public double WTF { get; set; }
}
I also have a View Model for the Element:
public class ElementViewModel : XTRRABase
{
public Element _element;
public ElementViewModel(Element element)
{
_element = element;
}
public int Number
{
get { return _element.Number; }
set { _element.Number = value; NotifyPropertyChanged(); }
}
public double WTF
{
get { return _element.WTF; }
set { _element.WTF = value; NotifyPropertyChanged(); }
}
public String ElementInfo
{
get { return XTRRAApp.Application.AtomicElementList.GetElements()[Number]; }
set { }
}
}
The ElementInfo property returns the element name (like "6 (Carbon)" in the example).
In the parent view model (the object containing the Elements property), the property looks like this:
ElementListViewModel _elements;
[PropertyOrder(4), DisplayName("Elements")]
[ExpandableObject]
[Editor(typeof(ElementHeaderUCEditor), typeof(ElementHeaderUCEditor))]
public ElementListViewModel Elements
{
get { return (_elements = new ElementListViewModel(_material.Elements) ); }
set {}
}
Note that this object is both ExpandableObject and has a defined editor ElementHeaderUCEditor
The ElementHeaderUCEditor defines the IntegerUpDown spin box and the 'elements' label. It's XAML looks like this:
<UserControl x:Class="XTRRAApp.View.Editors.ElementHeaderUCEditor"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
<StackPanel Orientation="Horizontal" Background="White">
<xctk:IntegerUpDown Text="{Binding Value.Count,UpdateSourceTrigger=PropertyChanged}" Width="100" Margin="2,2,2,2" ParsingNumberStyle="Integer"/>
<Label Content="Elements" Width="Auto" Margin="2,2,2,2"/>
</StackPanel>
</UserControl>
and the code-behind:
public partial class ElementHeaderUCEditor : UserControl, ITypeEditor
{
public ElementHeaderUCEditor()
{
InitializeComponent();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(ElementListViewModel), typeof(ElementHeaderUCEditor),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public ElementListViewModel Value
{
get { return (ElementListViewModel)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
Binding binding = new Binding("Value");
binding.Source = propertyItem;
binding.Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
BindingOperations.SetBinding(this, ElementHeaderUCEditor.ValueProperty, binding);
return this;
}
}
Next, here is the ElementListViewModel which provides the data for the List:
[DisplayName("Elements")]
public class ElementListViewModel : XTRRABase
{
protected List<Element> _elements;
public ElementListViewModel(List<Element> elements)
{
_elements = elements;
}
[Browsable(false)]
public int Count
{
get { return _elements.Count; }
set
{
while(value < _elements.Count)
{
_elements.RemoveAt(_elements.Count - 1);
}
while(value > _elements.Count)
{
_elements.Add(new Element(0,0));
}
NotifyPropertyChanged();
NotifyPropertyChanged("Elements");
}
}
[PropertyOrder(1), DisplayName("Elements")]
[Editor(typeof(ElementUCEditor), typeof(ElementUCEditor))]
public ObservableCollection<ElementViewModel> Elements
{
get
{
ObservableCollection<ElementViewModel> list = new ObservableCollection<ElementViewModel>();
foreach(Element element in _elements)
{
list.Add(new ElementViewModel(element));
}
return list;
}
set { }
}
}
XTRRABase is just a common base class I use to avoid duplicating the notification code:
public abstract class XTRRABase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The Elements, as you can see are displayed using another User Control `ElementUCEditor' it's XAML looks like this:
and the code-behind for it:
public partial class ElementUCEditor : UserControl, ITypeEditor
{
public ElementUCEditor()
{
InitializeComponent();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(ElementListViewModel), typeof(ElementUCEditor),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public ElementListViewModel Value
{
get { return (ElementListViewModel)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
Binding binding = new Binding("Value");
binding.Source = propertyItem;
binding.Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
BindingOperations.SetBinding(this, ElementUCEditor.ValueProperty, binding);
return this;
}
}
I used a propertyGrid as CustomEditor:
public partial class PropertyGridEditor : ITypeEditor
{
public PropertyGridEditor()
{
InitializeComponent();
}
public FrameworkElement ResolveEditor(PropertyItem propertyItem)
{
if (propertyItem.Value != null)
{
var objects = propertyItem.Value;
foreach (var o in (IEnumerable)objects)
{
var propertyGrid = new Xceed.Wpf.Toolkit.PropertyGrid.PropertyGrid
{
IsCategorized = false,
IsMiscCategoryLabelHidden = true,
ShowAdvancedOptions = false,
ShowDescriptionByTooltip = true,
ShowPreview = false,
ShowSearchBox = false,
ShowSortOptions = false,
ShowTitle = true,
ShowSummary = false,
SelectedObject = o,
};
Container.Children.Add(propertyGrid);
}
}
return this;
}
}
XAML
<UserControl x:Class="ibKastl.Helper.UserControls.PropertyGrid.Editor.PropertyGridEditor"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical" Name="Container">
</StackPanel>
</UserControl>
So you can add any Enumerable to a Property grid