【问题标题】:How can I combine some UserControls in SilverLight?如何在 SilverLight 中组合一些用户控件?
【发布时间】:2011-09-28 05:53:26
【问题描述】:

也许这是一个简单的问题,但我找不到答案。 我有三个仅颜色不同的用户控件。其中有一个代码:

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

我怎样才能使用这个控件集合的元素?

有没有一种简单(或正确)的方式来做到这一点?

【问题讨论】:

  • 不清楚你在追求什么。您说您在上面设置了三个用户控件DataContext="{Binding Source={StaticResource Children}, Path=Children}",并且您想将这三个组合到Chilren 集合中? “控件集合”是什么意思?
  • 是的,我想将这三个组合到儿童系列 @bitbonk
  • 你不能因为它是NodeViewModel 实例的集合而不是UserControl 实例的集合。
  • 在决定如何实现此控件之前,我需要知道以下两个问题的答案:1) 可以向视图模型添加其他属性吗? 2) 这三个用户控件在 xaml 代码中有何不同?如果将它们声明为&lt;local:NodePicture NodePictureColor="Yellow" /&gt; 之类的名称会更容易。
  • 1) 是的,您可以添加其他属性。 2)正如我在本页顶部所说:“它们仅在颜色上有所不同”。 @漩涡

标签: c# asp.net silverlight silverlight-4.0 silverlight-3.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 控件与自定义ItemsPanelTemplateListBoxItem ControlTemplate 一起使用。但这是更复杂的解决方案。

【讨论】:

  • 感谢您对这个问题的关注。我想补充一下我的问题:正如您所说的那样,子节点应该显示在主页的画布上。如果可能的话,你能附上你的答案吗)@vorrtex
  • @revolutionkpi 不在 MainPage 上,而是在 NodePicture 控件的画布上。我更改了一些课程,并在答案末尾添加了Update 部分。
猜你喜欢
  • 1970-01-01
  • 2011-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 2011-05-21
相关资源
最近更新 更多