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.
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.
<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>
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.
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 .
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}"/>
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