Czy jest możliwe Związanie właściwości Dzieci Canvasa w XAML?

Jestem trochę zaskoczony, że nie można założyć oprawy dla Canvas.Dzieci przez XAML. Musiałem uciekać się do podejścia code-behind, które wygląda mniej więcej tak:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    DesignerViewModel dvm = this.DataContext as DesignerViewModel;
    dvm.Document.Items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Items_CollectionChanged);

    foreach (UIElement element in dvm.Document.Items)
        designerCanvas.Children.Add(element);
}

private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    ObservableCollection<UIElement> collection = sender as ObservableCollection<UIElement>;

    foreach (UIElement element in collection)
        if (!designerCanvas.Children.Contains(element))
            designerCanvas.Children.Add(element);

    List<UIElement> removeList = new List<UIElement>();
    foreach (UIElement element in designerCanvas.Children)
        if (!collection.Contains(element))
            removeList.Add(element);

    foreach (UIElement element in removeList)
        designerCanvas.Children.Remove(element);
}

Wolałbym po prostu ustawić Wiązanie w XAML TAK:

<Canvas x:Name="designerCanvas"
        Children="{Binding Document.Items}"
        Width="{Binding Document.Width}"
        Height="{Binding Document.Height}">
</Canvas>

Czy istnieje sposób, aby to osiągnąć bez uciekania się do podejścia opartego na kodach? Trochę wygooglowałem na ten temat, ale nie mam zbyt wiele na ten konkretny problem.

I don ' t like my obecne podejście, ponieważ mucksuje mój ładny Model-View-ViewModel, uświadamiając Widok o jego ViewModel.

Author: H.B., 2009-05-20

5 answers

Nie wierzę, że możliwe jest użycie wiązania z własnością dzieci. Próbowałem to dzisiaj zrobić i to mnie wkurzyło, tak jak Ciebie.

Płótno jest bardzo prymitywnym pojemnikiem. To naprawdę nie jest przeznaczone do tego rodzaju pracy. Powinieneś zajrzeć do jednego z wielu Przedmiotówkontrole. Można powiązać ObservableCollection modeli danych w modelu ViewModel z ich właściwością ItemsSource i użyć DataTemplates do obsługi sposobu renderowania każdego z elementów w Kontrola.

Jeśli nie możesz znaleźć kontrolera ItemsControl, który renderuje twoje elementy w zadowalający sposób, być może będziesz musiał utworzyć niestandardową kontrolkę, która zrobi to, czego potrzebujesz.

 12
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-20 20:05:07
<ItemsControl ItemsSource="{Binding Path=Circles}">
    <ItemsControl.ItemsPanel>
         <ItemsPanelTemplate>
              <Canvas Background="White" Width="500" Height="500"  />
         </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Ellipse Fill="{Binding Path=Color, Converter={StaticResource colorBrushConverter}}" Width="25" Height="25" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
            <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>
 143
Author: kenwarner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-23 01:18:19

Inni udzielili rozszerzalnych odpowiedzi na temat tego, jak zrobić to, co naprawdę chcesz zrobić. Wyjaśnię tylko, dlaczego nie możesz związać Children bezpośrednio.

Problem jest bardzo prosty - obiekt docelowy wiązania danych nie może być właściwością tylko do odczytu, a Panel.Children jest tylko do odczytu. Nie ma tam specjalnej obsługi zbiorów. W przeciwieństwie do tego, ItemsControl.ItemsSource jest właściwością do odczytu/zapisu, nawet jeśli jest typu collection-rzadkim zdarzeniem dla klasy. NET, ale wymaganym do obsługi scenariusza wiązania.

 26
Author: Pavel Minaev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 21:57:06

ItemsControl jest przeznaczony do tworzenia dynamicznych kolekcji kontrolek interfejsu użytkownika z innych kolekcji, nawet spoza kolekcji danych interfejsu użytkownika.

Możesz użyć szablonu an ItemsControl, aby narysować na Canvas. Idealny sposób polegałby na ustawieniu panelu tylnego na Canvas, a następnie ustawieniu właściwości Canvas.Left i Canvas.Top na bezpośrednich dzieciach. Nie mogłem tego uruchomić, ponieważ ItemsControl owija swoje dzieci kontenerami i trudno jest ustawić właściwości Canvas na tych kontenerach.

Zamiast tego używam a Grid jako kosz na wszystkie przedmioty i narysuj je każdy na własną rękę Canvas. Istnieje kilka kosztów z tego podejścia.

<ItemsControl x:Name="Collection" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:MyPoint}">
            <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <Ellipse Width="10" Height="10" Fill="Black" Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"/>
            </Canvas>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Oto kod, którego użyłem do skonfigurowania kolekcji źródłowej:

List<MyPoint> points = new List<MyPoint>();

points.Add(new MyPoint(2, 100));
points.Add(new MyPoint(50, 20));
points.Add(new MyPoint(200, 200));
points.Add(new MyPoint(300, 370));

Collection.ItemsSource = points;

MyPoint jest klasą niestandardową, która zachowuje się tak samo jak wersja System. Stworzyłem go, aby zademonstrować, że możesz używać własnych klas niestandardowych.

Ostatni szczegół: możesz powiązać właściwość ItemsSource z dowolną kolekcją. Na przykład:

<ItemsControls ItemsSource="{Binding Document.Items}"><!--etc, etc...-->

Aby uzyskać więcej informacji na temat ItemsControl i jak to działa, zapoznaj się z tymi dokumentami: odniesienie do biblioteki MSDN; Szablony danych; Seria Dr WPF o ItemsControl .

 20
Author: Josh G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-30 15:10:19
internal static class CanvasAssistant
{
    #region Dependency Properties

    public static readonly DependencyProperty BoundChildrenProperty =
        DependencyProperty.RegisterAttached("BoundChildren", typeof (object), typeof (CanvasAssistant),
                                            new FrameworkPropertyMetadata(null, onBoundChildrenChanged));

    #endregion

    public static void SetBoundChildren(DependencyObject dependencyObject, string value)
    {
        dependencyObject.SetValue(BoundChildrenProperty, value);
    }

    private static void onBoundChildrenChanged(DependencyObject dependencyObject,
                                               DependencyPropertyChangedEventArgs e)
    {
        if (dependencyObject == null)
        {
            return;
        }
        var canvas = dependencyObject as Canvas;
        if (canvas == null) return;

        var objects = (ObservableCollection<UIElement>) e.NewValue;

        if (objects == null)
        {
            canvas.Children.Clear();
            return;
        }

        //TODO: Create Method for that.
        objects.CollectionChanged += (sender, args) =>
                                            {
                                                if (args.Action == NotifyCollectionChangedAction.Add)
                                                    foreach (object item in args.NewItems)
                                                    {
                                                        canvas.Children.Add((UIElement) item);
                                                    }
                                                if (args.Action == NotifyCollectionChangedAction.Remove)
                                                    foreach (object item in args.OldItems)
                                                    {
                                                        canvas.Children.Remove((UIElement) item);
                                                    }
                                            };

        foreach (UIElement item in objects)
        {
            canvas.Children.Add(item);
        }
    }
}

I używając:

<Canvas x:Name="PART_SomeCanvas"
        Controls:CanvasAssistant.BoundChildren="{TemplateBinding SomeItems}"/>
 14
Author: Ivan Shikht,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-20 12:37:00