views:

33

answers:

0

Hi,

I want to create a canvas where a user can drop UI elements (representing tasks). He can then drag them to rearrange them. The elements are contained in an ObservableCollection that is the DataContext.

I can set the Left and Top properties of the Canvas, but the objects position is not affected. Any ideas?

Thanks,

Karel

UPDATE: forgotton ItemsControl descendant (thanks Phil):

 public class CustomItemsCollection    : ItemsControl
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {            
        FrameworkElement contentitem = element as FrameworkElement;
        // contentitem.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
        // contentitem.VerticalAlignment = System.Windows.VerticalAlignment.Top;
        Binding leftBinding = new Binding("Left"); 
        leftBinding.Mode = BindingMode.TwoWay;

        contentitem.SetBinding(Canvas.LeftProperty, leftBinding);

        Binding topBinding = new Binding("Top");
        topBinding.Mode = BindingMode.TwoWay;
        contentitem.SetBinding(Canvas.TopProperty, topBinding);
        base.PrepareContainerForItemOverride(element, item);
    }
}

More info:

Binding objects derived from usercontrol provoce an exception so after some googling I created a valueconverter:

public class UIElementWrapper : IValueConverter
{
    private Dictionary<object, object> CollectionsPool = new Dictionary<object, object>();

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is INotifyCollectionChanged && value is IList)
        {
            ((INotifyCollectionChanged)value).CollectionChanged += UIElementWrapper_CollectionChanged;

            var result = new ObservableCollection<ProxyObject>();
            foreach (var item in (IList)value)
            {
                result.Add(new ProxyObject(item));
            }

            CollectionsPool.Add(result, value);
            return result;
        }
        else
        {
            throw new ArgumentException("value");
        }
    }

    void UIElementWrapper_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (CollectionsPool.ContainsValue(sender))
        {
            foreach (IList result in CollectionsPool.Keys)
            {
                if (CollectionsPool[result] == sender)
                {
                    switch (e.Action)
                    {
                        case NotifyCollectionChangedAction.Add:
                            var index = e.NewStartingIndex;
                            foreach (var item in e.NewItems)
                            {
                                result.Insert(index++, new ProxyObject(item));
                            }
                            break;
                        case NotifyCollectionChangedAction.Remove:
                            foreach (var item in e.OldItems)
                            {
                                var deleteList = new List<ProxyObject>();
                                foreach (ProxyObject p in result)
                                {
                                    if (p.Value == item) deleteList.Add(p);
                                }
                                foreach (var p in deleteList)
                                {
                                    result.Remove(p);
                                }
                            }
                            break;
                        case NotifyCollectionChangedAction.Replace:
                            result[e.OldStartingIndex] = new ProxyObject(e.NewItems[0]);
                            break;
                        case NotifyCollectionChangedAction.Reset:
                            result.Clear();
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

public class ProxyObject
{
    public ProxyObject(object value)
    {
        Value = value;
    }
    public object Value { get; private set; }
}

This works. But when I bind Left and Top properties of the elements contained in the ObservableCollection they are not used: all items are placed at position 0,0

Here is the code behind:

public MainPage()
    {
        InitializeComponent();

        ObservableCollection<Border> items = new ObservableCollection<Border>();
        double left = 0.0;
        double top = 0.0;
        int i = 0;
        Border item = (Border)XamlReader.Load(
           "<Border Background=\"Green\" Height=\"140\" Width=\"180\"  xmlns=\"http://schemas.microsoft.com/client/2007\"&gt;&lt;/Border&gt;");
        item.Name = string.Format("name {0}", i);
        item.SetValue(Canvas.LeftProperty, left);
        item.SetValue(Canvas.TopProperty, top);
        items.Add(item);

        i++;
        left += 200;
        top += 150;
        item = (Border)XamlReader.Load(
            "<Border Background=\"Yellow\" Margin=\"160, 120, 0, 0\" Height=\"120\" Width=\"160\"  xmlns=\"http://schemas.microsoft.com/client/2007\"&gt;&lt;/Border&gt;");
        item.Name = string.Format("name {0}", i);
        item.SetValue(Canvas.LeftProperty, left);
        item.SetValue(Canvas.TopProperty, top);
        items.Add(item);

        i++;
        left += 170;
        top += 130;
        item = (Border)XamlReader.Load(
            "<Border Background=\"Red\" Height=\"60\" Width=\"80\"  xmlns=\"http://schemas.microsoft.com/client/2007\"&gt;&lt;/Border&gt;");
        item.Name = string.Format("name {0}", i);
        item.SetValue(Canvas.LeftProperty, left);
        item.SetValue(Canvas.TopProperty, top);
        items.Add(item);

        left += 90;
        top += 70;
        item = (Border)XamlReader.Load(
            "<Border Background=\"Blue\" Height=\"30\" Width=\"40\"  xmlns=\"http://schemas.microsoft.com/client/2007\"&gt;&lt;/Border&gt;");
        item.Name = string.Format("name {0}", i);

        item.SetValue(Canvas.LeftProperty, left);
        item.SetValue(Canvas.TopProperty, top);
        items.Add(item);



        try
        {
            // this.customItemsCollection1.ItemsSource = items;
            LayoutRoot.DataContext = items;
        }
        catch (Exception ex)
        {
            textBlock1.Text = ex.Message;
        }
    }

and here is the xaml:

<UserControl x:Class="ItemsControlTestProject.MainPage"
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:lh="clr-namespace:ItemsControlTestProject.Helpers"
         mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="560" xmlns:my="clr-namespace:ItemsControlTestProject">


<UserControl.Resources>
        <lh:UIElementWrapper x:Key="UIElementWrapper"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="AntiqueWhite">
            <my:CustomItemsCollection Canvas.Left="0" Canvas.Top="0" Background="Coral" x:Name="customItemsCollection1" Margin="0,0,0,0" Width="532" ItemsSource="{Binding Converter={StaticResource UIElementWrapper}}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas   Background="LightGoldenrodYellow" Canvas.Left="0" Canvas.Top="0" Width="350" Height="350"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ContentPresenter Content="{Binding Value}"  Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
        </my:CustomItemsCollection>
            <TextBlock  Height="61" Name="textBlock1" Text="TextBlock" AllowDrop="True" Width="526" Canvas.Left="6" Canvas.Top="367" Margin="20,388,14,0" />        
    </Grid>
</UserControl>





<UserControl.Resources>
        <lh:UIElementWrapper x:Key="UIElementWrapper"/>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="AntiqueWhite">
            <my:CustomItemsCollection Canvas.Left="0" Canvas.Top="0" Background="Coral" x:Name="customItemsCollection1" Margin="0,0,0,0" Width="532" ItemsSource="{Binding Converter={StaticResource UIElementWrapper}}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas   Background="LightGoldenrodYellow" Canvas.Left="0" Canvas.Top="0" Width="350" Height="350"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ContentPresenter Content="{Binding Value}"  Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
        </my:CustomItemsCollection>
            <TextBlock  Height="61" Name="textBlock1" Text="TextBlock" AllowDrop="True" Width="526" Canvas.Left="6" Canvas.Top="367" Margin="20,388,14,0" />        
    </Grid>
</UserControl>