也许这是一个简单的问题,但我找不到答案。 我有三个用户控件,只有颜色不同。其中有一个代码:
<UserControl x:Class="SilverlightApplication14.NodePicture"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SilverlightApplication14">
<UserControl.Resources>
<local:NodeViewModel x:Key="Children" />
</UserControl.Resources>
<Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
HorizontalAlignment="Center" DataContext="{Binding Source={StaticResource Children}, Path=Children}" >
<Canvas x:Name="ParentCanvas" Background="White" Width="100" Height="100" >
<Rectangle Fill="Yellow" Stroke="Blue" Width="100" Height="100" >
</Rectangle >
</Canvas>
<Image HorizontalAlignment="Center"
Source="add.png"
Stretch="Fill"
Width="16"
VerticalAlignment="Top"
Margin="0,0,2,2"
Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
</Image>
</Grid>
</UserControl>
如何将它们组合成ObservableCollection Children?
public class NodeViewModel : INotifyPropertyChanged
{
public ObservableCollection<NodeViewModel> Children
{
get { return _children; }
set
{
_children = value;
NotifyChange("Children");
}
}
private void NotifyChange(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
我如何使用此控件集合的then元素?
这样做有简单(或正确的方法)吗?
答案 0 :(得分:1)
据我所知,你有3个用户控件,其名称类似于NodePicture,GreenNodePicture和BlueNodePicture。 首先,如果3个控件的差异很小,那么最好只有一个控件使用某个属性值来切换颜色。
假设您的控件因画布上矩形的背景颜色而异。所以我会改变你的控制:
<Grid x:Name="LayoutRootNodePicture" Height="100" Width="100"
HorizontalAlignment="Center">
<Canvas x:Name="ParentCanvas" Background="{Binding NodeColor}" Width="100" Height="100" >
</Canvas>
<Image HorizontalAlignment="Center"
Source="add.png"
Stretch="Fill"
Width="16"
VerticalAlignment="Top"
Margin="0,0,2,2"
Height="16" MouseLeftButtonDown="Image_MouseLeftButtonDown">
</Image>
</Grid>
我删除了Resources
部分,因为视图不应该创建新的视图模型对象,它应该使用现有的DataContext。您可以看到矩形的背景颜色基于视图模型的属性NodeColor
。
我们将此属性添加到视图模型中:
public class NodeViewModel : INotifyPropertyChanged
{
private SolidColorBrush _nodeColor;
public SolidColorBrush NodeColor
{
get { return _nodeColor; }
set
{
_nodeColor = value;
NotifyChange("NodeColor");
}
}
//...
现在,如果要显示3个不同颜色的控件,则应创建3个具有不同属性的视图模型。以下是红色,蓝色和绿色视图模型的示例:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var redBrush = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0));
var greenBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
var blueBrush = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255));
this.DataContext = new MainViewModel
{
Nodes = new ObservableCollection<NodeViewModel>{
new NodeViewModel
{
NodeColor = redBrush,
Children = new ObservableCollection<NodeViewModel>{
new NodeViewModel { NodeColor = greenBrush, LeftOffset = 65, TopOffset = 10},
new NodeViewModel { NodeColor = greenBrush, LeftOffset = 55, TopOffset = 60}
}
}, //red
new NodeViewModel { NodeColor = greenBrush}, //green
new NodeViewModel { NodeColor = blueBrush} //blue
}
};
}
}
public class MainViewModel
{
public ObservableCollection<NodeViewModel> Nodes { get; set; }
}
使用数据模板将视图模型转换为视图:
<ListBox ItemsSource="{Binding Nodes}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:NodePicture DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我没有使用Children属性,因为我不知道在哪里使用它。也许子节点显示在画布上。无论如何,如果它很重要 - 你可以提供额外的信息,我会帮助你。
<强>更新强>
在画布上绘制子项的最简单方法是添加依赖项属性,该属性在更新集合时更新画布:
public partial class NodePicture : UserControl
{
public NodePicture()
{
InitializeComponent();
}
public IEnumerable<NodeViewModel> ChildViewModels
{
get { return (IEnumerable<NodeViewModel>)GetValue(ChildViewModelsProperty); }
set { SetValue(ChildViewModelsProperty, value); }
}
public static readonly DependencyProperty ChildViewModelsProperty =
DependencyProperty.Register("ChildViewModels", typeof(IEnumerable<NodeViewModel>), typeof(NodePicture),
new PropertyMetadata(null, (s, e) => ((NodePicture)s).UpdateCanvas()));
private void UpdateCanvas()
{
this.ParentCanvas.Children.Clear();
var items = this.ChildViewModels;
if(items == null)
return;
var controls = items.Select(item=>
{
var e = new Ellipse{Width = 20, Height = 20};
e.Fill = item.NodeColor;
//Or using the data binding
//BindingOperations.SetBinding(e, Ellipse.FillProperty, new Binding("NodeColor") { Source = item });
Canvas.SetLeft(e, item.LeftOffset);
Canvas.SetTop(e, item.TopOffset);
return e;
});
foreach(var c in controls)
this.ParentCanvas.Children.Add(c);
}
TopOffset和LeftOffset是NodeViewModel类的属性。 之后,您应该在xaml代码中设置此属性:
<DataTemplate>
<local:NodePicture DataContext="{Binding}" ChildViewModels="{Binding Children}" />
</DataTemplate>
它不适用于ObservableColelction
类,因为我没有处理CollectionChanged
事件。
另一种方法 - 将ListBox
控件与自定义ItemsPanelTemplate
和ListBoxItem ControlTemplate
一起使用。但这是一个更复杂的解决方案。