1

我想创建一个画布,用户可以在其中放置 UI 元素(代表任务)。然后他可以拖动它们以重新排列它们。这些元素包含在作为 DataContext 的 ObservableCollection 中。

我可以设置 Canvas 的 Left 和 Top 属性,但对象位置不受影响。有任何想法吗?

谢谢,

卡雷尔

更新:忘记 ItemsControl 后代(感谢菲尔):

 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);
    }
}

更多信息:

从 usercontrol 派生的绑定对象会引发异常,因此经过一番谷歌搜索后,我创建了一个 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; }
}

这行得通。但是当我绑定 ObservableCollection 中包含的元素的 Left 和 Top 属性时,它们没有被使用:所有项目都放置在位置 0,0

这是后面的代码:

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\"></Border>");
        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\"></Border>");
        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\"></Border>");
        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\"></Border>");
        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;
        }
    }

这是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>
4

1 回答 1

2

问题是您在绑定之前设置了依赖属性 Canvas.Top,方法是将其添加到集合中。在此“添加”之前,您要绑定的对象没有属性 Canvas.Top

只需更改顺序

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

..它应该工作

/尼克尔

于 2011-09-01T06:00:12.823 回答