XAML에서 Canvas의 Children 속성을 바인딩 할 수 있습니까?
XAML을 통해 Canvas.Children에 대한 바인딩을 설정할 수 없다는 것에 조금 놀랐습니다. 다음과 같은 코드 숨김 접근 방식을 사용해야했습니다.
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);
}
차라리 다음과 같이 XAML에서 바인딩을 설정하고 싶습니다.
<Canvas x:Name="designerCanvas"
Children="{Binding Document.Items}"
Width="{Binding Document.Width}"
Height="{Binding Document.Height}">
</Canvas>
코드 숨김 접근 방식을 사용하지 않고이를 수행 할 수있는 방법이 있습니까? 나는 주제에 대해 인터넷 검색을 수행했지만이 특정 문제에 대해 많은 것을 얻지 못했습니다.
View가 ViewModel을 인식하도록하여 멋진 Model-View-ViewModel을 엉망으로 만들기 때문에 현재 접근 방식이 마음에 들지 않습니다.
나는 Children 속성에 바인딩을 사용할 수 있다고 생각하지 않습니다. 사실 오늘 그렇게하려고했는데 당신처럼 오류가 났어요.
Canvas는 매우 초보적인 컨테이너입니다. 실제로 이런 종류의 작업을 위해 설계되지 않았습니다. 많은 ItemsControl 중 하나를 살펴 봐야합니다. ViewModel의 ObservableCollection 데이터 모델을 ItemsSource 속성에 바인딩하고 DataTemplate을 사용하여 컨트롤에서 각 항목이 렌더링되는 방식을 처리 할 수 있습니다.
만족스러운 방식으로 항목을 렌더링하는 ItemsControl을 찾을 수없는 경우 필요한 작업을 수행하는 사용자 지정 컨트롤을 만들어야 할 수 있습니다.
<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>
다른 사람들은 이미 실제로하고 싶은 일을하는 방법에 대해 확장 가능한 답변을 제공했습니다. Children
직접 바인딩 할 수없는 이유를 설명하겠습니다 .
문제는 매우 간단합니다. 데이터 바인딩 대상은 읽기 전용 속성이 될 수 없으며 Panel.Children
읽기 전용입니다. 컬렉션에 대한 특별한 처리는 없습니다. 대조적으로, ItemsControl.ItemsSource
은 (는) 컬렉션 유형이지만 읽기 / 쓰기 속성입니다. .NET 클래스에서는 드물지만 바인딩 시나리오를 지원하기 위해 필요합니다.
ItemsControl
다른 컬렉션, 심지어 비 UI 데이터 컬렉션에서 UI 컨트롤의 동적 컬렉션을 생성하도록 설계되었습니다.
템플릿 ItemsControl
을 사용하여 Canvas
. 이상적인 방법은 배경 패널을 a Canvas
로 설정 한 다음 직계 자식에 대해 Canvas.Left
및 Canvas.Top
속성 을 설정하는 것 입니다. ItemsControl
자식을 컨테이너로 감싸고 Canvas
이러한 컨테이너에 속성 을 설정하기가 어렵 기 때문에이 작업을 수행 할 수 없습니다 .
대신를 Grid
모든 항목에 대한 저장소로 사용 하고 각 항목을 개별적으로 그립니다 Canvas
. 이 접근 방식에는 약간의 오버 헤드가 있습니다.
<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>
소스 컬렉션을 설정하는 데 사용한 코드는 다음과 같습니다.
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
System
버전 처럼 동작하는 사용자 정의 클래스입니다 . 사용자 정의 클래스를 사용할 수 있음을 보여주기 위해 만들었습니다.
마지막 세부 사항 : ItemsSource 속성을 원하는 컬렉션에 바인딩 할 수 있습니다. 예를 들면 :
<ItemsControls ItemsSource="{Binding Document.Items}"><!--etc, etc...-->
ItemsControl 및 작동 방식에 대한 자세한 내용은 다음 문서를 확인하십시오. MSDN Library Reference ; 데이터 템플릿 ; ItemsControl에 대한 Dr WPF의 시리즈 .
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);
}
}
}
그리고 사용 :
<Canvas x:Name="PART_SomeCanvas"
Controls:CanvasAssistant.BoundChildren="{TemplateBinding SomeItems}"/>
'Program Tip' 카테고리의 다른 글
iPhone에서 Core Data 프로그램에 대한 고유 ID 생성 (0) | 2020.12.13 |
---|---|
자바 스크립트 : 하나의 값을 여러 값과 비교하는 가장 예쁜 방법 (0) | 2020.12.13 |
C ++에서 int에서 상속 할 수없는 이유는 무엇입니까? (0) | 2020.12.13 |
루비에서 사용자의 홈 디렉토리를 얻는 크로스 플랫폼 수단? (0) | 2020.12.13 |
하나의 문에 이름이있는 숫자 형 벡터를 만드시겠습니까? (0) | 2020.12.13 |