1

I'm allowing user to drag/drop some objects from a toolbox and of course each object has a unique id. As soon as object is used, let's say placed on a grid or canvas, I need to show its properties so I need an array of objects where each object can hold its own properties.

Can you give me some advice and direction on how to implement a class to handle multiple objects while each object can hold on to let's say 10 properties?

akjoshi
  • 15,374
  • 13
  • 103
  • 121
amit kohan
  • 1,612
  • 2
  • 25
  • 47

2 Answers2

1

The best solution is to use a PropertyGrid control; your application looks similar to Visual Studio and your implementation will be similar to that.

Have a look at this SO question for available PropertyGrid options you have - Is there a Property Dialog control that i can use in my WPF App?

Now you can define a class for each control and declare normal CLR properties for that control; properties you don't want to display in PropertyGrid can be marked with BrowsableAttribute and PropertyGrid will honor that.

In case you want more control over what properties are displayed, you can create your own custom attribute and modify PropertyGrid implementation to use that attribute and display properties marked with this attribute.

Community
  • 1
  • 1
akjoshi
  • 15,374
  • 13
  • 103
  • 121
1

Can you give me some advice and direction on how to implement a class to handle multiple objects while each object can hold on to let's say 10 properties?

There is no need for you to implement such a class. The way I would handle this problem would be to have a common base class for all the objects in the toolbox (ToolboxItem for example) which only exposes properties and functionality common to all items in the toolbox.

public abstract class ToolboxItem
{
    public string Name { get; set; }
    public Point Position { get; set; }
}

You can then derive your specific items from this class E.G. TextToolboxItem and RectangleToolboxItem (or whatever you want). The derived classes can then expose only the properties they require.

public class TextToolboxItem : ToolboxItem
{
    public string Text { get; set; }
}

public class RectangleToolboxItem : ToolboxItem
{
    public Rect Bounds { get; set; }
}

To store these you could just use a generic collection such as:

ObservableCollection<ToolboxItem> items = new ObservableCollection<ToolboxItems>();

As long as the items derive from ToolboxItem they can all be held within the single collection and the individual properties can all be bound to using WPF's data binding features.

You can then create and expose the data in the following way:

public partial class MainWindow : Window
{
    private ObservableCollection<ToolboxItem> items;

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;

        items = new ObservableCollection<ToolboxItem>
        {
            new TextToolboxItem { Name = "primaryText", 
                                  Text = "Hello world", 
                                  Position = new Point(40, 130) },
            new TextToolboxItem { Name = "secondaryText", 
                                  Text = "Hello world (again)", 
                                  Position = new Point(200, 30) },
            new RectangleToolboxItem { Position = new Point(50,300), 
                                       Name = "Rect1", 
                                       Bounds = new Rect(0, 0, 150, 85) },
        };
    }

    public ObservableCollection<ToolboxItem> Items { get { return items; } }
}

To display this information in the user interface I would do the following:

  • Use a grid to split the view into two sections. The first is where the properties of the selected item will be displayed and the second displays the 'design surface'
  • Use a ContentPresenter to display the properties of the selected item.
  • Use a ListBox with a custom ItemsPanel and ItemContainerStyle to 'draw' your items onto the design surface.
  • Use a DataTemplate to tell WPF how to render each item in both the 'property grid' and the 'design surface' (This post describes how to use a different DataTemplate for different objects).

The xaml required to achieve this is shown below:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:this="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="3*" />
        <ColumnDefinition Width="7*" />
    </Grid.ColumnDefinitions>

    <ContentPresenter Content="{Binding ElementName=listBox, Path=SelectedItem}"
                      Margin="5">
        <ContentPresenter.Resources>
            <DataTemplate DataType="{x:Type this:TextToolboxItem}">
                <StackPanel>
                    <TextBlock Text="{Binding Name}"/>
                    <TextBlock Text="{Binding Position}"/>
                    <TextBlock Text="{Binding Text}"/>
                </StackPanel>
            </DataTemplate>
            <DataTemplate DataType="{x:Type this:RectangleToolboxItem}">
                <StackPanel>
                    <TextBlock Text="{Binding Name}"/>
                    <TextBlock Text="{Binding Position}"/>
                    <TextBlock Text="{Binding Bounds}"/>
                </StackPanel>
            </DataTemplate>
        </ContentPresenter.Resources>
    </ContentPresenter>

    <ListBox x:Name="listBox" Grid.Column="1"
             Margin="5" ItemsSource="{Binding Items}">
        <ListBox.Resources>
            <DataTemplate DataType="{x:Type this:TextToolboxItem}">
                <TextBox Text="{Binding Text}"
                         Margin="10"/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type this:RectangleToolboxItem}">
                <Rectangle Width="{Binding Bounds.Width}"
                           Height="{Binding Bounds.Height}"
                           Stroke="DarkRed" Fill="Pink"/>
            </DataTemplate>
        </ListBox.Resources>

        <ListBox.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Left" Value="{Binding Position.X}"/>
                <Setter Property="Canvas.Top" Value="{Binding Position.Y}"/>
            </Style>
        </ListBox.ItemContainerStyle>

        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>

</Grid>

The end result looks like this:

enter image description here

Notice that the properties of the selected item are shown in the left hand section of the window.

Now this solution is currently very crude but does demonstrate a starting point for you to develop this further. Ideas for improvement include:

  • Re-factoring the code into a viewModel so that it is MVVM compliant.
  • Handling drag and drop of the items on the 'design surface'.
  • Changing the `ContentPresenter' for a property grid to give you much richer support for displaying and editing the properties of the selected object.
Community
  • 1
  • 1
Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100